From 37261a904ce2fbd4137180500c57f75f29945828 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Mon, 27 Apr 2020 17:51:01 +1200 Subject: Print 0 when `end` and `offset` is 0, and also simplify the suggestion --- tests/ui/manual_memcpy.stderr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/ui/manual_memcpy.stderr b/tests/ui/manual_memcpy.stderr index 3dbb2155d4d..ec80f6070d6 100644 --- a/tests/ui/manual_memcpy.stderr +++ b/tests/ui/manual_memcpy.stderr @@ -58,13 +58,13 @@ error: it looks like you're manually copying between slices --> $DIR/manual_memcpy.rs:94:14 | LL | for i in from..from + src.len() { - | ^^^^^^^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..from + src.len()].clone_from_slice(&src[0..(from + src.len() - from)])` + | ^^^^^^^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..from + src.len()].clone_from_slice(&src[..(from + src.len() - from)])` error: it looks like you're manually copying between slices --> $DIR/manual_memcpy.rs:98:14 | LL | for i in from..from + 3 { - | ^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..from + 3].clone_from_slice(&src[0..(from + 3 - from)])` + | ^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..from + 3].clone_from_slice(&src[..(from + 3 - from)])` error: it looks like you're manually copying between slices --> $DIR/manual_memcpy.rs:105:14 -- cgit 1.4.1-3-g733a5 From 75ad839cd26c1da17fe6ba3aae1153ee96de26c8 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Mon, 27 Apr 2020 18:04:37 +1200 Subject: Do not trigger `manual_memcpy` for `RangeTo` --- clippy_lints/src/loops.rs | 50 ++++++++++++++++++++----------------------- tests/ui/manual_memcpy.rs | 5 +++++ tests/ui/manual_memcpy.stderr | 2 +- 3 files changed, 29 insertions(+), 28 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 6b5a8498dc9..ca61c97e3e3 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -951,7 +951,7 @@ fn detect_manual_memcpy<'a, 'tcx>( ) { if let Some(higher::Range { start: Some(start), - ref end, + end: Some(end), limits, }) = higher::range(cx, arg) { @@ -990,35 +990,31 @@ fn detect_manual_memcpy<'a, 'tcx>( } }; - let print_limit = |end: &Option<&Expr<'_>>, offset: Offset, var_name: &str| { - if let Some(end) = *end { - if_chain! { - if let ExprKind::MethodCall(ref method, _, ref len_args) = end.kind; - if method.ident.name == sym!(len); - if len_args.len() == 1; - if let Some(arg) = len_args.get(0); - if snippet(cx, arg.span, "??") == var_name; - then { - return if offset.negate { - format!("({} - {})", snippet(cx, end.span, ".len()"), offset.value) - } else { - String::new() - }; - } + let print_limit = |end: &Expr<'_>, offset: Offset, var_name: &str| { + if_chain! { + if let ExprKind::MethodCall(ref method, _, ref len_args) = end.kind; + if method.ident.name == sym!(len); + if len_args.len() == 1; + if let Some(arg) = len_args.get(0); + if snippet(cx, arg.span, "??") == var_name; + then { + return if offset.negate { + format!("({} - {})", snippet(cx, end.span, ".len()"), offset.value) + } else { + String::new() + }; } + } - let end_str = match limits { - ast::RangeLimits::Closed => { - let end = sugg::Sugg::hir(cx, end, ""); - format!("{}", end + sugg::ONE) - }, - ast::RangeLimits::HalfOpen => format!("{}", snippet(cx, end.span, "..")), - }; + let end_str = match limits { + ast::RangeLimits::Closed => { + let end = sugg::Sugg::hir(cx, end, ""); + format!("{}", end + sugg::ONE) + }, + ast::RangeLimits::HalfOpen => format!("{}", snippet(cx, end.span, "..")), + }; - print_sum(&Offset::positive(end_str), &offset) - } else { - "..".into() - } + print_sum(&Offset::positive(end_str), &offset) }; // The only statements in the for loops can be indexed assignments from diff --git a/tests/ui/manual_memcpy.rs b/tests/ui/manual_memcpy.rs index aa347288875..1f41838fa16 100644 --- a/tests/ui/manual_memcpy.rs +++ b/tests/ui/manual_memcpy.rs @@ -98,6 +98,11 @@ pub fn manual_copy(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { for i in from..from + 3 { dst[i] = src[i - from]; } + + // `RangeTo` `for` loop - don't trigger lint + for i in 0.. { + dst[i] = src[i]; + } } #[warn(clippy::needless_range_loop, clippy::manual_memcpy)] diff --git a/tests/ui/manual_memcpy.stderr b/tests/ui/manual_memcpy.stderr index ec80f6070d6..95114c46f36 100644 --- a/tests/ui/manual_memcpy.stderr +++ b/tests/ui/manual_memcpy.stderr @@ -67,7 +67,7 @@ LL | for i in from..from + 3 { | ^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..from + 3].clone_from_slice(&src[..(from + 3 - from)])` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:105:14 + --> $DIR/manual_memcpy.rs:110:14 | LL | for i in 0..src.len() { | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..])` -- cgit 1.4.1-3-g733a5 From 461f4a34660691675434a318ac4fd61a83444428 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Thu, 30 Apr 2020 17:32:37 +1200 Subject: Add missing tests --- tests/ui/manual_memcpy.rs | 10 ++++++++++ tests/ui/manual_memcpy.stderr | 16 ++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/ui/manual_memcpy.rs b/tests/ui/manual_memcpy.rs index 1f41838fa16..9c24d6d4db1 100644 --- a/tests/ui/manual_memcpy.rs +++ b/tests/ui/manual_memcpy.rs @@ -99,6 +99,16 @@ pub fn manual_copy(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { dst[i] = src[i - from]; } + #[allow(clippy::identity_op)] + for i in 0..5 { + dst[i - 0] = src[i]; + } + + #[allow(clippy::reverse_range_loop)] + for i in 0..0 { + dst[i] = src[i]; + } + // `RangeTo` `for` loop - don't trigger lint for i in 0.. { dst[i] = src[i]; diff --git a/tests/ui/manual_memcpy.stderr b/tests/ui/manual_memcpy.stderr index 95114c46f36..bad84a58900 100644 --- a/tests/ui/manual_memcpy.stderr +++ b/tests/ui/manual_memcpy.stderr @@ -67,10 +67,22 @@ LL | for i in from..from + 3 { | ^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..from + 3].clone_from_slice(&src[..(from + 3 - from)])` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:110:14 + --> $DIR/manual_memcpy.rs:103:14 + | +LL | for i in 0..5 { + | ^^^^ help: try replacing the loop by: `dst[..5].clone_from_slice(&src[..5])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:108:14 + | +LL | for i in 0..0 { + | ^^^^ help: try replacing the loop by: `dst[..0].clone_from_slice(&src[..0])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:120:14 | LL | for i in 0..src.len() { | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..])` -error: aborting due to 11 previous errors +error: aborting due to 13 previous errors -- cgit 1.4.1-3-g733a5 From f072ded3bf6286668ff8eade5b58e471dbe66f2a Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 1 May 2020 01:44:17 +0200 Subject: Implement the manual_non_exhaustive lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 + clippy_lints/src/manual_non_exhaustive.rs | 167 ++++++++++++++++++++++++++++++ src/lintlist/mod.rs | 7 ++ tests/ui/manual_non_exhaustive.rs | 124 ++++++++++++++++++++++ tests/ui/manual_non_exhaustive.stderr | 48 +++++++++ 6 files changed, 352 insertions(+) create mode 100644 clippy_lints/src/manual_non_exhaustive.rs create mode 100644 tests/ui/manual_non_exhaustive.rs create mode 100644 tests/ui/manual_non_exhaustive.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 575773580c0..facf363e371 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1423,6 +1423,7 @@ Released 2018-09-13 [`macro_use_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_use_imports [`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion [`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy +[`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive [`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic [`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap [`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c995be5edc2..64f3a8a0ebb 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -247,6 +247,7 @@ mod literal_representation; mod loops; mod macro_use; mod main_recursion; +mod manual_non_exhaustive; mod map_clone; mod map_unit_fn; mod match_on_vec_items; @@ -628,6 +629,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &loops::WHILE_LET_ON_ITERATOR, ¯o_use::MACRO_USE_IMPORTS, &main_recursion::MAIN_RECURSION, + &manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, &map_clone::MAP_CLONE, &map_unit_fn::OPTION_MAP_UNIT_FN, &map_unit_fn::RESULT_MAP_UNIT_FN, @@ -1064,6 +1066,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls); store.register_late_pass(|| box if_let_mutex::IfLetMutex); store.register_late_pass(|| box match_on_vec_items::MatchOnVecItems); + store.register_early_pass(|| box manual_non_exhaustive::ManualNonExhaustive); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1280,6 +1283,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::WHILE_LET_LOOP), LintId::of(&loops::WHILE_LET_ON_ITERATOR), LintId::of(&main_recursion::MAIN_RECURSION), + LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(&map_clone::MAP_CLONE), LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN), LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN), @@ -1474,6 +1478,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::NEEDLESS_RANGE_LOOP), LintId::of(&loops::WHILE_LET_ON_ITERATOR), LintId::of(&main_recursion::MAIN_RECURSION), + LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(&map_clone::MAP_CLONE), LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH), LintId::of(&matches::MATCH_OVERLAPPING_ARM), diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs new file mode 100644 index 00000000000..ca2a2cf2e1e --- /dev/null +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -0,0 +1,167 @@ +use crate::utils::{snippet_opt, span_lint_and_then}; +use if_chain::if_chain; +use rustc_ast::ast::{Attribute, Ident, Item, ItemKind, StructField, TyKind, Variant, VariantData, VisibilityKind}; +use rustc_attr as attr; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for manual implementations of the non-exhaustive pattern. + /// + /// **Why is this bad?** Using the #[non_exhaustive] attribute expresses better the intent + /// and allows possible optimizations when applied to enums. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// struct S { + /// pub a: i32, + /// pub b: i32, + /// _c: (), + /// } + /// + /// enum E { + /// A, + /// B, + /// #[doc(hidden)] + /// _C, + /// } + /// + /// struct T(pub i32, pub i32, ()); + /// ``` + /// Use instead: + /// ```rust + /// #[non_exhaustive] + /// struct S { + /// pub a: i32, + /// pub b: i32, + /// } + /// + /// #[non_exhaustive] + /// enum E { + /// A, + /// B, + /// } + /// + /// #[non_exhaustive] + /// struct T(pub i32, pub i32); + /// ``` + pub MANUAL_NON_EXHAUSTIVE, + style, + "manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]" +} + +declare_lint_pass!(ManualNonExhaustive => [MANUAL_NON_EXHAUSTIVE]); + +impl EarlyLintPass for ManualNonExhaustive { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + match &item.kind { + ItemKind::Enum(def, _) => { + check_manual_non_exhaustive_enum(cx, item, &def.variants); + }, + ItemKind::Struct(variant_data, _) => { + if let VariantData::Unit(..) = variant_data { + return; + } + + check_manual_non_exhaustive_struct(cx, item, variant_data); + }, + _ => {}, + } + } +} + +fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants: &[Variant]) { + fn is_non_exhaustive_marker(variant: &Variant) -> bool { + matches!(variant.data, VariantData::Unit(_)) + && variant.ident.as_str().starts_with('_') + && variant.attrs.iter().any(|a| is_doc_hidden(a)) + } + + fn is_doc_hidden(attr: &Attribute) -> bool { + attr.check_name(sym!(doc)) + && match attr.meta_item_list() { + Some(l) => attr::list_contains_name(&l, sym!(hidden)), + None => false, + } + } + + if_chain! { + if !attr::contains_name(&item.attrs, sym!(non_exhaustive)); + let markers = variants.iter().filter(|v| is_non_exhaustive_marker(v)).count(); + if markers == 1 && variants.len() > markers; + if let Some(variant) = variants.last(); + if is_non_exhaustive_marker(variant); + then { + span_lint_and_then( + cx, + MANUAL_NON_EXHAUSTIVE, + item.span, + "this seems like a manual implementation of the non-exhaustive pattern", + |diag| { + let header_span = cx.sess.source_map().span_until_char(item.span, '{'); + + if let Some(snippet) = snippet_opt(cx, header_span) { + diag.span_suggestion( + item.span, + "add the attribute", + format!("#[non_exhaustive] {}", snippet), + Applicability::Unspecified, + ); + diag.span_help(variant.span, "and remove this variant"); + } + }); + } + } +} + +fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data: &VariantData) { + fn is_private(field: &StructField) -> bool { + matches!(field.vis.node, VisibilityKind::Inherited) + } + + fn is_non_exhaustive_marker(name: &Option) -> bool { + name.map(|n| n.as_str().starts_with('_')).unwrap_or(true) + } + + let fields = data.fields(); + let private_fields = fields.iter().filter(|f| is_private(f)).count(); + let public_fields = fields.iter().filter(|f| f.vis.node.is_pub()).count(); + + if_chain! { + if !attr::contains_name(&item.attrs, sym!(non_exhaustive)); + if private_fields == 1 && public_fields >= private_fields && public_fields == fields.len() - 1; + if let Some(field) = fields.iter().find(|f| is_private(f)); + if is_non_exhaustive_marker(&field.ident); + if let TyKind::Tup(tup_fields) = &field.ty.kind; + if tup_fields.is_empty(); + then { + span_lint_and_then( + cx, + MANUAL_NON_EXHAUSTIVE, + item.span, + "this seems like a manual implementation of the non-exhaustive pattern", + |diag| { + let delimiter = match data { + VariantData::Struct(..) => '{', + VariantData::Tuple(..) => '(', + _ => unreachable!(), + }; + let header_span = cx.sess.source_map().span_until_char(item.span, delimiter); + + if let Some(snippet) = snippet_opt(cx, header_span) { + diag.span_suggestion( + item.span, + "add the attribute", + format!("#[non_exhaustive] {}", snippet), + Applicability::Unspecified, + ); + diag.span_help(field.span, "and remove this field"); + } + }); + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 72675c25175..c5360002fa0 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1088,6 +1088,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "loops", }, + Lint { + name: "manual_non_exhaustive", + group: "style", + desc: "manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]", + deprecation: None, + module: "manual_non_exhaustive", + }, Lint { name: "manual_saturating_arithmetic", group: "style", diff --git a/tests/ui/manual_non_exhaustive.rs b/tests/ui/manual_non_exhaustive.rs new file mode 100644 index 00000000000..9c239db6e00 --- /dev/null +++ b/tests/ui/manual_non_exhaustive.rs @@ -0,0 +1,124 @@ +#![warn(clippy::manual_non_exhaustive)] +#![allow(unused)] + +mod enums { + enum E { + A, + B, + #[doc(hidden)] + _C, + } + + // last variant does not have doc hidden attribute, should be ignored + enum NoDocHidden { + A, + B, + _C, + } + + // name of variant with doc hidden does not start with underscore, should be ignored + enum NoUnderscore { + A, + B, + #[doc(hidden)] + C, + } + + // variant with doc hidden is not unit, should be ignored + enum NotUnit { + A, + B, + #[doc(hidden)] + _C(bool), + } + + // variant with doc hidden is not the last one, should be ignored + enum NotLast { + A, + #[doc(hidden)] + _B, + C, + } + + // variant with doc hidden is the only one, should be ignored + enum OnlyMarker { + #[doc(hidden)] + _A, + } + + // variant with multiple non-exhaustive "markers", should be ignored + enum MultipleMarkers { + A, + #[doc(hidden)] + _B, + #[doc(hidden)] + _C, + } + + // already non_exhaustive, should be ignored + #[non_exhaustive] + enum NonExhaustive { + A, + B, + } +} + +mod structs { + struct S { + pub a: i32, + pub b: i32, + _c: (), + } + + // some other fields are private, should be ignored + struct PrivateFields { + a: i32, + pub b: i32, + _c: (), + } + + // private field name does not start with underscore, should be ignored + struct NoUnderscore { + pub a: i32, + pub b: i32, + c: (), + } + + // private field is not unit type, should be ignored + struct NotUnit { + pub a: i32, + pub b: i32, + _c: i32, + } + + // private field is the only field, should be ignored + struct OnlyMarker { + _a: (), + } + + // already non exhaustive, should be ignored + #[non_exhaustive] + struct NonExhaustive { + pub a: i32, + pub b: i32, + } +} + +mod tuple_structs { + struct T(pub i32, pub i32, ()); + + // some other fields are private, should be ignored + struct PrivateFields(pub i32, i32, ()); + + // private field is not unit type, should be ignored + struct NotUnit(pub i32, pub i32, i32); + + // private field is the only field, should be ignored + struct OnlyMarker(()); + + // already non exhaustive, should be ignored + #[non_exhaustive] + struct NonExhaustive(pub i32, pub i32); +} + +fn main() {} diff --git a/tests/ui/manual_non_exhaustive.stderr b/tests/ui/manual_non_exhaustive.stderr new file mode 100644 index 00000000000..d6719bca0d4 --- /dev/null +++ b/tests/ui/manual_non_exhaustive.stderr @@ -0,0 +1,48 @@ +error: this seems like a manual implementation of the non-exhaustive pattern + --> $DIR/manual_non_exhaustive.rs:5:5 + | +LL | / enum E { +LL | | A, +LL | | B, +LL | | #[doc(hidden)] +LL | | _C, +LL | | } + | |_____^ help: add the attribute: `#[non_exhaustive] enum E` + | + = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings` +help: and remove this variant + --> $DIR/manual_non_exhaustive.rs:9:9 + | +LL | _C, + | ^^ + +error: this seems like a manual implementation of the non-exhaustive pattern + --> $DIR/manual_non_exhaustive.rs:67:5 + | +LL | / struct S { +LL | | pub a: i32, +LL | | pub b: i32, +LL | | _c: (), +LL | | } + | |_____^ help: add the attribute: `#[non_exhaustive] struct S` + | +help: and remove this field + --> $DIR/manual_non_exhaustive.rs:70:9 + | +LL | _c: (), + | ^^^^^^ + +error: this seems like a manual implementation of the non-exhaustive pattern + --> $DIR/manual_non_exhaustive.rs:108:5 + | +LL | struct T(pub i32, pub i32, ()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[non_exhaustive] struct T` + | +help: and remove this field + --> $DIR/manual_non_exhaustive.rs:108:32 + | +LL | struct T(pub i32, pub i32, ()); + | ^^ + +error: aborting due to 3 previous errors + -- cgit 1.4.1-3-g733a5 From 42b0b4754c881101cefb0307c489d6159c19b2f3 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 1 May 2020 22:37:14 +0200 Subject: Apply suggestions from PR review --- clippy_lints/src/manual_non_exhaustive.rs | 84 +++++++++++++++++-------------- tests/ui/manual_non_exhaustive.rs | 39 +++++++++----- tests/ui/manual_non_exhaustive.stderr | 81 ++++++++++++++++++++++++----- 3 files changed, 139 insertions(+), 65 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index ca2a2cf2e1e..a4273da1d74 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -1,10 +1,11 @@ use crate::utils::{snippet_opt, span_lint_and_then}; use if_chain::if_chain; -use rustc_ast::ast::{Attribute, Ident, Item, ItemKind, StructField, TyKind, Variant, VariantData, VisibilityKind}; +use rustc_ast::ast::{Attribute, Item, ItemKind, StructField, Variant, VariantData, VisibilityKind}; use rustc_attr as attr; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; declare_clippy_lint! { /// **What it does:** Checks for manual implementations of the non-exhaustive pattern. @@ -90,11 +91,9 @@ fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants } if_chain! { - if !attr::contains_name(&item.attrs, sym!(non_exhaustive)); - let markers = variants.iter().filter(|v| is_non_exhaustive_marker(v)).count(); - if markers == 1 && variants.len() > markers; - if let Some(variant) = variants.last(); - if is_non_exhaustive_marker(variant); + let mut markers = variants.iter().filter(|v| is_non_exhaustive_marker(v)); + if let Some(marker) = markers.next(); + if markers.count() == 0 && variants.len() > 1; then { span_lint_and_then( cx, @@ -102,17 +101,20 @@ fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants item.span, "this seems like a manual implementation of the non-exhaustive pattern", |diag| { - let header_span = cx.sess.source_map().span_until_char(item.span, '{'); - - if let Some(snippet) = snippet_opt(cx, header_span) { - diag.span_suggestion( - item.span, - "add the attribute", - format!("#[non_exhaustive] {}", snippet), - Applicability::Unspecified, - ); - diag.span_help(variant.span, "and remove this variant"); + if_chain! { + if !attr::contains_name(&item.attrs, sym!(non_exhaustive)); + let header_span = cx.sess.source_map().span_until_char(item.span, '{'); + if let Some(snippet) = snippet_opt(cx, header_span); + then { + diag.span_suggestion( + header_span, + "add the attribute", + format!("#[non_exhaustive] {}", snippet), + Applicability::Unspecified, + ); + } } + diag.span_help(marker.span, "remove this variant"); }); } } @@ -123,8 +125,18 @@ fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data: matches!(field.vis.node, VisibilityKind::Inherited) } - fn is_non_exhaustive_marker(name: &Option) -> bool { - name.map(|n| n.as_str().starts_with('_')).unwrap_or(true) + fn is_non_exhaustive_marker(field: &StructField) -> bool { + is_private(field) && field.ty.kind.is_unit() && field.ident.map_or(true, |n| n.as_str().starts_with('_')) + } + + fn find_header_span(cx: &EarlyContext<'_>, item: &Item, data: &VariantData) -> Span { + let delimiter = match data { + VariantData::Struct(..) => '{', + VariantData::Tuple(..) => '(', + _ => unreachable!("`VariantData::Unit` is already handled above"), + }; + + cx.sess.source_map().span_until_char(item.span, delimiter) } let fields = data.fields(); @@ -132,12 +144,8 @@ fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data: let public_fields = fields.iter().filter(|f| f.vis.node.is_pub()).count(); if_chain! { - if !attr::contains_name(&item.attrs, sym!(non_exhaustive)); - if private_fields == 1 && public_fields >= private_fields && public_fields == fields.len() - 1; - if let Some(field) = fields.iter().find(|f| is_private(f)); - if is_non_exhaustive_marker(&field.ident); - if let TyKind::Tup(tup_fields) = &field.ty.kind; - if tup_fields.is_empty(); + if private_fields == 1 && public_fields >= 1 && public_fields == fields.len() - 1; + if let Some(marker) = fields.iter().find(|f| is_non_exhaustive_marker(f)); then { span_lint_and_then( cx, @@ -145,22 +153,20 @@ fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data: item.span, "this seems like a manual implementation of the non-exhaustive pattern", |diag| { - let delimiter = match data { - VariantData::Struct(..) => '{', - VariantData::Tuple(..) => '(', - _ => unreachable!(), - }; - let header_span = cx.sess.source_map().span_until_char(item.span, delimiter); - - if let Some(snippet) = snippet_opt(cx, header_span) { - diag.span_suggestion( - item.span, - "add the attribute", - format!("#[non_exhaustive] {}", snippet), - Applicability::Unspecified, - ); - diag.span_help(field.span, "and remove this field"); + if_chain! { + if !attr::contains_name(&item.attrs, sym!(non_exhaustive)); + let header_span = find_header_span(cx, item, data); + if let Some(snippet) = snippet_opt(cx, header_span); + then { + diag.span_suggestion( + header_span, + "add the attribute", + format!("#[non_exhaustive] {}", snippet), + Applicability::Unspecified, + ); + } } + diag.span_help(marker.span, "remove this field"); }); } } diff --git a/tests/ui/manual_non_exhaustive.rs b/tests/ui/manual_non_exhaustive.rs index 9c239db6e00..7a788f48520 100644 --- a/tests/ui/manual_non_exhaustive.rs +++ b/tests/ui/manual_non_exhaustive.rs @@ -9,7 +9,16 @@ mod enums { _C, } - // last variant does not have doc hidden attribute, should be ignored + // user forgot to remove the marker + #[non_exhaustive] + enum Ep { + A, + B, + #[doc(hidden)] + _C, + } + + // marker variant does not have doc hidden attribute, should be ignored enum NoDocHidden { A, B, @@ -32,21 +41,13 @@ mod enums { _C(bool), } - // variant with doc hidden is not the last one, should be ignored - enum NotLast { - A, - #[doc(hidden)] - _B, - C, - } - // variant with doc hidden is the only one, should be ignored enum OnlyMarker { #[doc(hidden)] _A, } - // variant with multiple non-exhaustive "markers", should be ignored + // variant with multiple markers, should be ignored enum MultipleMarkers { A, #[doc(hidden)] @@ -55,7 +56,7 @@ mod enums { _C, } - // already non_exhaustive, should be ignored + // already non_exhaustive and no markers, should be ignored #[non_exhaustive] enum NonExhaustive { A, @@ -70,6 +71,14 @@ mod structs { _c: (), } + // user forgot to remove the private field + #[non_exhaustive] + struct Sp { + pub a: i32, + pub b: i32, + _c: (), + } + // some other fields are private, should be ignored struct PrivateFields { a: i32, @@ -96,7 +105,7 @@ mod structs { _a: (), } - // already non exhaustive, should be ignored + // already non exhaustive and no private fields, should be ignored #[non_exhaustive] struct NonExhaustive { pub a: i32, @@ -107,6 +116,10 @@ mod structs { mod tuple_structs { struct T(pub i32, pub i32, ()); + // user forgot to remove the private field + #[non_exhaustive] + struct Tp(pub i32, pub i32, ()); + // some other fields are private, should be ignored struct PrivateFields(pub i32, i32, ()); @@ -116,7 +129,7 @@ mod tuple_structs { // private field is the only field, should be ignored struct OnlyMarker(()); - // already non exhaustive, should be ignored + // already non exhaustive and no private fields, should be ignored #[non_exhaustive] struct NonExhaustive(pub i32, pub i32); } diff --git a/tests/ui/manual_non_exhaustive.stderr b/tests/ui/manual_non_exhaustive.stderr index d6719bca0d4..613c5e8ca1d 100644 --- a/tests/ui/manual_non_exhaustive.stderr +++ b/tests/ui/manual_non_exhaustive.stderr @@ -1,48 +1,103 @@ error: this seems like a manual implementation of the non-exhaustive pattern --> $DIR/manual_non_exhaustive.rs:5:5 | -LL | / enum E { +LL | enum E { + | ^----- + | | + | _____help: add the attribute: `#[non_exhaustive] enum E` + | | LL | | A, LL | | B, LL | | #[doc(hidden)] LL | | _C, LL | | } - | |_____^ help: add the attribute: `#[non_exhaustive] enum E` + | |_____^ | = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings` -help: and remove this variant +help: remove this variant --> $DIR/manual_non_exhaustive.rs:9:9 | LL | _C, | ^^ error: this seems like a manual implementation of the non-exhaustive pattern - --> $DIR/manual_non_exhaustive.rs:67:5 + --> $DIR/manual_non_exhaustive.rs:14:5 | -LL | / struct S { +LL | / enum Ep { +LL | | A, +LL | | B, +LL | | #[doc(hidden)] +LL | | _C, +LL | | } + | |_____^ + | +help: remove this variant + --> $DIR/manual_non_exhaustive.rs:18:9 + | +LL | _C, + | ^^ + +error: this seems like a manual implementation of the non-exhaustive pattern + --> $DIR/manual_non_exhaustive.rs:68:5 + | +LL | struct S { + | ^------- + | | + | _____help: add the attribute: `#[non_exhaustive] struct S` + | | +LL | | pub a: i32, +LL | | pub b: i32, +LL | | _c: (), +LL | | } + | |_____^ + | +help: remove this field + --> $DIR/manual_non_exhaustive.rs:71:9 + | +LL | _c: (), + | ^^^^^^ + +error: this seems like a manual implementation of the non-exhaustive pattern + --> $DIR/manual_non_exhaustive.rs:76:5 + | +LL | / struct Sp { LL | | pub a: i32, LL | | pub b: i32, LL | | _c: (), LL | | } - | |_____^ help: add the attribute: `#[non_exhaustive] struct S` + | |_____^ | -help: and remove this field - --> $DIR/manual_non_exhaustive.rs:70:9 +help: remove this field + --> $DIR/manual_non_exhaustive.rs:79:9 | LL | _c: (), | ^^^^^^ error: this seems like a manual implementation of the non-exhaustive pattern - --> $DIR/manual_non_exhaustive.rs:108:5 + --> $DIR/manual_non_exhaustive.rs:117:5 | LL | struct T(pub i32, pub i32, ()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[non_exhaustive] struct T` + | --------^^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: add the attribute: `#[non_exhaustive] struct T` | -help: and remove this field - --> $DIR/manual_non_exhaustive.rs:108:32 +help: remove this field + --> $DIR/manual_non_exhaustive.rs:117:32 | LL | struct T(pub i32, pub i32, ()); | ^^ -error: aborting due to 3 previous errors +error: this seems like a manual implementation of the non-exhaustive pattern + --> $DIR/manual_non_exhaustive.rs:121:5 + | +LL | struct Tp(pub i32, pub i32, ()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove this field + --> $DIR/manual_non_exhaustive.rs:121:33 + | +LL | struct Tp(pub i32, pub i32, ()); + | ^^ + +error: aborting due to 6 previous errors -- cgit 1.4.1-3-g733a5 From 72ce6d5be9c54775b847bc0641f8d909b2977126 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Fri, 24 Apr 2020 16:46:56 +0200 Subject: Fix unwrap lint when checks are done in parameters --- clippy_lints/src/unwrap.rs | 21 ++++++++++++----- tests/ui/checked_unwrap/simple_conditionals.rs | 27 ++++++++++++++++++++++ tests/ui/checked_unwrap/simple_conditionals.stderr | 24 +++++++++---------- 3 files changed, 54 insertions(+), 18 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index 5235c98efab..f3844c7d3b6 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -1,4 +1,7 @@ -use crate::utils::{higher::if_block, is_type_diagnostic_item, span_lint_and_then, usage::is_potentially_mutated}; +use crate::utils::{ + differing_macro_contexts, higher::if_block, is_type_diagnostic_item, span_lint_and_then, + usage::is_potentially_mutated, +}; use if_chain::if_chain; use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Path, QPath, UnOp}; @@ -73,6 +76,8 @@ struct UnwrapInfo<'tcx> { ident: &'tcx Path<'tcx>, /// The check, like `x.is_ok()` check: &'tcx Expr<'tcx>, + /// The branch where the check takes place, like `if x.is_ok() { .. }` + branch: &'tcx Expr<'tcx>, /// Whether `is_some()` or `is_ok()` was called (as opposed to `is_err()` or `is_none()`). safe_to_unwrap: bool, } @@ -82,19 +87,20 @@ struct UnwrapInfo<'tcx> { fn collect_unwrap_info<'a, 'tcx>( cx: &'a LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>, + branch: &'tcx Expr<'_>, invert: bool, ) -> Vec> { if let ExprKind::Binary(op, left, right) = &expr.kind { match (invert, op.node) { (false, BinOpKind::And) | (false, BinOpKind::BitAnd) | (true, BinOpKind::Or) | (true, BinOpKind::BitOr) => { - let mut unwrap_info = collect_unwrap_info(cx, left, invert); - unwrap_info.append(&mut collect_unwrap_info(cx, right, invert)); + let mut unwrap_info = collect_unwrap_info(cx, left, branch, invert); + unwrap_info.append(&mut collect_unwrap_info(cx, right, branch, invert)); return unwrap_info; }, _ => (), } } else if let ExprKind::Unary(UnOp::UnNot, expr) = &expr.kind { - return collect_unwrap_info(cx, expr, !invert); + return collect_unwrap_info(cx, expr, branch, !invert); } else { if_chain! { if let ExprKind::MethodCall(method_name, _, args) = &expr.kind; @@ -111,7 +117,7 @@ fn collect_unwrap_info<'a, 'tcx>( _ => unreachable!(), }; let safe_to_unwrap = unwrappable != invert; - return vec![UnwrapInfo { ident: path, check: expr, safe_to_unwrap }]; + return vec![UnwrapInfo { ident: path, check: expr, branch, safe_to_unwrap }]; } } } @@ -121,7 +127,7 @@ fn collect_unwrap_info<'a, 'tcx>( impl<'a, 'tcx> UnwrappableVariablesVisitor<'a, 'tcx> { fn visit_branch(&mut self, cond: &'tcx Expr<'_>, branch: &'tcx Expr<'_>, else_branch: bool) { let prev_len = self.unwrappables.len(); - for unwrap_info in collect_unwrap_info(self.cx, cond, else_branch) { + for unwrap_info in collect_unwrap_info(self.cx, cond, branch, else_branch) { if is_potentially_mutated(unwrap_info.ident, cond, self.cx) || is_potentially_mutated(unwrap_info.ident, branch, self.cx) { @@ -158,6 +164,9 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { let call_to_unwrap = method_name.ident.name == sym!(unwrap); if let Some(unwrappable) = self.unwrappables.iter() .find(|u| u.ident.res == path.res); + // Span contexts should not differ with the conditional branch + if !differing_macro_contexts(unwrappable.branch.span, expr.span); + if !differing_macro_contexts(unwrappable.branch.span, unwrappable.check.span); then { if call_to_unwrap == unwrappable.safe_to_unwrap { span_lint_and_then( diff --git a/tests/ui/checked_unwrap/simple_conditionals.rs b/tests/ui/checked_unwrap/simple_conditionals.rs index b0fc26ff76d..3e7b4b390ba 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.rs +++ b/tests/ui/checked_unwrap/simple_conditionals.rs @@ -9,6 +9,30 @@ macro_rules! m { }; } +macro_rules! checks_in_param { + ($a:expr, $b:expr) => { + if $a { + $b; + } + }; +} + +macro_rules! checks_unwrap { + ($a:expr, $b:expr) => { + if $a.is_some() { + $b; + } + }; +} + +macro_rules! checks_some { + ($a:expr, $b:expr) => { + if $a { + $b.unwrap(); + } + }; +} + fn main() { let x = Some(()); if x.is_some() { @@ -22,6 +46,9 @@ fn main() { x.unwrap(); // unnecessary } m!(x); + checks_in_param!(x.is_some(), x.unwrap()); // ok + checks_unwrap!(x, x.unwrap()); // ok + checks_some!(x.is_some(), x); // ok let mut x: Result<(), ()> = Ok(()); if x.is_ok() { x.unwrap(); // unnecessary diff --git a/tests/ui/checked_unwrap/simple_conditionals.stderr b/tests/ui/checked_unwrap/simple_conditionals.stderr index e40542e2e4f..4013d1ed667 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.stderr +++ b/tests/ui/checked_unwrap/simple_conditionals.stderr @@ -1,5 +1,5 @@ error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. - --> $DIR/simple_conditionals.rs:15:9 + --> $DIR/simple_conditionals.rs:39:9 | LL | if x.is_some() { | ----------- the check is happening here @@ -13,7 +13,7 @@ LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: This call to `unwrap()` will always panic. - --> $DIR/simple_conditionals.rs:17:9 + --> $DIR/simple_conditionals.rs:41:9 | LL | if x.is_some() { | ----------- because of this check @@ -28,7 +28,7 @@ LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] | ^^^^^^^^^^^^^^^^^^^^^^^^ error: This call to `unwrap()` will always panic. - --> $DIR/simple_conditionals.rs:20:9 + --> $DIR/simple_conditionals.rs:44:9 | LL | if x.is_none() { | ----------- because of this check @@ -36,7 +36,7 @@ LL | x.unwrap(); // will panic | ^^^^^^^^^^ error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. - --> $DIR/simple_conditionals.rs:22:9 + --> $DIR/simple_conditionals.rs:46:9 | LL | if x.is_none() { | ----------- the check is happening here @@ -58,7 +58,7 @@ LL | m!(x); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. - --> $DIR/simple_conditionals.rs:27:9 + --> $DIR/simple_conditionals.rs:54:9 | LL | if x.is_ok() { | --------- the check is happening here @@ -66,7 +66,7 @@ LL | x.unwrap(); // unnecessary | ^^^^^^^^^^ error: This call to `unwrap_err()` will always panic. - --> $DIR/simple_conditionals.rs:28:9 + --> $DIR/simple_conditionals.rs:55:9 | LL | if x.is_ok() { | --------- because of this check @@ -75,7 +75,7 @@ LL | x.unwrap_err(); // will panic | ^^^^^^^^^^^^^^ error: This call to `unwrap()` will always panic. - --> $DIR/simple_conditionals.rs:30:9 + --> $DIR/simple_conditionals.rs:57:9 | LL | if x.is_ok() { | --------- because of this check @@ -84,7 +84,7 @@ LL | x.unwrap(); // will panic | ^^^^^^^^^^ error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. - --> $DIR/simple_conditionals.rs:31:9 + --> $DIR/simple_conditionals.rs:58:9 | LL | if x.is_ok() { | --------- the check is happening here @@ -93,7 +93,7 @@ LL | x.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ error: This call to `unwrap()` will always panic. - --> $DIR/simple_conditionals.rs:34:9 + --> $DIR/simple_conditionals.rs:61:9 | LL | if x.is_err() { | ---------- because of this check @@ -101,7 +101,7 @@ LL | x.unwrap(); // will panic | ^^^^^^^^^^ error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. - --> $DIR/simple_conditionals.rs:35:9 + --> $DIR/simple_conditionals.rs:62:9 | LL | if x.is_err() { | ---------- the check is happening here @@ -110,7 +110,7 @@ LL | x.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. - --> $DIR/simple_conditionals.rs:37:9 + --> $DIR/simple_conditionals.rs:64:9 | LL | if x.is_err() { | ---------- the check is happening here @@ -119,7 +119,7 @@ LL | x.unwrap(); // unnecessary | ^^^^^^^^^^ error: This call to `unwrap_err()` will always panic. - --> $DIR/simple_conditionals.rs:38:9 + --> $DIR/simple_conditionals.rs:65:9 | LL | if x.is_err() { | ---------- because of this check -- cgit 1.4.1-3-g733a5 From d0c1f8ada2306801f2a6ce193e1f9f75471dbb3c Mon Sep 17 00:00:00 2001 From: Aleksei Latyshev Date: Sat, 2 May 2020 14:18:27 +0300 Subject: fix fp on while-let-on-iterator - fix `is_refutable` for slice patterns - fix `is_refutable` for bindings - add some TODO-s for cases, which can not be fixed easily --- clippy_lints/src/utils/mod.rs | 34 ++++++++++++-------- tests/ui/while_let_on_iterator.fixed | 58 +++++++++++++++++++++++++++++++++++ tests/ui/while_let_on_iterator.rs | 58 +++++++++++++++++++++++++++++++++++ tests/ui/while_let_on_iterator.stderr | 28 ++++++++++++++--- 4 files changed, 160 insertions(+), 18 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 04b4b423761..1c7b40fa908 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -933,6 +933,7 @@ pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_, '_>, expr: &Exp } /// Returns `true` if a pattern is refutable. +// TODO: should be implemented using rustc/mir_build/hair machinery pub fn is_refutable(cx: &LateContext<'_, '_>, pat: &Pat<'_>) -> bool { fn is_enum_variant(cx: &LateContext<'_, '_>, qpath: &QPath<'_>, id: HirId) -> bool { matches!( @@ -946,27 +947,34 @@ pub fn is_refutable(cx: &LateContext<'_, '_>, pat: &Pat<'_>) -> bool { } match pat.kind { - PatKind::Binding(..) | PatKind::Wild => false, + PatKind::Wild => false, + PatKind::Binding(_, _, _, pat) => pat.map_or(false, |pat| is_refutable(cx, pat)), PatKind::Box(ref pat) | PatKind::Ref(ref pat, _) => is_refutable(cx, pat), PatKind::Lit(..) | PatKind::Range(..) => true, PatKind::Path(ref qpath) => is_enum_variant(cx, qpath, pat.hir_id), - PatKind::Or(ref pats) | PatKind::Tuple(ref pats, _) => are_refutable(cx, pats.iter().map(|pat| &**pat)), + PatKind::Or(ref pats) => { + // TODO: should be the honest check, that pats is exhaustive set + are_refutable(cx, pats.iter().map(|pat| &**pat)) + }, + PatKind::Tuple(ref pats, _) => are_refutable(cx, pats.iter().map(|pat| &**pat)), PatKind::Struct(ref qpath, ref fields, _) => { - if is_enum_variant(cx, qpath, pat.hir_id) { - true - } else { - are_refutable(cx, fields.iter().map(|field| &*field.pat)) - } + is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| &*field.pat)) }, PatKind::TupleStruct(ref qpath, ref pats, _) => { - if is_enum_variant(cx, qpath, pat.hir_id) { - true - } else { - are_refutable(cx, pats.iter().map(|pat| &**pat)) - } + is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats.iter().map(|pat| &**pat)) }, PatKind::Slice(ref head, ref middle, ref tail) => { - are_refutable(cx, head.iter().chain(middle).chain(tail.iter()).map(|pat| &**pat)) + match &cx.tables.node_type(pat.hir_id).kind { + ty::Slice(..) => { + // [..] is the only irrefutable slice pattern. + !head.is_empty() || middle.is_none() || !tail.is_empty() + }, + ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter()).map(|pat| &**pat)), + _ => { + // unreachable!() + true + }, + } }, } } diff --git a/tests/ui/while_let_on_iterator.fixed b/tests/ui/while_let_on_iterator.fixed index f5fcabf63fd..e99c98ac79f 100644 --- a/tests/ui/while_let_on_iterator.fixed +++ b/tests/ui/while_let_on_iterator.fixed @@ -2,6 +2,7 @@ #![warn(clippy::while_let_on_iterator)] #![allow(clippy::never_loop, unreachable_code, unused_mut)] +#![feature(or_patterns)] fn base() { let mut iter = 1..20; @@ -77,6 +78,62 @@ fn refutable() { // */ } +fn refutable2() { + // Issue 3780 + { + let v = vec![1, 2, 3]; + let mut it = v.windows(2); + while let Some([x, y]) = it.next() { + println!("x: {}", x); + println!("y: {}", y); + } + + let mut it = v.windows(2); + while let Some([x, ..]) = it.next() { + println!("x: {}", x); + } + + let mut it = v.windows(2); + while let Some([.., y]) = it.next() { + println!("y: {}", y); + } + + let mut it = v.windows(2); + for [..] in it {} + + let v = vec![[1], [2], [3]]; + let mut it = v.iter(); + while let Some([1]) = it.next() {} + + let mut it = v.iter(); + for [_x] in it {} + } + + // binding + { + let v = vec![1, 2, 3]; + let mut it = v.iter(); + while let Some(x @ 1) = it.next() { + println!("{}", x); + } + + let v = vec![[1], [2], [3]]; + let mut it = v.iter(); + for x @ [_] in it { + println!("{:?}", x); + } + } + + // false negative + { + let v = vec![1, 2, 3]; + let mut it = v.iter().map(Some); + while let Some(Some(_) | None) = it.next() { + println!("1"); + } + } +} + fn nested_loops() { let a = [42, 1337]; let mut y = a.iter(); @@ -152,6 +209,7 @@ fn issue1654() { fn main() { base(); refutable(); + refutable2(); nested_loops(); issue1121(); issue2965(); diff --git a/tests/ui/while_let_on_iterator.rs b/tests/ui/while_let_on_iterator.rs index 04dce8a0289..ba13172428e 100644 --- a/tests/ui/while_let_on_iterator.rs +++ b/tests/ui/while_let_on_iterator.rs @@ -2,6 +2,7 @@ #![warn(clippy::while_let_on_iterator)] #![allow(clippy::never_loop, unreachable_code, unused_mut)] +#![feature(or_patterns)] fn base() { let mut iter = 1..20; @@ -77,6 +78,62 @@ fn refutable() { // */ } +fn refutable2() { + // Issue 3780 + { + let v = vec![1, 2, 3]; + let mut it = v.windows(2); + while let Some([x, y]) = it.next() { + println!("x: {}", x); + println!("y: {}", y); + } + + let mut it = v.windows(2); + while let Some([x, ..]) = it.next() { + println!("x: {}", x); + } + + let mut it = v.windows(2); + while let Some([.., y]) = it.next() { + println!("y: {}", y); + } + + let mut it = v.windows(2); + while let Some([..]) = it.next() {} + + let v = vec![[1], [2], [3]]; + let mut it = v.iter(); + while let Some([1]) = it.next() {} + + let mut it = v.iter(); + while let Some([_x]) = it.next() {} + } + + // binding + { + let v = vec![1, 2, 3]; + let mut it = v.iter(); + while let Some(x @ 1) = it.next() { + println!("{}", x); + } + + let v = vec![[1], [2], [3]]; + let mut it = v.iter(); + while let Some(x @ [_]) = it.next() { + println!("{:?}", x); + } + } + + // false negative + { + let v = vec![1, 2, 3]; + let mut it = v.iter().map(Some); + while let Some(Some(_) | None) = it.next() { + println!("1"); + } + } +} + fn nested_loops() { let a = [42, 1337]; let mut y = a.iter(); @@ -152,6 +209,7 @@ fn issue1654() { fn main() { base(); refutable(); + refutable2(); nested_loops(); issue1121(); issue2965(); diff --git a/tests/ui/while_let_on_iterator.stderr b/tests/ui/while_let_on_iterator.stderr index 6de138d7227..aa980d9965c 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:9:5 | LL | while let Option::Some(x) = iter.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in iter` @@ -7,22 +7,40 @@ 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:14: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:19: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:97:9 + --> $DIR/while_let_on_iterator.rs:102: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:109: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:122: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:154:9 | LL | while let Some(_) = y.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in y` -error: aborting due to 4 previous errors +error: aborting due to 7 previous errors -- cgit 1.4.1-3-g733a5 From e7138e06291d13163e8ab2a57fe2465451fac193 Mon Sep 17 00:00:00 2001 From: CrazyRoka Date: Sat, 2 May 2020 14:25:45 +0300 Subject: Fix match on vec items: match on vec[..] - Added new tests - Fixed false positive when matching on full range, which will never panic --- clippy_lints/src/match_on_vec_items.rs | 21 ++++++++++++++++----- tests/ui/match_on_vec_items.rs | 22 ++++++++++++++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/match_on_vec_items.rs b/clippy_lints/src/match_on_vec_items.rs index 421571d2f2f..236362130e5 100644 --- a/clippy_lints/src/match_on_vec_items.rs +++ b/clippy_lints/src/match_on_vec_items.rs @@ -1,4 +1,4 @@ -use crate::utils::{is_type_diagnostic_item, snippet, span_lint_and_sugg, walk_ptrs_ty}; +use crate::utils::{is_type_diagnostic_item, match_type, snippet, span_lint_and_sugg, walk_ptrs_ty}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, MatchSource}; @@ -75,10 +75,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MatchOnVecItems { fn is_vec_indexing<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { if_chain! { - if let ExprKind::Index(ref array, _) = expr.kind; - let ty = cx.tables.expr_ty(array); - let ty = walk_ptrs_ty(ty); - if is_type_diagnostic_item(cx, ty, sym!(vec_type)); + if let ExprKind::Index(ref array, ref index) = expr.kind; + if is_vector(cx, array); + if !is_full_range(cx, index); then { return Some(expr); @@ -87,3 +86,15 @@ fn is_vec_indexing<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'tcx>) None } + +fn is_vector(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { + let ty = cx.tables.expr_ty(expr); + let ty = walk_ptrs_ty(ty); + is_type_diagnostic_item(cx, ty, sym!(vec_type)) +} + +fn is_full_range(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { + let ty = cx.tables.expr_ty(expr); + let ty = walk_ptrs_ty(ty); + match_type(cx, ty, &["core", "ops", "range", "RangeFull"]) +} diff --git a/tests/ui/match_on_vec_items.rs b/tests/ui/match_on_vec_items.rs index 0bb39d77e46..30415e3b94d 100644 --- a/tests/ui/match_on_vec_items.rs +++ b/tests/ui/match_on_vec_items.rs @@ -120,6 +120,27 @@ fn match_with_array() { } } +fn match_with_endless_range() { + let arr = vec![0, 1, 2, 3]; + let range = ..; + + // Ok + match arr[range] { + [0, 1] => println!("0 1"), + [1, 2] => println!("1 2"), + [0, 1, 2, 3] => println!("0, 1, 2, 3"), + _ => {}, + } + + // Ok + match arr[..] { + [0, 1] => println!("0 1"), + [1, 2] => println!("1 2"), + [0, 1, 2, 3] => println!("0, 1, 2, 3"), + _ => {}, + } +} + fn main() { match_with_wildcard(); match_without_wildcard(); @@ -127,4 +148,5 @@ fn main() { match_vec_ref(); match_with_get(); match_with_array(); + match_with_endless_range(); } -- cgit 1.4.1-3-g733a5 From 780a63a1ba84597eaf33e4d546bcf0edd6225836 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Tue, 5 May 2020 15:11:59 +0200 Subject: Update ui tests --- tests/ui/builtin-type-shadow.stderr | 2 -- 1 file changed, 2 deletions(-) (limited to 'tests') diff --git a/tests/ui/builtin-type-shadow.stderr b/tests/ui/builtin-type-shadow.stderr index b6a4adde848..bc785b075e0 100644 --- a/tests/ui/builtin-type-shadow.stderr +++ b/tests/ui/builtin-type-shadow.stderr @@ -18,8 +18,6 @@ LL | 42 | = note: expected type parameter `u32` found type `{integer}` - = help: type parameters must be constrained to match other types - = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From 3b58d66b22fe9107a78b99c267c71322276aa15a Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Thu, 7 May 2020 21:41:23 +0200 Subject: Add the manual_async_fn lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 ++ clippy_lints/src/manual_async_fn.rs | 160 ++++++++++++++++++++++++++++++++++++ clippy_lints/src/utils/paths.rs | 3 + src/lintlist/mod.rs | 7 ++ tests/ui/future_not_send.rs | 1 + tests/ui/future_not_send.stderr | 6 +- tests/ui/manual_async_fn.fixed | 55 +++++++++++++ tests/ui/manual_async_fn.rs | 67 +++++++++++++++ tests/ui/manual_async_fn.stderr | 93 +++++++++++++++++++++ 10 files changed, 395 insertions(+), 3 deletions(-) create mode 100644 clippy_lints/src/manual_async_fn.rs create mode 100644 tests/ui/manual_async_fn.fixed create mode 100644 tests/ui/manual_async_fn.rs create mode 100644 tests/ui/manual_async_fn.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index facf363e371..8457d6ad05c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1422,6 +1422,7 @@ Released 2018-09-13 [`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal [`macro_use_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_use_imports [`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion +[`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn [`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy [`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive [`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 97785cba883..fb2e9932b8e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -247,6 +247,7 @@ mod literal_representation; mod loops; mod macro_use; mod main_recursion; +mod manual_async_fn; mod manual_non_exhaustive; mod map_clone; mod map_unit_fn; @@ -629,6 +630,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &loops::WHILE_LET_ON_ITERATOR, ¯o_use::MACRO_USE_IMPORTS, &main_recursion::MAIN_RECURSION, + &manual_async_fn::MANUAL_ASYNC_FN, &manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, &map_clone::MAP_CLONE, &map_unit_fn::OPTION_MAP_UNIT_FN, @@ -1067,6 +1069,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box if_let_mutex::IfLetMutex); store.register_late_pass(|| box match_on_vec_items::MatchOnVecItems); store.register_early_pass(|| box manual_non_exhaustive::ManualNonExhaustive); + store.register_late_pass(|| box manual_async_fn::ManualAsyncFn); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1284,6 +1287,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(&map_clone::MAP_CLONE), LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN), @@ -1478,6 +1482,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::NEEDLESS_RANGE_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_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(&map_clone::MAP_CLONE), LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH), diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs new file mode 100644 index 00000000000..ab8829abbf2 --- /dev/null +++ b/clippy_lints/src/manual_async_fn.rs @@ -0,0 +1,160 @@ +use crate::utils::paths::{FUTURE_CORE, FUTURE_FROM_GENERATOR, FUTURE_STD}; +use crate::utils::{match_function_call, match_path, snippet_block, snippet_opt, span_lint_and_then}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{ + AsyncGeneratorKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericBound, HirId, IsAsync, + ItemKind, TraitRef, Ty, TyKind, TypeBindingKind, +}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** It checks for manual implementations of `async` functions. + /// + /// **Why is this bad?** It's more idiomatic to use the dedicated syntax. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// use std::future::Future; + /// + /// fn foo() -> Future { async { 42 } } + /// ``` + /// Use instead: + /// ```rust + /// use std::future::Future; + /// + /// async fn foo() -> i32 { 42 } + /// ``` + pub MANUAL_ASYNC_FN, + style, + "manual implementations of `async` functions can be simplified using the dedicated syntax" +} + +declare_lint_pass!(ManualAsyncFn => [MANUAL_ASYNC_FN]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ManualAsyncFn { + fn check_fn( + &mut self, + cx: &LateContext<'a, 'tcx>, + kind: FnKind<'tcx>, + decl: &'tcx FnDecl<'_>, + body: &'tcx Body<'_>, + span: Span, + _: HirId, + ) { + if_chain! { + if let Some(header) = kind.header(); + if let IsAsync::NotAsync = header.asyncness; + // Check that this function returns `impl Future` + if let FnRetTy::Return(ret_ty) = decl.output; + if let Some(trait_ref) = future_trait_ref(cx, ret_ty); + if let Some(output) = future_output_ty(trait_ref); + // Check that the body of the function consists of one async block + if let ExprKind::Block(block, _) = body.value.kind; + if block.stmts.is_empty(); + if let Some(closure_body) = desugared_async_block(cx, block); + then { + let header_span = span.with_hi(ret_ty.span.hi()); + + span_lint_and_then( + cx, + MANUAL_ASYNC_FN, + header_span, + "this function can be simplified using async syntax", + |diag| { + if_chain! { + if let Some(header_snip) = snippet_opt(cx, header_span); + if let Some(ret_pos) = header_snip.rfind("->"); + if let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output); + then { + let help = format!("make the function `async` and {}", ret_sugg); + diag.span_suggestion( + header_span, + &help, + format!("async {}{}", &header_snip[..ret_pos], ret_snip), + Applicability::MachineApplicable + ); + + let body_snip = snippet_block(cx, closure_body.value.span, "..", Some(block.span)); + diag.span_suggestion( + block.span, + "move the body of the async block to the enclosing function", + body_snip.to_string(), + Applicability::MachineApplicable + ); + } + } + }, + ); + } + } + } +} + +fn future_trait_ref<'tcx>(cx: &LateContext<'_, 'tcx>, ty: &'tcx Ty<'tcx>) -> Option<&'tcx TraitRef<'tcx>> { + if_chain! { + if let TyKind::Def(item_id, _) = ty.kind; + let item = cx.tcx.hir().item(item_id.id); + if let ItemKind::OpaqueTy(opaque) = &item.kind; + if opaque.bounds.len() == 1; + if let GenericBound::Trait(poly, _) = &opaque.bounds[0]; + let path = poly.trait_ref.path; + if match_path(&path, &FUTURE_CORE) || match_path(&path, &FUTURE_STD); + then { + return Some(&poly.trait_ref); + } + } + + None +} + +fn future_output_ty<'tcx>(trait_ref: &'tcx TraitRef<'tcx>) -> Option<&'tcx Ty<'tcx>> { + if_chain! { + if let Some(segment) = trait_ref.path.segments.last(); + if let Some(args) = segment.args; + if args.bindings.len() == 1; + let binding = &args.bindings[0]; + if binding.ident.as_str() == "Output"; + if let TypeBindingKind::Equality{ty: output} = binding.kind; + then { + return Some(output) + } + } + + None +} + +fn desugared_async_block<'tcx>(cx: &LateContext<'_, 'tcx>, block: &'tcx Block<'tcx>) -> Option<&'tcx Body<'tcx>> { + if_chain! { + if let Some(block_expr) = block.expr; + if let Some(args) = match_function_call(cx, block_expr, &FUTURE_FROM_GENERATOR); + 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; + then { + return Some(closure_body); + } + } + + None +} + +fn suggested_ret(cx: &LateContext<'_, '_>, output: &Ty<'_>) -> Option<(&'static str, String)> { + match output.kind { + TyKind::Tup(tys) if tys.is_empty() => { + let sugg = "remove the return type"; + Some((sugg, "".into())) + }, + _ => { + let sugg = "return the output of the future directly"; + snippet_opt(cx, output.span).map(|snip| (sugg, format!("-> {}", snip))) + }, + } +} diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 79ca6845c6f..74040a96c78 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -42,6 +42,9 @@ pub const FMT_ARGUMENTS_NEW_V1_FORMATTED: [&str; 4] = ["core", "fmt", "Arguments pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"]; pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"]; pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"]; +pub const FUTURE_CORE: [&str; 3] = ["core", "future", "Future"]; +pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; +pub const FUTURE_STD: [&str; 3] = ["std", "future", "Future"]; pub const HASH: [&str; 2] = ["hash", "Hash"]; pub const HASHMAP: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"]; pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"]; diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index a7685f67211..51d1cb2216a 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1081,6 +1081,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "main_recursion", }, + Lint { + name: "manual_async_fn", + group: "style", + desc: "manual implementations of `async` functions can be simplified using the dedicated syntax", + deprecation: None, + module: "manual_async_fn", + }, Lint { name: "manual_memcpy", group: "perf", diff --git a/tests/ui/future_not_send.rs b/tests/ui/future_not_send.rs index 6d09d71a630..d3a920de4b6 100644 --- a/tests/ui/future_not_send.rs +++ b/tests/ui/future_not_send.rs @@ -41,6 +41,7 @@ impl Dummy { self.private_future().await; } + #[allow(clippy::manual_async_fn)] pub fn public_send(&self) -> impl std::future::Future { async { false } } diff --git a/tests/ui/future_not_send.stderr b/tests/ui/future_not_send.stderr index 3b4968ef0a6..d1863701bfe 100644 --- a/tests/ui/future_not_send.stderr +++ b/tests/ui/future_not_send.stderr @@ -96,13 +96,13 @@ LL | } = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Sync` error: future cannot be sent between threads safely - --> $DIR/future_not_send.rs:49:37 + --> $DIR/future_not_send.rs:50:37 | LL | async fn generic_future(t: T) -> T | ^ future returned by `generic_future` is not `Send` | note: future is not `Send` as this value is used across an await - --> $DIR/future_not_send.rs:54:5 + --> $DIR/future_not_send.rs:55:5 | LL | let rt = &t; | -- has type `&T` which is not `Send` @@ -114,7 +114,7 @@ LL | } = note: `T` doesn't implement `std::marker::Sync` error: future cannot be sent between threads safely - --> $DIR/future_not_send.rs:65:34 + --> $DIR/future_not_send.rs:66:34 | LL | async fn unclear_future(t: T) {} | ^ diff --git a/tests/ui/manual_async_fn.fixed b/tests/ui/manual_async_fn.fixed new file mode 100644 index 00000000000..84c02bba4dc --- /dev/null +++ b/tests/ui/manual_async_fn.fixed @@ -0,0 +1,55 @@ +// run-rustfix +// edition:2018 +#![warn(clippy::manual_async_fn)] +#![allow(unused)] + +use std::future::Future; + +async fn fut() -> i32 { 42 } + +async fn empty_fut() {} + +async fn core_fut() -> i32 { 42 } + +// should be ignored +fn has_other_stmts() -> impl core::future::Future { + let _ = 42; + async move { 42 } +} + +// should be ignored +fn not_fut() -> i32 { + 42 +} + +// should be ignored +async fn already_async() -> impl Future { + async { 42 } +} + +struct S {} +impl S { + async fn inh_fut() -> i32 { 42 } + + async fn meth_fut(&self) -> i32 { 42 } + + async fn empty_fut(&self) {} + + // should be ignored + fn not_fut(&self) -> i32 { + 42 + } + + // should be ignored + fn has_other_stmts() -> impl core::future::Future { + let _ = 42; + async move { 42 } + } + + // should be ignored + async fn already_async(&self) -> impl Future { + async { 42 } + } +} + +fn main() {} diff --git a/tests/ui/manual_async_fn.rs b/tests/ui/manual_async_fn.rs new file mode 100644 index 00000000000..bd5f9d1cf5f --- /dev/null +++ b/tests/ui/manual_async_fn.rs @@ -0,0 +1,67 @@ +// run-rustfix +// edition:2018 +#![warn(clippy::manual_async_fn)] +#![allow(unused)] + +use std::future::Future; + +fn fut() -> impl Future { + async { 42 } +} + +fn empty_fut() -> impl Future { + async {} +} + +fn core_fut() -> impl core::future::Future { + async move { 42 } +} + +// should be ignored +fn has_other_stmts() -> impl core::future::Future { + let _ = 42; + async move { 42 } +} + +// should be ignored +fn not_fut() -> i32 { + 42 +} + +// should be ignored +async fn already_async() -> impl Future { + async { 42 } +} + +struct S {} +impl S { + fn inh_fut() -> impl Future { + async { 42 } + } + + fn meth_fut(&self) -> impl Future { + async { 42 } + } + + fn empty_fut(&self) -> impl Future { + async {} + } + + // should be ignored + fn not_fut(&self) -> i32 { + 42 + } + + // should be ignored + fn has_other_stmts() -> impl core::future::Future { + let _ = 42; + async move { 42 } + } + + // should be ignored + async fn already_async(&self) -> impl Future { + async { 42 } + } +} + +fn main() {} diff --git a/tests/ui/manual_async_fn.stderr b/tests/ui/manual_async_fn.stderr new file mode 100644 index 00000000000..016fdf95977 --- /dev/null +++ b/tests/ui/manual_async_fn.stderr @@ -0,0 +1,93 @@ +error: this function can be simplified using async syntax + --> $DIR/manual_async_fn.rs:8:1 + | +LL | fn fut() -> impl Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::manual-async-fn` implied by `-D warnings` +help: make the function `async` and return the output of the future directly + | +LL | async fn fut() -> i32 { + | ^^^^^^^^^^^^^^^^^^^^^ +help: move the body of the async block to the enclosing function + | +LL | fn fut() -> impl Future { 42 } + | ^^^^^^ + +error: this function can be simplified using async syntax + --> $DIR/manual_async_fn.rs:12:1 + | +LL | fn empty_fut() -> impl Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: make the function `async` and remove the return type + | +LL | async fn empty_fut() { + | ^^^^^^^^^^^^^^^^^^^^ +help: move the body of the async block to the enclosing function + | +LL | fn empty_fut() -> impl Future {} + | ^^ + +error: this function can be simplified using async syntax + --> $DIR/manual_async_fn.rs:16:1 + | +LL | fn core_fut() -> impl core::future::Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: make the function `async` and return the output of the future directly + | +LL | async fn core_fut() -> i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: move the body of the async block to the enclosing function + | +LL | fn core_fut() -> impl core::future::Future { 42 } + | ^^^^^^ + +error: this function can be simplified using async syntax + --> $DIR/manual_async_fn.rs:38:5 + | +LL | fn inh_fut() -> impl Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: make the function `async` and return the output of the future directly + | +LL | async fn inh_fut() -> i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +help: move the body of the async block to the enclosing function + | +LL | fn inh_fut() -> impl Future { 42 } + | ^^^^^^ + +error: this function can be simplified using async syntax + --> $DIR/manual_async_fn.rs:42:5 + | +LL | fn meth_fut(&self) -> impl Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: make the function `async` and return the output of the future directly + | +LL | async fn meth_fut(&self) -> i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: move the body of the async block to the enclosing function + | +LL | fn meth_fut(&self) -> impl Future { 42 } + | ^^^^^^ + +error: this function can be simplified using async syntax + --> $DIR/manual_async_fn.rs:46:5 + | +LL | fn empty_fut(&self) -> impl Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: make the function `async` and remove the return type + | +LL | async fn empty_fut(&self) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +help: move the body of the async block to the enclosing function + | +LL | fn empty_fut(&self) -> impl Future {} + | ^^ + +error: aborting due to 6 previous errors + -- cgit 1.4.1-3-g733a5 From 3e4bc026e2706b1cb21bad6d55726f8b5a1d4cf1 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Thu, 7 May 2020 22:40:28 +0200 Subject: Apply suggestions from PR review --- clippy_lints/src/manual_async_fn.rs | 9 ++++----- clippy_lints/src/utils/paths.rs | 2 -- tests/ui/manual_async_fn.fixed | 14 +++++++++++++- tests/ui/manual_async_fn.rs | 14 +++++++++++++- tests/ui/manual_async_fn.stderr | 25 +++++++++++++++---------- 5 files changed, 45 insertions(+), 19 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index b4be3ecfd16..cb72a240582 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -1,5 +1,5 @@ -use crate::utils::paths::{FUTURE_CORE, FUTURE_FROM_GENERATOR, FUTURE_STD}; -use crate::utils::{match_function_call, match_path, snippet_block, snippet_opt, span_lint_and_then}; +use crate::utils::paths::FUTURE_FROM_GENERATOR; +use crate::utils::{match_function_call, snippet_block, snippet_opt, span_lint_and_then}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; @@ -66,7 +66,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ManualAsyncFn { cx, MANUAL_ASYNC_FN, header_span, - "this function can be simplified using async syntax", + "this function can be simplified using the `async fn` syntax", |diag| { if_chain! { if let Some(header_snip) = snippet_opt(cx, header_span); @@ -104,8 +104,7 @@ fn future_trait_ref<'tcx>(cx: &LateContext<'_, 'tcx>, ty: &'tcx Ty<'tcx>) -> Opt if let ItemKind::OpaqueTy(opaque) = &item.kind; if opaque.bounds.len() == 1; if let GenericBound::Trait(poly, _) = &opaque.bounds[0]; - let path = poly.trait_ref.path; - if match_path(&path, &FUTURE_CORE) || match_path(&path, &FUTURE_STD); + if poly.trait_ref.trait_def_id() == cx.tcx.lang_items().future_trait(); then { return Some(&poly.trait_ref); } diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 74040a96c78..b3ad2ad9d99 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -42,9 +42,7 @@ pub const FMT_ARGUMENTS_NEW_V1_FORMATTED: [&str; 4] = ["core", "fmt", "Arguments pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"]; pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"]; pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"]; -pub const FUTURE_CORE: [&str; 3] = ["core", "future", "Future"]; pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; -pub const FUTURE_STD: [&str; 3] = ["std", "future", "Future"]; pub const HASH: [&str; 2] = ["hash", "Hash"]; pub const HASHMAP: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"]; pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"]; diff --git a/tests/ui/manual_async_fn.fixed b/tests/ui/manual_async_fn.fixed index 84c02bba4dc..6bb1032a172 100644 --- a/tests/ui/manual_async_fn.fixed +++ b/tests/ui/manual_async_fn.fixed @@ -29,7 +29,19 @@ async fn already_async() -> impl Future { struct S {} impl S { - async fn inh_fut() -> i32 { 42 } + async fn inh_fut() -> i32 { + // NOTE: this code is here just to check that the identation is correct in the suggested fix + let a = 42; + let b = 21; + if a < b { + let c = 21; + let d = 42; + if c < d { + let _ = 42; + } + } + 42 + } async fn meth_fut(&self) -> i32 { 42 } diff --git a/tests/ui/manual_async_fn.rs b/tests/ui/manual_async_fn.rs index bd5f9d1cf5f..d50c919188b 100644 --- a/tests/ui/manual_async_fn.rs +++ b/tests/ui/manual_async_fn.rs @@ -36,7 +36,19 @@ async fn already_async() -> impl Future { struct S {} impl S { fn inh_fut() -> impl Future { - async { 42 } + async { + // NOTE: this code is here just to check that the identation is correct in the suggested fix + let a = 42; + let b = 21; + if a < b { + let c = 21; + let d = 42; + if c < d { + let _ = 42; + } + } + 42 + } } fn meth_fut(&self) -> impl Future { diff --git a/tests/ui/manual_async_fn.stderr b/tests/ui/manual_async_fn.stderr index 016fdf95977..f278ee41aa3 100644 --- a/tests/ui/manual_async_fn.stderr +++ b/tests/ui/manual_async_fn.stderr @@ -1,4 +1,4 @@ -error: this function can be simplified using async syntax +error: this function can be simplified using the `async fn` syntax --> $DIR/manual_async_fn.rs:8:1 | LL | fn fut() -> impl Future { @@ -14,7 +14,7 @@ help: move the body of the async block to the enclosing function LL | fn fut() -> impl Future { 42 } | ^^^^^^ -error: this function can be simplified using async syntax +error: this function can be simplified using the `async fn` syntax --> $DIR/manual_async_fn.rs:12:1 | LL | fn empty_fut() -> impl Future { @@ -29,7 +29,7 @@ help: move the body of the async block to the enclosing function LL | fn empty_fut() -> impl Future {} | ^^ -error: this function can be simplified using async syntax +error: this function can be simplified using the `async fn` syntax --> $DIR/manual_async_fn.rs:16:1 | LL | fn core_fut() -> impl core::future::Future { @@ -44,7 +44,7 @@ help: move the body of the async block to the enclosing function LL | fn core_fut() -> impl core::future::Future { 42 } | ^^^^^^ -error: this function can be simplified using async syntax +error: this function can be simplified using the `async fn` syntax --> $DIR/manual_async_fn.rs:38:5 | LL | fn inh_fut() -> impl Future { @@ -56,11 +56,16 @@ LL | async fn inh_fut() -> i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: move the body of the async block to the enclosing function | -LL | fn inh_fut() -> impl Future { 42 } - | ^^^^^^ +LL | fn inh_fut() -> impl Future { +LL | // NOTE: this code is here just to check that the identation is correct in the suggested fix +LL | let a = 42; +LL | let b = 21; +LL | if a < b { +LL | let c = 21; + ... -error: this function can be simplified using async syntax - --> $DIR/manual_async_fn.rs:42:5 +error: this function can be simplified using the `async fn` syntax + --> $DIR/manual_async_fn.rs:54:5 | LL | fn meth_fut(&self) -> impl Future { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -74,8 +79,8 @@ help: move the body of the async block to the enclosing function LL | fn meth_fut(&self) -> impl Future { 42 } | ^^^^^^ -error: this function can be simplified using async syntax - --> $DIR/manual_async_fn.rs:46:5 +error: this function can be simplified using the `async fn` syntax + --> $DIR/manual_async_fn.rs:58:5 | LL | fn empty_fut(&self) -> impl Future { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From 0c14ea8ed79ebf0b7368659282136e876f247cc9 Mon Sep 17 00:00:00 2001 From: Glenn Hope Date: Sun, 3 May 2020 10:56:25 -0700 Subject: Allow 'use super::*;' imports --- clippy_lints/src/wildcard_imports.rs | 19 +++++++++++++++--- tests/ui/wildcard_imports.fixed | 38 ++++++++++++++++++++++++++++++++++++ tests/ui/wildcard_imports.rs | 38 ++++++++++++++++++++++++++++++++++++ tests/ui/wildcard_imports.stderr | 20 ++++++++++++++++++- 4 files changed, 111 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index f3038861cee..70ad9a60a02 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -3,7 +3,7 @@ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{ def::{DefKind, Res}, - Item, ItemKind, UseKind, + Item, ItemKind, PathSegment, UseKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -83,8 +83,8 @@ impl LateLintPass<'_, '_> for WildcardImports { if_chain! { if !in_macro(item.span); if let ItemKind::Use(use_path, UseKind::Glob) = &item.kind; - // don't lint prelude glob imports - if !use_path.segments.iter().last().map_or(false, |ps| ps.ident.as_str() == "prelude"); + if !is_prelude_import(use_path.segments); + if !is_super_only_import_in_test(use_path.segments); let used_imports = cx.tcx.names_imported_by_glob_use(item.hir_id.owner); if !used_imports.is_empty(); // Already handled by `unused_imports` then { @@ -154,3 +154,16 @@ impl LateLintPass<'_, '_> for WildcardImports { } } } + +// Allow "...prelude::*" imports. +// Many crates have a prelude, and it is imported as a glob by design. +fn is_prelude_import(segments: &[PathSegment<'_>]) -> bool { + segments.iter().last().map_or(false, |ps| ps.ident.as_str() == "prelude") +} + +// Allow "super::*" imports. +// This is intended primarily to ease the process of writing unit tests. +fn is_super_only_import_in_test(segments: &[PathSegment<'_>]) -> bool { + segments.iter().len() == 1 && + segments.first().map_or(false, |ps| ps.ident.as_str() == "super") +} diff --git a/tests/ui/wildcard_imports.fixed b/tests/ui/wildcard_imports.fixed index ed6cc00ef04..003f11009a0 100644 --- a/tests/ui/wildcard_imports.fixed +++ b/tests/ui/wildcard_imports.fixed @@ -155,3 +155,41 @@ fn test_weird_formatting() { exported(); foo(); } + +mod test_super_imports { + fn foofoo() {} + + mod use_super { + use super::*; + + fn with_super() { + let _ = foofoo(); + } + } + + mod use_explicit { + use test_super_imports::foofoo; + + fn with_explicit() { + let _ = foofoo(); + } + } + + mod use_double_super { + mod inner { + use super::super::foofoo; + + fn with_double_super() { + let _ = foofoo(); + } + } + } + + mod use_super_explicit { + use super::super::test_super_imports::foofoo; + + fn with_super_explicit() { + let _ = foofoo(); + } + } +} diff --git a/tests/ui/wildcard_imports.rs b/tests/ui/wildcard_imports.rs index c6d6efaece8..7bd57c7965a 100644 --- a/tests/ui/wildcard_imports.rs +++ b/tests/ui/wildcard_imports.rs @@ -156,3 +156,41 @@ fn test_weird_formatting() { exported(); foo(); } + +mod test_super_imports { + fn foofoo() {} + + mod use_super { + use super::*; + + fn with_super() { + let _ = foofoo(); + } + } + + mod use_explicit { + use test_super_imports::*; + + fn with_explicit() { + let _ = foofoo(); + } + } + + mod use_double_super { + mod inner { + use super::super::*; + + fn with_double_super() { + let _ = foofoo(); + } + } + } + + mod use_super_explicit { + use super::super::test_super_imports::*; + + fn with_super_explicit() { + let _ = foofoo(); + } + } +} diff --git a/tests/ui/wildcard_imports.stderr b/tests/ui/wildcard_imports.stderr index 050e4c6304f..858dc28797f 100644 --- a/tests/ui/wildcard_imports.stderr +++ b/tests/ui/wildcard_imports.stderr @@ -92,5 +92,23 @@ LL | use crate:: fn_mod:: LL | | *; | |_________^ help: try: `crate:: fn_mod::foo` -error: aborting due to 15 previous errors +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:172:13 + | +LL | use test_super_imports::*; + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `test_super_imports::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:181:17 + | +LL | use super::super::*; + | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:190:13 + | +LL | use super::super::test_super_imports::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::test_super_imports::foofoo` + +error: aborting due to 18 previous errors -- cgit 1.4.1-3-g733a5 From 56f4e1c3a8be54c1c80de366618e83d8d7a6e9eb Mon Sep 17 00:00:00 2001 From: Glenn Hope Date: Thu, 7 May 2020 08:06:30 -0700 Subject: Check if the parent module name contains "test" --- clippy_lints/src/wildcard_imports.rs | 19 ++++++++++++------- tests/ui/wildcard_imports.fixed | 16 ++++++++++++---- tests/ui/wildcard_imports.rs | 16 ++++++++++++---- tests/ui/wildcard_imports.stderr | 14 ++++++++++---- 4 files changed, 46 insertions(+), 19 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index e7400642b07..a22d0e6775d 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -84,7 +84,7 @@ impl LateLintPass<'_, '_> for WildcardImports { if !in_macro(item.span); if let ItemKind::Use(use_path, UseKind::Glob) = &item.kind; if !is_prelude_import(use_path.segments); - if !is_super_only_import_in_test(use_path.segments); + if !(is_super_only_import(use_path.segments) && is_in_test_module(cx, item)); let used_imports = cx.tcx.names_imported_by_glob_use(item.hir_id.owner); if !used_imports.is_empty(); // Already handled by `unused_imports` then { @@ -109,8 +109,7 @@ impl LateLintPass<'_, '_> for WildcardImports { span = use_path.span.with_hi(item.span.hi() - BytePos(1)); } ( - span, - false, + span, false, ) }; @@ -164,8 +163,14 @@ fn is_prelude_import(segments: &[PathSegment<'_>]) -> bool { .map_or(false, |ps| ps.ident.as_str() == "prelude") } -// Allow "super::*" imports. -// This is intended primarily to ease the process of writing unit tests. -fn is_super_only_import_in_test(segments: &[PathSegment<'_>]) -> bool { - segments.iter().len() == 1 && segments.first().map_or(false, |ps| ps.ident.as_str() == "super") +// Allow "super::*" imports in tests. +fn is_super_only_import(segments: &[PathSegment<'_>]) -> bool { + segments.len() == 1 && segments[0].ident.as_str() == "super" +} + +fn is_in_test_module(cx: &LateContext<'_, '_>, item: &Item<'_>) -> bool { + let parent = cx.tcx.hir().get_parent_node(item.hir_id); + let parent_item = cx.tcx.hir().expect_item(parent); + let parent_name = parent_item.ident.name.as_str(); + parent_name.contains("test") } diff --git a/tests/ui/wildcard_imports.fixed b/tests/ui/wildcard_imports.fixed index 003f11009a0..1c5c01f65d1 100644 --- a/tests/ui/wildcard_imports.fixed +++ b/tests/ui/wildcard_imports.fixed @@ -159,7 +159,15 @@ fn test_weird_formatting() { mod test_super_imports { fn foofoo() {} - mod use_super { + mod use_super_should_be_replaced { + use super::foofoo; + + fn with_super() { + let _ = foofoo(); + } + } + + mod use_super_in_test_should_pass { use super::*; fn with_super() { @@ -167,7 +175,7 @@ mod test_super_imports { } } - mod use_explicit { + mod use_explicit_should_be_replaced { use test_super_imports::foofoo; fn with_explicit() { @@ -175,7 +183,7 @@ mod test_super_imports { } } - mod use_double_super { + mod use_double_super_should_be_replaced { mod inner { use super::super::foofoo; @@ -185,7 +193,7 @@ mod test_super_imports { } } - mod use_super_explicit { + mod use_super_explicit_should_be_replaced { use super::super::test_super_imports::foofoo; fn with_super_explicit() { diff --git a/tests/ui/wildcard_imports.rs b/tests/ui/wildcard_imports.rs index 7bd57c7965a..f783149ef93 100644 --- a/tests/ui/wildcard_imports.rs +++ b/tests/ui/wildcard_imports.rs @@ -160,7 +160,7 @@ fn test_weird_formatting() { mod test_super_imports { fn foofoo() {} - mod use_super { + mod use_super_should_be_replaced { use super::*; fn with_super() { @@ -168,7 +168,15 @@ mod test_super_imports { } } - mod use_explicit { + mod use_super_in_test_should_pass { + use super::*; + + fn with_super() { + let _ = foofoo(); + } + } + + mod use_explicit_should_be_replaced { use test_super_imports::*; fn with_explicit() { @@ -176,7 +184,7 @@ mod test_super_imports { } } - mod use_double_super { + mod use_double_super_should_be_replaced { mod inner { use super::super::*; @@ -186,7 +194,7 @@ mod test_super_imports { } } - mod use_super_explicit { + mod use_super_explicit_should_be_replaced { use super::super::test_super_imports::*; fn with_super_explicit() { diff --git a/tests/ui/wildcard_imports.stderr b/tests/ui/wildcard_imports.stderr index 858dc28797f..649d550a88d 100644 --- a/tests/ui/wildcard_imports.stderr +++ b/tests/ui/wildcard_imports.stderr @@ -93,22 +93,28 @@ LL | | *; | |_________^ help: try: `crate:: fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:172:13 + --> $DIR/wildcard_imports.rs:164:13 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::foofoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:180:13 | LL | use test_super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^ help: try: `test_super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:181:17 + --> $DIR/wildcard_imports.rs:189:17 | LL | use super::super::*; | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:190:13 + --> $DIR/wildcard_imports.rs:198:13 | LL | use super::super::test_super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::test_super_imports::foofoo` -error: aborting due to 18 previous errors +error: aborting due to 19 previous errors -- cgit 1.4.1-3-g733a5 From ad92486d52fdede5c712dd27c868c942d8240704 Mon Sep 17 00:00:00 2001 From: Glenn Hope Date: Thu, 7 May 2020 14:41:54 -0700 Subject: Add check for "test" in parent name. Include flag for disabling wildcard import exceptions --- clippy_lints/src/lib.rs | 3 +- clippy_lints/src/utils/conf.rs | 2 + clippy_lints/src/wildcard_imports.rs | 48 ++++++++++++++++++---- .../toml_unknown_key/conf_unknown_key.stderr | 2 +- tests/ui/wildcard_imports.fixed | 17 +++++--- tests/ui/wildcard_imports.rs | 17 +++++--- tests/ui/wildcard_imports.stderr | 14 +++---- 7 files changed, 75 insertions(+), 28 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index fb2e9932b8e..4b67c84e38e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1058,7 +1058,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let max_struct_bools = conf.max_struct_bools; store.register_early_pass(move || box excessive_bools::ExcessiveBools::new(max_struct_bools, max_fn_params_bools)); store.register_early_pass(|| box option_env_unwrap::OptionEnvUnwrap); - store.register_late_pass(|| box wildcard_imports::WildcardImports); + let warn_on_all_wildcard_imports = conf.warn_on_all_wildcard_imports; + store.register_late_pass(move || box wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports)); store.register_early_pass(|| box macro_use::MacroUseImports); store.register_late_pass(|| box verbose_file_reads::VerboseFileReads); store.register_late_pass(|| box redundant_pub_crate::RedundantPubCrate::default()); diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 4b81ff33495..57b9eafd14d 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -158,6 +158,8 @@ define_Conf! { (max_struct_bools, "max_struct_bools": u64, 3), /// Lint: FN_PARAMS_EXCESSIVE_BOOLS. The maximum number of bools function parameters can have (max_fn_params_bools, "max_fn_params_bools": u64, 3), + /// Lint: WILDCARD_IMPORTS. Whether to allow certain wildcard imports (prelude, super in tests). + (warn_on_all_wildcard_imports, "warn_on_all_wildcard_imports": bool, false), } impl Default for Conf { diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index a22d0e6775d..43d0d1b9e96 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -6,7 +6,7 @@ use rustc_hir::{ Item, ItemKind, PathSegment, UseKind, }; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::BytePos; declare_clippy_lint! { @@ -73,18 +73,38 @@ declare_clippy_lint! { "lint `use _::*` statements" } -declare_lint_pass!(WildcardImports => [ENUM_GLOB_USE, WILDCARD_IMPORTS]); +#[derive(Default)] +pub struct WildcardImports { + warn_on_all: bool, + is_test_module: bool, + test_modules_deep: u32, +} + +impl WildcardImports { + pub fn new(warn_on_all: bool) -> Self { + Self { + warn_on_all, + is_test_module: false, + test_modules_deep: 0, + } + } +} + +impl_lint_pass!(WildcardImports => [ENUM_GLOB_USE, WILDCARD_IMPORTS]); impl LateLintPass<'_, '_> for WildcardImports { fn check_item(&mut self, cx: &LateContext<'_, '_>, item: &Item<'_>) { if item.vis.node.is_pub() || item.vis.node.is_pub_restricted() { return; } + if is_test_module(item) { + self.is_test_module = true; + self.test_modules_deep += 1; + } if_chain! { if !in_macro(item.span); if let ItemKind::Use(use_path, UseKind::Glob) = &item.kind; - if !is_prelude_import(use_path.segments); - if !(is_super_only_import(use_path.segments) && is_in_test_module(cx, item)); + if self.warn_on_all || !self.check_exceptions(use_path.segments); let used_imports = cx.tcx.names_imported_by_glob_use(item.hir_id.owner); if !used_imports.is_empty(); // Already handled by `unused_imports` then { @@ -152,6 +172,19 @@ impl LateLintPass<'_, '_> for WildcardImports { } } } + + fn check_item_post(&mut self, _: &LateContext<'_, '_>, _: &Item<'_>) { + if self.is_test_module { + self.is_test_module = false; + self.test_modules_deep -= 1; + } + } +} + +impl WildcardImports { + fn check_exceptions(&self, segments: &[PathSegment<'_>]) -> bool { + is_prelude_import(segments) || (is_super_only_import(segments) && self.test_modules_deep > 0) + } } // Allow "...prelude::*" imports. @@ -168,9 +201,6 @@ fn is_super_only_import(segments: &[PathSegment<'_>]) -> bool { segments.len() == 1 && segments[0].ident.as_str() == "super" } -fn is_in_test_module(cx: &LateContext<'_, '_>, item: &Item<'_>) -> bool { - let parent = cx.tcx.hir().get_parent_node(item.hir_id); - let parent_item = cx.tcx.hir().expect_item(parent); - let parent_name = parent_item.ident.name.as_str(); - parent_name.contains("test") +fn is_test_module(item: &Item<'_>) -> bool { + item.ident.name.as_str().contains("test") } 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 18f5d994ba8..53970af4107 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 `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`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-struct-bools`, `max-fn-params-bools`, `third-party` at line 5 column 1 +error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `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`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `third-party` at line 5 column 1 error: aborting due to previous error diff --git a/tests/ui/wildcard_imports.fixed b/tests/ui/wildcard_imports.fixed index 1c5c01f65d1..98bf6acfe55 100644 --- a/tests/ui/wildcard_imports.fixed +++ b/tests/ui/wildcard_imports.fixed @@ -156,10 +156,10 @@ fn test_weird_formatting() { foo(); } -mod test_super_imports { +mod super_imports { fn foofoo() {} - mod use_super_should_be_replaced { + mod should_be_replaced { use super::foofoo; fn with_super() { @@ -167,7 +167,7 @@ mod test_super_imports { } } - mod use_super_in_test_should_pass { + mod test_should_pass { use super::*; fn with_super() { @@ -175,8 +175,15 @@ mod test_super_imports { } } + mod inner { + fn test_should_pass() { + use super::*; + let _ = foofoo(); + } + } + mod use_explicit_should_be_replaced { - use test_super_imports::foofoo; + use super_imports::foofoo; fn with_explicit() { let _ = foofoo(); @@ -194,7 +201,7 @@ mod test_super_imports { } mod use_super_explicit_should_be_replaced { - use super::super::test_super_imports::foofoo; + use super::super::super_imports::foofoo; fn with_super_explicit() { let _ = foofoo(); diff --git a/tests/ui/wildcard_imports.rs b/tests/ui/wildcard_imports.rs index f783149ef93..9275c5a0928 100644 --- a/tests/ui/wildcard_imports.rs +++ b/tests/ui/wildcard_imports.rs @@ -157,10 +157,10 @@ fn test_weird_formatting() { foo(); } -mod test_super_imports { +mod super_imports { fn foofoo() {} - mod use_super_should_be_replaced { + mod should_be_replaced { use super::*; fn with_super() { @@ -168,7 +168,7 @@ mod test_super_imports { } } - mod use_super_in_test_should_pass { + mod test_should_pass { use super::*; fn with_super() { @@ -176,8 +176,15 @@ mod test_super_imports { } } + mod inner { + fn test_should_pass() { + use super::*; + let _ = foofoo(); + } + } + mod use_explicit_should_be_replaced { - use test_super_imports::*; + use super_imports::*; fn with_explicit() { let _ = foofoo(); @@ -195,7 +202,7 @@ mod test_super_imports { } mod use_super_explicit_should_be_replaced { - use super::super::test_super_imports::*; + use super::super::super_imports::*; fn with_super_explicit() { let _ = foofoo(); diff --git a/tests/ui/wildcard_imports.stderr b/tests/ui/wildcard_imports.stderr index 649d550a88d..bd000ce8161 100644 --- a/tests/ui/wildcard_imports.stderr +++ b/tests/ui/wildcard_imports.stderr @@ -99,22 +99,22 @@ LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:180:13 + --> $DIR/wildcard_imports.rs:187:13 | -LL | use test_super_imports::*; - | ^^^^^^^^^^^^^^^^^^^^^ help: try: `test_super_imports::foofoo` +LL | use super_imports::*; + | ^^^^^^^^^^^^^^^^ help: try: `super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:189:17 + --> $DIR/wildcard_imports.rs:196:17 | LL | use super::super::*; | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:198:13 + --> $DIR/wildcard_imports.rs:205:13 | -LL | use super::super::test_super_imports::*; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::test_super_imports::foofoo` +LL | use super::super::super_imports::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` error: aborting due to 19 previous errors -- cgit 1.4.1-3-g733a5 From 4db6abcd50eb07a7fa8349a059f80792b7b19a2e Mon Sep 17 00:00:00 2001 From: Glenn Hope Date: Sat, 9 May 2020 08:04:07 -0700 Subject: Remove check for Fn, reflect this in test cases, make test cases more robust/explicit --- clippy_lints/src/wildcard_imports.rs | 13 +++++++++---- tests/ui/wildcard_imports.fixed | 25 +++++++++++++++++++++++-- tests/ui/wildcard_imports.rs | 24 ++++++++++++++++++++++-- tests/ui/wildcard_imports.stderr | 14 ++++++++++---- 4 files changed, 64 insertions(+), 12 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index 48405a00d55..e12a6659ab5 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -43,9 +43,14 @@ declare_clippy_lint! { /// /// This can lead to confusing error messages at best and to unexpected behavior at worst. /// - /// Note that this will not warn about wildcard imports from modules named `prelude`; many - /// crates (including the standard library) provide modules named "prelude" specifically - /// designed for wildcard import. + /// **Exceptions:** + /// + /// Wildcard imports are allowed from modules named `prelude`. Many crates (including the standard library) + /// provide modules named "prelude" specifically designed for wildcard import. + /// + /// `use super::*` is allowed in test modules. This is defined as any module with "test" in the name. + /// + /// These exceptions can be disabled using the `warn-on-all-wildcard-imports` configuration flag. /// /// **Known problems:** If macros are imported through the wildcard, this macro is not included /// by the suggestion and has to be added by hand. @@ -198,5 +203,5 @@ fn is_super_only_import(segments: &[PathSegment<'_>]) -> bool { } fn is_test_module_or_function(item: &Item<'_>) -> bool { - matches!(item.kind, ItemKind::Fn(..) | ItemKind::Mod(..)) && item.ident.name.as_str().contains("test") + matches!(item.kind, ItemKind::Mod(..)) && item.ident.name.as_str().contains("test") } diff --git a/tests/ui/wildcard_imports.fixed b/tests/ui/wildcard_imports.fixed index 98bf6acfe55..b47c8f23e24 100644 --- a/tests/ui/wildcard_imports.fixed +++ b/tests/ui/wildcard_imports.fixed @@ -175,13 +175,34 @@ mod super_imports { } } - mod inner { - fn test_should_pass() { + mod test_should_pass_inside_function { + fn with_super_inside_function() { use super::*; let _ = foofoo(); } } + mod test_should_pass_further_inside { + fn insidefoo() {} + mod inner { + use super::*; + fn with_super() { + let _ = insidefoo(); + } + } + } + + mod should_be_replaced_futher_inside { + fn insidefoo() {} + mod inner { + use super::insidefoo; + fn with_super() { + let _ = insidefoo(); + } + } + } + + mod use_explicit_should_be_replaced { use super_imports::foofoo; diff --git a/tests/ui/wildcard_imports.rs b/tests/ui/wildcard_imports.rs index 9275c5a0928..3ad1a29aeba 100644 --- a/tests/ui/wildcard_imports.rs +++ b/tests/ui/wildcard_imports.rs @@ -176,13 +176,33 @@ mod super_imports { } } - mod inner { - fn test_should_pass() { + mod test_should_pass_inside_function { + fn with_super_inside_function() { use super::*; let _ = foofoo(); } } + mod test_should_pass_further_inside { + fn insidefoo() {} + mod inner { + use super::*; + fn with_super() { + let _ = insidefoo(); + } + } + } + + mod should_be_replaced_futher_inside { + fn insidefoo() {} + mod inner { + use super::*; + fn with_super() { + let _ = insidefoo(); + } + } + } + mod use_explicit_should_be_replaced { use super_imports::*; diff --git a/tests/ui/wildcard_imports.stderr b/tests/ui/wildcard_imports.stderr index bd000ce8161..de07bd1d69b 100644 --- a/tests/ui/wildcard_imports.stderr +++ b/tests/ui/wildcard_imports.stderr @@ -99,22 +99,28 @@ LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:187:13 + --> $DIR/wildcard_imports.rs:199:17 + | +LL | use super::*; + | ^^^^^^^^ help: try: `super::insidefoo` + +error: usage of wildcard import + --> $DIR/wildcard_imports.rs:208:13 | LL | use super_imports::*; | ^^^^^^^^^^^^^^^^ help: try: `super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:196:17 + --> $DIR/wildcard_imports.rs:217:17 | LL | use super::super::*; | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:205:13 + --> $DIR/wildcard_imports.rs:226:13 | LL | use super::super::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` -error: aborting due to 19 previous errors +error: aborting due to 20 previous errors -- cgit 1.4.1-3-g733a5 From a339766136a183327faaf13b987be30b2940872e Mon Sep 17 00:00:00 2001 From: Glenn Hope Date: Sat, 9 May 2020 08:33:35 -0700 Subject: Fix test from auto-formatter fix --- tests/ui/wildcard_imports.fixed | 1 - 1 file changed, 1 deletion(-) (limited to 'tests') diff --git a/tests/ui/wildcard_imports.fixed b/tests/ui/wildcard_imports.fixed index b47c8f23e24..67423e6ec1d 100644 --- a/tests/ui/wildcard_imports.fixed +++ b/tests/ui/wildcard_imports.fixed @@ -202,7 +202,6 @@ mod super_imports { } } - mod use_explicit_should_be_replaced { use super_imports::foofoo; -- cgit 1.4.1-3-g733a5 From 0ba61c612ee873314d252ca1f747c14a2f0161ba Mon Sep 17 00:00:00 2001 From: Glenn Hope Date: Sat, 9 May 2020 10:14:29 -0700 Subject: Check is_macro inside check_exceptions, update references to fix test --- clippy_lints/src/wildcard_imports.rs | 13 +++++++------ tests/ui/wildcard_imports.stderr | 6 +++--- 2 files changed, 10 insertions(+), 9 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index e12a6659ab5..2c4e24780e7 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -101,12 +101,11 @@ impl LateLintPass<'_, '_> for WildcardImports { return; } if is_test_module_or_function(item) { - self.test_modules_deep += 1; + self.test_modules_deep = self.test_modules_deep.saturating_add(1); } if_chain! { - if self.warn_on_all || !in_macro(item.span); if let ItemKind::Use(use_path, UseKind::Glob) = &item.kind; - if self.warn_on_all || !self.check_exceptions(use_path.segments); + if self.warn_on_all || !self.check_exceptions(item, use_path.segments); let used_imports = cx.tcx.names_imported_by_glob_use(item.hir_id.owner); if !used_imports.is_empty(); // Already handled by `unused_imports` then { @@ -177,14 +176,16 @@ impl LateLintPass<'_, '_> for WildcardImports { fn check_item_post(&mut self, _: &LateContext<'_, '_>, item: &Item<'_>) { if is_test_module_or_function(item) { - self.test_modules_deep -= 1; + self.test_modules_deep = self.test_modules_deep.saturating_sub(1); } } } impl WildcardImports { - fn check_exceptions(&self, segments: &[PathSegment<'_>]) -> bool { - is_prelude_import(segments) || (is_super_only_import(segments) && self.test_modules_deep > 0) + fn check_exceptions(&self, item: &Item<'_>, segments: &[PathSegment<'_>]) -> bool { + in_macro(item.span) + || is_prelude_import(segments) + || (is_super_only_import(segments) && self.test_modules_deep > 0) } } diff --git a/tests/ui/wildcard_imports.stderr b/tests/ui/wildcard_imports.stderr index de07bd1d69b..fab43b738eb 100644 --- a/tests/ui/wildcard_imports.stderr +++ b/tests/ui/wildcard_imports.stderr @@ -105,19 +105,19 @@ LL | use super::*; | ^^^^^^^^ help: try: `super::insidefoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:208:13 + --> $DIR/wildcard_imports.rs:207:13 | LL | use super_imports::*; | ^^^^^^^^^^^^^^^^ help: try: `super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:217:17 + --> $DIR/wildcard_imports.rs:216:17 | LL | use super::super::*; | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:226:13 + --> $DIR/wildcard_imports.rs:225:13 | LL | use super::super::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` -- cgit 1.4.1-3-g733a5 From eec17d2c21605655188eaa13fd73d43c99162805 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Mon, 11 May 2020 21:40:33 +0200 Subject: Update failing test --- tests/ui/implicit_saturating_sub.fixed | 4 ++-- tests/ui/implicit_saturating_sub.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/tests/ui/implicit_saturating_sub.fixed b/tests/ui/implicit_saturating_sub.fixed index e0b5b31a00c..859765d08a7 100644 --- a/tests/ui/implicit_saturating_sub.fixed +++ b/tests/ui/implicit_saturating_sub.fixed @@ -29,8 +29,8 @@ fn main() { // Lint u_16 = u_16.saturating_sub(1); - let mut end_32: u32 = 7000; - let mut start_32: u32 = 7010; + let mut end_32: u32 = 7010; + let mut start_32: u32 = 7000; let mut u_32: u32 = end_32 - start_32; diff --git a/tests/ui/implicit_saturating_sub.rs b/tests/ui/implicit_saturating_sub.rs index 39d81608922..24cb216e79b 100644 --- a/tests/ui/implicit_saturating_sub.rs +++ b/tests/ui/implicit_saturating_sub.rs @@ -35,8 +35,8 @@ fn main() { u_16 -= 1; } - let mut end_32: u32 = 7000; - let mut start_32: u32 = 7010; + let mut end_32: u32 = 7010; + let mut start_32: u32 = 7000; let mut u_32: u32 = end_32 - start_32; -- cgit 1.4.1-3-g733a5 From f20b96277397db2c9021d06cf8647014ccdc0a39 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 13 May 2020 01:04:16 +0200 Subject: unused_unit: lint also in type parameters and where clauses --- clippy_lints/src/returns.rs | 64 +++++++++++++++++++++++++------------- tests/ui/unused_unit.fixed | 21 ++++++++++--- tests/ui/unused_unit.rs | 18 +++++++++-- tests/ui/unused_unit.stderr | 76 ++++++++++++++++++++++++++++++++++++++------- 4 files changed, 138 insertions(+), 41 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 5c9117d5b81..35464f629c3 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -248,28 +248,7 @@ impl EarlyLintPass for Return { if let ast::TyKind::Tup(ref vals) = ty.kind; if vals.is_empty() && !ty.span.from_expansion() && get_def(span) == get_def(ty.span); then { - let (rspan, appl) = if let Ok(fn_source) = - cx.sess().source_map() - .span_to_snippet(span.with_hi(ty.span.hi())) { - if let Some(rpos) = fn_source.rfind("->") { - #[allow(clippy::cast_possible_truncation)] - (ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), - Applicability::MachineApplicable) - } else { - (ty.span, Applicability::MaybeIncorrect) - } - } else { - (ty.span, Applicability::MaybeIncorrect) - }; - span_lint_and_sugg( - cx, - UNUSED_UNIT, - rspan, - "unneeded unit return type", - "remove the `-> ()`", - String::new(), - appl, - ); + lint_unneeded_unit_return(cx, ty, span); } } } @@ -313,6 +292,22 @@ impl EarlyLintPass for Return { _ => (), } } + + fn check_poly_trait_ref(&mut self, cx: &EarlyContext<'_>, poly: &ast::PolyTraitRef, _: &ast::TraitBoundModifier) { + let segments = &poly.trait_ref.path.segments; + + if_chain! { + if segments.len() == 1; + if ["Fn", "FnMut", "FnOnce"].contains(&&*segments[0].ident.name.as_str()); + if let Some(args) = &segments[0].args; + if let ast::GenericArgs::Parenthesized(generic_args) = &**args; + if let ast::FnRetTy::Ty(ty) = &generic_args.output; + if ty.kind.is_unit(); + then { + lint_unneeded_unit_return(cx, ty, generic_args.span); + } + } + } } fn attr_is_cfg(attr: &ast::Attribute) -> bool { @@ -337,3 +332,28 @@ fn is_unit_expr(expr: &ast::Expr) -> bool { false } } + +fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) { + let (ret_span, appl) = if let Ok(fn_source) = cx.sess().source_map().span_to_snippet(span.with_hi(ty.span.hi())) { + if let Some(rpos) = fn_source.rfind("->") { + #[allow(clippy::cast_possible_truncation)] + ( + ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), + Applicability::MachineApplicable, + ) + } else { + (ty.span, Applicability::MaybeIncorrect) + } + } else { + (ty.span, Applicability::MaybeIncorrect) + }; + span_lint_and_sugg( + cx, + UNUSED_UNIT, + ret_span, + "unneeded unit return type", + "remove the `-> ()`", + String::new(), + appl, + ); +} diff --git a/tests/ui/unused_unit.fixed b/tests/ui/unused_unit.fixed index 3f63624720f..07f2791786d 100644 --- a/tests/ui/unused_unit.fixed +++ b/tests/ui/unused_unit.fixed @@ -14,11 +14,10 @@ struct Unitter; impl Unitter { - // try to disorient the lint with multiple unit returns and newlines #[allow(clippy::no_effect)] - pub fn get_unit (), G>(&self, f: F, _g: G) - where G: Fn() -> () { - let _y: &dyn Fn() -> () = &f; + pub fn get_unit(&self, f: F, _g: G) + where G: Fn() { + let _y: &dyn Fn() = &f; (); // this should not lint, as it's not in return type position } } @@ -30,6 +29,20 @@ impl Into<()> for Unitter { } } +trait Trait { + fn redundant(&self, _f: F, _g: G, _h: H) + where + G: FnMut() , + H: Fn() ; +} + +impl Trait for Unitter { + fn redundant(&self, _f: F, _g: G, _h: H) + where + G: FnMut() , + H: Fn() {} +} + fn return_unit() { } #[allow(clippy::needless_return)] diff --git a/tests/ui/unused_unit.rs b/tests/ui/unused_unit.rs index 8fc072ebd69..e2c6afb020f 100644 --- a/tests/ui/unused_unit.rs +++ b/tests/ui/unused_unit.rs @@ -14,10 +14,8 @@ struct Unitter; impl Unitter { - // try to disorient the lint with multiple unit returns and newlines #[allow(clippy::no_effect)] - pub fn get_unit (), G>(&self, f: F, _g: G) -> - () + pub fn get_unit (), G>(&self, f: F, _g: G) -> () where G: Fn() -> () { let _y: &dyn Fn() -> () = &f; (); // this should not lint, as it's not in return type position @@ -31,6 +29,20 @@ impl Into<()> for Unitter { } } +trait Trait { + fn redundant (), G, H>(&self, _f: F, _g: G, _h: H) + where + G: FnMut() -> (), + H: Fn() -> (); +} + +impl Trait for Unitter { + fn redundant (), G, H>(&self, _f: F, _g: G, _h: H) + where + G: FnMut() -> (), + H: Fn() -> () {} +} + fn return_unit() -> () { () } #[allow(clippy::needless_return)] diff --git a/tests/ui/unused_unit.stderr b/tests/ui/unused_unit.stderr index a013d2b3495..81e6738e6bf 100644 --- a/tests/ui/unused_unit.stderr +++ b/tests/ui/unused_unit.stderr @@ -1,10 +1,8 @@ error: unneeded unit return type - --> $DIR/unused_unit.rs:19:59 + --> $DIR/unused_unit.rs:18:29 | -LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> - | ___________________________________________________________^ -LL | | () - | |__________^ help: remove the `-> ()` +LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () + | ^^^^^ help: remove the `-> ()` | note: the lint level is defined here --> $DIR/unused_unit.rs:12:9 @@ -13,40 +11,94 @@ LL | #![deny(clippy::unused_unit)] | ^^^^^^^^^^^^^^^^^^^ error: unneeded unit return type - --> $DIR/unused_unit.rs:29:19 + --> $DIR/unused_unit.rs:19:19 + | +LL | where G: Fn() -> () { + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:18:59 + | +LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:20:27 + | +LL | let _y: &dyn Fn() -> () = &f; + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:27:19 | LL | fn into(self) -> () { | ^^^^^ help: remove the `-> ()` error: unneeded unit expression - --> $DIR/unused_unit.rs:30:9 + --> $DIR/unused_unit.rs:28:9 | LL | () | ^^ help: remove the final `()` error: unneeded unit return type - --> $DIR/unused_unit.rs:34:18 + --> $DIR/unused_unit.rs:33:30 + | +LL | fn redundant (), G, H>(&self, _f: F, _g: G, _h: H) + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:35:20 + | +LL | G: FnMut() -> (), + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:36:17 + | +LL | H: Fn() -> (); + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:40:30 + | +LL | fn redundant (), G, H>(&self, _f: F, _g: G, _h: H) + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:42:20 + | +LL | G: FnMut() -> (), + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:43:17 + | +LL | H: Fn() -> () {} + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:46:18 | LL | fn return_unit() -> () { () } | ^^^^^ help: remove the `-> ()` error: unneeded unit expression - --> $DIR/unused_unit.rs:34:26 + --> $DIR/unused_unit.rs:46:26 | LL | fn return_unit() -> () { () } | ^^ help: remove the final `()` error: unneeded `()` - --> $DIR/unused_unit.rs:44:14 + --> $DIR/unused_unit.rs:56:14 | LL | break(); | ^^ help: remove the `()` error: unneeded `()` - --> $DIR/unused_unit.rs:46:11 + --> $DIR/unused_unit.rs:58:11 | LL | return(); | ^^ help: remove the `()` -error: aborting due to 7 previous errors +error: aborting due to 16 previous errors -- cgit 1.4.1-3-g733a5 From 8ffa0bfaa2452eb9c80bf0f1909b039efc8dd0c3 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Mon, 11 May 2020 00:52:33 +0200 Subject: New lint: reversed_empty_ranges --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 3 + clippy_lints/src/ranges.rs | 112 +++++++++++++++++++++++- tests/ui/reversed_empty_ranges_fixable.fixed | 24 +++++ tests/ui/reversed_empty_ranges_fixable.rs | 24 +++++ tests/ui/reversed_empty_ranges_fixable.stderr | 47 ++++++++++ tests/ui/reversed_empty_ranges_unfixable.rs | 15 ++++ tests/ui/reversed_empty_ranges_unfixable.stderr | 34 +++++++ 8 files changed, 258 insertions(+), 2 deletions(-) create mode 100644 tests/ui/reversed_empty_ranges_fixable.fixed create mode 100644 tests/ui/reversed_empty_ranges_fixable.rs create mode 100644 tests/ui/reversed_empty_ranges_fixable.stderr create mode 100644 tests/ui/reversed_empty_ranges_unfixable.rs create mode 100644 tests/ui/reversed_empty_ranges_unfixable.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 8457d6ad05c..33b277fbd31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1546,6 +1546,7 @@ Released 2018-09-13 [`result_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unwrap_or_else [`result_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unwrap_used [`reverse_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#reverse_range_loop +[`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges [`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition [`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some [`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 51b5401da7d..e1cb10a4651 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -770,6 +770,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &ranges::RANGE_MINUS_ONE, &ranges::RANGE_PLUS_ONE, &ranges::RANGE_ZIP_WITH_LEN, + &ranges::REVERSED_EMPTY_RANGES, &redundant_clone::REDUNDANT_CLONE, &redundant_field_names::REDUNDANT_FIELD_NAMES, &redundant_pattern_matching::REDUNDANT_PATTERN_MATCHING, @@ -1384,6 +1385,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&question_mark::QUESTION_MARK), LintId::of(&ranges::RANGE_MINUS_ONE), LintId::of(&ranges::RANGE_ZIP_WITH_LEN), + LintId::of(&ranges::REVERSED_EMPTY_RANGES), LintId::of(&redundant_clone::REDUNDANT_CLONE), LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), LintId::of(&redundant_pattern_matching::REDUNDANT_PATTERN_MATCHING), @@ -1675,6 +1677,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&open_options::NONSENSICAL_OPEN_OPTIONS), LintId::of(&option_env_unwrap::OPTION_ENV_UNWRAP), LintId::of(&ptr::MUT_FROM_REF), + LintId::of(&ranges::REVERSED_EMPTY_RANGES), LintId::of(®ex::INVALID_REGEX), LintId::of(&serde_api::SERDE_API_MISUSE), LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index d7ce2e66d69..86d55ccabb6 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -1,14 +1,17 @@ +use crate::consts::{constant, Constant}; use if_chain::if_chain; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Spanned; +use std::cmp::Ordering; use crate::utils::sugg::Sugg; +use crate::utils::{get_parent_expr, is_integer_const, snippet, snippet_opt, span_lint, span_lint_and_then}; use crate::utils::{higher, SpanlessEq}; -use crate::utils::{is_integer_const, snippet, snippet_opt, span_lint, span_lint_and_then}; declare_clippy_lint! { /// **What it does:** Checks for zipping a collection with the range of @@ -84,10 +87,44 @@ declare_clippy_lint! { "`x..=(y-1)` reads better as `x..y`" } +declare_clippy_lint! { + /// **What it does:** Checks for range expressions `x..y` where both `x` and `y` + /// are constant and `x` is greater or equal to `y`. + /// + /// **Why is this bad?** Empty ranges yield no values so iterating them is a no-op. + /// Moreover, trying to use a reversed range to index a slice will panic at run-time. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// fn main() { + /// (10..=0).for_each(|x| println!("{}", x)); + /// + /// let arr = [1, 2, 3, 4, 5]; + /// let sub = &arr[3..1]; + /// } + /// ``` + /// Use instead: + /// ```rust + /// fn main() { + /// (0..=10).rev().for_each(|x| println!("{}", x)); + /// + /// let arr = [1, 2, 3, 4, 5]; + /// let sub = &arr[1..3]; + /// } + /// ``` + pub REVERSED_EMPTY_RANGES, + correctness, + "reversing the limits of range expressions, resulting in empty ranges" +} + declare_lint_pass!(Ranges => [ RANGE_ZIP_WITH_LEN, RANGE_PLUS_ONE, - RANGE_MINUS_ONE + RANGE_MINUS_ONE, + REVERSED_EMPTY_RANGES, ]); impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Ranges { @@ -124,6 +161,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Ranges { check_exclusive_range_plus_one(cx, expr); check_inclusive_range_minus_one(cx, expr); + check_reversed_empty_range(cx, expr); } } @@ -202,6 +240,76 @@ fn check_inclusive_range_minus_one(cx: &LateContext<'_, '_>, expr: &Expr<'_>) { } } +fn check_reversed_empty_range(cx: &LateContext<'_, '_>, expr: &Expr<'_>) { + fn inside_indexing_expr(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { + matches!( + get_parent_expr(cx, expr), + Some(Expr { + kind: ExprKind::Index(..), + .. + }) + ) + } + + fn is_empty_range(limits: RangeLimits, ordering: Ordering) -> bool { + match limits { + RangeLimits::HalfOpen => ordering != Ordering::Less, + RangeLimits::Closed => ordering == Ordering::Greater, + } + } + + if_chain! { + if let Some(higher::Range { start: Some(start), end: Some(end), limits }) = higher::range(cx, expr); + let ty = cx.tables.expr_ty(start); + if let ty::Int(_) | ty::Uint(_) = ty.kind; + if let Some((start_idx, _)) = constant(cx, cx.tables, start); + if let Some((end_idx, _)) = constant(cx, cx.tables, end); + if let Some(ordering) = Constant::partial_cmp(cx.tcx, ty, &start_idx, &end_idx); + if is_empty_range(limits, ordering); + then { + if inside_indexing_expr(cx, expr) { + let (reason, outcome) = if ordering == Ordering::Equal { + ("empty", "always yield an empty slice") + } else { + ("reversed", "panic at run-time") + }; + + span_lint( + cx, + REVERSED_EMPTY_RANGES, + expr.span, + &format!("this range is {} and using it to index a slice will {}", reason, outcome), + ); + } else { + span_lint_and_then( + cx, + REVERSED_EMPTY_RANGES, + expr.span, + "this range is empty so it will yield no values", + |diag| { + if ordering != Ordering::Equal { + let start_snippet = snippet(cx, start.span, "_"); + let end_snippet = snippet(cx, end.span, "_"); + let dots = match limits { + RangeLimits::HalfOpen => "..", + RangeLimits::Closed => "..=" + }; + + diag.span_suggestion( + expr.span, + "consider using the following if you are attempting to iterate over this \ + range in reverse", + format!("({}{}{}).rev()", end_snippet, dots, start_snippet), + Applicability::MaybeIncorrect, + ); + } + }, + ); + } + } + } +} + fn y_plus_one<'t>(cx: &LateContext<'_, '_>, expr: &'t Expr<'_>) -> Option<&'t Expr<'t>> { match expr.kind { ExprKind::Binary( diff --git a/tests/ui/reversed_empty_ranges_fixable.fixed b/tests/ui/reversed_empty_ranges_fixable.fixed new file mode 100644 index 00000000000..ee2cbc3cf54 --- /dev/null +++ b/tests/ui/reversed_empty_ranges_fixable.fixed @@ -0,0 +1,24 @@ +// run-rustfix +#![warn(clippy::reversed_empty_ranges)] + +const ANSWER: i32 = 42; + +fn main() { + (21..=42).rev().for_each(|x| println!("{}", x)); + let _ = (21..ANSWER).rev().filter(|x| x % 2 == 0).take(10).collect::>(); + + for _ in (-42..=-21).rev() {} + for _ in (21u32..42u32).rev() {} + + // These should be ignored as they are not empty ranges: + + (21..=42).for_each(|x| println!("{}", x)); + (21..42).for_each(|x| println!("{}", x)); + + let arr = [1, 2, 3, 4, 5]; + let _ = &arr[1..=3]; + let _ = &arr[1..3]; + + for _ in 21..=42 {} + for _ in 21..42 {} +} diff --git a/tests/ui/reversed_empty_ranges_fixable.rs b/tests/ui/reversed_empty_ranges_fixable.rs new file mode 100644 index 00000000000..6ed5ca6daa0 --- /dev/null +++ b/tests/ui/reversed_empty_ranges_fixable.rs @@ -0,0 +1,24 @@ +// run-rustfix +#![warn(clippy::reversed_empty_ranges)] + +const ANSWER: i32 = 42; + +fn main() { + (42..=21).for_each(|x| println!("{}", x)); + let _ = (ANSWER..21).filter(|x| x % 2 == 0).take(10).collect::>(); + + for _ in -21..=-42 {} + for _ in 42u32..21u32 {} + + // These should be ignored as they are not empty ranges: + + (21..=42).for_each(|x| println!("{}", x)); + (21..42).for_each(|x| println!("{}", x)); + + let arr = [1, 2, 3, 4, 5]; + let _ = &arr[1..=3]; + let _ = &arr[1..3]; + + for _ in 21..=42 {} + for _ in 21..42 {} +} diff --git a/tests/ui/reversed_empty_ranges_fixable.stderr b/tests/ui/reversed_empty_ranges_fixable.stderr new file mode 100644 index 00000000000..97933b8ff85 --- /dev/null +++ b/tests/ui/reversed_empty_ranges_fixable.stderr @@ -0,0 +1,47 @@ +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_fixable.rs:7:5 + | +LL | (42..=21).for_each(|x| println!("{}", x)); + | ^^^^^^^^^ + | + = note: `-D clippy::reversed-empty-ranges` implied by `-D warnings` +help: consider using the following if you are attempting to iterate over this range in reverse + | +LL | (21..=42).rev().for_each(|x| println!("{}", x)); + | ^^^^^^^^^^^^^^^ + +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_fixable.rs:8:13 + | +LL | let _ = (ANSWER..21).filter(|x| x % 2 == 0).take(10).collect::>(); + | ^^^^^^^^^^^^ + | +help: consider using the following if you are attempting to iterate over this range in reverse + | +LL | let _ = (21..ANSWER).rev().filter(|x| x % 2 == 0).take(10).collect::>(); + | ^^^^^^^^^^^^^^^^^^ + +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_fixable.rs:10:14 + | +LL | for _ in -21..=-42 {} + | ^^^^^^^^^ + | +help: consider using the following if you are attempting to iterate over this range in reverse + | +LL | for _ in (-42..=-21).rev() {} + | ^^^^^^^^^^^^^^^^^ + +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_fixable.rs:11:14 + | +LL | for _ in 42u32..21u32 {} + | ^^^^^^^^^^^^ + | +help: consider using the following if you are attempting to iterate over this range in reverse + | +LL | for _ in (21u32..42u32).rev() {} + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/reversed_empty_ranges_unfixable.rs b/tests/ui/reversed_empty_ranges_unfixable.rs new file mode 100644 index 00000000000..c9ca4c47668 --- /dev/null +++ b/tests/ui/reversed_empty_ranges_unfixable.rs @@ -0,0 +1,15 @@ +#![warn(clippy::reversed_empty_ranges)] + +const ANSWER: i32 = 42; +const SOME_NUM: usize = 3; + +fn main() { + let _ = (42 + 10..42 + 10).map(|x| x / 2).find(|&x| x == 21); + + let arr = [1, 2, 3, 4, 5]; + let _ = &arr[3usize..=1usize]; + let _ = &arr[SOME_NUM..1]; + let _ = &arr[3..3]; + + for _ in ANSWER..ANSWER {} +} diff --git a/tests/ui/reversed_empty_ranges_unfixable.stderr b/tests/ui/reversed_empty_ranges_unfixable.stderr new file mode 100644 index 00000000000..12e5483ecdf --- /dev/null +++ b/tests/ui/reversed_empty_ranges_unfixable.stderr @@ -0,0 +1,34 @@ +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_unfixable.rs:7:13 + | +LL | let _ = (42 + 10..42 + 10).map(|x| x / 2).find(|&x| x == 21); + | ^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::reversed-empty-ranges` implied by `-D warnings` + +error: this range is reversed and using it to index a slice will panic at run-time + --> $DIR/reversed_empty_ranges_unfixable.rs:10:18 + | +LL | let _ = &arr[3usize..=1usize]; + | ^^^^^^^^^^^^^^^ + +error: this range is reversed and using it to index a slice will panic at run-time + --> $DIR/reversed_empty_ranges_unfixable.rs:11:18 + | +LL | let _ = &arr[SOME_NUM..1]; + | ^^^^^^^^^^^ + +error: this range is empty and using it to index a slice will always yield an empty slice + --> $DIR/reversed_empty_ranges_unfixable.rs:12:18 + | +LL | let _ = &arr[3..3]; + | ^^^^ + +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_unfixable.rs:14:14 + | +LL | for _ in ANSWER..ANSWER {} + | ^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + -- cgit 1.4.1-3-g733a5 From 0f2b1193f9501ffd06f9bf2ea8ab85a4db92f47b Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Mon, 11 May 2020 00:53:31 +0200 Subject: Remove reverse_range_loop lint --- CHANGELOG.md | 1 - clippy_lints/src/lib.rs | 7 +-- clippy_lints/src/loops.rs | 102 +------------------------------------ src/lintlist/mod.rs | 6 +-- tests/ui/for_loop_fixable.fixed | 54 -------------------- tests/ui/for_loop_fixable.rs | 54 -------------------- tests/ui/for_loop_fixable.stderr | 88 ++++++-------------------------- tests/ui/for_loop_unfixable.rs | 18 ------- tests/ui/for_loop_unfixable.stderr | 11 ++-- tests/ui/manual_memcpy.rs | 2 +- 10 files changed, 32 insertions(+), 311 deletions(-) (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 33b277fbd31..b25ef049356 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1545,7 +1545,6 @@ Released 2018-09-13 [`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn [`result_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unwrap_or_else [`result_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unwrap_used -[`reverse_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#reverse_range_loop [`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges [`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition [`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index e1cb10a4651..0c4daeb731f 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -624,7 +624,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &loops::NEEDLESS_COLLECT, &loops::NEEDLESS_RANGE_LOOP, &loops::NEVER_LOOP, - &loops::REVERSE_RANGE_LOOP, &loops::WHILE_IMMUTABLE_CONDITION, &loops::WHILE_LET_LOOP, &loops::WHILE_LET_ON_ITERATOR, @@ -1284,7 +1283,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::NEEDLESS_COLLECT), LintId::of(&loops::NEEDLESS_RANGE_LOOP), LintId::of(&loops::NEVER_LOOP), - LintId::of(&loops::REVERSE_RANGE_LOOP), LintId::of(&loops::WHILE_IMMUTABLE_CONDITION), LintId::of(&loops::WHILE_LET_LOOP), LintId::of(&loops::WHILE_LET_ON_ITERATOR), @@ -1658,7 +1656,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::FOR_LOOP_OVER_RESULT), LintId::of(&loops::ITER_NEXT_LOOP), LintId::of(&loops::NEVER_LOOP), - LintId::of(&loops::REVERSE_RANGE_LOOP), LintId::of(&loops::WHILE_IMMUTABLE_CONDITION), LintId::of(&mem_discriminant::MEM_DISCRIMINANT_NON_ENUM), LintId::of(&mem_replace::MEM_REPLACE_WITH_UNINIT), @@ -1788,6 +1785,10 @@ fn register_removed_non_tool_lints(store: &mut rustc_lint::LintStore) { "unsafe_vector_initialization", "the replacement suggested by this lint had substantially different behavior", ); + store.register_removed( + "reverse_range_loop", + "this lint is now included in reversed_empty_ranges", + ); } /// Register renamed lints. diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 2bbf4dba614..0bc6b70855b 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1,4 +1,4 @@ -use crate::consts::{constant, Constant}; +use crate::consts::constant; use crate::reexport::Name; use crate::utils::paths; use crate::utils::usage::{is_unused, mutated_variables}; @@ -8,7 +8,7 @@ use crate::utils::{ multispan_sugg, snippet, snippet_opt, snippet_with_applicability, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, SpanlessEq, }; -use crate::utils::{is_type_diagnostic_item, qpath_res, same_tys, sext, sugg}; +use crate::utils::{is_type_diagnostic_item, qpath_res, same_tys, sugg}; use if_chain::if_chain; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -270,30 +270,6 @@ declare_clippy_lint! { "collecting an iterator when collect is not needed" } -declare_clippy_lint! { - /// **What it does:** Checks for loops over ranges `x..y` where both `x` and `y` - /// are constant and `x` is greater or equal to `y`, unless the range is - /// reversed or has a negative `.step_by(_)`. - /// - /// **Why is it bad?** Such loops will either be skipped or loop until - /// wrap-around (in debug code, this may `panic!()`). Both options are probably - /// not intended. - /// - /// **Known problems:** The lint cannot catch loops over dynamically defined - /// ranges. Doing this would require simulating all possible inputs and code - /// paths through the program, which would be complex and error-prone. - /// - /// **Example:** - /// ```ignore - /// for x in 5..10 - 5 { - /// .. - /// } // oops, stray `-` - /// ``` - pub REVERSE_RANGE_LOOP, - correctness, - "iteration over an empty range, such as `10..0` or `5..5`" -} - declare_clippy_lint! { /// **What it does:** Checks `for` loops over slices with an explicit counter /// and suggests the use of `.enumerate()`. @@ -463,7 +439,6 @@ declare_lint_pass!(Loops => [ FOR_LOOP_OVER_OPTION, WHILE_LET_LOOP, NEEDLESS_COLLECT, - REVERSE_RANGE_LOOP, EXPLICIT_COUNTER_LOOP, EMPTY_LOOP, WHILE_LET_ON_ITERATOR, @@ -761,7 +736,6 @@ fn check_for_loop<'a, 'tcx>( expr: &'tcx Expr<'_>, ) { check_for_loop_range(cx, pat, arg, body, expr); - check_for_loop_reverse_range(cx, arg, expr); check_for_loop_arg(cx, pat, arg, expr); check_for_loop_explicit_counter(cx, pat, arg, body, expr); check_for_loop_over_map_kv(cx, pat, arg, body, expr); @@ -1248,78 +1222,6 @@ fn is_end_eq_array_len<'tcx>( false } -fn check_for_loop_reverse_range<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, arg: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) { - // if this for loop is iterating over a two-sided range... - if let Some(higher::Range { - start: Some(start), - end: Some(end), - limits, - }) = higher::range(cx, arg) - { - // ...and both sides are compile-time constant integers... - if let Some((start_idx, _)) = constant(cx, cx.tables, start) { - if let Some((end_idx, _)) = constant(cx, cx.tables, end) { - // ...and the start index is greater than the end index, - // this loop will never run. This is often confusing for developers - // who think that this will iterate from the larger value to the - // smaller value. - let ty = cx.tables.expr_ty(start); - let (sup, eq) = match (start_idx, end_idx) { - (Constant::Int(start_idx), Constant::Int(end_idx)) => ( - match ty.kind { - ty::Int(ity) => sext(cx.tcx, start_idx, ity) > sext(cx.tcx, end_idx, ity), - ty::Uint(_) => start_idx > end_idx, - _ => false, - }, - start_idx == end_idx, - ), - _ => (false, false), - }; - - if sup { - let start_snippet = snippet(cx, start.span, "_"); - let end_snippet = snippet(cx, end.span, "_"); - let dots = if limits == ast::RangeLimits::Closed { - "..=" - } else { - ".." - }; - - span_lint_and_then( - cx, - REVERSE_RANGE_LOOP, - expr.span, - "this range is empty so this for loop will never run", - |diag| { - diag.span_suggestion( - arg.span, - "consider using the following if you are attempting to iterate over this \ - range in reverse", - format!( - "({end}{dots}{start}).rev()", - end = end_snippet, - dots = dots, - start = start_snippet - ), - Applicability::MaybeIncorrect, - ); - }, - ); - } else if eq && limits != ast::RangeLimits::Closed { - // if they are equal, it's also problematic - this loop - // will never run. - span_lint( - cx, - REVERSE_RANGE_LOOP, - expr.span, - "this range is empty so this for loop will never run", - ); - } - } - } - } -} - fn lint_iter_method(cx: &LateContext<'_, '_>, args: &[Expr<'_>], arg: &Expr<'_>, method_name: &str) { let mut applicability = Applicability::MachineApplicable; let object = snippet_with_applicability(cx, args[0].span, "_", &mut applicability); diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 51d1cb2216a..e1a6d4bdd31 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1922,11 +1922,11 @@ pub static ref ALL_LINTS: Vec = vec![ module: "methods", }, Lint { - name: "reverse_range_loop", + name: "reversed_empty_ranges", group: "correctness", - desc: "iteration over an empty range, such as `10..0` or `5..5`", + desc: "reversing the limits of range expressions, resulting in empty ranges", deprecation: None, - module: "loops", + module: "ranges", }, Lint { name: "same_functions_in_if_condition", diff --git a/tests/ui/for_loop_fixable.fixed b/tests/ui/for_loop_fixable.fixed index 5fc84ada9ef..249a88a0b39 100644 --- a/tests/ui/for_loop_fixable.fixed +++ b/tests/ui/for_loop_fixable.fixed @@ -21,7 +21,6 @@ impl Unrelated { clippy::explicit_iter_loop, clippy::explicit_into_iter_loop, clippy::iter_next_loop, - clippy::reverse_range_loop, clippy::for_kv_map )] #[allow( @@ -32,61 +31,8 @@ impl Unrelated { )] #[allow(clippy::many_single_char_names, unused_variables)] fn main() { - const MAX_LEN: usize = 42; let mut vec = vec![1, 2, 3, 4]; - for i in (0..10).rev() { - println!("{}", i); - } - - for i in (0..=10).rev() { - println!("{}", i); - } - - for i in (0..MAX_LEN).rev() { - println!("{}", i); - } - - for i in 5..=5 { - // not an error, this is the range with only one element “5” - println!("{}", i); - } - - for i in 0..10 { - // not an error, the start index is less than the end index - println!("{}", i); - } - - for i in -10..0 { - // not an error - println!("{}", i); - } - - for i in (10..0).map(|x| x * 2) { - // not an error, it can't be known what arbitrary methods do to a range - println!("{}", i); - } - - // testing that the empty range lint folds constants - for i in (5 + 4..10).rev() { - println!("{}", i); - } - - for i in ((3 - 1)..(5 + 2)).rev() { - println!("{}", i); - } - - for i in (2 * 2)..(2 * 3) { - // no error, 4..6 is fine - println!("{}", i); - } - - let x = 42; - for i in x..10 { - // no error, not constant-foldable - println!("{}", i); - } - // See #601 for i in 0..10 { // no error, id_col does not exist outside the loop diff --git a/tests/ui/for_loop_fixable.rs b/tests/ui/for_loop_fixable.rs index 4165b0dc004..306d85a6351 100644 --- a/tests/ui/for_loop_fixable.rs +++ b/tests/ui/for_loop_fixable.rs @@ -21,7 +21,6 @@ impl Unrelated { clippy::explicit_iter_loop, clippy::explicit_into_iter_loop, clippy::iter_next_loop, - clippy::reverse_range_loop, clippy::for_kv_map )] #[allow( @@ -32,61 +31,8 @@ impl Unrelated { )] #[allow(clippy::many_single_char_names, unused_variables)] fn main() { - const MAX_LEN: usize = 42; let mut vec = vec![1, 2, 3, 4]; - for i in 10..0 { - println!("{}", i); - } - - for i in 10..=0 { - println!("{}", i); - } - - for i in MAX_LEN..0 { - println!("{}", i); - } - - for i in 5..=5 { - // not an error, this is the range with only one element “5” - println!("{}", i); - } - - for i in 0..10 { - // not an error, the start index is less than the end index - println!("{}", i); - } - - for i in -10..0 { - // not an error - println!("{}", i); - } - - for i in (10..0).map(|x| x * 2) { - // not an error, it can't be known what arbitrary methods do to a range - println!("{}", i); - } - - // testing that the empty range lint folds constants - for i in 10..5 + 4 { - println!("{}", i); - } - - for i in (5 + 2)..(3 - 1) { - println!("{}", i); - } - - for i in (2 * 2)..(2 * 3) { - // no error, 4..6 is fine - println!("{}", i); - } - - let x = 42; - for i in x..10 { - // no error, not constant-foldable - println!("{}", i); - } - // See #601 for i in 0..10 { // no error, id_col does not exist outside the loop diff --git a/tests/ui/for_loop_fixable.stderr b/tests/ui/for_loop_fixable.stderr index cffb4b9f0a9..ddfe66d675f 100644 --- a/tests/ui/for_loop_fixable.stderr +++ b/tests/ui/for_loop_fixable.stderr @@ -1,61 +1,5 @@ -error: this range is empty so this for loop will never run - --> $DIR/for_loop_fixable.rs:38:14 - | -LL | for i in 10..0 { - | ^^^^^ - | - = note: `-D clippy::reverse-range-loop` implied by `-D warnings` -help: consider using the following if you are attempting to iterate over this range in reverse - | -LL | for i in (0..10).rev() { - | ^^^^^^^^^^^^^ - -error: this range is empty so this for loop will never run - --> $DIR/for_loop_fixable.rs:42:14 - | -LL | for i in 10..=0 { - | ^^^^^^ - | -help: consider using the following if you are attempting to iterate over this range in reverse - | -LL | for i in (0..=10).rev() { - | ^^^^^^^^^^^^^^ - -error: this range is empty so this for loop will never run - --> $DIR/for_loop_fixable.rs:46:14 - | -LL | for i in MAX_LEN..0 { - | ^^^^^^^^^^ - | -help: consider using the following if you are attempting to iterate over this range in reverse - | -LL | for i in (0..MAX_LEN).rev() { - | ^^^^^^^^^^^^^^^^^^ - -error: this range is empty so this for loop will never run - --> $DIR/for_loop_fixable.rs:71:14 - | -LL | for i in 10..5 + 4 { - | ^^^^^^^^^ - | -help: consider using the following if you are attempting to iterate over this range in reverse - | -LL | for i in (5 + 4..10).rev() { - | ^^^^^^^^^^^^^^^^^ - -error: this range is empty so this for loop will never run - --> $DIR/for_loop_fixable.rs:75:14 - | -LL | for i in (5 + 2)..(3 - 1) { - | ^^^^^^^^^^^^^^^^ - | -help: consider using the following if you are attempting to iterate over this range in reverse - | -LL | for i in ((3 - 1)..(5 + 2)).rev() { - | ^^^^^^^^^^^^^^^^^^^^^^^^ - error: it is more concise to loop over references to containers instead of using explicit iteration methods - --> $DIR/for_loop_fixable.rs:97:15 + --> $DIR/for_loop_fixable.rs:43:15 | LL | for _v in vec.iter() {} | ^^^^^^^^^^ help: to write this more concisely, try: `&vec` @@ -63,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:99:15 + --> $DIR/for_loop_fixable.rs:45: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:102:15 + --> $DIR/for_loop_fixable.rs:48:15 | LL | for _v in out_vec.into_iter() {} | ^^^^^^^^^^^^^^^^^^^ help: to write this more concisely, try: `out_vec` @@ -77,76 +21,76 @@ 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:107:15 + --> $DIR/for_loop_fixable.rs:53: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:111:15 + --> $DIR/for_loop_fixable.rs:57: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:116:15 + --> $DIR/for_loop_fixable.rs:62: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:119:15 + --> $DIR/for_loop_fixable.rs:65: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:122:15 + --> $DIR/for_loop_fixable.rs:68: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:125:15 + --> $DIR/for_loop_fixable.rs:71: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:128:15 + --> $DIR/for_loop_fixable.rs:74: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:131:15 + --> $DIR/for_loop_fixable.rs:77: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:134:15 + --> $DIR/for_loop_fixable.rs:80: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:309:18 + --> $DIR/for_loop_fixable.rs:255: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:329:18 + --> $DIR/for_loop_fixable.rs:275: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:331:18 + --> $DIR/for_loop_fixable.rs:277:18 | LL | for _ in r.into_iter() {} | ^^^^^^^^^^^^^ help: to write this more concisely, try: `r` -error: aborting due to 20 previous errors +error: aborting due to 15 previous errors diff --git a/tests/ui/for_loop_unfixable.rs b/tests/ui/for_loop_unfixable.rs index 179b255e08c..e73536052f0 100644 --- a/tests/ui/for_loop_unfixable.rs +++ b/tests/ui/for_loop_unfixable.rs @@ -5,7 +5,6 @@ clippy::explicit_iter_loop, clippy::explicit_into_iter_loop, clippy::iter_next_loop, - clippy::reverse_range_loop, clippy::for_kv_map )] #[allow( @@ -16,25 +15,8 @@ unused, dead_code )] -#[allow(clippy::many_single_char_names, unused_variables)] fn main() { - for i in 5..5 { - println!("{}", i); - } - let vec = vec![1, 2, 3, 4]; for _v in vec.iter().next() {} - - for i in (5 + 2)..(8 - 1) { - println!("{}", i); - } - - const ZERO: usize = 0; - - for i in ZERO..vec.len() { - if f(&vec[i], &vec[i]) { - panic!("at the disco"); - } - } } diff --git a/tests/ui/for_loop_unfixable.stderr b/tests/ui/for_loop_unfixable.stderr index 1da8e0f3588..1c9287b6acb 100644 --- a/tests/ui/for_loop_unfixable.stderr +++ b/tests/ui/for_loop_unfixable.stderr @@ -1,9 +1,10 @@ -error[E0425]: cannot find function `f` in this scope - --> $DIR/for_loop_unfixable.rs:36:12 +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 | -LL | if f(&vec[i], &vec[i]) { - | ^ help: a local variable with a similar name exists: `i` +LL | for _v in vec.iter().next() {} + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::iter-next-loop` implied by `-D warnings` error: aborting due to previous error -For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/manual_memcpy.rs b/tests/ui/manual_memcpy.rs index 9c24d6d4db1..0083f94798f 100644 --- a/tests/ui/manual_memcpy.rs +++ b/tests/ui/manual_memcpy.rs @@ -104,7 +104,7 @@ pub fn manual_copy(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { dst[i - 0] = src[i]; } - #[allow(clippy::reverse_range_loop)] + #[allow(clippy::reversed_empty_ranges)] for i in 0..0 { dst[i] = src[i]; } -- cgit 1.4.1-3-g733a5 From 064431d22fbc28f958a1d18da8a5e01ff99dadb0 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Mon, 11 May 2020 23:48:48 +0200 Subject: Re-add old tests for empty range loops --- tests/ui/reversed_empty_ranges_loops_fixable.fixed | 57 ++++++++++++++++++ tests/ui/reversed_empty_ranges_loops_fixable.rs | 57 ++++++++++++++++++ .../ui/reversed_empty_ranges_loops_fixable.stderr | 69 ++++++++++++++++++++++ tests/ui/reversed_empty_ranges_loops_unfixable.rs | 11 ++++ .../reversed_empty_ranges_loops_unfixable.stderr | 16 +++++ 5 files changed, 210 insertions(+) create mode 100644 tests/ui/reversed_empty_ranges_loops_fixable.fixed create mode 100644 tests/ui/reversed_empty_ranges_loops_fixable.rs create mode 100644 tests/ui/reversed_empty_ranges_loops_fixable.stderr create mode 100644 tests/ui/reversed_empty_ranges_loops_unfixable.rs create mode 100644 tests/ui/reversed_empty_ranges_loops_unfixable.stderr (limited to 'tests') diff --git a/tests/ui/reversed_empty_ranges_loops_fixable.fixed b/tests/ui/reversed_empty_ranges_loops_fixable.fixed new file mode 100644 index 00000000000..f1503ed6d12 --- /dev/null +++ b/tests/ui/reversed_empty_ranges_loops_fixable.fixed @@ -0,0 +1,57 @@ +// run-rustfix +#![warn(clippy::reversed_empty_ranges)] + +fn main() { + const MAX_LEN: usize = 42; + + for i in (0..10).rev() { + println!("{}", i); + } + + for i in (0..=10).rev() { + println!("{}", i); + } + + for i in (0..MAX_LEN).rev() { + println!("{}", i); + } + + for i in 5..=5 { + // not an error, this is the range with only one element “5” + println!("{}", i); + } + + for i in 0..10 { + // not an error, the start index is less than the end index + println!("{}", i); + } + + for i in -10..0 { + // not an error + println!("{}", i); + } + + for i in (0..10).rev().map(|x| x * 2) { + println!("{}", i); + } + + // testing that the empty range lint folds constants + for i in (5 + 4..10).rev() { + println!("{}", i); + } + + for i in ((3 - 1)..(5 + 2)).rev() { + println!("{}", i); + } + + for i in (2 * 2)..(2 * 3) { + // no error, 4..6 is fine + println!("{}", i); + } + + let x = 42; + for i in x..10 { + // no error, not constant-foldable + println!("{}", i); + } +} diff --git a/tests/ui/reversed_empty_ranges_loops_fixable.rs b/tests/ui/reversed_empty_ranges_loops_fixable.rs new file mode 100644 index 00000000000..a733788dc22 --- /dev/null +++ b/tests/ui/reversed_empty_ranges_loops_fixable.rs @@ -0,0 +1,57 @@ +// run-rustfix +#![warn(clippy::reversed_empty_ranges)] + +fn main() { + const MAX_LEN: usize = 42; + + for i in 10..0 { + println!("{}", i); + } + + for i in 10..=0 { + println!("{}", i); + } + + for i in MAX_LEN..0 { + println!("{}", i); + } + + for i in 5..=5 { + // not an error, this is the range with only one element “5” + println!("{}", i); + } + + for i in 0..10 { + // not an error, the start index is less than the end index + println!("{}", i); + } + + for i in -10..0 { + // not an error + println!("{}", i); + } + + for i in (10..0).map(|x| x * 2) { + println!("{}", i); + } + + // testing that the empty range lint folds constants + for i in 10..5 + 4 { + println!("{}", i); + } + + for i in (5 + 2)..(3 - 1) { + println!("{}", i); + } + + for i in (2 * 2)..(2 * 3) { + // no error, 4..6 is fine + println!("{}", i); + } + + let x = 42; + for i in x..10 { + // no error, not constant-foldable + println!("{}", i); + } +} diff --git a/tests/ui/reversed_empty_ranges_loops_fixable.stderr b/tests/ui/reversed_empty_ranges_loops_fixable.stderr new file mode 100644 index 00000000000..e89e040a0ff --- /dev/null +++ b/tests/ui/reversed_empty_ranges_loops_fixable.stderr @@ -0,0 +1,69 @@ +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_loops_fixable.rs:7:14 + | +LL | for i in 10..0 { + | ^^^^^ + | + = note: `-D clippy::reversed-empty-ranges` implied by `-D warnings` +help: consider using the following if you are attempting to iterate over this range in reverse + | +LL | for i in (0..10).rev() { + | ^^^^^^^^^^^^^ + +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_loops_fixable.rs:11:14 + | +LL | for i in 10..=0 { + | ^^^^^^ + | +help: consider using the following if you are attempting to iterate over this range in reverse + | +LL | for i in (0..=10).rev() { + | ^^^^^^^^^^^^^^ + +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_loops_fixable.rs:15:14 + | +LL | for i in MAX_LEN..0 { + | ^^^^^^^^^^ + | +help: consider using the following if you are attempting to iterate over this range in reverse + | +LL | for i in (0..MAX_LEN).rev() { + | ^^^^^^^^^^^^^^^^^^ + +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_loops_fixable.rs:34:14 + | +LL | for i in (10..0).map(|x| x * 2) { + | ^^^^^^^ + | +help: consider using the following if you are attempting to iterate over this range in reverse + | +LL | for i in (0..10).rev().map(|x| x * 2) { + | ^^^^^^^^^^^^^ + +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_loops_fixable.rs:39:14 + | +LL | for i in 10..5 + 4 { + | ^^^^^^^^^ + | +help: consider using the following if you are attempting to iterate over this range in reverse + | +LL | for i in (5 + 4..10).rev() { + | ^^^^^^^^^^^^^^^^^ + +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_loops_fixable.rs:43:14 + | +LL | for i in (5 + 2)..(3 - 1) { + | ^^^^^^^^^^^^^^^^ + | +help: consider using the following if you are attempting to iterate over this range in reverse + | +LL | for i in ((3 - 1)..(5 + 2)).rev() { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors + diff --git a/tests/ui/reversed_empty_ranges_loops_unfixable.rs b/tests/ui/reversed_empty_ranges_loops_unfixable.rs new file mode 100644 index 00000000000..c4c57224416 --- /dev/null +++ b/tests/ui/reversed_empty_ranges_loops_unfixable.rs @@ -0,0 +1,11 @@ +#![warn(clippy::reversed_empty_ranges)] + +fn main() { + for i in 5..5 { + println!("{}", i); + } + + for i in (5 + 2)..(8 - 1) { + println!("{}", i); + } +} diff --git a/tests/ui/reversed_empty_ranges_loops_unfixable.stderr b/tests/ui/reversed_empty_ranges_loops_unfixable.stderr new file mode 100644 index 00000000000..30095d20cfd --- /dev/null +++ b/tests/ui/reversed_empty_ranges_loops_unfixable.stderr @@ -0,0 +1,16 @@ +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_loops_unfixable.rs:4:14 + | +LL | for i in 5..5 { + | ^^^^ + | + = note: `-D clippy::reversed-empty-ranges` implied by `-D warnings` + +error: this range is empty so it will yield no values + --> $DIR/reversed_empty_ranges_loops_unfixable.rs:8:14 + | +LL | for i in (5 + 2)..(8 - 1) { + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From e4cd8e7961b553cac44671d63bc6dfc2223ea66b Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Tue, 12 May 2020 22:05:56 +0200 Subject: Fix ICE caused in unwrap module --- clippy_lints/src/unwrap.rs | 12 ++++++++++-- tests/ui/checked_unwrap/simple_conditionals.rs | 21 +++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index f3844c7d3b6..8b971e7064b 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -8,6 +8,7 @@ use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Path, QPath, UnO use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::Ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; @@ -90,6 +91,14 @@ fn collect_unwrap_info<'a, 'tcx>( branch: &'tcx Expr<'_>, invert: bool, ) -> Vec> { + fn is_relevant_option_call(cx: &LateContext<'_, '_>, ty: Ty<'_>, method_name: &str) -> bool { + is_type_diagnostic_item(cx, ty, sym!(option_type)) && ["is_some", "is_none"].contains(&method_name) + } + + fn is_relevant_result_call(cx: &LateContext<'_, '_>, ty: Ty<'_>, method_name: &str) -> bool { + is_type_diagnostic_item(cx, ty, sym!(result_type)) && ["is_ok", "is_err"].contains(&method_name) + } + if let ExprKind::Binary(op, left, right) = &expr.kind { match (invert, op.node) { (false, BinOpKind::And) | (false, BinOpKind::BitAnd) | (true, BinOpKind::Or) | (true, BinOpKind::BitOr) => { @@ -106,9 +115,8 @@ fn collect_unwrap_info<'a, 'tcx>( if let ExprKind::MethodCall(method_name, _, args) = &expr.kind; if let ExprKind::Path(QPath::Resolved(None, path)) = &args[0].kind; let ty = cx.tables.expr_ty(&args[0]); - if is_type_diagnostic_item(cx, ty, sym!(option_type)) || is_type_diagnostic_item(cx, ty, sym!(result_type)); let name = method_name.ident.as_str(); - if ["is_some", "is_none", "is_ok", "is_err"].contains(&&*name); + if is_relevant_option_call(cx, ty, &name) || is_relevant_result_call(cx, ty, &name); then { assert!(args.len() == 1); let unwrappable = match name.as_ref() { diff --git a/tests/ui/checked_unwrap/simple_conditionals.rs b/tests/ui/checked_unwrap/simple_conditionals.rs index 3e7b4b390ba..49794e0c241 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.rs +++ b/tests/ui/checked_unwrap/simple_conditionals.rs @@ -78,3 +78,24 @@ fn main() { assert!(x.is_ok(), "{:?}", x.unwrap_err()); // ok, it's a common test pattern } + +mod issue_5579 { + trait IsErr { + fn is_err(&self, err: &str) -> bool; + } + + impl IsErr for Option { + fn is_err(&self, _err: &str) -> bool { + true + } + } + + #[allow(unused)] + fn boom() { + let t = Some(1); + + if t.is_err("") { + t.unwrap(); + } + } +} -- cgit 1.4.1-3-g733a5 From 8d1029d3ca013687422b58d0e99084a4e3421089 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 13 May 2020 20:47:44 +0200 Subject: Move test for issue 5579 under tests/ui/crashes --- tests/ui/checked_unwrap/simple_conditionals.rs | 21 --------------------- tests/ui/crashes/ice-5579.rs | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 21 deletions(-) create mode 100644 tests/ui/crashes/ice-5579.rs (limited to 'tests') diff --git a/tests/ui/checked_unwrap/simple_conditionals.rs b/tests/ui/checked_unwrap/simple_conditionals.rs index 49794e0c241..3e7b4b390ba 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.rs +++ b/tests/ui/checked_unwrap/simple_conditionals.rs @@ -78,24 +78,3 @@ fn main() { assert!(x.is_ok(), "{:?}", x.unwrap_err()); // ok, it's a common test pattern } - -mod issue_5579 { - trait IsErr { - fn is_err(&self, err: &str) -> bool; - } - - impl IsErr for Option { - fn is_err(&self, _err: &str) -> bool { - true - } - } - - #[allow(unused)] - fn boom() { - let t = Some(1); - - if t.is_err("") { - t.unwrap(); - } - } -} diff --git a/tests/ui/crashes/ice-5579.rs b/tests/ui/crashes/ice-5579.rs new file mode 100644 index 00000000000..e1842c73f0e --- /dev/null +++ b/tests/ui/crashes/ice-5579.rs @@ -0,0 +1,17 @@ +trait IsErr { + fn is_err(&self, err: &str) -> bool; +} + +impl IsErr for Option { + fn is_err(&self, _err: &str) -> bool { + true + } +} + +fn main() { + let t = Some(1); + + if t.is_err("") { + t.unwrap(); + } +} -- cgit 1.4.1-3-g733a5 From 9217675c7f6ccd2efa18047ebb0c86841683b6a5 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Thu, 14 May 2020 00:26:09 +0200 Subject: Fix comparison_chain false positive --- clippy_lints/src/comparison_chain.rs | 17 ++++++++-- tests/ui/comparison_chain.rs | 66 ++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs index 96df3ffe3ce..93e29edcaa5 100644 --- a/clippy_lints/src/comparison_chain.rs +++ b/clippy_lints/src/comparison_chain.rs @@ -81,12 +81,23 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ComparisonChain { // Check that both sets of operands are equal let mut spanless_eq = SpanlessEq::new(cx); - if (!spanless_eq.eq_expr(lhs1, lhs2) || !spanless_eq.eq_expr(rhs1, rhs2)) - && (!spanless_eq.eq_expr(lhs1, rhs2) || !spanless_eq.eq_expr(rhs1, lhs2)) - { + let same_fixed_operands = spanless_eq.eq_expr(lhs1, lhs2) && spanless_eq.eq_expr(rhs1, rhs2); + let same_transposed_operands = spanless_eq.eq_expr(lhs1, rhs2) && spanless_eq.eq_expr(rhs1, lhs2); + + if !same_fixed_operands && !same_transposed_operands { return; } + // Check that if the operation is the same, either it's not `==` or the operands are transposed + if kind1.node == kind2.node { + if kind1.node == BinOpKind::Eq { + return; + } + if !same_transposed_operands { + return; + } + } + // Check that the type being compared implements `core::cmp::Ord` let ty = cx.tables.expr_ty(lhs1); let is_ord = get_trait_def_id(cx, &paths::ORD).map_or(false, |id| implements_trait(cx, ty, id, &[])); diff --git a/tests/ui/comparison_chain.rs b/tests/ui/comparison_chain.rs index 9c2128469de..3b03f8c7dfe 100644 --- a/tests/ui/comparison_chain.rs +++ b/tests/ui/comparison_chain.rs @@ -137,4 +137,70 @@ fn h(x: T, y: T, z: T) { } } +// The following uses should be ignored +mod issue_5212 { + use super::{a, b, c}; + fn foo() -> u8 { + 21 + } + + fn same_operation_equals() { + // operands are fixed + + if foo() == 42 { + a() + } else if foo() == 42 { + b() + } + + if foo() == 42 { + a() + } else if foo() == 42 { + b() + } else { + c() + } + + // operands are transposed + + if foo() == 42 { + a() + } else if 42 == foo() { + b() + } + } + + fn same_operation_not_equals() { + // operands are fixed + + if foo() > 42 { + a() + } else if foo() > 42 { + b() + } + + if foo() > 42 { + a() + } else if foo() > 42 { + b() + } else { + c() + } + + if foo() < 42 { + a() + } else if foo() < 42 { + b() + } + + if foo() < 42 { + a() + } else if foo() < 42 { + b() + } else { + c() + } + } +} + fn main() {} -- cgit 1.4.1-3-g733a5 From 945c9447093a2ca944e70bae125f2af69f8eac16 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 3 May 2020 11:20:51 +0200 Subject: Merge `block_in_if_condition_expr` and `block_in_if_condition_stmt` lints into `block_in_if_condition` lint --- CHANGELOG.md | 3 +- clippy_lints/src/block_in_if_condition.rs | 51 +++++++++++++-------------- clippy_lints/src/lib.rs | 9 ++--- src/lintlist/mod.rs | 11 ++---- tests/ui/block_in_if_condition.fixed | 3 +- tests/ui/block_in_if_condition.rs | 3 +- tests/ui/block_in_if_condition.stderr | 10 +++--- tests/ui/block_in_if_condition_closure.rs | 5 ++- tests/ui/block_in_if_condition_closure.stderr | 6 ++-- 9 files changed, 41 insertions(+), 60 deletions(-) (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index b25ef049356..0b270e6acd2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1274,8 +1274,7 @@ Released 2018-09-13 [`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock [`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask [`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name -[`block_in_if_condition_expr`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_in_if_condition_expr -[`block_in_if_condition_stmt`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_in_if_condition_stmt +[`block_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_in_if_condition [`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison [`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const [`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box diff --git a/clippy_lints/src/block_in_if_condition.rs b/clippy_lints/src/block_in_if_condition.rs index 9e533eaa32c..8a5e595749f 100644 --- a/clippy_lints/src/block_in_if_condition.rs +++ b/clippy_lints/src/block_in_if_condition.rs @@ -8,43 +8,40 @@ use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { - /// **What it does:** Checks for `if` conditions that use blocks to contain an - /// expression. + /// **What it does:** Checks for `if` conditions that use blocks containing an + /// expression, statements or conditions that use closures with blocks. /// - /// **Why is this bad?** It isn't really Rust style, same as using parentheses - /// to contain expressions. + /// **Why is this bad?** Style, using blocks in the condition makes it hard to read. /// /// **Known problems:** None. /// - /// **Example:** + /// **Examples:** /// ```rust + /// // Bad /// if { true } { /* ... */ } + /// + /// // Good + /// if true { /* ... */ } /// ``` - pub BLOCK_IN_IF_CONDITION_EXPR, - style, - "braces that can be eliminated in conditions, e.g., `if { true } ...`" -} - -declare_clippy_lint! { - /// **What it does:** Checks for `if` conditions that use blocks containing - /// statements, or conditions that use closures with blocks. /// - /// **Why is this bad?** Using blocks in the condition makes it hard to read. + /// // or /// - /// **Known problems:** None. + /// ```rust + /// # fn somefunc() -> bool { true }; /// - /// **Example:** - /// ```rust,ignore - /// if { let x = somefunc(); x } {} - /// // or - /// if somefunc(|x| { x == 47 }) {} + /// // Bad + /// if { let x = somefunc(); x } { /* ... */ } + /// + /// // Good + /// let res = { let x = somefunc(); x }; + /// if res { /* ... */ } /// ``` - pub BLOCK_IN_IF_CONDITION_STMT, + pub BLOCK_IN_IF_CONDITION, style, - "complex blocks in conditions, e.g., `if { let x = true; x } ...`" + "useless or complex blocks that can be eliminated in conditions" } -declare_lint_pass!(BlockInIfCondition => [BLOCK_IN_IF_CONDITION_EXPR, BLOCK_IN_IF_CONDITION_STMT]); +declare_lint_pass!(BlockInIfCondition => [BLOCK_IN_IF_CONDITION]); struct ExVisitor<'a, 'tcx> { found_block: Option<&'tcx Expr<'tcx>>, @@ -72,7 +69,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ExVisitor<'a, 'tcx> { const BRACED_EXPR_MESSAGE: &str = "omit braces around single expression condition"; const COMPLEX_BLOCK_MESSAGE: &str = "in an `if` condition, avoid complex blocks or closures with blocks; \ - instead, move the block or closure higher and bind it with a `let`"; + instead, move the block or closure higher and bind it with a `let`"; impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BlockInIfCondition { fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { @@ -92,7 +89,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BlockInIfCondition { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, - BLOCK_IN_IF_CONDITION_EXPR, + BLOCK_IN_IF_CONDITION, cond.span, BRACED_EXPR_MESSAGE, "try", @@ -118,7 +115,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BlockInIfCondition { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, - BLOCK_IN_IF_CONDITION_STMT, + BLOCK_IN_IF_CONDITION, expr.span.with_hi(cond.span.hi()), COMPLEX_BLOCK_MESSAGE, "try", @@ -140,7 +137,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BlockInIfCondition { let mut visitor = ExVisitor { found_block: None, cx }; walk_expr(&mut visitor, cond); if let Some(block) = visitor.found_block { - span_lint(cx, BLOCK_IN_IF_CONDITION_STMT, block.span, COMPLEX_BLOCK_MESSAGE); + span_lint(cx, BLOCK_IN_IF_CONDITION, block.span, COMPLEX_BLOCK_MESSAGE); } } } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 0c4daeb731f..98b696533d8 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -507,8 +507,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &bit_mask::INEFFECTIVE_BIT_MASK, &bit_mask::VERBOSE_BIT_MASK, &blacklisted_name::BLACKLISTED_NAME, - &block_in_if_condition::BLOCK_IN_IF_CONDITION_EXPR, - &block_in_if_condition::BLOCK_IN_IF_CONDITION_STMT, + &block_in_if_condition::BLOCK_IN_IF_CONDITION, &booleans::LOGIC_BUG, &booleans::NONMINIMAL_BOOL, &bytecount::NAIVE_BYTECOUNT, @@ -1209,8 +1208,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&bit_mask::INEFFECTIVE_BIT_MASK), LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&blacklisted_name::BLACKLISTED_NAME), - LintId::of(&block_in_if_condition::BLOCK_IN_IF_CONDITION_EXPR), - LintId::of(&block_in_if_condition::BLOCK_IN_IF_CONDITION_STMT), + LintId::of(&block_in_if_condition::BLOCK_IN_IF_CONDITION), LintId::of(&booleans::LOGIC_BUG), LintId::of(&booleans::NONMINIMAL_BOOL), LintId::of(&bytecount::NAIVE_BYTECOUNT), @@ -1456,8 +1454,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&attrs::UNKNOWN_CLIPPY_LINTS), LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&blacklisted_name::BLACKLISTED_NAME), - LintId::of(&block_in_if_condition::BLOCK_IN_IF_CONDITION_EXPR), - LintId::of(&block_in_if_condition::BLOCK_IN_IF_CONDITION_STMT), + LintId::of(&block_in_if_condition::BLOCK_IN_IF_CONDITION), LintId::of(&collapsible_if::COLLAPSIBLE_IF), LintId::of(&comparison_chain::COMPARISON_CHAIN), LintId::of(&doc::MISSING_SAFETY_DOC), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index e1a6d4bdd31..4ae60f7d808 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -74,16 +74,9 @@ pub static ref ALL_LINTS: Vec = vec![ module: "blacklisted_name", }, Lint { - name: "block_in_if_condition_expr", + name: "block_in_if_condition", group: "style", - desc: "braces that can be eliminated in conditions, e.g., `if { true } ...`", - deprecation: None, - module: "block_in_if_condition", - }, - Lint { - name: "block_in_if_condition_stmt", - group: "style", - desc: "complex blocks in conditions, e.g., `if { let x = true; x } ...`", + desc: "useless or complex blocks that can be eliminated in conditions", deprecation: None, module: "block_in_if_condition", }, diff --git a/tests/ui/block_in_if_condition.fixed b/tests/ui/block_in_if_condition.fixed index 955801e40f9..ae01c6d3042 100644 --- a/tests/ui/block_in_if_condition.fixed +++ b/tests/ui/block_in_if_condition.fixed @@ -1,6 +1,5 @@ // run-rustfix -#![warn(clippy::block_in_if_condition_expr)] -#![warn(clippy::block_in_if_condition_stmt)] +#![warn(clippy::block_in_if_condition)] #![allow(unused, clippy::let_and_return)] #![warn(clippy::nonminimal_bool)] diff --git a/tests/ui/block_in_if_condition.rs b/tests/ui/block_in_if_condition.rs index a6ea01d5fc5..88555dc47c2 100644 --- a/tests/ui/block_in_if_condition.rs +++ b/tests/ui/block_in_if_condition.rs @@ -1,6 +1,5 @@ // run-rustfix -#![warn(clippy::block_in_if_condition_expr)] -#![warn(clippy::block_in_if_condition_stmt)] +#![warn(clippy::block_in_if_condition)] #![allow(unused, clippy::let_and_return)] #![warn(clippy::nonminimal_bool)] diff --git a/tests/ui/block_in_if_condition.stderr b/tests/ui/block_in_if_condition.stderr index b0a0a276c89..89e9ad26f49 100644 --- a/tests/ui/block_in_if_condition.stderr +++ b/tests/ui/block_in_if_condition.stderr @@ -1,5 +1,5 @@ error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` - --> $DIR/block_in_if_condition.rs:27:5 + --> $DIR/block_in_if_condition.rs:26:5 | LL | / if { LL | | let x = 3; @@ -7,7 +7,7 @@ LL | | x == 3 LL | | } { | |_____^ | - = note: `-D clippy::block-in-if-condition-stmt` implied by `-D warnings` + = note: `-D clippy::block-in-if-condition` implied by `-D warnings` help: try | LL | let res = { @@ -17,15 +17,13 @@ LL | }; if res { | error: omit braces around single expression condition - --> $DIR/block_in_if_condition.rs:38:8 + --> $DIR/block_in_if_condition.rs:37:8 | LL | if { true } { | ^^^^^^^^ help: try: `true` - | - = note: `-D clippy::block-in-if-condition-expr` implied by `-D warnings` error: this boolean expression can be simplified - --> $DIR/block_in_if_condition.rs:47:8 + --> $DIR/block_in_if_condition.rs:46:8 | LL | if true && x == 3 { | ^^^^^^^^^^^^^^ help: try: `x == 3` diff --git a/tests/ui/block_in_if_condition_closure.rs b/tests/ui/block_in_if_condition_closure.rs index bac3eda5e7f..87b3fb94daf 100644 --- a/tests/ui/block_in_if_condition_closure.rs +++ b/tests/ui/block_in_if_condition_closure.rs @@ -1,5 +1,4 @@ -#![warn(clippy::block_in_if_condition_expr)] -#![warn(clippy::block_in_if_condition_stmt)] +#![warn(clippy::block_in_if_condition)] #![allow(unused, clippy::let_and_return)] fn predicate bool, T>(pfn: F, val: T) -> bool { @@ -10,7 +9,7 @@ fn pred_test() { let v = 3; let sky = "blue"; // This is a sneaky case, where the block isn't directly in the condition, - // but is actually nside a closure that the condition is using. + // but is actually inside a closure that the condition is using. // The same principle applies -- add some extra expressions to make sure // linter isn't confused by them. if v == 3 diff --git a/tests/ui/block_in_if_condition_closure.stderr b/tests/ui/block_in_if_condition_closure.stderr index 86cd24fe763..3df25691c3c 100644 --- a/tests/ui/block_in_if_condition_closure.stderr +++ b/tests/ui/block_in_if_condition_closure.stderr @@ -1,5 +1,5 @@ error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` - --> $DIR/block_in_if_condition_closure.rs:19:17 + --> $DIR/block_in_if_condition_closure.rs:18:17 | LL | |x| { | _________________^ @@ -8,10 +8,10 @@ LL | | x == target LL | | }, | |_____________^ | - = note: `-D clippy::block-in-if-condition-stmt` implied by `-D warnings` + = note: `-D clippy::block-in-if-condition` implied by `-D warnings` error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` - --> $DIR/block_in_if_condition_closure.rs:28:13 + --> $DIR/block_in_if_condition_closure.rs:27:13 | LL | |x| { | _____________^ -- cgit 1.4.1-3-g733a5 From 6cbdd1e49dbb2355ac1036946a5a635e22023c6f Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 3 May 2020 12:28:40 +0200 Subject: Merge `option_map_unwrap_or`, `option_map_unwrap_or_else` and `result_map_unwrap_or_else` lints into `map_unwrap` lint --- CHANGELOG.md | 4 +- clippy_lints/src/lib.rs | 8 +- clippy_lints/src/methods/mod.rs | 92 ++++--------- clippy_lints/src/methods/option_map_unwrap_or.rs | 6 +- src/lintlist/mod.rs | 28 +--- tests/ui/map_unwrap.rs | 99 ++++++++++++++ tests/ui/map_unwrap.stderr | 161 +++++++++++++++++++++++ tests/ui/option_map_unwrap_or.rs | 88 ------------- tests/ui/option_map_unwrap_or.stderr | 138 ------------------- tests/ui/result_map_unwrap_or_else.rs | 23 ---- tests/ui/result_map_unwrap_or_else.stderr | 27 ---- 11 files changed, 301 insertions(+), 373 deletions(-) create mode 100644 tests/ui/map_unwrap.rs create mode 100644 tests/ui/map_unwrap.stderr delete mode 100644 tests/ui/option_map_unwrap_or.rs delete mode 100644 tests/ui/option_map_unwrap_or.stderr delete mode 100644 tests/ui/result_map_unwrap_or_else.rs delete mode 100644 tests/ui/result_map_unwrap_or_else.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b270e6acd2..28b05044db6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1430,6 +1430,7 @@ Released 2018-09-13 [`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone [`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry [`map_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten +[`map_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap [`match_as_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_as_ref [`match_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_bool [`match_on_vec_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_on_vec_items @@ -1499,8 +1500,6 @@ Released 2018-09-13 [`option_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_expect_used [`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none [`option_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn -[`option_map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or -[`option_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or_else [`option_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_option [`option_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_unwrap_used [`or_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_fun_call @@ -1542,7 +1541,6 @@ Released 2018-09-13 [`result_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_expect_used [`result_map_or_into_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_or_into_option [`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn -[`result_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unwrap_or_else [`result_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unwrap_used [`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges [`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 98b696533d8..c9a2ef49907 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -673,19 +673,17 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::ITER_SKIP_NEXT, &methods::MANUAL_SATURATING_ARITHMETIC, &methods::MAP_FLATTEN, + &methods::MAP_UNWRAP, &methods::NEW_RET_NO_SELF, &methods::OK_EXPECT, &methods::OPTION_AND_THEN_SOME, &methods::OPTION_AS_REF_DEREF, &methods::OPTION_EXPECT_USED, &methods::OPTION_MAP_OR_NONE, - &methods::OPTION_MAP_UNWRAP_OR, - &methods::OPTION_MAP_UNWRAP_OR_ELSE, &methods::OPTION_UNWRAP_USED, &methods::OR_FUN_CALL, &methods::RESULT_EXPECT_USED, &methods::RESULT_MAP_OR_INTO_OPTION, - &methods::RESULT_MAP_UNWRAP_OR_ELSE, &methods::RESULT_UNWRAP_USED, &methods::SEARCH_IS_SOME, &methods::SHOULD_IMPLEMENT_TRAIT, @@ -1152,9 +1150,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::FIND_MAP), LintId::of(&methods::INEFFICIENT_TO_STRING), LintId::of(&methods::MAP_FLATTEN), - LintId::of(&methods::OPTION_MAP_UNWRAP_OR), - LintId::of(&methods::OPTION_MAP_UNWRAP_OR_ELSE), - LintId::of(&methods::RESULT_MAP_UNWRAP_OR_ELSE), + LintId::of(&methods::MAP_UNWRAP), LintId::of(&misc::USED_UNDERSCORE_BINDING), LintId::of(&misc_early::UNSEPARATED_LITERAL_SUFFIX), LintId::of(&mut_mut::MUT_MUT), diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 3676dc5b09d..401298b2d51 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -257,59 +257,40 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for usage of `_.map(_).unwrap_or(_)`. + /// **What it does:** Checks for usage of `option.map(_).unwrap_or(_)` or `option.map(_).unwrap_or_else(_)` or + /// `result.map(_).unwrap_or_else(_)`. /// - /// **Why is this bad?** Readability, this can be written more concisely as - /// `_.map_or(_, _)`. + /// **Why is this bad?** Readability, these can be written more concisely (resp.) as + /// `option.map_or(_, _)`, `option.map_or_else(_, _)` and `result.map_or_else(_, _)`. /// /// **Known problems:** The order of the arguments is not in execution order /// - /// **Example:** + /// **Examples:** /// ```rust /// # let x = Some(1); - /// x.map(|a| a + 1).unwrap_or(0); - /// ``` - pub OPTION_MAP_UNWRAP_OR, - pedantic, - "using `Option.map(f).unwrap_or(a)`, which is more succinctly expressed as `map_or(a, f)`" -} - -declare_clippy_lint! { - /// **What it does:** Checks for usage of `_.map(_).unwrap_or_else(_)`. - /// - /// **Why is this bad?** Readability, this can be written more concisely as - /// `_.map_or_else(_, _)`. /// - /// **Known problems:** The order of the arguments is not in execution order. + /// // Bad + /// x.map(|a| a + 1).unwrap_or(0); /// - /// **Example:** - /// ```rust - /// # let x = Some(1); - /// # fn some_function() -> usize { 1 } - /// x.map(|a| a + 1).unwrap_or_else(some_function); + /// // Good + /// x.map_or(0, |a| a + 1); /// ``` - pub OPTION_MAP_UNWRAP_OR_ELSE, - pedantic, - "using `Option.map(f).unwrap_or_else(g)`, which is more succinctly expressed as `map_or_else(g, f)`" -} - -declare_clippy_lint! { - /// **What it does:** Checks for usage of `result.map(_).unwrap_or_else(_)`. /// - /// **Why is this bad?** Readability, this can be written more concisely as - /// `result.map_or_else(_, _)`. - /// - /// **Known problems:** None. + /// // or /// - /// **Example:** /// ```rust /// # let x: Result = Ok(1); /// # fn some_function(foo: ()) -> usize { 1 } + /// + /// // Bad /// x.map(|a| a + 1).unwrap_or_else(some_function); + /// + /// // Good + /// x.map_or_else(some_function, |a| a + 1); /// ``` - pub RESULT_MAP_UNWRAP_OR_ELSE, + pub MAP_UNWRAP, pedantic, - "using `Result.map(f).unwrap_or_else(g)`, which is more succinctly expressed as `.map_or_else(g, f)`" + "using `.map(f).unwrap_or(a)` or `.map(f).unwrap_or_else(func)`, which are more succinctly expressed as `map_or(a, f)` or `map_or_else(a, f)`" } declare_clippy_lint! { @@ -1294,9 +1275,7 @@ declare_lint_pass!(Methods => [ WRONG_SELF_CONVENTION, WRONG_PUB_SELF_CONVENTION, OK_EXPECT, - OPTION_MAP_UNWRAP_OR, - OPTION_MAP_UNWRAP_OR_ELSE, - RESULT_MAP_UNWRAP_OR_ELSE, + MAP_UNWRAP, RESULT_MAP_OR_INTO_OPTION, OPTION_MAP_OR_NONE, OPTION_AND_THEN_SOME, @@ -1503,9 +1482,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods { cx, lint, first_arg.pat.span, - &format!( - "methods called `{}` usually take {}; consider choosing a less \ - ambiguous name", + &format!("methods called `{}` usually take {}; consider choosing a less ambiguous name", conv, &self_kinds .iter() @@ -1678,7 +1655,7 @@ fn lint_or_fun_call<'a, 'tcx>( let self_ty = cx.tables.expr_ty(self_expr); if let Some(&(_, fn_has_arguments, poss, suffix)) = - know_types.iter().find(|&&i| match_type(cx, self_ty, i.0)); + know_types.iter().find(|&&i| match_type(cx, self_ty, i.0)); if poss.contains(&name); @@ -1931,7 +1908,7 @@ fn lint_clone_on_copy(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, arg: &hir: CLONE_DOUBLE_REF, expr.span, "using `clone` on a double-reference; \ - this will copy the reference instead of cloning the inner type", + this will copy the reference instead of cloning the inner type", |diag| { if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) { let mut ty = innermost; @@ -2121,7 +2098,7 @@ fn lint_iter_cloned_collect<'a, 'tcx>( ITER_CLONED_COLLECT, to_replace, "called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and \ - more readable", + more readable", "try", ".to_vec()".to_string(), Applicability::MachineApplicable, @@ -2436,7 +2413,7 @@ fn lint_unwrap(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, unwrap_args: &[hi None, &format!( "if you don't want to handle the `{}` case gracefully, consider \ - using `expect()` to provide a better panic message", + using `expect()` to provide a better panic message", none_value, ), ); @@ -2494,7 +2471,7 @@ fn lint_map_flatten<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr< // lint if caller of `.map().flatten()` is an Iterator if match_trait_method(cx, expr, &paths::ITERATOR) { let msg = "called `map(..).flatten()` on an `Iterator`. \ - This is more succinctly expressed by calling `.flat_map(..)`"; + This is more succinctly expressed by calling `.flat_map(..)`"; let self_snippet = snippet(cx, map_args[0].span, ".."); let func_snippet = snippet(cx, map_args[1].span, ".."); let hint = format!("{0}.flat_map({1})", self_snippet, func_snippet); @@ -2555,10 +2532,10 @@ fn lint_map_unwrap_or_else<'a, 'tcx>( // lint message let msg = if is_option { "called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling \ - `map_or_else(g, f)` instead" + `map_or_else(g, f)` instead" } else { "called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling \ - `.map_or_else(g, f)` instead" + `.map_or_else(g, f)` instead" }; // get snippets for args to map() and unwrap_or_else() let map_snippet = snippet(cx, map_args[1].span, ".."); @@ -2570,11 +2547,7 @@ fn lint_map_unwrap_or_else<'a, 'tcx>( if same_span && !multiline { span_lint_and_note( cx, - if is_option { - OPTION_MAP_UNWRAP_OR_ELSE - } else { - RESULT_MAP_UNWRAP_OR_ELSE - }, + MAP_UNWRAP, expr.span, msg, None, @@ -2584,16 +2557,7 @@ fn lint_map_unwrap_or_else<'a, 'tcx>( ), ); } else if same_span && multiline { - span_lint( - cx, - if is_option { - OPTION_MAP_UNWRAP_OR_ELSE - } else { - RESULT_MAP_UNWRAP_OR_ELSE - }, - expr.span, - msg, - ); + span_lint(cx, MAP_UNWRAP, expr.span, msg); }; } } diff --git a/clippy_lints/src/methods/option_map_unwrap_or.rs b/clippy_lints/src/methods/option_map_unwrap_or.rs index bf9dd3c9369..fcaa9b47e64 100644 --- a/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -9,7 +9,7 @@ use rustc_middle::hir::map::Map; use rustc_span::source_map::Span; use rustc_span::symbol::Symbol; -use super::OPTION_MAP_UNWRAP_OR; +use super::MAP_UNWRAP; /// lint use of `map().unwrap_or()` for `Option`s pub(super) fn lint<'a, 'tcx>( @@ -62,11 +62,11 @@ pub(super) fn lint<'a, 'tcx>( }; let msg = &format!( "called `map(f).unwrap_or({})` on an `Option` value. \ - This can be done more directly by calling `{}` instead", + This can be done more directly by calling `{}` instead", arg, suggest ); - span_lint_and_then(cx, OPTION_MAP_UNWRAP_OR, expr.span, msg, |diag| { + span_lint_and_then(cx, MAP_UNWRAP, expr.span, msg, |diag| { let map_arg_span = map_args[1].span; let mut suggestion = vec![ diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 4ae60f7d808..d3bd9f66e38 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1137,6 +1137,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "methods", }, + Lint { + name: "map_unwrap", + group: "pedantic", + desc: "using `.map(f).unwrap_or(a)` or `.map(f).unwrap_or_else(func)`, which are more succinctly expressed as `map_or(a, f)` or `map_or_else(a, f)`", + deprecation: None, + module: "methods", + }, Lint { name: "match_as_ref", group: "complexity", @@ -1613,20 +1620,6 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "map_unit_fn", }, - Lint { - name: "option_map_unwrap_or", - group: "pedantic", - desc: "using `Option.map(f).unwrap_or(a)`, which is more succinctly expressed as `map_or(a, f)`", - deprecation: None, - module: "methods", - }, - Lint { - name: "option_map_unwrap_or_else", - group: "pedantic", - desc: "using `Option.map(f).unwrap_or_else(g)`, which is more succinctly expressed as `map_or_else(g, f)`", - deprecation: None, - module: "methods", - }, Lint { name: "option_option", group: "pedantic", @@ -1900,13 +1893,6 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "map_unit_fn", }, - Lint { - name: "result_map_unwrap_or_else", - group: "pedantic", - desc: "using `Result.map(f).unwrap_or_else(g)`, which is more succinctly expressed as `.map_or_else(g, f)`", - deprecation: None, - module: "methods", - }, Lint { name: "result_unwrap_used", group: "restriction", diff --git a/tests/ui/map_unwrap.rs b/tests/ui/map_unwrap.rs new file mode 100644 index 00000000000..53e50368231 --- /dev/null +++ b/tests/ui/map_unwrap.rs @@ -0,0 +1,99 @@ +// FIXME: Add "run-rustfix" once it's supported for multipart suggestions +// aux-build:option_helpers.rs + +#![warn(clippy::map_unwrap)] + +#[macro_use] +extern crate option_helpers; + +use std::collections::HashMap; + +#[rustfmt::skip] +fn option_methods() { + let opt = Some(1); + + // Check for `option.map(_).unwrap_or(_)` use. + // Single line case. + let _ = opt.map(|x| x + 1) + // Should lint even though this call is on a separate line. + .unwrap_or(0); + // Multi-line cases. + let _ = opt.map(|x| { + x + 1 + } + ).unwrap_or(0); + let _ = opt.map(|x| x + 1) + .unwrap_or({ + 0 + }); + // Single line `map(f).unwrap_or(None)` case. + let _ = opt.map(|x| Some(x + 1)).unwrap_or(None); + // Multi-line `map(f).unwrap_or(None)` cases. + let _ = opt.map(|x| { + Some(x + 1) + } + ).unwrap_or(None); + let _ = opt + .map(|x| Some(x + 1)) + .unwrap_or(None); + // macro case + let _ = opt_map!(opt, |x| x + 1).unwrap_or(0); // should not lint + + // Should not lint if not copyable + let id: String = "identifier".to_string(); + let _ = Some("prefix").map(|p| format!("{}.{}", p, id)).unwrap_or(id); + // ...but DO lint if the `unwrap_or` argument is not used in the `map` + let id: String = "identifier".to_string(); + let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id); + + // Check for `option.map(_).unwrap_or_else(_)` use. + // single line case + let _ = opt.map(|x| x + 1) + // Should lint even though this call is on a separate line. + .unwrap_or_else(|| 0); + // Multi-line cases. + let _ = opt.map(|x| { + x + 1 + } + ).unwrap_or_else(|| 0); + let _ = opt.map(|x| x + 1) + .unwrap_or_else(|| + 0 + ); + // Macro case. + // Should not lint. + let _ = opt_map!(opt, |x| x + 1).unwrap_or_else(|| 0); + + // Issue #4144 + { + let mut frequencies = HashMap::new(); + let word = "foo"; + + frequencies + .get_mut(word) + .map(|count| { + *count += 1; + }) + .unwrap_or_else(|| { + frequencies.insert(word.to_owned(), 1); + }); + } +} + +fn result_methods() { + let res: Result = Ok(1); + + // Check for `result.map(_).unwrap_or_else(_)` use. + // single line case + let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); // should lint even though this call is on a separate line + // multi line cases + let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); + let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); + // macro case + let _ = opt_map!(res, |x| x + 1).unwrap_or_else(|e| 0); // should not lint +} + +fn main() { + option_methods(); + result_methods(); +} diff --git a/tests/ui/map_unwrap.stderr b/tests/ui/map_unwrap.stderr new file mode 100644 index 00000000000..2610923275d --- /dev/null +++ b/tests/ui/map_unwrap.stderr @@ -0,0 +1,161 @@ +error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead + --> $DIR/map_unwrap.rs:17:13 + | +LL | let _ = opt.map(|x| x + 1) + | _____________^ +LL | | // Should lint even though this call is on a separate line. +LL | | .unwrap_or(0); + | |_____________________^ + | + = note: `-D clippy::map-unwrap` implied by `-D warnings` +help: use `map_or(a, f)` instead + | +LL | let _ = opt.map_or(0, |x| x + 1); + | ^^^^^^ ^^ -- + +error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead + --> $DIR/map_unwrap.rs:21:13 + | +LL | let _ = opt.map(|x| { + | _____________^ +LL | | x + 1 +LL | | } +LL | | ).unwrap_or(0); + | |__________________^ + | +help: use `map_or(a, f)` instead + | +LL | let _ = opt.map_or(0, |x| { +LL | x + 1 +LL | } +LL | ); + | + +error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead + --> $DIR/map_unwrap.rs:25:13 + | +LL | let _ = opt.map(|x| x + 1) + | _____________^ +LL | | .unwrap_or({ +LL | | 0 +LL | | }); + | |__________^ + | +help: use `map_or(a, f)` instead + | +LL | let _ = opt.map_or({ +LL | 0 +LL | }, |x| x + 1); + | + +error: called `map(f).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead + --> $DIR/map_unwrap.rs:30:13 + | +LL | let _ = opt.map(|x| Some(x + 1)).unwrap_or(None); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `and_then(f)` instead + | +LL | let _ = opt.and_then(|x| Some(x + 1)); + | ^^^^^^^^ -- + +error: called `map(f).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead + --> $DIR/map_unwrap.rs:32:13 + | +LL | let _ = opt.map(|x| { + | _____________^ +LL | | Some(x + 1) +LL | | } +LL | | ).unwrap_or(None); + | |_____________________^ + | +help: use `and_then(f)` instead + | +LL | let _ = opt.and_then(|x| { +LL | Some(x + 1) +LL | } +LL | ); + | + +error: called `map(f).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead + --> $DIR/map_unwrap.rs:36:13 + | +LL | let _ = opt + | _____________^ +LL | | .map(|x| Some(x + 1)) +LL | | .unwrap_or(None); + | |________________________^ + | +help: use `and_then(f)` instead + | +LL | .and_then(|x| Some(x + 1)); + | ^^^^^^^^ -- + +error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead + --> $DIR/map_unwrap.rs:47:13 + | +LL | let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `map_or(a, f)` instead + | +LL | let _ = Some("prefix").map_or(id, |p| format!("{}.", p)); + | ^^^^^^ ^^^ -- + +error: called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling `map_or_else(g, f)` instead + --> $DIR/map_unwrap.rs:51:13 + | +LL | let _ = opt.map(|x| x + 1) + | _____________^ +LL | | // Should lint even though this call is on a separate line. +LL | | .unwrap_or_else(|| 0); + | |_____________________________^ + | + = note: replace `map(|x| x + 1).unwrap_or_else(|| 0)` with `map_or_else(|| 0, |x| x + 1)` + +error: called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling `map_or_else(g, f)` instead + --> $DIR/map_unwrap.rs:55:13 + | +LL | let _ = opt.map(|x| { + | _____________^ +LL | | x + 1 +LL | | } +LL | | ).unwrap_or_else(|| 0); + | |__________________________^ + +error: called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling `map_or_else(g, f)` instead + --> $DIR/map_unwrap.rs:59:13 + | +LL | let _ = opt.map(|x| x + 1) + | _____________^ +LL | | .unwrap_or_else(|| +LL | | 0 +LL | | ); + | |_________^ + +error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead + --> $DIR/map_unwrap.rs:88:13 + | +LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); // should lint even though this call is on a separate line + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` + +error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead + --> $DIR/map_unwrap.rs:90:13 + | +LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` + +error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead + --> $DIR/map_unwrap.rs:91:13 + | +LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` + +error: aborting due to 13 previous errors + diff --git a/tests/ui/option_map_unwrap_or.rs b/tests/ui/option_map_unwrap_or.rs deleted file mode 100644 index 0364d83663a..00000000000 --- a/tests/ui/option_map_unwrap_or.rs +++ /dev/null @@ -1,88 +0,0 @@ -// FIXME: Add "run-rustfix" once it's supported for multipart suggestions -// aux-build:option_helpers.rs - -#![warn(clippy::option_map_unwrap_or, clippy::option_map_unwrap_or_else)] - -#[macro_use] -extern crate option_helpers; - -use std::collections::HashMap; - -/// Checks implementation of the following lints: -/// * `OPTION_MAP_UNWRAP_OR` -/// * `OPTION_MAP_UNWRAP_OR_ELSE` -#[rustfmt::skip] -fn option_methods() { - let opt = Some(1); - - // Check `OPTION_MAP_UNWRAP_OR`. - // Single line case. - let _ = opt.map(|x| x + 1) - // Should lint even though this call is on a separate line. - .unwrap_or(0); - // Multi-line cases. - let _ = opt.map(|x| { - x + 1 - } - ).unwrap_or(0); - let _ = opt.map(|x| x + 1) - .unwrap_or({ - 0 - }); - // Single line `map(f).unwrap_or(None)` case. - let _ = opt.map(|x| Some(x + 1)).unwrap_or(None); - // Multi-line `map(f).unwrap_or(None)` cases. - let _ = opt.map(|x| { - Some(x + 1) - } - ).unwrap_or(None); - let _ = opt - .map(|x| Some(x + 1)) - .unwrap_or(None); - // macro case - let _ = opt_map!(opt, |x| x + 1).unwrap_or(0); // should not lint - - // Should not lint if not copyable - let id: String = "identifier".to_string(); - let _ = Some("prefix").map(|p| format!("{}.{}", p, id)).unwrap_or(id); - // ...but DO lint if the `unwrap_or` argument is not used in the `map` - let id: String = "identifier".to_string(); - let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id); - - // Check OPTION_MAP_UNWRAP_OR_ELSE - // single line case - let _ = opt.map(|x| x + 1) - // Should lint even though this call is on a separate line. - .unwrap_or_else(|| 0); - // Multi-line cases. - let _ = opt.map(|x| { - x + 1 - } - ).unwrap_or_else(|| 0); - let _ = opt.map(|x| x + 1) - .unwrap_or_else(|| - 0 - ); - // Macro case. - // Should not lint. - let _ = opt_map!(opt, |x| x + 1).unwrap_or_else(|| 0); - - // Issue #4144 - { - let mut frequencies = HashMap::new(); - let word = "foo"; - - frequencies - .get_mut(word) - .map(|count| { - *count += 1; - }) - .unwrap_or_else(|| { - frequencies.insert(word.to_owned(), 1); - }); - } -} - -fn main() { - option_methods(); -} diff --git a/tests/ui/option_map_unwrap_or.stderr b/tests/ui/option_map_unwrap_or.stderr deleted file mode 100644 index f05f2893de2..00000000000 --- a/tests/ui/option_map_unwrap_or.stderr +++ /dev/null @@ -1,138 +0,0 @@ -error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead - --> $DIR/option_map_unwrap_or.rs:20:13 - | -LL | let _ = opt.map(|x| x + 1) - | _____________^ -LL | | // Should lint even though this call is on a separate line. -LL | | .unwrap_or(0); - | |_____________________^ - | - = note: `-D clippy::option-map-unwrap-or` implied by `-D warnings` -help: use `map_or(a, f)` instead - | -LL | let _ = opt.map_or(0, |x| x + 1); - | ^^^^^^ ^^ -- - -error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead - --> $DIR/option_map_unwrap_or.rs:24:13 - | -LL | let _ = opt.map(|x| { - | _____________^ -LL | | x + 1 -LL | | } -LL | | ).unwrap_or(0); - | |__________________^ - | -help: use `map_or(a, f)` instead - | -LL | let _ = opt.map_or(0, |x| { -LL | x + 1 -LL | } -LL | ); - | - -error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead - --> $DIR/option_map_unwrap_or.rs:28:13 - | -LL | let _ = opt.map(|x| x + 1) - | _____________^ -LL | | .unwrap_or({ -LL | | 0 -LL | | }); - | |__________^ - | -help: use `map_or(a, f)` instead - | -LL | let _ = opt.map_or({ -LL | 0 -LL | }, |x| x + 1); - | - -error: called `map(f).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead - --> $DIR/option_map_unwrap_or.rs:33:13 - | -LL | let _ = opt.map(|x| Some(x + 1)).unwrap_or(None); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use `and_then(f)` instead - | -LL | let _ = opt.and_then(|x| Some(x + 1)); - | ^^^^^^^^ -- - -error: called `map(f).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead - --> $DIR/option_map_unwrap_or.rs:35:13 - | -LL | let _ = opt.map(|x| { - | _____________^ -LL | | Some(x + 1) -LL | | } -LL | | ).unwrap_or(None); - | |_____________________^ - | -help: use `and_then(f)` instead - | -LL | let _ = opt.and_then(|x| { -LL | Some(x + 1) -LL | } -LL | ); - | - -error: called `map(f).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead - --> $DIR/option_map_unwrap_or.rs:39:13 - | -LL | let _ = opt - | _____________^ -LL | | .map(|x| Some(x + 1)) -LL | | .unwrap_or(None); - | |________________________^ - | -help: use `and_then(f)` instead - | -LL | .and_then(|x| Some(x + 1)); - | ^^^^^^^^ -- - -error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead - --> $DIR/option_map_unwrap_or.rs:50:13 - | -LL | let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use `map_or(a, f)` instead - | -LL | let _ = Some("prefix").map_or(id, |p| format!("{}.", p)); - | ^^^^^^ ^^^ -- - -error: called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling `map_or_else(g, f)` instead - --> $DIR/option_map_unwrap_or.rs:54:13 - | -LL | let _ = opt.map(|x| x + 1) - | _____________^ -LL | | // Should lint even though this call is on a separate line. -LL | | .unwrap_or_else(|| 0); - | |_____________________________^ - | - = note: `-D clippy::option-map-unwrap-or-else` implied by `-D warnings` - = note: replace `map(|x| x + 1).unwrap_or_else(|| 0)` with `map_or_else(|| 0, |x| x + 1)` - -error: called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling `map_or_else(g, f)` instead - --> $DIR/option_map_unwrap_or.rs:58:13 - | -LL | let _ = opt.map(|x| { - | _____________^ -LL | | x + 1 -LL | | } -LL | | ).unwrap_or_else(|| 0); - | |__________________________^ - -error: called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling `map_or_else(g, f)` instead - --> $DIR/option_map_unwrap_or.rs:62:13 - | -LL | let _ = opt.map(|x| x + 1) - | _____________^ -LL | | .unwrap_or_else(|| -LL | | 0 -LL | | ); - | |_________^ - -error: aborting due to 10 previous errors - diff --git a/tests/ui/result_map_unwrap_or_else.rs b/tests/ui/result_map_unwrap_or_else.rs deleted file mode 100644 index 40751bfebe6..00000000000 --- a/tests/ui/result_map_unwrap_or_else.rs +++ /dev/null @@ -1,23 +0,0 @@ -// aux-build:option_helpers.rs - -//! Checks implementation of `RESULT_MAP_UNWRAP_OR_ELSE` - -#![warn(clippy::result_map_unwrap_or_else)] - -#[macro_use] -extern crate option_helpers; - -fn result_methods() { - let res: Result = Ok(1); - - // Check RESULT_MAP_UNWRAP_OR_ELSE - // single line case - let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); // should lint even though this call is on a separate line - // multi line cases - let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); - let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); - // macro case - let _ = opt_map!(res, |x| x + 1).unwrap_or_else(|e| 0); // should not lint -} - -fn main() {} diff --git a/tests/ui/result_map_unwrap_or_else.stderr b/tests/ui/result_map_unwrap_or_else.stderr deleted file mode 100644 index ec7bc8f1241..00000000000 --- a/tests/ui/result_map_unwrap_or_else.stderr +++ /dev/null @@ -1,27 +0,0 @@ -error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead - --> $DIR/result_map_unwrap_or_else.rs:15:13 - | -LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); // should lint even though this call is on a separate line - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::result-map-unwrap-or-else` implied by `-D warnings` - = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` - -error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead - --> $DIR/result_map_unwrap_or_else.rs:17:13 - | -LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` - -error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead - --> $DIR/result_map_unwrap_or_else.rs:18:13 - | -LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` - -error: aborting due to 3 previous errors - -- cgit 1.4.1-3-g733a5 From bcf61666bd903c0d13c081cf222b423e45cd854e Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 3 May 2020 13:11:18 +0200 Subject: Merge `option_unwrap_used` and `result_unwrap_used` lints into `unwrap_used` lint --- CHANGELOG.md | 2 +- clippy_lints/src/lib.rs | 6 ++-- clippy_lints/src/methods/mod.rs | 68 +++++++++++++++-------------------------- src/lintlist/mod.rs | 14 ++++----- tests/ui/unwrap.rs | 2 +- tests/ui/unwrap.stderr | 3 +- 6 files changed, 37 insertions(+), 58 deletions(-) (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 28b05044db6..78f98bba2b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1501,7 +1501,6 @@ Released 2018-09-13 [`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none [`option_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn [`option_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_option -[`option_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_unwrap_used [`or_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_fun_call [`out_of_bounds_indexing`]: https://rust-lang.github.io/rust-clippy/master/index.html#out_of_bounds_indexing [`overflow_check_conditional`]: https://rust-lang.github.io/rust-clippy/master/index.html#overflow_check_conditional @@ -1622,6 +1621,7 @@ Released 2018-09-13 [`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label [`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self [`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit +[`unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used [`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug [`use_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_self [`used_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#used_underscore_binding diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c9a2ef49907..bb3bc0b4545 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -680,11 +680,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::OPTION_AS_REF_DEREF, &methods::OPTION_EXPECT_USED, &methods::OPTION_MAP_OR_NONE, - &methods::OPTION_UNWRAP_USED, &methods::OR_FUN_CALL, &methods::RESULT_EXPECT_USED, &methods::RESULT_MAP_OR_INTO_OPTION, - &methods::RESULT_UNWRAP_USED, &methods::SEARCH_IS_SOME, &methods::SHOULD_IMPLEMENT_TRAIT, &methods::SINGLE_CHAR_PATTERN, @@ -695,6 +693,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::UNINIT_ASSUMED_INIT, &methods::UNNECESSARY_FILTER_MAP, &methods::UNNECESSARY_FOLD, + &methods::UNWRAP_USED, &methods::USELESS_ASREF, &methods::WRONG_PUB_SELF_CONVENTION, &methods::WRONG_SELF_CONVENTION, @@ -1090,9 +1089,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::FILETYPE_IS_FILE), LintId::of(&methods::GET_UNWRAP), LintId::of(&methods::OPTION_EXPECT_USED), - LintId::of(&methods::OPTION_UNWRAP_USED), LintId::of(&methods::RESULT_EXPECT_USED), - LintId::of(&methods::RESULT_UNWRAP_USED), + LintId::of(&methods::UNWRAP_USED), LintId::of(&methods::WRONG_PUB_SELF_CONVENTION), LintId::of(&misc::FLOAT_CMP_CONST), LintId::of(&misc_early::UNNEEDED_FIELD_PATTERN), diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 401298b2d51..1af4d03c7a2 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -33,40 +33,15 @@ use crate::utils::{ }; declare_clippy_lint! { - /// **What it does:** Checks for `.unwrap()` calls on `Option`s. + /// **What it does:** Checks for `.unwrap()` calls on `Option`s and on `Result`s. /// - /// **Why is this bad?** Usually it is better to handle the `None` case, or to - /// at least call `.expect(_)` with a more helpful message. Still, for a lot of + /// **Why is this bad?** It is better to handle the `None` or `Err` case, + /// or at least call `.expect(_)` with a more helpful message. Still, for a lot of /// quick-and-dirty code, `unwrap` is a good choice, which is why this lint is /// `Allow` by default. /// - /// **Known problems:** None. - /// - /// **Example:** - /// - /// Using unwrap on an `Option`: - /// - /// ```rust - /// let opt = Some(1); - /// opt.unwrap(); - /// ``` - /// - /// Better: - /// - /// ```rust - /// let opt = Some(1); - /// opt.expect("more helpful message"); - /// ``` - pub OPTION_UNWRAP_USED, - restriction, - "using `Option.unwrap()`, which should at least get a better message using `expect()`" -} - -declare_clippy_lint! { - /// **What it does:** Checks for `.unwrap()` calls on `Result`s. - /// - /// **Why is this bad?** `result.unwrap()` will let the thread panic on `Err` - /// values. Normally, you want to implement more sophisticated error handling, + /// `result.unwrap()` will let the thread panic on `Err` values. + /// Normally, you want to implement more sophisticated error handling, /// and propagate errors upwards with `?` operator. /// /// Even if you want to panic on errors, not all `Error`s implement good @@ -75,23 +50,31 @@ declare_clippy_lint! { /// /// **Known problems:** None. /// - /// **Example:** - /// Using unwrap on an `Result`: - /// + /// **Examples:** /// ```rust - /// let res: Result = Ok(1); - /// res.unwrap(); + /// # let opt = Some(1); + /// + /// // Bad + /// opt.unwrap(); + /// + /// // Good + /// opt.expect("more helpful message"); /// ``` /// - /// Better: + /// // or /// /// ```rust - /// let res: Result = Ok(1); + /// # let res: Result = Ok(1); + /// + /// // Bad + /// res.unwrap(); + /// + /// // Good /// res.expect("more helpful message"); /// ``` - pub RESULT_UNWRAP_USED, + pub UNWRAP_USED, restriction, - "using `Result.unwrap()`, which might be better handled" + "using `.unwrap()` on `Result` or `Option`, which should at least get a better message using `expect()`" } declare_clippy_lint! { @@ -1267,8 +1250,7 @@ declare_clippy_lint! { } declare_lint_pass!(Methods => [ - OPTION_UNWRAP_USED, - RESULT_UNWRAP_USED, + UNWRAP_USED, OPTION_EXPECT_USED, RESULT_EXPECT_USED, SHOULD_IMPLEMENT_TRAIT, @@ -2397,9 +2379,9 @@ fn lint_unwrap(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, unwrap_args: &[hi let obj_ty = walk_ptrs_ty(cx.tables.expr_ty(&unwrap_args[0])); let mess = if is_type_diagnostic_item(cx, obj_ty, sym!(option_type)) { - Some((OPTION_UNWRAP_USED, "an Option", "None")) + Some((UNWRAP_USED, "an Option", "None")) } else if is_type_diagnostic_item(cx, obj_ty, sym!(result_type)) { - Some((RESULT_UNWRAP_USED, "a Result", "Err")) + Some((UNWRAP_USED, "a Result", "Err")) } else { None }; diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index d3bd9f66e38..5cbf3ef028c 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1627,13 +1627,6 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "types", }, - Lint { - name: "option_unwrap_used", - group: "restriction", - desc: "using `Option.unwrap()`, which should at least get a better message using `expect()`", - deprecation: None, - module: "methods", - }, Lint { name: "or_fun_call", group: "perf", @@ -2404,6 +2397,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "returns", }, + Lint { + name: "unwrap_used", + group: "restriction", + desc: "using `.unwrap()` on `Result` or `Option`, which should at least get a better message using `expect()`", + deprecation: None, + module: "methods", + }, Lint { name: "use_debug", group: "restriction", diff --git a/tests/ui/unwrap.rs b/tests/ui/unwrap.rs index fcd1fcd14d4..a4a3cd1d379 100644 --- a/tests/ui/unwrap.rs +++ b/tests/ui/unwrap.rs @@ -1,4 +1,4 @@ -#![warn(clippy::option_unwrap_used, clippy::result_unwrap_used)] +#![warn(clippy::unwrap_used)] fn unwrap_option() { let opt = Some(0); diff --git a/tests/ui/unwrap.stderr b/tests/ui/unwrap.stderr index b90ce68fa97..4f0858005f6 100644 --- a/tests/ui/unwrap.stderr +++ b/tests/ui/unwrap.stderr @@ -4,7 +4,7 @@ error: used `unwrap()` on `an Option` value LL | let _ = opt.unwrap(); | ^^^^^^^^^^^^ | - = note: `-D clippy::option-unwrap-used` implied by `-D warnings` + = note: `-D clippy::unwrap-used` implied by `-D warnings` = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: used `unwrap()` on `a Result` value @@ -13,7 +13,6 @@ error: used `unwrap()` on `a Result` value LL | let _ = res.unwrap(); | ^^^^^^^^^^^^ | - = note: `-D clippy::result-unwrap-used` implied by `-D warnings` = help: if you don't want to handle the `Err` case gracefully, consider using `expect()` to provide a better panic message error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From 0e8be599cd04a8566224c63eeb07f5fa04605702 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 3 May 2020 13:32:17 +0200 Subject: Merge `option_expect_used` and `result_expect_used` lints into `expect_used` lint --- CHANGELOG.md | 3 +- clippy_lints/src/lib.rs | 6 ++-- clippy_lints/src/methods/mod.rs | 63 +++++++++++++++-------------------------- src/lintlist/mod.rs | 21 +++++--------- tests/ui/expect.rs | 2 +- tests/ui/expect.stderr | 3 +- 6 files changed, 35 insertions(+), 63 deletions(-) (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 78f98bba2b4..4eeb71fa5c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1337,6 +1337,7 @@ Released 2018-09-13 [`excessive_precision`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision [`exit`]: https://rust-lang.github.io/rust-clippy/master/index.html#exit [`expect_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_fun_call +[`expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_used [`expl_impl_clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#expl_impl_clone_on_copy [`explicit_counter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_counter_loop [`explicit_deref_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_deref_methods @@ -1497,7 +1498,6 @@ Released 2018-09-13 [`option_and_then_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_and_then_some [`option_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref [`option_env_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_env_unwrap -[`option_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_expect_used [`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none [`option_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn [`option_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_option @@ -1537,7 +1537,6 @@ Released 2018-09-13 [`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro [`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts [`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs -[`result_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_expect_used [`result_map_or_into_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_or_into_option [`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn [`result_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unwrap_used diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index bb3bc0b4545..eaef1f543d3 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -657,6 +657,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::CLONE_ON_COPY, &methods::CLONE_ON_REF_PTR, &methods::EXPECT_FUN_CALL, + &methods::EXPECT_USED, &methods::FILETYPE_IS_FILE, &methods::FILTER_MAP, &methods::FILTER_MAP_NEXT, @@ -678,10 +679,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::OK_EXPECT, &methods::OPTION_AND_THEN_SOME, &methods::OPTION_AS_REF_DEREF, - &methods::OPTION_EXPECT_USED, &methods::OPTION_MAP_OR_NONE, &methods::OR_FUN_CALL, - &methods::RESULT_EXPECT_USED, &methods::RESULT_MAP_OR_INTO_OPTION, &methods::SEARCH_IS_SOME, &methods::SHOULD_IMPLEMENT_TRAIT, @@ -1086,10 +1085,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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::OPTION_EXPECT_USED), - LintId::of(&methods::RESULT_EXPECT_USED), LintId::of(&methods::UNWRAP_USED), LintId::of(&methods::WRONG_PUB_SELF_CONVENTION), LintId::of(&misc::FLOAT_CMP_CONST), diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 1af4d03c7a2..2e75de019b6 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -78,61 +78,45 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for `.expect()` calls on `Option`s. + /// **What it does:** Checks for `.expect()` calls on `Option`s and `Result`s. /// - /// **Why is this bad?** Usually it is better to handle the `None` case. Still, - /// for a lot of quick-and-dirty code, `expect` is a good choice, which is why - /// this lint is `Allow` by default. + /// **Why is this bad?** Usually it is better to handle the `None` or `Err` case. + /// Still, for a lot of quick-and-dirty code, `expect` is a good choice, which is why + /// this lint is `Allow` by default. /// - /// **Known problems:** None. + /// `result.expect()` will let the thread panic on `Err` + /// values. Normally, you want to implement more sophisticated error handling, + /// and propagate errors upwards with `?` operator. /// - /// **Example:** + /// **Known problems:** None. /// - /// Using expect on an `Option`: + /// **Examples:** + /// ```rust,ignore + /// # let opt = Some(1); /// - /// ```rust - /// let opt = Some(1); + /// // Bad /// opt.expect("one"); - /// ``` - /// - /// Better: /// - /// ```rust,ignore + /// // Good /// let opt = Some(1); /// opt?; /// ``` - pub OPTION_EXPECT_USED, - restriction, - "using `Option.expect()`, which might be better handled" -} - -declare_clippy_lint! { - /// **What it does:** Checks for `.expect()` calls on `Result`s. - /// - /// **Why is this bad?** `result.expect()` will let the thread panic on `Err` - /// values. Normally, you want to implement more sophisticated error handling, - /// and propagate errors upwards with `?` operator. - /// - /// **Known problems:** None. /// - /// **Example:** - /// Using expect on an `Result`: + /// // or /// /// ```rust - /// let res: Result = Ok(1); - /// res.expect("one"); - /// ``` + /// # let res: Result = Ok(1); /// - /// Better: + /// // Bad + /// res.expect("one"); /// - /// ```rust - /// let res: Result = Ok(1); + /// // Good /// res?; /// # Ok::<(), ()>(()) /// ``` - pub RESULT_EXPECT_USED, + pub EXPECT_USED, restriction, - "using `Result.expect()`, which might be better handled" + "using `.expect()` on `Result` or `Option`, which might be better handled" } declare_clippy_lint! { @@ -1251,8 +1235,7 @@ declare_clippy_lint! { declare_lint_pass!(Methods => [ UNWRAP_USED, - OPTION_EXPECT_USED, - RESULT_EXPECT_USED, + EXPECT_USED, SHOULD_IMPLEMENT_TRAIT, WRONG_SELF_CONVENTION, WRONG_PUB_SELF_CONVENTION, @@ -2407,9 +2390,9 @@ fn lint_expect(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, expect_args: &[hi let obj_ty = walk_ptrs_ty(cx.tables.expr_ty(&expect_args[0])); let mess = if is_type_diagnostic_item(cx, obj_ty, sym!(option_type)) { - Some((OPTION_EXPECT_USED, "an Option", "None")) + Some((EXPECT_USED, "an Option", "None")) } else if is_type_diagnostic_item(cx, obj_ty, sym!(result_type)) { - Some((RESULT_EXPECT_USED, "a Result", "Err")) + Some((EXPECT_USED, "a Result", "Err")) } else { None }; diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 5cbf3ef028c..4e79ce96bb5 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -514,6 +514,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "methods", }, + Lint { + name: "expect_used", + group: "restriction", + desc: "using `.expect()` on `Result` or `Option`, which might be better handled", + deprecation: None, + module: "methods", + }, Lint { name: "expl_impl_clone_on_copy", group: "pedantic", @@ -1599,13 +1606,6 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "option_env_unwrap", }, - Lint { - name: "option_expect_used", - group: "restriction", - desc: "using `Option.expect()`, which might be better handled", - deprecation: None, - module: "methods", - }, Lint { name: "option_map_or_none", group: "style", @@ -1865,13 +1865,6 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "matches", }, - Lint { - name: "result_expect_used", - group: "restriction", - desc: "using `Result.expect()`, which might be better handled", - deprecation: None, - module: "methods", - }, Lint { name: "result_map_or_into_option", group: "style", diff --git a/tests/ui/expect.rs b/tests/ui/expect.rs index 0bd4252c49a..1073acf6f0c 100644 --- a/tests/ui/expect.rs +++ b/tests/ui/expect.rs @@ -1,4 +1,4 @@ -#![warn(clippy::option_expect_used, clippy::result_expect_used)] +#![warn(clippy::expect_used)] fn expect_option() { let opt = Some(0); diff --git a/tests/ui/expect.stderr b/tests/ui/expect.stderr index adf9f4f1921..9d3fc7df15c 100644 --- a/tests/ui/expect.stderr +++ b/tests/ui/expect.stderr @@ -4,7 +4,7 @@ error: used `expect()` on `an Option` value LL | let _ = opt.expect(""); | ^^^^^^^^^^^^^^ | - = note: `-D clippy::option-expect-used` implied by `-D warnings` + = note: `-D clippy::expect-used` implied by `-D warnings` = help: if this value is an `None`, it will panic error: used `expect()` on `a Result` value @@ -13,7 +13,6 @@ error: used `expect()` on `a Result` value LL | let _ = res.expect(""); | ^^^^^^^^^^^^^^ | - = note: `-D clippy::result-expect-used` implied by `-D warnings` = help: if this value is an `Err`, it will panic error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From adbdf7549c6b24c37629eabdc4be0346e0c8fd56 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 3 May 2020 15:16:00 +0200 Subject: Merge `for_loop_over_option` and `for_loop_over_result` lints into `for_loop_over_fallible` lint --- CHANGELOG.md | 3 +- clippy_lints/src/lib.rs | 9 ++-- clippy_lints/src/loops.rs | 64 +++++++++++-------------- src/lintlist/mod.rs | 11 +---- tests/ui/for_loop_over_fallible.rs | 57 +++++++++++++++++++++++ tests/ui/for_loop_over_fallible.stderr | 71 ++++++++++++++++++++++++++++ tests/ui/for_loop_over_option_result.rs | 59 ----------------------- tests/ui/for_loop_over_option_result.stderr | 72 ----------------------------- 8 files changed, 161 insertions(+), 185 deletions(-) create mode 100644 tests/ui/for_loop_over_fallible.rs create mode 100644 tests/ui/for_loop_over_fallible.stderr delete mode 100644 tests/ui/for_loop_over_option_result.rs delete mode 100644 tests/ui/for_loop_over_option_result.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 4eeb71fa5c5..3f9486e0972 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1361,8 +1361,7 @@ Released 2018-09-13 [`fn_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast [`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_loop_over_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loop_over_option -[`for_loop_over_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loop_over_result +[`for_loop_over_fallible`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loop_over_fallible [`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 [`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index eaef1f543d3..8de94d19d31 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -615,8 +615,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &loops::EXPLICIT_INTO_ITER_LOOP, &loops::EXPLICIT_ITER_LOOP, &loops::FOR_KV_MAP, - &loops::FOR_LOOP_OVER_OPTION, - &loops::FOR_LOOP_OVER_RESULT, + &loops::FOR_LOOP_OVER_FALLIBLE, &loops::ITER_NEXT_LOOP, &loops::MANUAL_MEMCPY, &loops::MUT_RANGE_BOUND, @@ -1265,8 +1264,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::EMPTY_LOOP), LintId::of(&loops::EXPLICIT_COUNTER_LOOP), LintId::of(&loops::FOR_KV_MAP), - LintId::of(&loops::FOR_LOOP_OVER_OPTION), - LintId::of(&loops::FOR_LOOP_OVER_RESULT), + LintId::of(&loops::FOR_LOOP_OVER_FALLIBLE), LintId::of(&loops::ITER_NEXT_LOOP), LintId::of(&loops::MANUAL_MEMCPY), LintId::of(&loops::MUT_RANGE_BOUND), @@ -1641,8 +1639,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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::FOR_LOOP_OVER_OPTION), - LintId::of(&loops::FOR_LOOP_OVER_RESULT), + LintId::of(&loops::FOR_LOOP_OVER_FALLIBLE), LintId::of(&loops::ITER_NEXT_LOOP), LintId::of(&loops::NEVER_LOOP), LintId::of(&loops::WHILE_IMMUTABLE_CONDITION), diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 0bc6b70855b..da6793a69d6 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -168,7 +168,7 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for `for` loops over `Option` values. + /// **What it does:** Checks for `for` loops over `Option` or `Result` values. /// /// **Why is this bad?** Readability. This is more clearly expressed as an `if /// let`. @@ -176,47 +176,38 @@ declare_clippy_lint! { /// **Known problems:** None. /// /// **Example:** - /// ```ignore - /// for x in option { - /// .. + /// ```rust + /// # let opt = Some(1); + /// + /// // Bad + /// for x in opt { + /// // .. /// } - /// ``` /// - /// This should be - /// ```ignore - /// if let Some(x) = option { - /// .. + /// // Good + /// if let Some(x) = opt { + /// // .. /// } /// ``` - pub FOR_LOOP_OVER_OPTION, - correctness, - "for-looping over an `Option`, which is more clearly expressed as an `if let`" -} - -declare_clippy_lint! { - /// **What it does:** Checks for `for` loops over `Result` values. /// - /// **Why is this bad?** Readability. This is more clearly expressed as an `if - /// let`. + /// // or /// - /// **Known problems:** None. + /// ```rust + /// # let res: Result = Ok(1); /// - /// **Example:** - /// ```ignore - /// for x in result { - /// .. + /// // Bad + /// for x in &res { + /// // .. /// } - /// ``` /// - /// This should be - /// ```ignore - /// if let Ok(x) = result { - /// .. + /// // Good + /// if let Ok(x) = res { + /// // .. /// } /// ``` - pub FOR_LOOP_OVER_RESULT, + pub FOR_LOOP_OVER_FALLIBLE, correctness, - "for-looping over a `Result`, which is more clearly expressed as an `if let`" + "for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`" } declare_clippy_lint! { @@ -435,8 +426,7 @@ declare_lint_pass!(Loops => [ EXPLICIT_ITER_LOOP, EXPLICIT_INTO_ITER_LOOP, ITER_NEXT_LOOP, - FOR_LOOP_OVER_RESULT, - FOR_LOOP_OVER_OPTION, + FOR_LOOP_OVER_FALLIBLE, WHILE_LET_LOOP, NEEDLESS_COLLECT, EXPLICIT_COUNTER_LOOP, @@ -1283,7 +1273,7 @@ fn check_for_loop_arg(cx: &LateContext<'_, '_>, pat: &Pat<'_>, arg: &Expr<'_>, e ITER_NEXT_LOOP, expr.span, "you are iterating over `Iterator::next()` which is an Option; this will compile but is \ - probably not what you want", + probably not what you want", ); next_loop_linted = true; } @@ -1300,11 +1290,11 @@ fn check_arg_type(cx: &LateContext<'_, '_>, pat: &Pat<'_>, arg: &Expr<'_>) { if is_type_diagnostic_item(cx, ty, sym!(option_type)) { span_lint_and_help( cx, - FOR_LOOP_OVER_OPTION, + FOR_LOOP_OVER_FALLIBLE, arg.span, &format!( "for loop over `{0}`, which is an `Option`. This is more readably written as an \ - `if let` statement.", + `if let` statement.", snippet(cx, arg.span, "_") ), None, @@ -1317,11 +1307,11 @@ fn check_arg_type(cx: &LateContext<'_, '_>, pat: &Pat<'_>, arg: &Expr<'_>) { } else if is_type_diagnostic_item(cx, ty, sym!(result_type)) { span_lint_and_help( cx, - FOR_LOOP_OVER_RESULT, + FOR_LOOP_OVER_FALLIBLE, arg.span, &format!( "for loop over `{0}`, which is a `Result`. This is more readably written as an \ - `if let` statement.", + `if let` statement.", snippet(cx, arg.span, "_") ), None, diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 4e79ce96bb5..0ea0f55a381 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -676,16 +676,9 @@ pub static ref ALL_LINTS: Vec = vec![ module: "loops", }, Lint { - name: "for_loop_over_option", + name: "for_loop_over_fallible", group: "correctness", - desc: "for-looping over an `Option`, which is more clearly expressed as an `if let`", - deprecation: None, - module: "loops", - }, - Lint { - name: "for_loop_over_result", - group: "correctness", - desc: "for-looping over a `Result`, which is more clearly expressed as an `if let`", + desc: "for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`", deprecation: None, module: "loops", }, diff --git a/tests/ui/for_loop_over_fallible.rs b/tests/ui/for_loop_over_fallible.rs new file mode 100644 index 00000000000..e52468cdd4b --- /dev/null +++ b/tests/ui/for_loop_over_fallible.rs @@ -0,0 +1,57 @@ +#![warn(clippy::for_loop_over_fallible)] + +fn for_loop_over_fallible() { + let option = Some(1); + let result = option.ok_or("x not found"); + let v = vec![0, 1, 2]; + + // check over an `Option` + for x in option { + println!("{}", x); + } + + // check over a `Result` + for x in result { + println!("{}", x); + } + + for x in option.ok_or("x not found") { + println!("{}", x); + } + + // make sure LOOP_OVER_NEXT lint takes clippy::precedence when next() is the last call + // in the chain + for x in v.iter().next() { + println!("{}", x); + } + + // make sure we lint when next() is not the last call in the chain + for x in v.iter().next().and(Some(0)) { + println!("{}", x); + } + + for x in v.iter().next().ok_or("x not found") { + println!("{}", x); + } + + // check for false positives + + // for loop false positive + for x in v { + println!("{}", x); + } + + // while let false positive for Option + while let Some(x) = option { + println!("{}", x); + break; + } + + // while let false positive for Result + while let Ok(x) = result { + println!("{}", x); + break; + } +} + +fn main() {} diff --git a/tests/ui/for_loop_over_fallible.stderr b/tests/ui/for_loop_over_fallible.stderr new file mode 100644 index 00000000000..4ce9a144ad8 --- /dev/null +++ b/tests/ui/for_loop_over_fallible.stderr @@ -0,0 +1,71 @@ +error: for loop over `option`, which is an `Option`. This is more readably written as an `if let` statement. + --> $DIR/for_loop_over_fallible.rs:9:14 + | +LL | for x in option { + | ^^^^^^ + | + = note: `-D clippy::for-loop-over-fallible` implied by `-D warnings` + = help: consider replacing `for x in option` with `if let Some(x) = option` + +error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement. + --> $DIR/for_loop_over_fallible.rs:14:14 + | +LL | for x in result { + | ^^^^^^ + | + = help: consider replacing `for x in result` with `if let Ok(x) = result` + +error: for loop over `option.ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement. + --> $DIR/for_loop_over_fallible.rs:18:14 + | +LL | for x in option.ok_or("x not found") { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider replacing `for x in option.ok_or("x not found")` with `if let Ok(x) = option.ok_or("x not found")` + +error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want + --> $DIR/for_loop_over_fallible.rs:24:14 + | +LL | for x in v.iter().next() { + | ^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::iter_next_loop)]` on by default + +error: for loop over `v.iter().next().and(Some(0))`, which is an `Option`. This is more readably written as an `if let` statement. + --> $DIR/for_loop_over_fallible.rs:29:14 + | +LL | for x in v.iter().next().and(Some(0)) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider replacing `for x in v.iter().next().and(Some(0))` with `if let Some(x) = v.iter().next().and(Some(0))` + +error: for loop over `v.iter().next().ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement. + --> $DIR/for_loop_over_fallible.rs:33:14 + | +LL | for x in v.iter().next().ok_or("x not found") { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider replacing `for x in v.iter().next().ok_or("x not found")` with `if let Ok(x) = v.iter().next().ok_or("x not found")` + +error: this loop never actually loops + --> $DIR/for_loop_over_fallible.rs:45:5 + | +LL | / while let Some(x) = option { +LL | | println!("{}", x); +LL | | break; +LL | | } + | |_____^ + | + = note: `#[deny(clippy::never_loop)]` on by default + +error: this loop never actually loops + --> $DIR/for_loop_over_fallible.rs:51:5 + | +LL | / while let Ok(x) = result { +LL | | println!("{}", x); +LL | | break; +LL | | } + | |_____^ + +error: aborting due to 8 previous errors + diff --git a/tests/ui/for_loop_over_option_result.rs b/tests/ui/for_loop_over_option_result.rs deleted file mode 100644 index 6b207b26b6b..00000000000 --- a/tests/ui/for_loop_over_option_result.rs +++ /dev/null @@ -1,59 +0,0 @@ -#![warn(clippy::for_loop_over_option, clippy::for_loop_over_result)] - -/// Tests for_loop_over_result and for_loop_over_option - -fn for_loop_over_option_and_result() { - let option = Some(1); - let result = option.ok_or("x not found"); - let v = vec![0, 1, 2]; - - // check FOR_LOOP_OVER_OPTION lint - for x in option { - println!("{}", x); - } - - // check FOR_LOOP_OVER_RESULT lint - for x in result { - println!("{}", x); - } - - for x in option.ok_or("x not found") { - println!("{}", x); - } - - // make sure LOOP_OVER_NEXT lint takes clippy::precedence when next() is the last call - // in the chain - for x in v.iter().next() { - println!("{}", x); - } - - // make sure we lint when next() is not the last call in the chain - for x in v.iter().next().and(Some(0)) { - println!("{}", x); - } - - for x in v.iter().next().ok_or("x not found") { - println!("{}", x); - } - - // check for false positives - - // for loop false positive - for x in v { - println!("{}", x); - } - - // while let false positive for Option - while let Some(x) = option { - println!("{}", x); - break; - } - - // while let false positive for Result - while let Ok(x) = result { - println!("{}", x); - break; - } -} - -fn main() {} diff --git a/tests/ui/for_loop_over_option_result.stderr b/tests/ui/for_loop_over_option_result.stderr deleted file mode 100644 index 194a0bfec5b..00000000000 --- a/tests/ui/for_loop_over_option_result.stderr +++ /dev/null @@ -1,72 +0,0 @@ -error: for loop over `option`, which is an `Option`. This is more readably written as an `if let` statement. - --> $DIR/for_loop_over_option_result.rs:11:14 - | -LL | for x in option { - | ^^^^^^ - | - = note: `-D clippy::for-loop-over-option` implied by `-D warnings` - = help: consider replacing `for x in option` with `if let Some(x) = option` - -error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement. - --> $DIR/for_loop_over_option_result.rs:16:14 - | -LL | for x in result { - | ^^^^^^ - | - = note: `-D clippy::for-loop-over-result` implied by `-D warnings` - = help: consider replacing `for x in result` with `if let Ok(x) = result` - -error: for loop over `option.ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement. - --> $DIR/for_loop_over_option_result.rs:20:14 - | -LL | for x in option.ok_or("x not found") { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider replacing `for x in option.ok_or("x not found")` with `if let Ok(x) = option.ok_or("x not found")` - -error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want - --> $DIR/for_loop_over_option_result.rs:26:14 - | -LL | for x in v.iter().next() { - | ^^^^^^^^^^^^^^^ - | - = note: `#[deny(clippy::iter_next_loop)]` on by default - -error: for loop over `v.iter().next().and(Some(0))`, which is an `Option`. This is more readably written as an `if let` statement. - --> $DIR/for_loop_over_option_result.rs:31:14 - | -LL | for x in v.iter().next().and(Some(0)) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider replacing `for x in v.iter().next().and(Some(0))` with `if let Some(x) = v.iter().next().and(Some(0))` - -error: for loop over `v.iter().next().ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement. - --> $DIR/for_loop_over_option_result.rs:35:14 - | -LL | for x in v.iter().next().ok_or("x not found") { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider replacing `for x in v.iter().next().ok_or("x not found")` with `if let Ok(x) = v.iter().next().ok_or("x not found")` - -error: this loop never actually loops - --> $DIR/for_loop_over_option_result.rs:47:5 - | -LL | / while let Some(x) = option { -LL | | println!("{}", x); -LL | | break; -LL | | } - | |_____^ - | - = note: `#[deny(clippy::never_loop)]` on by default - -error: this loop never actually loops - --> $DIR/for_loop_over_option_result.rs:53:5 - | -LL | / while let Ok(x) = result { -LL | | println!("{}", x); -LL | | break; -LL | | } - | |_____^ - -error: aborting due to 8 previous errors - -- cgit 1.4.1-3-g733a5 From 94e4b5ec316993200d75276b4e7c16a059bf3a57 Mon Sep 17 00:00:00 2001 From: Vardan Margaryan Date: Sun, 10 May 2020 00:08:41 +0300 Subject: Add the redundant_wildcard_enum_match lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/matches.rs | 48 ++++++++++++++++++++++ src/lintlist/mod.rs | 7 ++++ tests/ui/match_wildcard_for_single_variants.fixed | 18 ++++++++ tests/ui/match_wildcard_for_single_variants.rs | 18 ++++++++ tests/ui/match_wildcard_for_single_variants.stderr | 10 +++++ 7 files changed, 104 insertions(+) create mode 100644 tests/ui/match_wildcard_for_single_variants.fixed create mode 100644 tests/ui/match_wildcard_for_single_variants.rs create mode 100644 tests/ui/match_wildcard_for_single_variants.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index b25ef049356..d6298fec65a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1439,6 +1439,7 @@ Released 2018-09-13 [`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_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 diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 0c4daeb731f..41046c18ed2 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -641,6 +641,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &matches::MATCH_OVERLAPPING_ARM, &matches::MATCH_REF_PATS, &matches::MATCH_SINGLE_BINDING, + &matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS, &matches::MATCH_WILD_ERR_ARM, &matches::REST_PAT_IN_FULLY_BOUND_STRUCTS, &matches::SINGLE_MATCH, @@ -1147,6 +1148,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(¯o_use::MACRO_USE_IMPORTS), LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS), LintId::of(&matches::MATCH_BOOL), + LintId::of(&matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS), LintId::of(&matches::SINGLE_MATCH_ELSE), LintId::of(&methods::FILTER_MAP), LintId::of(&methods::FILTER_MAP_NEXT), diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 8f86535ef1e..42a6c416619 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -229,6 +229,40 @@ declare_clippy_lint! { "a wildcard enum match arm using `_`" } +declare_clippy_lint! { + /// **What it does:** Checks for wildcard enum matches for a single variant. + /// + /// **Why is this bad?** New enum variants added by library updates can be missed. + /// + /// **Known problems:** Suggested replacements may not use correct path to enum + /// if it's not present in the current scope. + /// + /// **Example:** + /// + /// ```rust + /// # enum Foo { A, B, C } + /// # let x = Foo::B; + /// match x { + /// Foo::A => {}, + /// Foo::B => {}, + /// _ => {}, + /// } + /// ``` + /// Use instead: + /// ```rust + /// # enum Foo { A, B, C } + /// # let x = Foo::B; + /// match x { + /// Foo::A => {}, + /// Foo::B => {}, + /// Foo::C => {}, + /// } + /// ``` + pub MATCH_WILDCARD_FOR_SINGLE_VARIANTS, + pedantic, + "a wildcard enum match for a single variant" +} + declare_clippy_lint! { /// **What it does:** Checks for wildcard pattern used with others patterns in same match arm. /// @@ -356,6 +390,7 @@ impl_lint_pass!(Matches => [ MATCH_WILD_ERR_ARM, MATCH_AS_REF, WILDCARD_ENUM_MATCH_ARM, + MATCH_WILDCARD_FOR_SINGLE_VARIANTS, WILDCARD_IN_OR_PATTERNS, MATCH_SINGLE_BINDING, INFALLIBLE_DESTRUCTURING_MATCH, @@ -766,6 +801,19 @@ fn check_wild_enum_match(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_ } } + if suggestion.len() == 1 { + // No need to check for non-exhaustive enum as in that case len would be greater than 1 + span_lint_and_sugg( + cx, + MATCH_WILDCARD_FOR_SINGLE_VARIANTS, + wildcard_span, + message, + "try this", + suggestion[0].clone(), + Applicability::MachineApplicable, + ) + }; + span_lint_and_sugg( cx, WILDCARD_ENUM_MATCH_ARM, diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index e1a6d4bdd31..250a7c09f78 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1200,6 +1200,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "matches", }, + Lint { + name: "match_wildcard_for_single_variants", + group: "pedantic", + desc: "a wildcard enum match for a single variant", + deprecation: None, + module: "matches", + }, Lint { name: "maybe_infinite_iter", group: "pedantic", diff --git a/tests/ui/match_wildcard_for_single_variants.fixed b/tests/ui/match_wildcard_for_single_variants.fixed new file mode 100644 index 00000000000..5f1a559f591 --- /dev/null +++ b/tests/ui/match_wildcard_for_single_variants.fixed @@ -0,0 +1,18 @@ +// run-rustfix + +#![warn(clippy::match_wildcard_for_single_variants)] +#![allow(dead_code)] + +enum Foo { + A, + B, + C, +} + +fn main() { + match Foo::A { + Foo::A => {}, + Foo::B => {}, + Foo::C => {}, + } +} diff --git a/tests/ui/match_wildcard_for_single_variants.rs b/tests/ui/match_wildcard_for_single_variants.rs new file mode 100644 index 00000000000..1159f9e722d --- /dev/null +++ b/tests/ui/match_wildcard_for_single_variants.rs @@ -0,0 +1,18 @@ +// run-rustfix + +#![warn(clippy::match_wildcard_for_single_variants)] +#![allow(dead_code)] + +enum Foo { + A, + B, + C, +} + +fn main() { + match Foo::A { + Foo::A => {}, + Foo::B => {}, + _ => {}, + } +} diff --git a/tests/ui/match_wildcard_for_single_variants.stderr b/tests/ui/match_wildcard_for_single_variants.stderr new file mode 100644 index 00000000000..128dd4808bf --- /dev/null +++ b/tests/ui/match_wildcard_for_single_variants.stderr @@ -0,0 +1,10 @@ +error: wildcard match will miss any future added variants + --> $DIR/match_wildcard_for_single_variants.rs:16:9 + | +LL | _ => {}, + | ^ help: try this: `Foo::C` + | + = note: `-D clippy::match-wildcard-for-single-variants` implied by `-D warnings` + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From 0ad9f7d651b52de4be6384c9b6dc893b389fd557 Mon Sep 17 00:00:00 2001 From: Vardan Margaryan Date: Sun, 10 May 2020 18:33:12 +0300 Subject: Fix trivial cases of new match_wildcard_for_single_variants lint --- clippy_lints/src/float_literal.rs | 2 +- clippy_lints/src/misc_early.rs | 2 +- clippy_lints/src/missing_const_for_fn.rs | 2 +- clippy_lints/src/needless_pass_by_value.rs | 2 +- clippy_lints/src/trivially_copy_pass_by_ref.rs | 2 +- tests/compile-test.rs | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/float_literal.rs b/clippy_lints/src/float_literal.rs index 3a52b1d3fc2..4c604cd0107 100644 --- a/clippy_lints/src/float_literal.rs +++ b/clippy_lints/src/float_literal.rs @@ -77,7 +77,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for FloatLiteral { let type_suffix = match lit_float_ty { LitFloatType::Suffixed(FloatTy::F32) => Some("f32"), LitFloatType::Suffixed(FloatTy::F64) => Some("f64"), - _ => None + LitFloatType::Unsuffixed => None }; let (is_whole, mut float_str) = match fty { FloatTy::F32 => { diff --git a/clippy_lints/src/misc_early.rs b/clippy_lints/src/misc_early.rs index 62ee051624b..552222eba2e 100644 --- a/clippy_lints/src/misc_early.rs +++ b/clippy_lints/src/misc_early.rs @@ -379,7 +379,7 @@ impl EarlyLintPass for MiscEarlyLints { let left_binding = match left { BindingMode::ByRef(Mutability::Mut) => "ref mut ", BindingMode::ByRef(Mutability::Not) => "ref ", - _ => "", + BindingMode::ByValue(..) => "", }; if let PatKind::Wild = right.kind { diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index 4301157e164..9cfc8d19134 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -113,7 +113,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingConstForFn { return; } }, - _ => return, + FnKind::Closure(..) => return, } let mir = cx.tcx.optimized_mir(def_id); diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index a21818701da..c099c553333 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -86,7 +86,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue { } }, FnKind::Method(..) => (), - _ => return, + FnKind::Closure(..) => return, } // Exclude non-inherent impls diff --git a/clippy_lints/src/trivially_copy_pass_by_ref.rs b/clippy_lints/src/trivially_copy_pass_by_ref.rs index 2c101220c5d..8e0cb94317a 100644 --- a/clippy_lints/src/trivially_copy_pass_by_ref.rs +++ b/clippy_lints/src/trivially_copy_pass_by_ref.rs @@ -161,7 +161,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TriviallyCopyPassByRef { } }, FnKind::Method(..) => (), - _ => return, + FnKind::Closure(..) => return, } // Exclude non-inherent impls diff --git a/tests/compile-test.rs b/tests/compile-test.rs index de2cf6d7873..a3df9d5ccbd 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -44,7 +44,7 @@ fn third_party_crates() -> String { for entry in fs::read_dir(dep_dir).unwrap() { let path = match entry { Ok(entry) => entry.path(), - _ => continue, + Err(_) => continue, }; if let Some(name) = path.file_name().and_then(OsStr::to_str) { for dep in CRATES { -- cgit 1.4.1-3-g733a5 From 93386563f66823ac7d10641c007b0bbc23ab09e6 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 3 May 2020 19:58:27 +0200 Subject: Rename lint `map_unwrap` to `map_unwrap_or` and register lints as renamed --- CHANGELOG.md | 3 +- clippy_lints/src/lib.rs | 15 ++- clippy_lints/src/methods/mod.rs | 8 +- clippy_lints/src/methods/option_map_unwrap_or.rs | 4 +- src/lintlist/mod.rs | 9 +- tests/ui/map_unwrap.rs | 99 -------------- tests/ui/map_unwrap.stderr | 161 ----------------------- tests/ui/map_unwrap_or.rs | 99 ++++++++++++++ tests/ui/map_unwrap_or.stderr | 161 +++++++++++++++++++++++ 9 files changed, 281 insertions(+), 278 deletions(-) delete mode 100644 tests/ui/map_unwrap.rs delete mode 100644 tests/ui/map_unwrap.stderr create mode 100644 tests/ui/map_unwrap_or.rs create mode 100644 tests/ui/map_unwrap_or.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f9486e0972..77272f4f78b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1430,7 +1430,7 @@ Released 2018-09-13 [`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone [`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry [`map_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten -[`map_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap +[`map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or [`match_as_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_as_ref [`match_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_bool [`match_on_vec_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_on_vec_items @@ -1538,7 +1538,6 @@ Released 2018-09-13 [`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs [`result_map_or_into_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_or_into_option [`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn -[`result_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unwrap_used [`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges [`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition [`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 8de94d19d31..ff67ccae794 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -673,7 +673,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::ITER_SKIP_NEXT, &methods::MANUAL_SATURATING_ARITHMETIC, &methods::MAP_FLATTEN, - &methods::MAP_UNWRAP, + &methods::MAP_UNWRAP_OR, &methods::NEW_RET_NO_SELF, &methods::OK_EXPECT, &methods::OPTION_AND_THEN_SOME, @@ -1145,7 +1145,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::FIND_MAP), LintId::of(&methods::INEFFICIENT_TO_STRING), LintId::of(&methods::MAP_FLATTEN), - LintId::of(&methods::MAP_UNWRAP), + LintId::of(&methods::MAP_UNWRAP_OR), LintId::of(&misc::USED_UNDERSCORE_BINDING), LintId::of(&misc_early::UNSEPARATED_LITERAL_SUFFIX), LintId::of(&mut_mut::MUT_MUT), @@ -1785,6 +1785,17 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) { ls.register_renamed("clippy::new_without_default_derive", "clippy::new_without_default"); ls.register_renamed("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"); ls.register_renamed("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"); + ls.register_renamed("clippy::block_in_if_condition_expr", "clippy::block_in_if_condition"); + ls.register_renamed("clippy::block_in_if_condition_stmt", "clippy::block_in_if_condition"); + ls.register_renamed("clippy::option_map_unwrap_or", "clippy::map_unwrap_or"); + ls.register_renamed("clippy::option_map_unwrap_or_else", "clippy::map_unwrap_or"); + ls.register_renamed("clippy::result_map_unwrap_or_else", "clippy::map_unwrap_or"); + ls.register_renamed("clippy::option_unwrap_used", "clippy::unwrap_used"); + ls.register_renamed("clippy::result_unwrap_used", "clippy::unwrap_used"); + ls.register_renamed("clippy::option_expect_used", "clippy::expect_used"); + ls.register_renamed("clippy::result_expect_used", "clippy::expect_used"); + ls.register_renamed("clippy::for_loop_over_option", "clippy::for_loop_over_fallible"); + ls.register_renamed("clippy::for_loop_over_result", "clippy::for_loop_over_fallible"); } // only exists to let the dogfood integration test works. diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 2e75de019b6..e6094edc5d7 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -255,7 +255,7 @@ declare_clippy_lint! { /// // Good /// x.map_or_else(some_function, |a| a + 1); /// ``` - pub MAP_UNWRAP, + pub MAP_UNWRAP_OR, pedantic, "using `.map(f).unwrap_or(a)` or `.map(f).unwrap_or_else(func)`, which are more succinctly expressed as `map_or(a, f)` or `map_or_else(a, f)`" } @@ -1240,7 +1240,7 @@ declare_lint_pass!(Methods => [ WRONG_SELF_CONVENTION, WRONG_PUB_SELF_CONVENTION, OK_EXPECT, - MAP_UNWRAP, + MAP_UNWRAP_OR, RESULT_MAP_OR_INTO_OPTION, OPTION_MAP_OR_NONE, OPTION_AND_THEN_SOME, @@ -2512,7 +2512,7 @@ fn lint_map_unwrap_or_else<'a, 'tcx>( if same_span && !multiline { span_lint_and_note( cx, - MAP_UNWRAP, + MAP_UNWRAP_OR, expr.span, msg, None, @@ -2522,7 +2522,7 @@ fn lint_map_unwrap_or_else<'a, 'tcx>( ), ); } else if same_span && multiline { - span_lint(cx, MAP_UNWRAP, expr.span, msg); + span_lint(cx, MAP_UNWRAP_OR, expr.span, msg); }; } } diff --git a/clippy_lints/src/methods/option_map_unwrap_or.rs b/clippy_lints/src/methods/option_map_unwrap_or.rs index fcaa9b47e64..20c60ef3318 100644 --- a/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -9,7 +9,7 @@ use rustc_middle::hir::map::Map; use rustc_span::source_map::Span; use rustc_span::symbol::Symbol; -use super::MAP_UNWRAP; +use super::MAP_UNWRAP_OR; /// lint use of `map().unwrap_or()` for `Option`s pub(super) fn lint<'a, 'tcx>( @@ -66,7 +66,7 @@ pub(super) fn lint<'a, 'tcx>( arg, suggest ); - span_lint_and_then(cx, MAP_UNWRAP, expr.span, msg, |diag| { + span_lint_and_then(cx, MAP_UNWRAP_OR, expr.span, msg, |diag| { let map_arg_span = map_args[1].span; let mut suggestion = vec![ diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 0ea0f55a381..e90b9c15747 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1138,7 +1138,7 @@ pub static ref ALL_LINTS: Vec = vec![ module: "methods", }, Lint { - name: "map_unwrap", + name: "map_unwrap_or", group: "pedantic", desc: "using `.map(f).unwrap_or(a)` or `.map(f).unwrap_or_else(func)`, which are more succinctly expressed as `map_or(a, f)` or `map_or_else(a, f)`", deprecation: None, @@ -1872,13 +1872,6 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "map_unit_fn", }, - Lint { - name: "result_unwrap_used", - group: "restriction", - desc: "using `Result.unwrap()`, which might be better handled", - deprecation: None, - module: "methods", - }, Lint { name: "reversed_empty_ranges", group: "correctness", diff --git a/tests/ui/map_unwrap.rs b/tests/ui/map_unwrap.rs deleted file mode 100644 index 53e50368231..00000000000 --- a/tests/ui/map_unwrap.rs +++ /dev/null @@ -1,99 +0,0 @@ -// FIXME: Add "run-rustfix" once it's supported for multipart suggestions -// aux-build:option_helpers.rs - -#![warn(clippy::map_unwrap)] - -#[macro_use] -extern crate option_helpers; - -use std::collections::HashMap; - -#[rustfmt::skip] -fn option_methods() { - let opt = Some(1); - - // Check for `option.map(_).unwrap_or(_)` use. - // Single line case. - let _ = opt.map(|x| x + 1) - // Should lint even though this call is on a separate line. - .unwrap_or(0); - // Multi-line cases. - let _ = opt.map(|x| { - x + 1 - } - ).unwrap_or(0); - let _ = opt.map(|x| x + 1) - .unwrap_or({ - 0 - }); - // Single line `map(f).unwrap_or(None)` case. - let _ = opt.map(|x| Some(x + 1)).unwrap_or(None); - // Multi-line `map(f).unwrap_or(None)` cases. - let _ = opt.map(|x| { - Some(x + 1) - } - ).unwrap_or(None); - let _ = opt - .map(|x| Some(x + 1)) - .unwrap_or(None); - // macro case - let _ = opt_map!(opt, |x| x + 1).unwrap_or(0); // should not lint - - // Should not lint if not copyable - let id: String = "identifier".to_string(); - let _ = Some("prefix").map(|p| format!("{}.{}", p, id)).unwrap_or(id); - // ...but DO lint if the `unwrap_or` argument is not used in the `map` - let id: String = "identifier".to_string(); - let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id); - - // Check for `option.map(_).unwrap_or_else(_)` use. - // single line case - let _ = opt.map(|x| x + 1) - // Should lint even though this call is on a separate line. - .unwrap_or_else(|| 0); - // Multi-line cases. - let _ = opt.map(|x| { - x + 1 - } - ).unwrap_or_else(|| 0); - let _ = opt.map(|x| x + 1) - .unwrap_or_else(|| - 0 - ); - // Macro case. - // Should not lint. - let _ = opt_map!(opt, |x| x + 1).unwrap_or_else(|| 0); - - // Issue #4144 - { - let mut frequencies = HashMap::new(); - let word = "foo"; - - frequencies - .get_mut(word) - .map(|count| { - *count += 1; - }) - .unwrap_or_else(|| { - frequencies.insert(word.to_owned(), 1); - }); - } -} - -fn result_methods() { - let res: Result = Ok(1); - - // Check for `result.map(_).unwrap_or_else(_)` use. - // single line case - let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); // should lint even though this call is on a separate line - // multi line cases - let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); - let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); - // macro case - let _ = opt_map!(res, |x| x + 1).unwrap_or_else(|e| 0); // should not lint -} - -fn main() { - option_methods(); - result_methods(); -} diff --git a/tests/ui/map_unwrap.stderr b/tests/ui/map_unwrap.stderr deleted file mode 100644 index 2610923275d..00000000000 --- a/tests/ui/map_unwrap.stderr +++ /dev/null @@ -1,161 +0,0 @@ -error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead - --> $DIR/map_unwrap.rs:17:13 - | -LL | let _ = opt.map(|x| x + 1) - | _____________^ -LL | | // Should lint even though this call is on a separate line. -LL | | .unwrap_or(0); - | |_____________________^ - | - = note: `-D clippy::map-unwrap` implied by `-D warnings` -help: use `map_or(a, f)` instead - | -LL | let _ = opt.map_or(0, |x| x + 1); - | ^^^^^^ ^^ -- - -error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead - --> $DIR/map_unwrap.rs:21:13 - | -LL | let _ = opt.map(|x| { - | _____________^ -LL | | x + 1 -LL | | } -LL | | ).unwrap_or(0); - | |__________________^ - | -help: use `map_or(a, f)` instead - | -LL | let _ = opt.map_or(0, |x| { -LL | x + 1 -LL | } -LL | ); - | - -error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead - --> $DIR/map_unwrap.rs:25:13 - | -LL | let _ = opt.map(|x| x + 1) - | _____________^ -LL | | .unwrap_or({ -LL | | 0 -LL | | }); - | |__________^ - | -help: use `map_or(a, f)` instead - | -LL | let _ = opt.map_or({ -LL | 0 -LL | }, |x| x + 1); - | - -error: called `map(f).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead - --> $DIR/map_unwrap.rs:30:13 - | -LL | let _ = opt.map(|x| Some(x + 1)).unwrap_or(None); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use `and_then(f)` instead - | -LL | let _ = opt.and_then(|x| Some(x + 1)); - | ^^^^^^^^ -- - -error: called `map(f).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead - --> $DIR/map_unwrap.rs:32:13 - | -LL | let _ = opt.map(|x| { - | _____________^ -LL | | Some(x + 1) -LL | | } -LL | | ).unwrap_or(None); - | |_____________________^ - | -help: use `and_then(f)` instead - | -LL | let _ = opt.and_then(|x| { -LL | Some(x + 1) -LL | } -LL | ); - | - -error: called `map(f).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead - --> $DIR/map_unwrap.rs:36:13 - | -LL | let _ = opt - | _____________^ -LL | | .map(|x| Some(x + 1)) -LL | | .unwrap_or(None); - | |________________________^ - | -help: use `and_then(f)` instead - | -LL | .and_then(|x| Some(x + 1)); - | ^^^^^^^^ -- - -error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead - --> $DIR/map_unwrap.rs:47:13 - | -LL | let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use `map_or(a, f)` instead - | -LL | let _ = Some("prefix").map_or(id, |p| format!("{}.", p)); - | ^^^^^^ ^^^ -- - -error: called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling `map_or_else(g, f)` instead - --> $DIR/map_unwrap.rs:51:13 - | -LL | let _ = opt.map(|x| x + 1) - | _____________^ -LL | | // Should lint even though this call is on a separate line. -LL | | .unwrap_or_else(|| 0); - | |_____________________________^ - | - = note: replace `map(|x| x + 1).unwrap_or_else(|| 0)` with `map_or_else(|| 0, |x| x + 1)` - -error: called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling `map_or_else(g, f)` instead - --> $DIR/map_unwrap.rs:55:13 - | -LL | let _ = opt.map(|x| { - | _____________^ -LL | | x + 1 -LL | | } -LL | | ).unwrap_or_else(|| 0); - | |__________________________^ - -error: called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling `map_or_else(g, f)` instead - --> $DIR/map_unwrap.rs:59:13 - | -LL | let _ = opt.map(|x| x + 1) - | _____________^ -LL | | .unwrap_or_else(|| -LL | | 0 -LL | | ); - | |_________^ - -error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead - --> $DIR/map_unwrap.rs:88:13 - | -LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); // should lint even though this call is on a separate line - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` - -error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead - --> $DIR/map_unwrap.rs:90:13 - | -LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` - -error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead - --> $DIR/map_unwrap.rs:91:13 - | -LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` - -error: aborting due to 13 previous errors - diff --git a/tests/ui/map_unwrap_or.rs b/tests/ui/map_unwrap_or.rs new file mode 100644 index 00000000000..585944032e7 --- /dev/null +++ b/tests/ui/map_unwrap_or.rs @@ -0,0 +1,99 @@ +// FIXME: Add "run-rustfix" once it's supported for multipart suggestions +// aux-build:option_helpers.rs + +#![warn(clippy::map_unwrap_or)] + +#[macro_use] +extern crate option_helpers; + +use std::collections::HashMap; + +#[rustfmt::skip] +fn option_methods() { + let opt = Some(1); + + // Check for `option.map(_).unwrap_or(_)` use. + // Single line case. + let _ = opt.map(|x| x + 1) + // Should lint even though this call is on a separate line. + .unwrap_or(0); + // Multi-line cases. + let _ = opt.map(|x| { + x + 1 + } + ).unwrap_or(0); + let _ = opt.map(|x| x + 1) + .unwrap_or({ + 0 + }); + // Single line `map(f).unwrap_or(None)` case. + let _ = opt.map(|x| Some(x + 1)).unwrap_or(None); + // Multi-line `map(f).unwrap_or(None)` cases. + let _ = opt.map(|x| { + Some(x + 1) + } + ).unwrap_or(None); + let _ = opt + .map(|x| Some(x + 1)) + .unwrap_or(None); + // macro case + let _ = opt_map!(opt, |x| x + 1).unwrap_or(0); // should not lint + + // Should not lint if not copyable + let id: String = "identifier".to_string(); + let _ = Some("prefix").map(|p| format!("{}.{}", p, id)).unwrap_or(id); + // ...but DO lint if the `unwrap_or` argument is not used in the `map` + let id: String = "identifier".to_string(); + let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id); + + // Check for `option.map(_).unwrap_or_else(_)` use. + // single line case + let _ = opt.map(|x| x + 1) + // Should lint even though this call is on a separate line. + .unwrap_or_else(|| 0); + // Multi-line cases. + let _ = opt.map(|x| { + x + 1 + } + ).unwrap_or_else(|| 0); + let _ = opt.map(|x| x + 1) + .unwrap_or_else(|| + 0 + ); + // Macro case. + // Should not lint. + let _ = opt_map!(opt, |x| x + 1).unwrap_or_else(|| 0); + + // Issue #4144 + { + let mut frequencies = HashMap::new(); + let word = "foo"; + + frequencies + .get_mut(word) + .map(|count| { + *count += 1; + }) + .unwrap_or_else(|| { + frequencies.insert(word.to_owned(), 1); + }); + } +} + +fn result_methods() { + let res: Result = Ok(1); + + // Check for `result.map(_).unwrap_or_else(_)` use. + // single line case + let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); // should lint even though this call is on a separate line + // multi line cases + let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); + let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); + // macro case + let _ = opt_map!(res, |x| x + 1).unwrap_or_else(|e| 0); // should not lint +} + +fn main() { + option_methods(); + result_methods(); +} diff --git a/tests/ui/map_unwrap_or.stderr b/tests/ui/map_unwrap_or.stderr new file mode 100644 index 00000000000..b62080a073f --- /dev/null +++ b/tests/ui/map_unwrap_or.stderr @@ -0,0 +1,161 @@ +error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead + --> $DIR/map_unwrap_or.rs:17:13 + | +LL | let _ = opt.map(|x| x + 1) + | _____________^ +LL | | // Should lint even though this call is on a separate line. +LL | | .unwrap_or(0); + | |_____________________^ + | + = note: `-D clippy::map-unwrap-or` implied by `-D warnings` +help: use `map_or(a, f)` instead + | +LL | let _ = opt.map_or(0, |x| x + 1); + | ^^^^^^ ^^ -- + +error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead + --> $DIR/map_unwrap_or.rs:21:13 + | +LL | let _ = opt.map(|x| { + | _____________^ +LL | | x + 1 +LL | | } +LL | | ).unwrap_or(0); + | |__________________^ + | +help: use `map_or(a, f)` instead + | +LL | let _ = opt.map_or(0, |x| { +LL | x + 1 +LL | } +LL | ); + | + +error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead + --> $DIR/map_unwrap_or.rs:25:13 + | +LL | let _ = opt.map(|x| x + 1) + | _____________^ +LL | | .unwrap_or({ +LL | | 0 +LL | | }); + | |__________^ + | +help: use `map_or(a, f)` instead + | +LL | let _ = opt.map_or({ +LL | 0 +LL | }, |x| x + 1); + | + +error: called `map(f).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead + --> $DIR/map_unwrap_or.rs:30:13 + | +LL | let _ = opt.map(|x| Some(x + 1)).unwrap_or(None); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `and_then(f)` instead + | +LL | let _ = opt.and_then(|x| Some(x + 1)); + | ^^^^^^^^ -- + +error: called `map(f).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead + --> $DIR/map_unwrap_or.rs:32:13 + | +LL | let _ = opt.map(|x| { + | _____________^ +LL | | Some(x + 1) +LL | | } +LL | | ).unwrap_or(None); + | |_____________________^ + | +help: use `and_then(f)` instead + | +LL | let _ = opt.and_then(|x| { +LL | Some(x + 1) +LL | } +LL | ); + | + +error: called `map(f).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead + --> $DIR/map_unwrap_or.rs:36:13 + | +LL | let _ = opt + | _____________^ +LL | | .map(|x| Some(x + 1)) +LL | | .unwrap_or(None); + | |________________________^ + | +help: use `and_then(f)` instead + | +LL | .and_then(|x| Some(x + 1)); + | ^^^^^^^^ -- + +error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead + --> $DIR/map_unwrap_or.rs:47:13 + | +LL | let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `map_or(a, f)` instead + | +LL | let _ = Some("prefix").map_or(id, |p| format!("{}.", p)); + | ^^^^^^ ^^^ -- + +error: called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling `map_or_else(g, f)` instead + --> $DIR/map_unwrap_or.rs:51:13 + | +LL | let _ = opt.map(|x| x + 1) + | _____________^ +LL | | // Should lint even though this call is on a separate line. +LL | | .unwrap_or_else(|| 0); + | |_____________________________^ + | + = note: replace `map(|x| x + 1).unwrap_or_else(|| 0)` with `map_or_else(|| 0, |x| x + 1)` + +error: called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling `map_or_else(g, f)` instead + --> $DIR/map_unwrap_or.rs:55:13 + | +LL | let _ = opt.map(|x| { + | _____________^ +LL | | x + 1 +LL | | } +LL | | ).unwrap_or_else(|| 0); + | |__________________________^ + +error: called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling `map_or_else(g, f)` instead + --> $DIR/map_unwrap_or.rs:59:13 + | +LL | let _ = opt.map(|x| x + 1) + | _____________^ +LL | | .unwrap_or_else(|| +LL | | 0 +LL | | ); + | |_________^ + +error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead + --> $DIR/map_unwrap_or.rs:88:13 + | +LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); // should lint even though this call is on a separate line + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` + +error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead + --> $DIR/map_unwrap_or.rs:90:13 + | +LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` + +error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead + --> $DIR/map_unwrap_or.rs:91:13 + | +LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` + +error: aborting due to 13 previous errors + -- cgit 1.4.1-3-g733a5 From ab87f87ba03518da23ca510249aa3f5908a42368 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Fri, 15 May 2020 18:20:07 +0200 Subject: Fix CHANGELOG.md and lint names plural --- CHANGELOG.md | 22 ++-- clippy_lints/src/block_in_if_condition.rs | 145 ------------------------ clippy_lints/src/blocks_in_if_conditions.rs | 145 ++++++++++++++++++++++++ clippy_lints/src/lib.rs | 24 ++-- clippy_lints/src/loops.rs | 8 +- src/lintlist/mod.rs | 6 +- tests/ui/block_in_if_condition.fixed | 74 ------------ tests/ui/block_in_if_condition.rs | 74 ------------ tests/ui/block_in_if_condition.stderr | 34 ------ tests/ui/block_in_if_condition_closure.rs | 47 -------- tests/ui/block_in_if_condition_closure.stderr | 24 ---- tests/ui/blocks_in_if_conditions.fixed | 74 ++++++++++++ tests/ui/blocks_in_if_conditions.rs | 74 ++++++++++++ tests/ui/blocks_in_if_conditions.stderr | 34 ++++++ tests/ui/blocks_in_if_conditions_closure.rs | 47 ++++++++ tests/ui/blocks_in_if_conditions_closure.stderr | 24 ++++ tests/ui/for_loop_over_fallible.rs | 57 ---------- tests/ui/for_loop_over_fallible.stderr | 71 ------------ tests/ui/for_loops_over_fallibles.rs | 57 ++++++++++ tests/ui/for_loops_over_fallibles.stderr | 71 ++++++++++++ 20 files changed, 556 insertions(+), 556 deletions(-) delete mode 100644 clippy_lints/src/block_in_if_condition.rs create mode 100644 clippy_lints/src/blocks_in_if_conditions.rs delete mode 100644 tests/ui/block_in_if_condition.fixed delete mode 100644 tests/ui/block_in_if_condition.rs delete mode 100644 tests/ui/block_in_if_condition.stderr delete mode 100644 tests/ui/block_in_if_condition_closure.rs delete mode 100644 tests/ui/block_in_if_condition_closure.stderr create mode 100644 tests/ui/blocks_in_if_conditions.fixed create mode 100644 tests/ui/blocks_in_if_conditions.rs create mode 100644 tests/ui/blocks_in_if_conditions.stderr create mode 100644 tests/ui/blocks_in_if_conditions_closure.rs create mode 100644 tests/ui/blocks_in_if_conditions_closure.stderr delete mode 100644 tests/ui/for_loop_over_fallible.rs delete mode 100644 tests/ui/for_loop_over_fallible.stderr create mode 100644 tests/ui/for_loops_over_fallibles.rs create mode 100644 tests/ui/for_loops_over_fallibles.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 77272f4f78b..d05819a973a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -198,7 +198,7 @@ Released 2020-03-12 ### Suggestion Improvements -* [`option_map_unwrap_or`] [#4634](https://github.com/rust-lang/rust-clippy/pull/4634) +* `option_map_unwrap_or` [#4634](https://github.com/rust-lang/rust-clippy/pull/4634) * [`wildcard_enum_match_arm`] [#4934](https://github.com/rust-lang/rust-clippy/pull/4934) * [`cognitive_complexity`] [#4935](https://github.com/rust-lang/rust-clippy/pull/4935) * [`decimal_literal_representation`] [#4956](https://github.com/rust-lang/rust-clippy/pull/4956) @@ -282,8 +282,8 @@ Released 2019-12-19 * [`panic`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657) * [`unreachable`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657) * [`todo`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657) - * [`option_expect_used`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657) - * [`result_expect_used`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657) + * `option_expect_used` [#4657](https://github.com/rust-lang/rust-clippy/pull/4657) + * `result_expect_used` [#4657](https://github.com/rust-lang/rust-clippy/pull/4657) * Move `redundant_clone` to perf group [#4509](https://github.com/rust-lang/rust-clippy/pull/4509) * Move `manual_mul_add` to nursery group [#4736](https://github.com/rust-lang/rust-clippy/pull/4736) * Expand `unit_cmp` to also work with `assert_eq!`, `debug_assert_eq!`, `assert_ne!` and `debug_assert_ne!` [#4613](https://github.com/rust-lang/rust-clippy/pull/4613) @@ -395,7 +395,7 @@ Released 2019-08-15 * Fix false positive in [`useless_attribute`] [#4107](https://github.com/rust-lang/rust-clippy/pull/4107) * Fix incorrect suggestion for [`float_cmp`] [#4214](https://github.com/rust-lang/rust-clippy/pull/4214) * Add suggestions for [`print_with_newline`] and [`write_with_newline`] [#4136](https://github.com/rust-lang/rust-clippy/pull/4136) -* Improve suggestions for [`option_map_unwrap_or_else`] and [`result_map_unwrap_or_else`] [#4164](https://github.com/rust-lang/rust-clippy/pull/4164) +* Improve suggestions for `option_map_unwrap_or_else` and `result_map_unwrap_or_else` [#4164](https://github.com/rust-lang/rust-clippy/pull/4164) * Improve suggestions for [`non_ascii_literal`] [#4119](https://github.com/rust-lang/rust-clippy/pull/4119) * Improve diagnostics for [`let_and_return`] [#4137](https://github.com/rust-lang/rust-clippy/pull/4137) * Improve diagnostics for [`trivially_copy_pass_by_ref`] [#4071](https://github.com/rust-lang/rust-clippy/pull/4071) @@ -448,7 +448,7 @@ Released 2019-05-20 * Fix false positive in [`needless_range_loop`] pertaining to structs without a `.iter()` * Fix false positive in [`bool_comparison`] pertaining to non-bool types * Fix false positive in [`redundant_closure`] pertaining to differences in borrows -* Fix false positive in [`option_map_unwrap_or`] on non-copy types +* Fix false positive in `option_map_unwrap_or` on non-copy types * Fix false positives in [`missing_const_for_fn`] pertaining to macros and trait method impls * Fix false positive in [`needless_pass_by_value`] pertaining to procedural macros * Fix false positive in [`needless_continue`] pertaining to loop labels @@ -794,7 +794,7 @@ Released 2018-09-13 ## 0.0.169 * Rustup to *rustc 1.23.0-nightly (3b82e4c74 2017-11-05)* -* New lints: [`just_underscores_and_digits`], [`result_map_unwrap_or_else`], [`transmute_bytes_to_str`] +* New lints: [`just_underscores_and_digits`], `result_map_unwrap_or_else`, [`transmute_bytes_to_str`] ## 0.0.168 * Rustup to *rustc 1.23.0-nightly (f0fe716db 2017-10-30)* @@ -1068,7 +1068,7 @@ Released 2018-09-13 ## 0.0.93 — 2016-10-03 * Rustup to *rustc 1.14.0-nightly (144af3e97 2016-10-02)* -* [`option_map_unwrap_or`] and [`option_map_unwrap_or_else`] are now +* `option_map_unwrap_or` and `option_map_unwrap_or_else` are now allowed by default. * New lint: [`explicit_into_iter_loop`] @@ -1087,8 +1087,8 @@ Released 2018-09-13 ## 0.0.88 — 2016-09-04 * Rustup to *rustc 1.13.0-nightly (70598e04f 2016-09-03)* * The following lints are not new but were only usable through the `clippy` - lint groups: [`filter_next`], [`for_loop_over_option`], - [`for_loop_over_result`] and [`match_overlapping_arm`]. You should now be + lint groups: [`filter_next`], `for_loop_over_option`, + `for_loop_over_result` and [`match_overlapping_arm`]. You should now be able to `#[allow/deny]` them individually and they are available directly through `cargo clippy`. @@ -1274,7 +1274,7 @@ Released 2018-09-13 [`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock [`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask [`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name -[`block_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_in_if_condition +[`blocks_in_if_conditions`]: https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_if_conditions [`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison [`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const [`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box @@ -1361,7 +1361,7 @@ Released 2018-09-13 [`fn_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast [`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_loop_over_fallible`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loop_over_fallible +[`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 [`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send diff --git a/clippy_lints/src/block_in_if_condition.rs b/clippy_lints/src/block_in_if_condition.rs deleted file mode 100644 index 8a5e595749f..00000000000 --- a/clippy_lints/src/block_in_if_condition.rs +++ /dev/null @@ -1,145 +0,0 @@ -use crate::utils::{differing_macro_contexts, higher, snippet_block_with_applicability, span_lint, span_lint_and_sugg}; -use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; -use rustc_hir::{BlockCheckMode, Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::hir::map::Map; -use rustc_middle::lint::in_external_macro; -use rustc_session::{declare_lint_pass, declare_tool_lint}; - -declare_clippy_lint! { - /// **What it does:** Checks for `if` conditions that use blocks containing an - /// expression, statements or conditions that use closures with blocks. - /// - /// **Why is this bad?** Style, using blocks in the condition makes it hard to read. - /// - /// **Known problems:** None. - /// - /// **Examples:** - /// ```rust - /// // Bad - /// if { true } { /* ... */ } - /// - /// // Good - /// if true { /* ... */ } - /// ``` - /// - /// // or - /// - /// ```rust - /// # fn somefunc() -> bool { true }; - /// - /// // Bad - /// if { let x = somefunc(); x } { /* ... */ } - /// - /// // Good - /// let res = { let x = somefunc(); x }; - /// if res { /* ... */ } - /// ``` - pub BLOCK_IN_IF_CONDITION, - style, - "useless or complex blocks that can be eliminated in conditions" -} - -declare_lint_pass!(BlockInIfCondition => [BLOCK_IN_IF_CONDITION]); - -struct ExVisitor<'a, 'tcx> { - found_block: Option<&'tcx Expr<'tcx>>, - cx: &'a LateContext<'a, 'tcx>, -} - -impl<'a, 'tcx> Visitor<'tcx> for ExVisitor<'a, 'tcx> { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - if let ExprKind::Closure(_, _, eid, _, _) = expr.kind { - let body = self.cx.tcx.hir().body(eid); - let ex = &body.value; - if matches!(ex.kind, ExprKind::Block(_, _)) && !body.value.span.from_expansion() { - self.found_block = Some(ex); - return; - } - } - walk_expr(self, expr); - } - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } -} - -const BRACED_EXPR_MESSAGE: &str = "omit braces around single expression condition"; -const COMPLEX_BLOCK_MESSAGE: &str = "in an `if` condition, avoid complex blocks or closures with blocks; \ - instead, move the block or closure higher and bind it with a `let`"; - -impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BlockInIfCondition { - fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { - if in_external_macro(cx.sess(), expr.span) { - return; - } - if let Some((cond, _, _)) = higher::if_block(&expr) { - if let ExprKind::Block(block, _) = &cond.kind { - if block.rules == BlockCheckMode::DefaultBlock { - if block.stmts.is_empty() { - if let Some(ex) = &block.expr { - // don't dig into the expression here, just suggest that they remove - // the block - if expr.span.from_expansion() || differing_macro_contexts(expr.span, ex.span) { - return; - } - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - BLOCK_IN_IF_CONDITION, - cond.span, - BRACED_EXPR_MESSAGE, - "try", - format!( - "{}", - snippet_block_with_applicability( - cx, - ex.span, - "..", - Some(expr.span), - &mut applicability - ) - ), - applicability, - ); - } - } else { - let span = block.expr.as_ref().map_or_else(|| block.stmts[0].span, |e| e.span); - if span.from_expansion() || differing_macro_contexts(expr.span, span) { - return; - } - // move block higher - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - BLOCK_IN_IF_CONDITION, - expr.span.with_hi(cond.span.hi()), - COMPLEX_BLOCK_MESSAGE, - "try", - format!( - "let res = {}; if res", - snippet_block_with_applicability( - cx, - block.span, - "..", - Some(expr.span), - &mut applicability - ), - ), - applicability, - ); - } - } - } else { - let mut visitor = ExVisitor { found_block: None, cx }; - walk_expr(&mut visitor, cond); - if let Some(block) = visitor.found_block { - span_lint(cx, BLOCK_IN_IF_CONDITION, block.span, COMPLEX_BLOCK_MESSAGE); - } - } - } - } -} diff --git a/clippy_lints/src/blocks_in_if_conditions.rs b/clippy_lints/src/blocks_in_if_conditions.rs new file mode 100644 index 00000000000..8fa9b05ca32 --- /dev/null +++ b/clippy_lints/src/blocks_in_if_conditions.rs @@ -0,0 +1,145 @@ +use crate::utils::{differing_macro_contexts, higher, snippet_block_with_applicability, span_lint, span_lint_and_sugg}; +use rustc_errors::Applicability; +use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; +use rustc_hir::{BlockCheckMode, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::hir::map::Map; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for `if` conditions that use blocks containing an + /// expression, statements or conditions that use closures with blocks. + /// + /// **Why is this bad?** Style, using blocks in the condition makes it hard to read. + /// + /// **Known problems:** None. + /// + /// **Examples:** + /// ```rust + /// // Bad + /// if { true } { /* ... */ } + /// + /// // Good + /// if true { /* ... */ } + /// ``` + /// + /// // or + /// + /// ```rust + /// # fn somefunc() -> bool { true }; + /// + /// // Bad + /// if { let x = somefunc(); x } { /* ... */ } + /// + /// // Good + /// let res = { let x = somefunc(); x }; + /// if res { /* ... */ } + /// ``` + pub BLOCKS_IN_IF_CONDITIONS, + style, + "useless or complex blocks that can be eliminated in conditions" +} + +declare_lint_pass!(BlocksInIfConditions => [BLOCKS_IN_IF_CONDITIONS]); + +struct ExVisitor<'a, 'tcx> { + found_block: Option<&'tcx Expr<'tcx>>, + cx: &'a LateContext<'a, 'tcx>, +} + +impl<'a, 'tcx> Visitor<'tcx> for ExVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { + if let ExprKind::Closure(_, _, eid, _, _) = expr.kind { + let body = self.cx.tcx.hir().body(eid); + let ex = &body.value; + if matches!(ex.kind, ExprKind::Block(_, _)) && !body.value.span.from_expansion() { + self.found_block = Some(ex); + return; + } + } + walk_expr(self, expr); + } + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +const BRACED_EXPR_MESSAGE: &str = "omit braces around single expression condition"; +const COMPLEX_BLOCK_MESSAGE: &str = "in an `if` condition, avoid complex blocks or closures with blocks; \ + instead, move the block or closure higher and bind it with a `let`"; + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BlocksInIfConditions { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if in_external_macro(cx.sess(), expr.span) { + return; + } + if let Some((cond, _, _)) = higher::if_block(&expr) { + if let ExprKind::Block(block, _) = &cond.kind { + if block.rules == BlockCheckMode::DefaultBlock { + if block.stmts.is_empty() { + if let Some(ex) = &block.expr { + // don't dig into the expression here, just suggest that they remove + // the block + if expr.span.from_expansion() || differing_macro_contexts(expr.span, ex.span) { + return; + } + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + BLOCKS_IN_IF_CONDITIONS, + cond.span, + BRACED_EXPR_MESSAGE, + "try", + format!( + "{}", + snippet_block_with_applicability( + cx, + ex.span, + "..", + Some(expr.span), + &mut applicability + ) + ), + applicability, + ); + } + } else { + let span = block.expr.as_ref().map_or_else(|| block.stmts[0].span, |e| e.span); + if span.from_expansion() || differing_macro_contexts(expr.span, span) { + return; + } + // move block higher + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + BLOCKS_IN_IF_CONDITIONS, + expr.span.with_hi(cond.span.hi()), + COMPLEX_BLOCK_MESSAGE, + "try", + format!( + "let res = {}; if res", + snippet_block_with_applicability( + cx, + block.span, + "..", + Some(expr.span), + &mut applicability + ), + ), + applicability, + ); + } + } + } else { + let mut visitor = ExVisitor { found_block: None, cx }; + walk_expr(&mut visitor, cond); + if let Some(block) = visitor.found_block { + span_lint(cx, BLOCKS_IN_IF_CONDITIONS, block.span, COMPLEX_BLOCK_MESSAGE); + } + } + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index ff67ccae794..eba4ab5056b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -180,7 +180,7 @@ mod attrs; mod await_holding_lock; mod bit_mask; mod blacklisted_name; -mod block_in_if_condition; +mod blocks_in_if_conditions; mod booleans; mod bytecount; mod cargo_common_metadata; @@ -507,7 +507,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &bit_mask::INEFFECTIVE_BIT_MASK, &bit_mask::VERBOSE_BIT_MASK, &blacklisted_name::BLACKLISTED_NAME, - &block_in_if_condition::BLOCK_IN_IF_CONDITION, + &blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS, &booleans::LOGIC_BUG, &booleans::NONMINIMAL_BOOL, &bytecount::NAIVE_BYTECOUNT, @@ -615,7 +615,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &loops::EXPLICIT_INTO_ITER_LOOP, &loops::EXPLICIT_ITER_LOOP, &loops::FOR_KV_MAP, - &loops::FOR_LOOP_OVER_FALLIBLE, + &loops::FOR_LOOPS_OVER_FALLIBLES, &loops::ITER_NEXT_LOOP, &loops::MANUAL_MEMCPY, &loops::MUT_RANGE_BOUND, @@ -894,7 +894,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box mut_reference::UnnecessaryMutPassed); store.register_late_pass(|| box len_zero::LenZero); store.register_late_pass(|| box attrs::Attributes); - store.register_late_pass(|| box block_in_if_condition::BlockInIfCondition); + store.register_late_pass(|| box blocks_in_if_conditions::BlocksInIfConditions); store.register_late_pass(|| box unicode::Unicode); store.register_late_pass(|| box strings::StringAdd); store.register_late_pass(|| box implicit_return::ImplicitReturn); @@ -1199,7 +1199,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&bit_mask::INEFFECTIVE_BIT_MASK), LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&blacklisted_name::BLACKLISTED_NAME), - LintId::of(&block_in_if_condition::BLOCK_IN_IF_CONDITION), + LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), LintId::of(&booleans::LOGIC_BUG), LintId::of(&booleans::NONMINIMAL_BOOL), LintId::of(&bytecount::NAIVE_BYTECOUNT), @@ -1264,7 +1264,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::EMPTY_LOOP), LintId::of(&loops::EXPLICIT_COUNTER_LOOP), LintId::of(&loops::FOR_KV_MAP), - LintId::of(&loops::FOR_LOOP_OVER_FALLIBLE), + LintId::of(&loops::FOR_LOOPS_OVER_FALLIBLES), LintId::of(&loops::ITER_NEXT_LOOP), LintId::of(&loops::MANUAL_MEMCPY), LintId::of(&loops::MUT_RANGE_BOUND), @@ -1444,7 +1444,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&attrs::UNKNOWN_CLIPPY_LINTS), LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&blacklisted_name::BLACKLISTED_NAME), - LintId::of(&block_in_if_condition::BLOCK_IN_IF_CONDITION), + LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), LintId::of(&collapsible_if::COLLAPSIBLE_IF), LintId::of(&comparison_chain::COMPARISON_CHAIN), LintId::of(&doc::MISSING_SAFETY_DOC), @@ -1639,7 +1639,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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::FOR_LOOP_OVER_FALLIBLE), + LintId::of(&loops::FOR_LOOPS_OVER_FALLIBLES), LintId::of(&loops::ITER_NEXT_LOOP), LintId::of(&loops::NEVER_LOOP), LintId::of(&loops::WHILE_IMMUTABLE_CONDITION), @@ -1785,8 +1785,8 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) { ls.register_renamed("clippy::new_without_default_derive", "clippy::new_without_default"); ls.register_renamed("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"); ls.register_renamed("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"); - ls.register_renamed("clippy::block_in_if_condition_expr", "clippy::block_in_if_condition"); - ls.register_renamed("clippy::block_in_if_condition_stmt", "clippy::block_in_if_condition"); + ls.register_renamed("clippy::block_in_if_condition_expr", "clippy::blocks_in_if_conditions"); + ls.register_renamed("clippy::block_in_if_condition_stmt", "clippy::blocks_in_if_conditions"); ls.register_renamed("clippy::option_map_unwrap_or", "clippy::map_unwrap_or"); ls.register_renamed("clippy::option_map_unwrap_or_else", "clippy::map_unwrap_or"); ls.register_renamed("clippy::result_map_unwrap_or_else", "clippy::map_unwrap_or"); @@ -1794,8 +1794,8 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) { ls.register_renamed("clippy::result_unwrap_used", "clippy::unwrap_used"); ls.register_renamed("clippy::option_expect_used", "clippy::expect_used"); ls.register_renamed("clippy::result_expect_used", "clippy::expect_used"); - ls.register_renamed("clippy::for_loop_over_option", "clippy::for_loop_over_fallible"); - ls.register_renamed("clippy::for_loop_over_result", "clippy::for_loop_over_fallible"); + ls.register_renamed("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles"); + ls.register_renamed("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles"); } // only exists to let the dogfood integration test works. diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index da6793a69d6..9c9d1a84003 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -205,7 +205,7 @@ declare_clippy_lint! { /// // .. /// } /// ``` - pub FOR_LOOP_OVER_FALLIBLE, + pub FOR_LOOPS_OVER_FALLIBLES, correctness, "for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`" } @@ -426,7 +426,7 @@ declare_lint_pass!(Loops => [ EXPLICIT_ITER_LOOP, EXPLICIT_INTO_ITER_LOOP, ITER_NEXT_LOOP, - FOR_LOOP_OVER_FALLIBLE, + FOR_LOOPS_OVER_FALLIBLES, WHILE_LET_LOOP, NEEDLESS_COLLECT, EXPLICIT_COUNTER_LOOP, @@ -1290,7 +1290,7 @@ fn check_arg_type(cx: &LateContext<'_, '_>, pat: &Pat<'_>, arg: &Expr<'_>) { if is_type_diagnostic_item(cx, ty, sym!(option_type)) { span_lint_and_help( cx, - FOR_LOOP_OVER_FALLIBLE, + FOR_LOOPS_OVER_FALLIBLES, arg.span, &format!( "for loop over `{0}`, which is an `Option`. This is more readably written as an \ @@ -1307,7 +1307,7 @@ fn check_arg_type(cx: &LateContext<'_, '_>, pat: &Pat<'_>, arg: &Expr<'_>) { } else if is_type_diagnostic_item(cx, ty, sym!(result_type)) { span_lint_and_help( cx, - FOR_LOOP_OVER_FALLIBLE, + FOR_LOOPS_OVER_FALLIBLES, arg.span, &format!( "for loop over `{0}`, which is a `Result`. This is more readably written as an \ diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index e90b9c15747..feada261a4c 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -74,11 +74,11 @@ pub static ref ALL_LINTS: Vec = vec![ module: "blacklisted_name", }, Lint { - name: "block_in_if_condition", + name: "blocks_in_if_conditions", group: "style", desc: "useless or complex blocks that can be eliminated in conditions", deprecation: None, - module: "block_in_if_condition", + module: "blocks_in_if_conditions", }, Lint { name: "bool_comparison", @@ -676,7 +676,7 @@ pub static ref ALL_LINTS: Vec = vec![ module: "loops", }, Lint { - name: "for_loop_over_fallible", + name: "for_loops_over_fallibles", group: "correctness", desc: "for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`", deprecation: None, diff --git a/tests/ui/block_in_if_condition.fixed b/tests/ui/block_in_if_condition.fixed deleted file mode 100644 index ae01c6d3042..00000000000 --- a/tests/ui/block_in_if_condition.fixed +++ /dev/null @@ -1,74 +0,0 @@ -// run-rustfix -#![warn(clippy::block_in_if_condition)] -#![allow(unused, clippy::let_and_return)] -#![warn(clippy::nonminimal_bool)] - -macro_rules! blocky { - () => {{ - true - }}; -} - -macro_rules! blocky_too { - () => {{ - let r = true; - r - }}; -} - -fn macro_if() { - if blocky!() {} - - if blocky_too!() {} -} - -fn condition_has_block() -> i32 { - let res = { - let x = 3; - x == 3 - }; if res { - 6 - } else { - 10 - } -} - -fn condition_has_block_with_single_expression() -> i32 { - if true { - 6 - } else { - 10 - } -} - -fn condition_is_normal() -> i32 { - let x = 3; - if x == 3 { - 6 - } else { - 10 - } -} - -fn condition_is_unsafe_block() { - let a: i32 = 1; - - // this should not warn because the condition is an unsafe block - if unsafe { 1u32 == std::mem::transmute(a) } { - println!("1u32 == a"); - } -} - -fn block_in_assert() { - let opt = Some(42); - assert!(opt - .as_ref() - .and_then(|val| { - let mut v = val * 2; - v -= 1; - Some(v * 3) - }) - .is_some()); -} - -fn main() {} diff --git a/tests/ui/block_in_if_condition.rs b/tests/ui/block_in_if_condition.rs deleted file mode 100644 index 88555dc47c2..00000000000 --- a/tests/ui/block_in_if_condition.rs +++ /dev/null @@ -1,74 +0,0 @@ -// run-rustfix -#![warn(clippy::block_in_if_condition)] -#![allow(unused, clippy::let_and_return)] -#![warn(clippy::nonminimal_bool)] - -macro_rules! blocky { - () => {{ - true - }}; -} - -macro_rules! blocky_too { - () => {{ - let r = true; - r - }}; -} - -fn macro_if() { - if blocky!() {} - - if blocky_too!() {} -} - -fn condition_has_block() -> i32 { - if { - let x = 3; - x == 3 - } { - 6 - } else { - 10 - } -} - -fn condition_has_block_with_single_expression() -> i32 { - if { true } { - 6 - } else { - 10 - } -} - -fn condition_is_normal() -> i32 { - let x = 3; - if true && x == 3 { - 6 - } else { - 10 - } -} - -fn condition_is_unsafe_block() { - let a: i32 = 1; - - // this should not warn because the condition is an unsafe block - if unsafe { 1u32 == std::mem::transmute(a) } { - println!("1u32 == a"); - } -} - -fn block_in_assert() { - let opt = Some(42); - assert!(opt - .as_ref() - .and_then(|val| { - let mut v = val * 2; - v -= 1; - Some(v * 3) - }) - .is_some()); -} - -fn main() {} diff --git a/tests/ui/block_in_if_condition.stderr b/tests/ui/block_in_if_condition.stderr deleted file mode 100644 index 89e9ad26f49..00000000000 --- a/tests/ui/block_in_if_condition.stderr +++ /dev/null @@ -1,34 +0,0 @@ -error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` - --> $DIR/block_in_if_condition.rs:26:5 - | -LL | / if { -LL | | let x = 3; -LL | | x == 3 -LL | | } { - | |_____^ - | - = note: `-D clippy::block-in-if-condition` implied by `-D warnings` -help: try - | -LL | let res = { -LL | let x = 3; -LL | x == 3 -LL | }; if res { - | - -error: omit braces around single expression condition - --> $DIR/block_in_if_condition.rs:37:8 - | -LL | if { true } { - | ^^^^^^^^ help: try: `true` - -error: this boolean expression can be simplified - --> $DIR/block_in_if_condition.rs:46:8 - | -LL | if true && x == 3 { - | ^^^^^^^^^^^^^^ help: try: `x == 3` - | - = note: `-D clippy::nonminimal-bool` implied by `-D warnings` - -error: aborting due to 3 previous errors - diff --git a/tests/ui/block_in_if_condition_closure.rs b/tests/ui/block_in_if_condition_closure.rs deleted file mode 100644 index 87b3fb94daf..00000000000 --- a/tests/ui/block_in_if_condition_closure.rs +++ /dev/null @@ -1,47 +0,0 @@ -#![warn(clippy::block_in_if_condition)] -#![allow(unused, clippy::let_and_return)] - -fn predicate bool, T>(pfn: F, val: T) -> bool { - pfn(val) -} - -fn pred_test() { - let v = 3; - let sky = "blue"; - // This is a sneaky case, where the block isn't directly in the condition, - // but is actually inside a closure that the condition is using. - // The same principle applies -- add some extra expressions to make sure - // linter isn't confused by them. - if v == 3 - && sky == "blue" - && predicate( - |x| { - let target = 3; - x == target - }, - v, - ) - {} - - if predicate( - |x| { - let target = 3; - x == target - }, - v, - ) {} -} - -fn closure_without_block() { - if predicate(|x| x == 3, 6) {} -} - -fn macro_in_closure() { - let option = Some(true); - - if option.unwrap_or_else(|| unimplemented!()) { - unimplemented!() - } -} - -fn main() {} diff --git a/tests/ui/block_in_if_condition_closure.stderr b/tests/ui/block_in_if_condition_closure.stderr deleted file mode 100644 index 3df25691c3c..00000000000 --- a/tests/ui/block_in_if_condition_closure.stderr +++ /dev/null @@ -1,24 +0,0 @@ -error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` - --> $DIR/block_in_if_condition_closure.rs:18:17 - | -LL | |x| { - | _________________^ -LL | | let target = 3; -LL | | x == target -LL | | }, - | |_____________^ - | - = note: `-D clippy::block-in-if-condition` implied by `-D warnings` - -error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` - --> $DIR/block_in_if_condition_closure.rs:27:13 - | -LL | |x| { - | _____________^ -LL | | let target = 3; -LL | | x == target -LL | | }, - | |_________^ - -error: aborting due to 2 previous errors - diff --git a/tests/ui/blocks_in_if_conditions.fixed b/tests/ui/blocks_in_if_conditions.fixed new file mode 100644 index 00000000000..9040552cefc --- /dev/null +++ b/tests/ui/blocks_in_if_conditions.fixed @@ -0,0 +1,74 @@ +// run-rustfix +#![warn(clippy::blocks_in_if_conditions)] +#![allow(unused, clippy::let_and_return)] +#![warn(clippy::nonminimal_bool)] + +macro_rules! blocky { + () => {{ + true + }}; +} + +macro_rules! blocky_too { + () => {{ + let r = true; + r + }}; +} + +fn macro_if() { + if blocky!() {} + + if blocky_too!() {} +} + +fn condition_has_block() -> i32 { + let res = { + let x = 3; + x == 3 + }; if res { + 6 + } else { + 10 + } +} + +fn condition_has_block_with_single_expression() -> i32 { + if true { + 6 + } else { + 10 + } +} + +fn condition_is_normal() -> i32 { + let x = 3; + if x == 3 { + 6 + } else { + 10 + } +} + +fn condition_is_unsafe_block() { + let a: i32 = 1; + + // this should not warn because the condition is an unsafe block + if unsafe { 1u32 == std::mem::transmute(a) } { + println!("1u32 == a"); + } +} + +fn block_in_assert() { + let opt = Some(42); + assert!(opt + .as_ref() + .and_then(|val| { + let mut v = val * 2; + v -= 1; + Some(v * 3) + }) + .is_some()); +} + +fn main() {} diff --git a/tests/ui/blocks_in_if_conditions.rs b/tests/ui/blocks_in_if_conditions.rs new file mode 100644 index 00000000000..2fe409b22d3 --- /dev/null +++ b/tests/ui/blocks_in_if_conditions.rs @@ -0,0 +1,74 @@ +// run-rustfix +#![warn(clippy::blocks_in_if_conditions)] +#![allow(unused, clippy::let_and_return)] +#![warn(clippy::nonminimal_bool)] + +macro_rules! blocky { + () => {{ + true + }}; +} + +macro_rules! blocky_too { + () => {{ + let r = true; + r + }}; +} + +fn macro_if() { + if blocky!() {} + + if blocky_too!() {} +} + +fn condition_has_block() -> i32 { + if { + let x = 3; + x == 3 + } { + 6 + } else { + 10 + } +} + +fn condition_has_block_with_single_expression() -> i32 { + if { true } { + 6 + } else { + 10 + } +} + +fn condition_is_normal() -> i32 { + let x = 3; + if true && x == 3 { + 6 + } else { + 10 + } +} + +fn condition_is_unsafe_block() { + let a: i32 = 1; + + // this should not warn because the condition is an unsafe block + if unsafe { 1u32 == std::mem::transmute(a) } { + println!("1u32 == a"); + } +} + +fn block_in_assert() { + let opt = Some(42); + assert!(opt + .as_ref() + .and_then(|val| { + let mut v = val * 2; + v -= 1; + Some(v * 3) + }) + .is_some()); +} + +fn main() {} diff --git a/tests/ui/blocks_in_if_conditions.stderr b/tests/ui/blocks_in_if_conditions.stderr new file mode 100644 index 00000000000..9bdddc8e152 --- /dev/null +++ b/tests/ui/blocks_in_if_conditions.stderr @@ -0,0 +1,34 @@ +error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` + --> $DIR/blocks_in_if_conditions.rs:26:5 + | +LL | / if { +LL | | let x = 3; +LL | | x == 3 +LL | | } { + | |_____^ + | + = note: `-D clippy::blocks-in-if-conditions` implied by `-D warnings` +help: try + | +LL | let res = { +LL | let x = 3; +LL | x == 3 +LL | }; if res { + | + +error: omit braces around single expression condition + --> $DIR/blocks_in_if_conditions.rs:37:8 + | +LL | if { true } { + | ^^^^^^^^ help: try: `true` + +error: this boolean expression can be simplified + --> $DIR/blocks_in_if_conditions.rs:46:8 + | +LL | if true && x == 3 { + | ^^^^^^^^^^^^^^ help: try: `x == 3` + | + = note: `-D clippy::nonminimal-bool` implied by `-D warnings` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/blocks_in_if_conditions_closure.rs b/tests/ui/blocks_in_if_conditions_closure.rs new file mode 100644 index 00000000000..acbabfa20d7 --- /dev/null +++ b/tests/ui/blocks_in_if_conditions_closure.rs @@ -0,0 +1,47 @@ +#![warn(clippy::blocks_in_if_conditions)] +#![allow(unused, clippy::let_and_return)] + +fn predicate bool, T>(pfn: F, val: T) -> bool { + pfn(val) +} + +fn pred_test() { + let v = 3; + let sky = "blue"; + // This is a sneaky case, where the block isn't directly in the condition, + // but is actually inside a closure that the condition is using. + // The same principle applies -- add some extra expressions to make sure + // linter isn't confused by them. + if v == 3 + && sky == "blue" + && predicate( + |x| { + let target = 3; + x == target + }, + v, + ) + {} + + if predicate( + |x| { + let target = 3; + x == target + }, + v, + ) {} +} + +fn closure_without_block() { + if predicate(|x| x == 3, 6) {} +} + +fn macro_in_closure() { + let option = Some(true); + + if option.unwrap_or_else(|| unimplemented!()) { + unimplemented!() + } +} + +fn main() {} diff --git a/tests/ui/blocks_in_if_conditions_closure.stderr b/tests/ui/blocks_in_if_conditions_closure.stderr new file mode 100644 index 00000000000..941d604dd5f --- /dev/null +++ b/tests/ui/blocks_in_if_conditions_closure.stderr @@ -0,0 +1,24 @@ +error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` + --> $DIR/blocks_in_if_conditions_closure.rs:18:17 + | +LL | |x| { + | _________________^ +LL | | let target = 3; +LL | | x == target +LL | | }, + | |_____________^ + | + = note: `-D clippy::blocks-in-if-conditions` implied by `-D warnings` + +error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` + --> $DIR/blocks_in_if_conditions_closure.rs:27:13 + | +LL | |x| { + | _____________^ +LL | | let target = 3; +LL | | x == target +LL | | }, + | |_________^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/for_loop_over_fallible.rs b/tests/ui/for_loop_over_fallible.rs deleted file mode 100644 index e52468cdd4b..00000000000 --- a/tests/ui/for_loop_over_fallible.rs +++ /dev/null @@ -1,57 +0,0 @@ -#![warn(clippy::for_loop_over_fallible)] - -fn for_loop_over_fallible() { - let option = Some(1); - let result = option.ok_or("x not found"); - let v = vec![0, 1, 2]; - - // check over an `Option` - for x in option { - println!("{}", x); - } - - // check over a `Result` - for x in result { - println!("{}", x); - } - - for x in option.ok_or("x not found") { - println!("{}", x); - } - - // make sure LOOP_OVER_NEXT lint takes clippy::precedence when next() is the last call - // in the chain - for x in v.iter().next() { - println!("{}", x); - } - - // make sure we lint when next() is not the last call in the chain - for x in v.iter().next().and(Some(0)) { - println!("{}", x); - } - - for x in v.iter().next().ok_or("x not found") { - println!("{}", x); - } - - // check for false positives - - // for loop false positive - for x in v { - println!("{}", x); - } - - // while let false positive for Option - while let Some(x) = option { - println!("{}", x); - break; - } - - // while let false positive for Result - while let Ok(x) = result { - println!("{}", x); - break; - } -} - -fn main() {} diff --git a/tests/ui/for_loop_over_fallible.stderr b/tests/ui/for_loop_over_fallible.stderr deleted file mode 100644 index 4ce9a144ad8..00000000000 --- a/tests/ui/for_loop_over_fallible.stderr +++ /dev/null @@ -1,71 +0,0 @@ -error: for loop over `option`, which is an `Option`. This is more readably written as an `if let` statement. - --> $DIR/for_loop_over_fallible.rs:9:14 - | -LL | for x in option { - | ^^^^^^ - | - = note: `-D clippy::for-loop-over-fallible` implied by `-D warnings` - = help: consider replacing `for x in option` with `if let Some(x) = option` - -error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement. - --> $DIR/for_loop_over_fallible.rs:14:14 - | -LL | for x in result { - | ^^^^^^ - | - = help: consider replacing `for x in result` with `if let Ok(x) = result` - -error: for loop over `option.ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement. - --> $DIR/for_loop_over_fallible.rs:18:14 - | -LL | for x in option.ok_or("x not found") { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider replacing `for x in option.ok_or("x not found")` with `if let Ok(x) = option.ok_or("x not found")` - -error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want - --> $DIR/for_loop_over_fallible.rs:24:14 - | -LL | for x in v.iter().next() { - | ^^^^^^^^^^^^^^^ - | - = note: `#[deny(clippy::iter_next_loop)]` on by default - -error: for loop over `v.iter().next().and(Some(0))`, which is an `Option`. This is more readably written as an `if let` statement. - --> $DIR/for_loop_over_fallible.rs:29:14 - | -LL | for x in v.iter().next().and(Some(0)) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider replacing `for x in v.iter().next().and(Some(0))` with `if let Some(x) = v.iter().next().and(Some(0))` - -error: for loop over `v.iter().next().ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement. - --> $DIR/for_loop_over_fallible.rs:33:14 - | -LL | for x in v.iter().next().ok_or("x not found") { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider replacing `for x in v.iter().next().ok_or("x not found")` with `if let Ok(x) = v.iter().next().ok_or("x not found")` - -error: this loop never actually loops - --> $DIR/for_loop_over_fallible.rs:45:5 - | -LL | / while let Some(x) = option { -LL | | println!("{}", x); -LL | | break; -LL | | } - | |_____^ - | - = note: `#[deny(clippy::never_loop)]` on by default - -error: this loop never actually loops - --> $DIR/for_loop_over_fallible.rs:51:5 - | -LL | / while let Ok(x) = result { -LL | | println!("{}", x); -LL | | break; -LL | | } - | |_____^ - -error: aborting due to 8 previous errors - diff --git a/tests/ui/for_loops_over_fallibles.rs b/tests/ui/for_loops_over_fallibles.rs new file mode 100644 index 00000000000..1b9dde87cd5 --- /dev/null +++ b/tests/ui/for_loops_over_fallibles.rs @@ -0,0 +1,57 @@ +#![warn(clippy::for_loops_over_fallibles)] + +fn for_loops_over_fallibles() { + let option = Some(1); + let result = option.ok_or("x not found"); + let v = vec![0, 1, 2]; + + // check over an `Option` + for x in option { + println!("{}", x); + } + + // check over a `Result` + for x in result { + println!("{}", x); + } + + for x in option.ok_or("x not found") { + println!("{}", x); + } + + // make sure LOOP_OVER_NEXT lint takes clippy::precedence when next() is the last call + // in the chain + for x in v.iter().next() { + println!("{}", x); + } + + // make sure we lint when next() is not the last call in the chain + for x in v.iter().next().and(Some(0)) { + println!("{}", x); + } + + for x in v.iter().next().ok_or("x not found") { + println!("{}", x); + } + + // check for false positives + + // for loop false positive + for x in v { + println!("{}", x); + } + + // while let false positive for Option + while let Some(x) = option { + println!("{}", x); + break; + } + + // while let false positive for Result + while let Ok(x) = result { + println!("{}", x); + break; + } +} + +fn main() {} diff --git a/tests/ui/for_loops_over_fallibles.stderr b/tests/ui/for_loops_over_fallibles.stderr new file mode 100644 index 00000000000..bef228d4b93 --- /dev/null +++ b/tests/ui/for_loops_over_fallibles.stderr @@ -0,0 +1,71 @@ +error: for loop over `option`, which is an `Option`. This is more readably written as an `if let` statement. + --> $DIR/for_loops_over_fallibles.rs:9:14 + | +LL | for x in option { + | ^^^^^^ + | + = note: `-D clippy::for-loops-over-fallibles` implied by `-D warnings` + = help: consider replacing `for x in option` with `if let Some(x) = option` + +error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement. + --> $DIR/for_loops_over_fallibles.rs:14:14 + | +LL | for x in result { + | ^^^^^^ + | + = help: consider replacing `for x in result` with `if let Ok(x) = result` + +error: for loop over `option.ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement. + --> $DIR/for_loops_over_fallibles.rs:18:14 + | +LL | for x in option.ok_or("x not found") { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider replacing `for x in option.ok_or("x not found")` with `if let Ok(x) = option.ok_or("x not found")` + +error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want + --> $DIR/for_loops_over_fallibles.rs:24:14 + | +LL | for x in v.iter().next() { + | ^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::iter_next_loop)]` on by default + +error: for loop over `v.iter().next().and(Some(0))`, which is an `Option`. This is more readably written as an `if let` statement. + --> $DIR/for_loops_over_fallibles.rs:29:14 + | +LL | for x in v.iter().next().and(Some(0)) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider replacing `for x in v.iter().next().and(Some(0))` with `if let Some(x) = v.iter().next().and(Some(0))` + +error: for loop over `v.iter().next().ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement. + --> $DIR/for_loops_over_fallibles.rs:33:14 + | +LL | for x in v.iter().next().ok_or("x not found") { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider replacing `for x in v.iter().next().ok_or("x not found")` with `if let Ok(x) = v.iter().next().ok_or("x not found")` + +error: this loop never actually loops + --> $DIR/for_loops_over_fallibles.rs:45:5 + | +LL | / while let Some(x) = option { +LL | | println!("{}", x); +LL | | break; +LL | | } + | |_____^ + | + = note: `#[deny(clippy::never_loop)]` on by default + +error: this loop never actually loops + --> $DIR/for_loops_over_fallibles.rs:51:5 + | +LL | / while let Ok(x) = result { +LL | | println!("{}", x); +LL | | break; +LL | | } + | |_____^ + +error: aborting due to 8 previous errors + -- cgit 1.4.1-3-g733a5 From fc8ab099c38952b91e38608c386314bde6dd2629 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 15 May 2020 21:17:37 +0200 Subject: identity_op: allow `1 << 0` --- clippy_lints/src/identity_op.rs | 22 ++++++++++++++++++++-- tests/ui/identity_op.rs | 5 +++++ tests/ui/identity_op.stderr | 20 +++++++++++++++++++- 3 files changed, 44 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/identity_op.rs b/clippy_lints/src/identity_op.rs index 088e4ab1921..78e07d25f67 100644 --- a/clippy_lints/src/identity_op.rs +++ b/clippy_lints/src/identity_op.rs @@ -1,4 +1,5 @@ -use rustc_hir::{BinOpKind, Expr, ExprKind}; +use if_chain::if_chain; +use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -32,7 +33,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IdentityOp { if e.span.from_expansion() { return; } - if let ExprKind::Binary(ref cmp, ref left, ref right) = e.kind { + if let ExprKind::Binary(cmp, ref left, ref right) = e.kind { + if is_allowed(cx, cmp, left, right) { + return; + } match cmp.node { BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => { check(cx, left, 0, e.span, right.span); @@ -54,6 +58,20 @@ impl<'a, 'tcx> LateLintPass<'a, '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.tables, right); + if let Some(Constant::Int(1)) = constant_simple(cx, cx.tables, left); + then { + return true; + } + } + + false +} + #[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.tables, e) { diff --git a/tests/ui/identity_op.rs b/tests/ui/identity_op.rs index ae2815d345a..ceaacaaf6bd 100644 --- a/tests/ui/identity_op.rs +++ b/tests/ui/identity_op.rs @@ -33,4 +33,9 @@ fn main() { let u: u8 = 0; u & 255; + + 1 << 0; // no error, this case is allowed, see issue 3430 + 42 << 0; + 1 >> 0; + 42 >> 0; } diff --git a/tests/ui/identity_op.stderr b/tests/ui/identity_op.stderr index 4742877706a..d8d44a74f9a 100644 --- a/tests/ui/identity_op.stderr +++ b/tests/ui/identity_op.stderr @@ -48,5 +48,23 @@ error: the operation is ineffective. Consider reducing it to `u` LL | u & 255; | ^^^^^^^ -error: aborting due to 8 previous errors +error: the operation is ineffective. Consider reducing it to `42` + --> $DIR/identity_op.rs:38:5 + | +LL | 42 << 0; + | ^^^^^^^ + +error: the operation is ineffective. Consider reducing it to `1` + --> $DIR/identity_op.rs:39:5 + | +LL | 1 >> 0; + | ^^^^^^ + +error: the operation is ineffective. Consider reducing it to `42` + --> $DIR/identity_op.rs:40:5 + | +LL | 42 >> 0; + | ^^^^^^^ + +error: aborting due to 11 previous errors -- cgit 1.4.1-3-g733a5 From d90625385e8ed0a9030e3ab2ea0990fce39c28bf Mon Sep 17 00:00:00 2001 From: Vardan Margaryan Date: Sat, 16 May 2020 00:19:30 +0300 Subject: Add more test cases for match_wildcard_for_single_variants --- tests/ui/match_wildcard_for_single_variants.fixed | 43 +++++++++++++++++++++- tests/ui/match_wildcard_for_single_variants.rs | 43 +++++++++++++++++++++- tests/ui/match_wildcard_for_single_variants.stderr | 22 ++++++++++- 3 files changed, 104 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/tests/ui/match_wildcard_for_single_variants.fixed b/tests/ui/match_wildcard_for_single_variants.fixed index 5f1a559f591..519200977a7 100644 --- a/tests/ui/match_wildcard_for_single_variants.fixed +++ b/tests/ui/match_wildcard_for_single_variants.fixed @@ -9,10 +9,51 @@ enum Foo { C, } +enum Color { + Red, + Green, + Blue, + Rgb(u8, u8, u8), +} + fn main() { - match Foo::A { + let f = Foo::A; + match f { Foo::A => {}, Foo::B => {}, Foo::C => {}, } + + let color = Color::Red; + + // check exhaustive bindings + match color { + Color::Red => {}, + Color::Green => {}, + Color::Rgb(_r, _g, _b) => {}, + Color::Blue => {}, + } + + // check exhaustive wild + match color { + Color::Red => {}, + Color::Green => {}, + Color::Rgb(..) => {}, + Color::Blue => {}, + } + match color { + Color::Red => {}, + Color::Green => {}, + Color::Rgb(_, _, _) => {}, + Color::Blue => {}, + } + + // shouldn't lint as there is one missing variant + // and one that isn't exhaustively covered + match color { + Color::Red => {}, + Color::Green => {}, + Color::Rgb(255, _, _) => {}, + _ => {}, + } } diff --git a/tests/ui/match_wildcard_for_single_variants.rs b/tests/ui/match_wildcard_for_single_variants.rs index 1159f9e722d..1df917e085c 100644 --- a/tests/ui/match_wildcard_for_single_variants.rs +++ b/tests/ui/match_wildcard_for_single_variants.rs @@ -9,10 +9,51 @@ enum Foo { C, } +enum Color { + Red, + Green, + Blue, + Rgb(u8, u8, u8), +} + fn main() { - match Foo::A { + let f = Foo::A; + match f { Foo::A => {}, Foo::B => {}, _ => {}, } + + let color = Color::Red; + + // check exhaustive bindings + match color { + Color::Red => {}, + Color::Green => {}, + Color::Rgb(_r, _g, _b) => {}, + _ => {}, + } + + // check exhaustive wild + match color { + Color::Red => {}, + Color::Green => {}, + Color::Rgb(..) => {}, + _ => {}, + } + match color { + Color::Red => {}, + Color::Green => {}, + Color::Rgb(_, _, _) => {}, + _ => {}, + } + + // shouldn't lint as there is one missing variant + // and one that isn't exhaustively covered + match color { + Color::Red => {}, + Color::Green => {}, + Color::Rgb(255, _, _) => {}, + _ => {}, + } } diff --git a/tests/ui/match_wildcard_for_single_variants.stderr b/tests/ui/match_wildcard_for_single_variants.stderr index 128dd4808bf..82790aa9e80 100644 --- a/tests/ui/match_wildcard_for_single_variants.stderr +++ b/tests/ui/match_wildcard_for_single_variants.stderr @@ -1,10 +1,28 @@ error: wildcard match will miss any future added variants - --> $DIR/match_wildcard_for_single_variants.rs:16:9 + --> $DIR/match_wildcard_for_single_variants.rs:24:9 | LL | _ => {}, | ^ help: try this: `Foo::C` | = note: `-D clippy::match-wildcard-for-single-variants` implied by `-D warnings` -error: aborting due to previous error +error: wildcard match will miss any future added variants + --> $DIR/match_wildcard_for_single_variants.rs:34:9 + | +LL | _ => {}, + | ^ help: try this: `Color::Blue` + +error: wildcard match will miss any future added variants + --> $DIR/match_wildcard_for_single_variants.rs:42:9 + | +LL | _ => {}, + | ^ help: try this: `Color::Blue` + +error: wildcard match will miss any future added variants + --> $DIR/match_wildcard_for_single_variants.rs:48:9 + | +LL | _ => {}, + | ^ help: try this: `Color::Blue` + +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From e55b920970fdc33f5ddaf7757738fbacdadf15ab Mon Sep 17 00:00:00 2001 From: ThibsG Date: Mon, 4 May 2020 17:09:02 +0200 Subject: Rename lint `identity_conversion` to `useless_conversion` --- CHANGELOG.md | 4 +- clippy_lints/src/identity_conversion.rs | 124 ------------------------------ clippy_lints/src/lib.rs | 11 +-- clippy_lints/src/useless_conversion.rs | 130 ++++++++++++++++++++++++++++++++ src/lintlist/mod.rs | 14 ++-- tests/ui/identity_conversion.fixed | 58 -------------- tests/ui/identity_conversion.rs | 58 -------------- tests/ui/identity_conversion.stderr | 68 ----------------- tests/ui/useless_conversion.fixed | 58 ++++++++++++++ tests/ui/useless_conversion.rs | 58 ++++++++++++++ tests/ui/useless_conversion.stderr | 68 +++++++++++++++++ 11 files changed, 329 insertions(+), 322 deletions(-) delete mode 100644 clippy_lints/src/identity_conversion.rs create mode 100644 clippy_lints/src/useless_conversion.rs delete mode 100644 tests/ui/identity_conversion.fixed delete mode 100644 tests/ui/identity_conversion.rs delete mode 100644 tests/ui/identity_conversion.stderr create mode 100644 tests/ui/useless_conversion.fixed create mode 100644 tests/ui/useless_conversion.rs create mode 100644 tests/ui/useless_conversion.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index d05819a973a..9e85e6da3b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -805,7 +805,7 @@ Released 2018-09-13 ## 0.0.166 * Rustup to *rustc 1.22.0-nightly (b7960878b 2017-10-18)* -* New lints: [`explicit_write`], [`identity_conversion`], [`implicit_hasher`], [`invalid_ref`], [`option_map_or_none`], +* New lints: [`explicit_write`], `identity_conversion`, [`implicit_hasher`], [`invalid_ref`], [`option_map_or_none`], [`range_minus_one`], [`range_plus_one`], [`transmute_int_to_bool`], [`transmute_int_to_char`], [`transmute_int_to_float`] @@ -1367,7 +1367,6 @@ Released 2018-09-13 [`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send [`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len [`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap -[`identity_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_conversion [`identity_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_op [`if_let_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_mutex [`if_let_redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_redundant_pattern_matching @@ -1624,6 +1623,7 @@ Released 2018-09-13 [`used_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#used_underscore_binding [`useless_asref`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_asref [`useless_attribute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_attribute +[`useless_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion [`useless_format`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_format [`useless_let_if_seq`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_let_if_seq [`useless_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_transmute diff --git a/clippy_lints/src/identity_conversion.rs b/clippy_lints/src/identity_conversion.rs deleted file mode 100644 index 33a9478f058..00000000000 --- a/clippy_lints/src/identity_conversion.rs +++ /dev/null @@ -1,124 +0,0 @@ -use crate::utils::{ - match_def_path, match_trait_method, paths, same_tys, snippet, snippet_with_macro_callsite, span_lint_and_sugg, -}; -use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, HirId, MatchSource}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_tool_lint, impl_lint_pass}; - -declare_clippy_lint! { - /// **What it does:** Checks for always-identical `Into`/`From`/`IntoIter` conversions. - /// - /// **Why is this bad?** Redundant code. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// // format!() returns a `String` - /// let s: String = format!("hello").into(); - /// ``` - pub IDENTITY_CONVERSION, - complexity, - "using always-identical `Into`/`From`/`IntoIter` conversions" -} - -#[derive(Default)] -pub struct IdentityConversion { - try_desugar_arm: Vec, -} - -impl_lint_pass!(IdentityConversion => [IDENTITY_CONVERSION]); - -impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IdentityConversion { - fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) { - if e.span.from_expansion() { - return; - } - - if Some(&e.hir_id) == self.try_desugar_arm.last() { - return; - } - - match e.kind { - ExprKind::Match(_, ref arms, MatchSource::TryDesugar) => { - let e = match arms[0].body.kind { - ExprKind::Ret(Some(ref e)) | ExprKind::Break(_, Some(ref e)) => e, - _ => return, - }; - if let ExprKind::Call(_, ref args) = e.kind { - self.try_desugar_arm.push(args[0].hir_id); - } - }, - - ExprKind::MethodCall(ref name, .., ref args) => { - if match_trait_method(cx, e, &paths::INTO) && &*name.ident.as_str() == "into" { - let a = cx.tables.expr_ty(e); - let b = cx.tables.expr_ty(&args[0]); - if same_tys(cx, a, b) { - let sugg = snippet_with_macro_callsite(cx, args[0].span, "").to_string(); - - span_lint_and_sugg( - cx, - IDENTITY_CONVERSION, - e.span, - "identical conversion", - "consider removing `.into()`", - sugg, - Applicability::MachineApplicable, // snippet - ); - } - } - if match_trait_method(cx, e, &paths::INTO_ITERATOR) && &*name.ident.as_str() == "into_iter" { - let a = cx.tables.expr_ty(e); - let b = cx.tables.expr_ty(&args[0]); - if same_tys(cx, a, b) { - let sugg = snippet(cx, args[0].span, "").into_owned(); - span_lint_and_sugg( - cx, - IDENTITY_CONVERSION, - e.span, - "identical conversion", - "consider removing `.into_iter()`", - sugg, - Applicability::MachineApplicable, // snippet - ); - } - } - }, - - ExprKind::Call(ref path, ref args) => { - if let ExprKind::Path(ref qpath) = path.kind { - if let Some(def_id) = cx.tables.qpath_res(qpath, path.hir_id).opt_def_id() { - if match_def_path(cx, def_id, &paths::FROM_FROM) { - let a = cx.tables.expr_ty(e); - let b = cx.tables.expr_ty(&args[0]); - if same_tys(cx, a, b) { - let sugg = snippet(cx, args[0].span.source_callsite(), "").into_owned(); - let sugg_msg = - format!("consider removing `{}()`", snippet(cx, path.span, "From::from")); - span_lint_and_sugg( - cx, - IDENTITY_CONVERSION, - e.span, - "identical conversion", - &sugg_msg, - sugg, - Applicability::MachineApplicable, // snippet - ); - } - } - } - } - }, - - _ => {}, - } - } - - fn check_expr_post(&mut self, _: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) { - if Some(&e.hir_id) == self.try_desugar_arm.last() { - self.try_desugar_arm.pop(); - } - } -} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index bda0d5c0458..4dda373738b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -221,7 +221,6 @@ mod formatting; mod functions; mod future_not_send; mod get_last_with_len; -mod identity_conversion; mod identity_op; mod if_let_mutex; mod if_let_some_result; @@ -324,6 +323,7 @@ mod unused_io_amount; mod unused_self; mod unwrap; mod use_self; +mod useless_conversion; mod vec; mod verbose_file_reads; mod wildcard_dependencies; @@ -577,7 +577,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &functions::TOO_MANY_LINES, &future_not_send::FUTURE_NOT_SEND, &get_last_with_len::GET_LAST_WITH_LEN, - &identity_conversion::IDENTITY_CONVERSION, &identity_op::IDENTITY_OP, &if_let_mutex::IF_LET_MUTEX, &if_let_some_result::IF_LET_SOME_RESULT, @@ -843,6 +842,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &unwrap::PANICKING_UNWRAP, &unwrap::UNNECESSARY_UNWRAP, &use_self::USE_SELF, + &useless_conversion::USELESS_CONVERSION, &utils::internal_lints::CLIPPY_LINTS_INTERNAL, &utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS, &utils::internal_lints::COMPILER_LINT_FUNCTIONS, @@ -980,7 +980,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box bytecount::ByteCount); store.register_late_pass(|| box infinite_iter::InfiniteIter); store.register_late_pass(|| box inline_fn_without_body::InlineFnWithoutBody); - store.register_late_pass(|| box identity_conversion::IdentityConversion::default()); + store.register_late_pass(|| box useless_conversion::UselessConversion::default()); store.register_late_pass(|| box types::ImplicitHasher); store.register_late_pass(|| box fallible_impl_from::FallibleImplFrom); store.register_late_pass(|| box types::UnitArg); @@ -1241,7 +1241,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&functions::NOT_UNSAFE_PTR_ARG_DEREF), LintId::of(&functions::TOO_MANY_ARGUMENTS), LintId::of(&get_last_with_len::GET_LAST_WITH_LEN), - LintId::of(&identity_conversion::IDENTITY_CONVERSION), LintId::of(&identity_op::IDENTITY_OP), LintId::of(&if_let_mutex::IF_LET_MUTEX), LintId::of(&if_let_some_result::IF_LET_SOME_RESULT), @@ -1427,6 +1426,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), LintId::of(&unwrap::PANICKING_UNWRAP), LintId::of(&unwrap::UNNECESSARY_UNWRAP), + LintId::of(&useless_conversion::USELESS_CONVERSION), LintId::of(&vec::USELESS_VEC), LintId::of(&write::PRINTLN_EMPTY_STRING), LintId::of(&write::PRINT_LITERAL), @@ -1546,7 +1546,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&format::USELESS_FORMAT), LintId::of(&functions::TOO_MANY_ARGUMENTS), LintId::of(&get_last_with_len::GET_LAST_WITH_LEN), - LintId::of(&identity_conversion::IDENTITY_CONVERSION), LintId::of(&identity_op::IDENTITY_OP), LintId::of(&int_plus_one::INT_PLUS_ONE), LintId::of(&lifetimes::EXTRA_UNUSED_LIFETIMES), @@ -1605,6 +1604,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::UNNECESSARY_CAST), LintId::of(&types::VEC_BOX), LintId::of(&unwrap::UNNECESSARY_UNWRAP), + LintId::of(&useless_conversion::USELESS_CONVERSION), LintId::of(&zero_div_zero::ZERO_DIVIDED_BY_ZERO), ]); @@ -1795,6 +1795,7 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) { ls.register_renamed("clippy::result_expect_used", "clippy::expect_used"); ls.register_renamed("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles"); ls.register_renamed("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles"); + ls.register_renamed("clippy::identity_conversion", "clippy::useless_conversion"); } // only exists to let the dogfood integration test works. diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs new file mode 100644 index 00000000000..95921518986 --- /dev/null +++ b/clippy_lints/src/useless_conversion.rs @@ -0,0 +1,130 @@ +use crate::utils::{ + match_def_path, match_trait_method, paths, same_tys, snippet, snippet_with_macro_callsite, span_lint_and_sugg, +}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, HirId, MatchSource}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +declare_clippy_lint! { + /// **What it does:** Checks for `Into`/`From`/`IntoIter` calls that useless converts + /// to the same type as caller. + /// + /// **Why is this bad?** Redundant code. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // Bad + /// // format!() returns a `String` + /// let s: String = format!("hello").into(); + /// + /// // Good + /// let s: String = format!("hello"); + /// ``` + pub USELESS_CONVERSION, + complexity, + "calls to `Into`/`From`/`IntoIter` that performs useless conversions to the same type" +} + +#[derive(Default)] +pub struct UselessConversion { + try_desugar_arm: Vec, +} + +impl_lint_pass!(UselessConversion => [USELESS_CONVERSION]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) { + if e.span.from_expansion() { + return; + } + + if Some(&e.hir_id) == self.try_desugar_arm.last() { + return; + } + + match e.kind { + ExprKind::Match(_, ref arms, MatchSource::TryDesugar) => { + let e = match arms[0].body.kind { + ExprKind::Ret(Some(ref e)) | ExprKind::Break(_, Some(ref e)) => e, + _ => return, + }; + if let ExprKind::Call(_, ref args) = e.kind { + self.try_desugar_arm.push(args[0].hir_id); + } + }, + + ExprKind::MethodCall(ref name, .., ref args) => { + if match_trait_method(cx, e, &paths::INTO) && &*name.ident.as_str() == "into" { + let a = cx.tables.expr_ty(e); + let b = cx.tables.expr_ty(&args[0]); + if same_tys(cx, a, b) { + let sugg = snippet_with_macro_callsite(cx, args[0].span, "").to_string(); + + span_lint_and_sugg( + cx, + USELESS_CONVERSION, + e.span, + "useless conversion", + "consider removing `.into()`", + sugg, + Applicability::MachineApplicable, // snippet + ); + } + } + if match_trait_method(cx, e, &paths::INTO_ITERATOR) && &*name.ident.as_str() == "into_iter" { + let a = cx.tables.expr_ty(e); + let b = cx.tables.expr_ty(&args[0]); + if same_tys(cx, a, b) { + let sugg = snippet(cx, args[0].span, "").into_owned(); + span_lint_and_sugg( + cx, + USELESS_CONVERSION, + e.span, + "useless conversion", + "consider removing `.into_iter()`", + sugg, + Applicability::MachineApplicable, // snippet + ); + } + } + }, + + ExprKind::Call(ref path, ref args) => { + if let ExprKind::Path(ref qpath) = path.kind { + if let Some(def_id) = cx.tables.qpath_res(qpath, path.hir_id).opt_def_id() { + if match_def_path(cx, def_id, &paths::FROM_FROM) { + let a = cx.tables.expr_ty(e); + let b = cx.tables.expr_ty(&args[0]); + if same_tys(cx, a, b) { + let sugg = snippet(cx, args[0].span.source_callsite(), "").into_owned(); + let sugg_msg = + format!("consider removing `{}()`", snippet(cx, path.span, "From::from")); + span_lint_and_sugg( + cx, + USELESS_CONVERSION, + e.span, + "useless conversion", + &sugg_msg, + sugg, + Applicability::MachineApplicable, // snippet + ); + } + } + } + } + }, + + _ => {}, + } + } + + fn check_expr_post(&mut self, _: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) { + if Some(&e.hir_id) == self.try_desugar_arm.last() { + self.try_desugar_arm.pop(); + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index e5e3bf453a0..e411e60782a 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -717,13 +717,6 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "methods", }, - Lint { - name: "identity_conversion", - group: "complexity", - desc: "using always-identical `Into`/`From`/`IntoIter` conversions", - deprecation: None, - module: "identity_conversion", - }, Lint { name: "identity_op", group: "complexity", @@ -2418,6 +2411,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "attrs", }, + Lint { + name: "useless_conversion", + group: "complexity", + desc: "calls to `Into`/`From`/`IntoIter` that performs useless conversions to the same type", + deprecation: None, + module: "useless_conversion", + }, Lint { name: "useless_format", group: "complexity", diff --git a/tests/ui/identity_conversion.fixed b/tests/ui/identity_conversion.fixed deleted file mode 100644 index dd3fc56e98b..00000000000 --- a/tests/ui/identity_conversion.fixed +++ /dev/null @@ -1,58 +0,0 @@ -// run-rustfix - -#![deny(clippy::identity_conversion)] - -fn test_generic(val: T) -> T { - let _ = val; - val -} - -fn test_generic2 + Into, U: From>(val: T) { - // ok - let _: i32 = val.into(); - let _: U = val.into(); - let _ = U::from(val); -} - -fn test_questionmark() -> Result<(), ()> { - { - let _: i32 = 0i32; - Ok(Ok(())) - }??; - Ok(()) -} - -fn test_issue_3913() -> Result<(), std::io::Error> { - use std::fs; - use std::path::Path; - - let path = Path::new("."); - for _ in fs::read_dir(path)? {} - - Ok(()) -} - -fn main() { - test_generic(10i32); - test_generic2::(10i32); - test_questionmark().unwrap(); - test_issue_3913().unwrap(); - - let _: String = "foo".into(); - let _: String = From::from("foo"); - let _ = String::from("foo"); - #[allow(clippy::identity_conversion)] - { - let _: String = "foo".into(); - let _ = String::from("foo"); - let _ = "".lines().into_iter(); - } - - let _: String = "foo".to_string(); - let _: String = "foo".to_string(); - let _ = "foo".to_string(); - let _ = format!("A: {:04}", 123); - let _ = "".lines(); - let _ = vec![1, 2, 3].into_iter(); - let _: String = format!("Hello {}", "world"); -} diff --git a/tests/ui/identity_conversion.rs b/tests/ui/identity_conversion.rs deleted file mode 100644 index 875ed7db373..00000000000 --- a/tests/ui/identity_conversion.rs +++ /dev/null @@ -1,58 +0,0 @@ -// run-rustfix - -#![deny(clippy::identity_conversion)] - -fn test_generic(val: T) -> T { - let _ = T::from(val); - val.into() -} - -fn test_generic2 + Into, U: From>(val: T) { - // ok - let _: i32 = val.into(); - let _: U = val.into(); - let _ = U::from(val); -} - -fn test_questionmark() -> Result<(), ()> { - { - let _: i32 = 0i32.into(); - Ok(Ok(())) - }??; - Ok(()) -} - -fn test_issue_3913() -> Result<(), std::io::Error> { - use std::fs; - use std::path::Path; - - let path = Path::new("."); - for _ in fs::read_dir(path)? {} - - Ok(()) -} - -fn main() { - test_generic(10i32); - test_generic2::(10i32); - test_questionmark().unwrap(); - test_issue_3913().unwrap(); - - let _: String = "foo".into(); - let _: String = From::from("foo"); - let _ = String::from("foo"); - #[allow(clippy::identity_conversion)] - { - let _: String = "foo".into(); - let _ = String::from("foo"); - let _ = "".lines().into_iter(); - } - - let _: String = "foo".to_string().into(); - let _: String = From::from("foo".to_string()); - let _ = String::from("foo".to_string()); - let _ = String::from(format!("A: {:04}", 123)); - let _ = "".lines().into_iter(); - let _ = vec![1, 2, 3].into_iter().into_iter(); - let _: String = format!("Hello {}", "world").into(); -} diff --git a/tests/ui/identity_conversion.stderr b/tests/ui/identity_conversion.stderr deleted file mode 100644 index 57626b23795..00000000000 --- a/tests/ui/identity_conversion.stderr +++ /dev/null @@ -1,68 +0,0 @@ -error: identical conversion - --> $DIR/identity_conversion.rs:6:13 - | -LL | let _ = T::from(val); - | ^^^^^^^^^^^^ help: consider removing `T::from()`: `val` - | -note: the lint level is defined here - --> $DIR/identity_conversion.rs:3:9 - | -LL | #![deny(clippy::identity_conversion)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: identical conversion - --> $DIR/identity_conversion.rs:7:5 - | -LL | val.into() - | ^^^^^^^^^^ help: consider removing `.into()`: `val` - -error: identical conversion - --> $DIR/identity_conversion.rs:19:22 - | -LL | let _: i32 = 0i32.into(); - | ^^^^^^^^^^^ help: consider removing `.into()`: `0i32` - -error: identical conversion - --> $DIR/identity_conversion.rs:51:21 - | -LL | let _: String = "foo".to_string().into(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `"foo".to_string()` - -error: identical conversion - --> $DIR/identity_conversion.rs:52:21 - | -LL | let _: String = From::from("foo".to_string()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `From::from()`: `"foo".to_string()` - -error: identical conversion - --> $DIR/identity_conversion.rs:53:13 - | -LL | let _ = String::from("foo".to_string()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `"foo".to_string()` - -error: identical conversion - --> $DIR/identity_conversion.rs:54:13 - | -LL | let _ = String::from(format!("A: {:04}", 123)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `format!("A: {:04}", 123)` - -error: identical conversion - --> $DIR/identity_conversion.rs:55:13 - | -LL | let _ = "".lines().into_iter(); - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()` - -error: identical conversion - --> $DIR/identity_conversion.rs:56:13 - | -LL | let _ = vec![1, 2, 3].into_iter().into_iter(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![1, 2, 3].into_iter()` - -error: identical conversion - --> $DIR/identity_conversion.rs:57:21 - | -LL | let _: String = format!("Hello {}", "world").into(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `format!("Hello {}", "world")` - -error: aborting due to 10 previous errors - diff --git a/tests/ui/useless_conversion.fixed b/tests/ui/useless_conversion.fixed new file mode 100644 index 00000000000..fdd4bc581f3 --- /dev/null +++ b/tests/ui/useless_conversion.fixed @@ -0,0 +1,58 @@ +// run-rustfix + +#![deny(clippy::useless_conversion)] + +fn test_generic(val: T) -> T { + let _ = val; + val +} + +fn test_generic2 + Into, U: From>(val: T) { + // ok + let _: i32 = val.into(); + let _: U = val.into(); + let _ = U::from(val); +} + +fn test_questionmark() -> Result<(), ()> { + { + let _: i32 = 0i32; + Ok(Ok(())) + }??; + Ok(()) +} + +fn test_issue_3913() -> Result<(), std::io::Error> { + use std::fs; + use std::path::Path; + + let path = Path::new("."); + for _ in fs::read_dir(path)? {} + + Ok(()) +} + +fn main() { + test_generic(10i32); + test_generic2::(10i32); + test_questionmark().unwrap(); + test_issue_3913().unwrap(); + + let _: String = "foo".into(); + let _: String = From::from("foo"); + let _ = String::from("foo"); + #[allow(clippy::useless_conversion)] + { + let _: String = "foo".into(); + let _ = String::from("foo"); + let _ = "".lines().into_iter(); + } + + let _: String = "foo".to_string(); + let _: String = "foo".to_string(); + let _ = "foo".to_string(); + let _ = format!("A: {:04}", 123); + let _ = "".lines(); + let _ = vec![1, 2, 3].into_iter(); + let _: String = format!("Hello {}", "world"); +} diff --git a/tests/ui/useless_conversion.rs b/tests/ui/useless_conversion.rs new file mode 100644 index 00000000000..4cae745e7c0 --- /dev/null +++ b/tests/ui/useless_conversion.rs @@ -0,0 +1,58 @@ +// run-rustfix + +#![deny(clippy::useless_conversion)] + +fn test_generic(val: T) -> T { + let _ = T::from(val); + val.into() +} + +fn test_generic2 + Into, U: From>(val: T) { + // ok + let _: i32 = val.into(); + let _: U = val.into(); + let _ = U::from(val); +} + +fn test_questionmark() -> Result<(), ()> { + { + let _: i32 = 0i32.into(); + Ok(Ok(())) + }??; + Ok(()) +} + +fn test_issue_3913() -> Result<(), std::io::Error> { + use std::fs; + use std::path::Path; + + let path = Path::new("."); + for _ in fs::read_dir(path)? {} + + Ok(()) +} + +fn main() { + test_generic(10i32); + test_generic2::(10i32); + test_questionmark().unwrap(); + test_issue_3913().unwrap(); + + let _: String = "foo".into(); + let _: String = From::from("foo"); + let _ = String::from("foo"); + #[allow(clippy::useless_conversion)] + { + let _: String = "foo".into(); + let _ = String::from("foo"); + let _ = "".lines().into_iter(); + } + + let _: String = "foo".to_string().into(); + let _: String = From::from("foo".to_string()); + let _ = String::from("foo".to_string()); + let _ = String::from(format!("A: {:04}", 123)); + let _ = "".lines().into_iter(); + let _ = vec![1, 2, 3].into_iter().into_iter(); + let _: String = format!("Hello {}", "world").into(); +} diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr new file mode 100644 index 00000000000..7df3507edfd --- /dev/null +++ b/tests/ui/useless_conversion.stderr @@ -0,0 +1,68 @@ +error: useless conversion + --> $DIR/useless_conversion.rs:6:13 + | +LL | let _ = T::from(val); + | ^^^^^^^^^^^^ help: consider removing `T::from()`: `val` + | +note: the lint level is defined here + --> $DIR/useless_conversion.rs:3:9 + | +LL | #![deny(clippy::useless_conversion)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: useless conversion + --> $DIR/useless_conversion.rs:7:5 + | +LL | val.into() + | ^^^^^^^^^^ help: consider removing `.into()`: `val` + +error: useless conversion + --> $DIR/useless_conversion.rs:19:22 + | +LL | let _: i32 = 0i32.into(); + | ^^^^^^^^^^^ help: consider removing `.into()`: `0i32` + +error: useless conversion + --> $DIR/useless_conversion.rs:51:21 + | +LL | let _: String = "foo".to_string().into(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `"foo".to_string()` + +error: useless conversion + --> $DIR/useless_conversion.rs:52:21 + | +LL | let _: String = From::from("foo".to_string()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `From::from()`: `"foo".to_string()` + +error: useless conversion + --> $DIR/useless_conversion.rs:53:13 + | +LL | let _ = String::from("foo".to_string()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `"foo".to_string()` + +error: useless conversion + --> $DIR/useless_conversion.rs:54:13 + | +LL | let _ = String::from(format!("A: {:04}", 123)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `format!("A: {:04}", 123)` + +error: useless conversion + --> $DIR/useless_conversion.rs:55:13 + | +LL | let _ = "".lines().into_iter(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()` + +error: useless conversion + --> $DIR/useless_conversion.rs:56:13 + | +LL | let _ = vec![1, 2, 3].into_iter().into_iter(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![1, 2, 3].into_iter()` + +error: useless conversion + --> $DIR/useless_conversion.rs:57:21 + | +LL | let _: String = format!("Hello {}", "world").into(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `format!("Hello {}", "world")` + +error: aborting due to 10 previous errors + -- cgit 1.4.1-3-g733a5 From 07f1edf2d43efa0ef53e5b6c56c895bc9738ab94 Mon Sep 17 00:00:00 2001 From: Aleksei Latyshev Date: Sat, 25 Apr 2020 23:33:11 +0300 Subject: improve and generalize `option_and_then_some` lint - rename it to bind_instead_of_map --- CHANGELOG.md | 4 +- clippy_lints/src/lib.rs | 7 +- clippy_lints/src/loops.rs | 2 +- clippy_lints/src/methods/bind_instead_of_map.rs | 309 ++++++++++++++++++++++++ clippy_lints/src/methods/mod.rs | 100 ++------ clippy_lints/src/types.rs | 2 +- src/lintlist/mod.rs | 14 +- tests/ui/bind_instead_of_map.fixed | 25 ++ tests/ui/bind_instead_of_map.rs | 25 ++ tests/ui/bind_instead_of_map.stderr | 26 ++ tests/ui/bind_instead_of_map_multipart.rs | 61 +++++ tests/ui/bind_instead_of_map_multipart.stderr | 73 ++++++ tests/ui/blocks_in_if_conditions.fixed | 4 +- tests/ui/blocks_in_if_conditions.rs | 4 +- tests/ui/option_and_then_some.fixed | 25 -- tests/ui/option_and_then_some.rs | 25 -- tests/ui/option_and_then_some.stderr | 20 -- tests/ui/option_map_or_none.fixed | 2 +- tests/ui/option_map_or_none.rs | 2 +- 19 files changed, 564 insertions(+), 166 deletions(-) create mode 100644 clippy_lints/src/methods/bind_instead_of_map.rs create mode 100644 tests/ui/bind_instead_of_map.fixed create mode 100644 tests/ui/bind_instead_of_map.rs create mode 100644 tests/ui/bind_instead_of_map.stderr create mode 100644 tests/ui/bind_instead_of_map_multipart.rs create mode 100644 tests/ui/bind_instead_of_map_multipart.stderr delete mode 100644 tests/ui/option_and_then_some.fixed delete mode 100644 tests/ui/option_and_then_some.rs delete mode 100644 tests/ui/option_and_then_some.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index d05819a973a..7abefe65424 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -315,7 +315,7 @@ Released 2019-11-07 * [`missing_safety_doc`] [#4535](https://github.com/rust-lang/rust-clippy/pull/4535) * [`mem_replace_with_uninit`] [#4511](https://github.com/rust-lang/rust-clippy/pull/4511) * [`suspicious_map`] [#4394](https://github.com/rust-lang/rust-clippy/pull/4394) - * [`option_and_then_some`] [#4386](https://github.com/rust-lang/rust-clippy/pull/4386) + * `option_and_then_some` [#4386](https://github.com/rust-lang/rust-clippy/pull/4386) * [`manual_saturating_arithmetic`] [#4498](https://github.com/rust-lang/rust-clippy/pull/4498) * Deprecate `unused_collect` lint. This is fully covered by rustc's `#[must_use]` on `collect` [#4348](https://github.com/rust-lang/rust-clippy/pull/4348) * Move `type_repetition_in_bounds` to pedantic group [#4403](https://github.com/rust-lang/rust-clippy/pull/4403) @@ -1273,6 +1273,7 @@ Released 2018-09-13 [`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops [`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock [`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask +[`bind_instead_of_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#bind_instead_of_map [`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name [`blocks_in_if_conditions`]: https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_if_conditions [`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison @@ -1494,7 +1495,6 @@ Released 2018-09-13 [`not_unsafe_ptr_arg_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#not_unsafe_ptr_arg_deref [`ok_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#ok_expect [`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref -[`option_and_then_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_and_then_some [`option_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref [`option_env_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_env_unwrap [`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index bda0d5c0458..ec198b684b6 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -650,6 +650,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &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::CHARS_LAST_CMP, &methods::CHARS_NEXT_CMP, &methods::CLONE_DOUBLE_REF, @@ -676,7 +677,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::MAP_UNWRAP_OR, &methods::NEW_RET_NO_SELF, &methods::OK_EXPECT, - &methods::OPTION_AND_THEN_SOME, &methods::OPTION_AS_REF_DEREF, &methods::OPTION_MAP_OR_NONE, &methods::OR_FUN_CALL, @@ -1291,6 +1291,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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::CHARS_LAST_CMP), LintId::of(&methods::CHARS_NEXT_CMP), LintId::of(&methods::CLONE_DOUBLE_REF), @@ -1307,7 +1308,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::MANUAL_SATURATING_ARITHMETIC), LintId::of(&methods::NEW_RET_NO_SELF), LintId::of(&methods::OK_EXPECT), - LintId::of(&methods::OPTION_AND_THEN_SOME), LintId::of(&methods::OPTION_AS_REF_DEREF), LintId::of(&methods::OPTION_MAP_OR_NONE), LintId::of(&methods::OR_FUN_CALL), @@ -1559,10 +1559,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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_NEXT), LintId::of(&methods::FLAT_MAP_IDENTITY), - LintId::of(&methods::OPTION_AND_THEN_SOME), LintId::of(&methods::OPTION_AS_REF_DEREF), LintId::of(&methods::SEARCH_IS_SOME), LintId::of(&methods::SKIP_WHILE_NEXT), @@ -1784,6 +1784,7 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) { ls.register_renamed("clippy::new_without_default_derive", "clippy::new_without_default"); ls.register_renamed("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"); ls.register_renamed("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"); + ls.register_renamed("clippy::option_and_then_some", "clippy::bind_instead_of_map"); ls.register_renamed("clippy::block_in_if_condition_expr", "clippy::blocks_in_if_conditions"); ls.register_renamed("clippy::block_in_if_condition_stmt", "clippy::blocks_in_if_conditions"); ls.register_renamed("clippy::option_map_unwrap_or", "clippy::map_unwrap_or"); diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 4a9c411d7c8..84e8a010738 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1462,7 +1462,7 @@ fn check_for_loop_over_map_kv<'a, 'tcx>( let map = sugg::Sugg::hir(cx, arg, "map"); multispan_sugg( diag, - "use the corresponding method".into(), + "use the corresponding method", vec![ (pat_span, snippet(cx, new_pat_span, kind).into_owned()), (arg_span, format!("{}.{}s{}()", map.maybe_par(), kind, mutbl)), diff --git a/clippy_lints/src/methods/bind_instead_of_map.rs b/clippy_lints/src/methods/bind_instead_of_map.rs new file mode 100644 index 00000000000..32e86637569 --- /dev/null +++ b/clippy_lints/src/methods/bind_instead_of_map.rs @@ -0,0 +1,309 @@ +use super::{contains_return, BIND_INSTEAD_OF_MAP}; +use crate::utils::{ + in_macro, match_qpath, match_type, method_calls, multispan_sugg_with_applicability, paths, remove_blocks, snippet, + snippet_with_macro_callsite, span_lint_and_sugg, span_lint_and_then, +}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::intravisit::{self, Visitor}; +use rustc_lint::LateContext; +use rustc_middle::hir::map::Map; +use rustc_span::Span; + +pub(crate) struct OptionAndThenSome; +impl BindInsteadOfMap for OptionAndThenSome { + const TYPE_NAME: &'static str = "Option"; + const TYPE_QPATH: &'static [&'static str] = &paths::OPTION; + + const BAD_METHOD_NAME: &'static str = "and_then"; + const BAD_VARIANT_NAME: &'static str = "Some"; + const BAD_VARIANT_QPATH: &'static [&'static str] = &paths::OPTION_SOME; + + const GOOD_METHOD_NAME: &'static str = "map"; +} + +pub(crate) struct ResultAndThenOk; +impl BindInsteadOfMap for ResultAndThenOk { + const TYPE_NAME: &'static str = "Result"; + const TYPE_QPATH: &'static [&'static str] = &paths::RESULT; + + const BAD_METHOD_NAME: &'static str = "and_then"; + const BAD_VARIANT_NAME: &'static str = "Ok"; + const BAD_VARIANT_QPATH: &'static [&'static str] = &paths::RESULT_OK; + + const GOOD_METHOD_NAME: &'static str = "map"; +} + +pub(crate) struct ResultOrElseErrInfo; +impl BindInsteadOfMap for ResultOrElseErrInfo { + const TYPE_NAME: &'static str = "Result"; + const TYPE_QPATH: &'static [&'static str] = &paths::RESULT; + + const BAD_METHOD_NAME: &'static str = "or_else"; + const BAD_VARIANT_NAME: &'static str = "Err"; + const BAD_VARIANT_QPATH: &'static [&'static str] = &paths::RESULT_ERR; + + const GOOD_METHOD_NAME: &'static str = "map_err"; +} + +pub(crate) trait BindInsteadOfMap { + const TYPE_NAME: &'static str; + const TYPE_QPATH: &'static [&'static str]; + + const BAD_METHOD_NAME: &'static str; + const BAD_VARIANT_NAME: &'static str; + const BAD_VARIANT_QPATH: &'static [&'static str]; + + const GOOD_METHOD_NAME: &'static str; + + fn no_op_msg() -> String { + format!( + "using `{}.{}({})`, which is a no-op", + Self::TYPE_NAME, + Self::BAD_METHOD_NAME, + Self::BAD_VARIANT_NAME + ) + } + + fn lint_msg() -> String { + format!( + "using `{}.{}(|x| {}(y))`, which is more succinctly expressed as `{}(|x| y)`", + Self::TYPE_NAME, + Self::BAD_METHOD_NAME, + Self::BAD_VARIANT_NAME, + Self::GOOD_METHOD_NAME + ) + } + + fn lint_closure_autofixable( + cx: &LateContext<'_, '_>, + expr: &hir::Expr<'_>, + args: &[hir::Expr<'_>], + closure_expr: &hir::Expr<'_>, + closure_args_span: Span, + ) -> bool { + if_chain! { + if let hir::ExprKind::Call(ref some_expr, ref some_args) = closure_expr.kind; + if let hir::ExprKind::Path(ref qpath) = some_expr.kind; + if match_qpath(qpath, Self::BAD_VARIANT_QPATH); + if some_args.len() == 1; + then { + let inner_expr = &some_args[0]; + + if contains_return(inner_expr) { + return false; + } + + let some_inner_snip = if inner_expr.span.from_expansion() { + snippet_with_macro_callsite(cx, inner_expr.span, "_") + } else { + snippet(cx, inner_expr.span, "_") + }; + + let closure_args_snip = snippet(cx, closure_args_span, ".."); + let option_snip = snippet(cx, args[0].span, ".."); + let note = format!("{}.{}({} {})", option_snip, Self::GOOD_METHOD_NAME, closure_args_snip, some_inner_snip); + span_lint_and_sugg( + cx, + BIND_INSTEAD_OF_MAP, + expr.span, + Self::lint_msg().as_ref(), + "try this", + note, + Applicability::MachineApplicable, + ); + true + } else { + false + } + } + } + + fn lint_closure(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, closure_expr: &hir::Expr<'_>) { + let mut suggs = Vec::new(); + let can_sugg = find_all_ret_expressions(cx, closure_expr, |ret_expr| { + if_chain! { + if !in_macro(ret_expr.span); + if let hir::ExprKind::Call(ref func_path, ref args) = ret_expr.kind; + if let hir::ExprKind::Path(ref qpath) = func_path.kind; + if match_qpath(qpath, Self::BAD_VARIANT_QPATH); + if args.len() == 1; + if !contains_return(&args[0]); + then { + suggs.push((ret_expr.span, args[0].span.source_callsite())); + true + } else { + false + } + } + }); + + if can_sugg { + span_lint_and_then(cx, BIND_INSTEAD_OF_MAP, expr.span, Self::lint_msg().as_ref(), |diag| { + multispan_sugg_with_applicability( + diag, + "try this", + Applicability::MachineApplicable, + std::iter::once((*method_calls(expr, 1).2.get(0).unwrap(), Self::GOOD_METHOD_NAME.into())).chain( + suggs + .into_iter() + .map(|(span1, span2)| (span1, snippet(cx, span2, "_").into())), + ), + ) + }); + } + } + + /// Lint use of `_.and_then(|x| Some(y))` for `Option`s + fn lint(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { + if !match_type(cx, cx.tables.expr_ty(&args[0]), Self::TYPE_QPATH) { + return; + } + + match args[1].kind { + hir::ExprKind::Closure(_, _, body_id, closure_args_span, _) => { + let closure_body = cx.tcx.hir().body(body_id); + let closure_expr = remove_blocks(&closure_body.value); + + if !Self::lint_closure_autofixable(cx, expr, args, closure_expr, closure_args_span) { + Self::lint_closure(cx, expr, closure_expr); + } + }, + // `_.and_then(Some)` case, which is no-op. + hir::ExprKind::Path(ref qpath) if match_qpath(qpath, Self::BAD_VARIANT_QPATH) => { + span_lint_and_sugg( + cx, + BIND_INSTEAD_OF_MAP, + expr.span, + Self::no_op_msg().as_ref(), + "use the expression directly", + snippet(cx, args[0].span, "..").into(), + Applicability::MachineApplicable, + ); + }, + _ => {}, + } + } +} + +/// returns `true` if expr contains match expr desugared from try +fn contains_try(expr: &hir::Expr<'_>) -> bool { + struct TryFinder { + found: bool, + } + + impl<'hir> intravisit::Visitor<'hir> for TryFinder { + type Map = Map<'hir>; + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::None + } + + fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) { + if self.found { + return; + } + match expr.kind { + hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar) => self.found = true, + _ => intravisit::walk_expr(self, expr), + } + } + } + + let mut visitor = TryFinder { found: false }; + visitor.visit_expr(expr); + visitor.found +} + +fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_, '_>, expr: &'hir hir::Expr<'hir>, callback: F) -> bool +where + F: FnMut(&'hir hir::Expr<'hir>) -> bool, +{ + struct RetFinder { + in_stmt: bool, + failed: bool, + cb: F, + } + + struct WithStmtGuarg<'a, F> { + val: &'a mut RetFinder, + prev_in_stmt: bool, + } + + impl RetFinder { + fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuarg<'_, F> { + let prev_in_stmt = std::mem::replace(&mut self.in_stmt, in_stmt); + WithStmtGuarg { + val: self, + prev_in_stmt, + } + } + } + + impl std::ops::Deref for WithStmtGuarg<'_, F> { + type Target = RetFinder; + + fn deref(&self) -> &Self::Target { + self.val + } + } + + impl std::ops::DerefMut for WithStmtGuarg<'_, F> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.val + } + } + + impl Drop for WithStmtGuarg<'_, F> { + fn drop(&mut self) { + self.val.in_stmt = self.prev_in_stmt; + } + } + + impl<'hir, F: FnMut(&'hir hir::Expr<'hir>) -> bool> intravisit::Visitor<'hir> for RetFinder { + type Map = Map<'hir>; + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::None + } + + fn visit_stmt(&mut self, stmt: &'hir hir::Stmt<'_>) { + intravisit::walk_stmt(&mut *self.inside_stmt(true), stmt) + } + + fn visit_expr(&mut self, expr: &'hir hir::Expr<'_>) { + if self.failed { + return; + } + if self.in_stmt { + match expr.kind { + hir::ExprKind::Ret(Some(expr)) => self.inside_stmt(false).visit_expr(expr), + _ => intravisit::walk_expr(self, expr), + } + } else { + match expr.kind { + hir::ExprKind::Match(cond, arms, _) => { + self.inside_stmt(true).visit_expr(cond); + for arm in arms { + self.visit_expr(arm.body); + } + }, + hir::ExprKind::Block(..) => intravisit::walk_expr(self, expr), + hir::ExprKind::Ret(Some(expr)) => self.visit_expr(expr), + _ => self.failed |= !(self.cb)(expr), + } + } + } + } + + !contains_try(expr) && { + let mut ret_finder = RetFinder { + in_stmt: false, + failed: false, + cb: callback, + }; + ret_finder.visit_expr(expr); + !ret_finder.failed + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index e6094edc5d7..626427c15ec 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1,3 +1,4 @@ +mod bind_instead_of_map; mod inefficient_to_string; mod manual_saturating_arithmetic; mod option_map_unwrap_or; @@ -7,6 +8,7 @@ use std::borrow::Cow; use std::fmt; use std::iter; +use bind_instead_of_map::BindInsteadOfMap; use if_chain::if_chain; use rustc_ast::ast; use rustc_errors::Applicability; @@ -306,27 +308,34 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for usage of `_.and_then(|x| Some(y))`. + /// **What it does:** Checks for usage of `_.and_then(|x| Some(y))`, `_.and_then(|x| Ok(y))` or + /// `_.or_else(|x| Err(y))`. /// /// **Why is this bad?** Readability, this can be written more concisely as - /// `_.map(|x| y)`. + /// `_.map(|x| y)` or `_.map_err(|x| y)`. /// /// **Known problems:** None /// /// **Example:** /// /// ```rust - /// let x = Some("foo"); - /// let _ = x.and_then(|s| Some(s.len())); + /// # fn opt() -> Option<&'static str> { Some("42") } + /// # fn res() -> Result<&'static str, &'static str> { Ok("42") } + /// let _ = opt().and_then(|s| Some(s.len())); + /// let _ = res().and_then(|s| if s.len() == 42 { Ok(10) } else { Ok(20) }); + /// let _ = res().or_else(|s| if s.len() == 42 { Err(10) } else { Err(20) }); /// ``` /// /// The correct use would be: /// /// ```rust - /// let x = Some("foo"); - /// let _ = x.map(|s| s.len()); + /// # fn opt() -> Option<&'static str> { Some("42") } + /// # fn res() -> Result<&'static str, &'static str> { Ok("42") } + /// let _ = opt().map(|s| s.len()); + /// let _ = res().map(|s| if s.len() == 42 { 10 } else { 20 }); + /// let _ = res().map_err(|s| if s.len() == 42 { 10 } else { 20 }); /// ``` - pub OPTION_AND_THEN_SOME, + pub BIND_INSTEAD_OF_MAP, complexity, "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`" } @@ -1243,7 +1252,7 @@ declare_lint_pass!(Methods => [ MAP_UNWRAP_OR, RESULT_MAP_OR_INTO_OPTION, OPTION_MAP_OR_NONE, - OPTION_AND_THEN_SOME, + BIND_INSTEAD_OF_MAP, OR_FUN_CALL, EXPECT_FUN_CALL, CHARS_NEXT_CMP, @@ -1302,7 +1311,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods { ["unwrap_or", "map"] => option_map_unwrap_or::lint(cx, expr, arg_lists[1], arg_lists[0], method_spans[1]), ["unwrap_or_else", "map"] => lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]), ["map_or", ..] => lint_map_or_none(cx, expr, arg_lists[0]), - ["and_then", ..] => lint_option_and_then_some(cx, expr, arg_lists[0]), + ["and_then", ..] => { + bind_instead_of_map::OptionAndThenSome::lint(cx, expr, arg_lists[0]); + bind_instead_of_map::ResultAndThenOk::lint(cx, expr, arg_lists[0]); + }, + ["or_else", ..] => { + bind_instead_of_map::ResultOrElseErrInfo::lint(cx, expr, arg_lists[0]); + }, ["next", "filter"] => lint_filter_next(cx, expr, arg_lists[1]), ["next", "skip_while"] => lint_skip_while_next(cx, expr, arg_lists[1]), ["map", "filter"] => lint_filter_map(cx, expr, arg_lists[1], arg_lists[0]), @@ -2601,73 +2616,6 @@ fn lint_map_or_none<'a, 'tcx>( ); } -/// Lint use of `_.and_then(|x| Some(y))` for `Option`s -fn lint_option_and_then_some(cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { - const LINT_MSG: &str = "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`"; - const NO_OP_MSG: &str = "using `Option.and_then(Some)`, which is a no-op"; - - let ty = cx.tables.expr_ty(&args[0]); - if !is_type_diagnostic_item(cx, ty, sym!(option_type)) { - return; - } - - match args[1].kind { - hir::ExprKind::Closure(_, _, body_id, closure_args_span, _) => { - let closure_body = cx.tcx.hir().body(body_id); - let closure_expr = remove_blocks(&closure_body.value); - if_chain! { - if let hir::ExprKind::Call(ref some_expr, ref some_args) = closure_expr.kind; - if let hir::ExprKind::Path(ref qpath) = some_expr.kind; - if match_qpath(qpath, &paths::OPTION_SOME); - if some_args.len() == 1; - then { - let inner_expr = &some_args[0]; - - if contains_return(inner_expr) { - return; - } - - let some_inner_snip = if inner_expr.span.from_expansion() { - snippet_with_macro_callsite(cx, inner_expr.span, "_") - } else { - snippet(cx, inner_expr.span, "_") - }; - - let closure_args_snip = snippet(cx, closure_args_span, ".."); - let option_snip = snippet(cx, args[0].span, ".."); - let note = format!("{}.map({} {})", option_snip, closure_args_snip, some_inner_snip); - span_lint_and_sugg( - cx, - OPTION_AND_THEN_SOME, - expr.span, - LINT_MSG, - "try this", - note, - Applicability::MachineApplicable, - ); - } - } - }, - // `_.and_then(Some)` case, which is no-op. - hir::ExprKind::Path(ref qpath) => { - if match_qpath(qpath, &paths::OPTION_SOME) { - let option_snip = snippet(cx, args[0].span, ".."); - let note = format!("{}", option_snip); - span_lint_and_sugg( - cx, - OPTION_AND_THEN_SOME, - expr.span, - NO_OP_MSG, - "use the expression directly", - note, - Applicability::MachineApplicable, - ); - } - }, - _ => {}, - } -} - /// lint use of `filter().next()` for `Iterators` fn lint_filter_next<'a, 'tcx>( cx: &LateContext<'a, 'tcx>, diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index f50adbc48ab..6ed9ff22e46 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -2230,7 +2230,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ImplicitHasher { ); if !vis.suggestions.is_empty() { - multispan_sugg(diag, "...and use generic constructor".into(), vis.suggestions); + multispan_sugg(diag, "...and use generic constructor", vis.suggestions); } } diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index e5e3bf453a0..5b4e2906b5f 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -66,6 +66,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "bit_mask", }, + Lint { + name: "bind_instead_of_map", + group: "complexity", + desc: "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`", + deprecation: None, + module: "methods", + }, Lint { name: "blacklisted_name", group: "style", @@ -1578,13 +1585,6 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "eq_op", }, - Lint { - name: "option_and_then_some", - group: "complexity", - desc: "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`", - deprecation: None, - module: "methods", - }, Lint { name: "option_as_ref_deref", group: "complexity", diff --git a/tests/ui/bind_instead_of_map.fixed b/tests/ui/bind_instead_of_map.fixed new file mode 100644 index 00000000000..5815550d7a6 --- /dev/null +++ b/tests/ui/bind_instead_of_map.fixed @@ -0,0 +1,25 @@ +// run-rustfix +#![deny(clippy::bind_instead_of_map)] + +// need a main anyway, use it get rid of unused warnings too +pub fn main() { + let x = Some(5); + // the easiest cases + let _ = x; + let _ = x.map(|o| o + 1); + // and an easy counter-example + let _ = x.and_then(|o| if o < 32 { Some(o) } else { None }); + + // Different type + let x: Result = Ok(1); + let _ = x; +} + +pub fn foo() -> Option { + let x = Some(String::from("hello")); + Some("hello".to_owned()).and_then(|s| Some(format!("{}{}", s, x?))) +} + +pub fn example2(x: bool) -> Option<&'static str> { + Some("a").and_then(|s| Some(if x { s } else { return None })) +} diff --git a/tests/ui/bind_instead_of_map.rs b/tests/ui/bind_instead_of_map.rs new file mode 100644 index 00000000000..623b100a4ce --- /dev/null +++ b/tests/ui/bind_instead_of_map.rs @@ -0,0 +1,25 @@ +// run-rustfix +#![deny(clippy::bind_instead_of_map)] + +// need a main anyway, use it get rid of unused warnings too +pub fn main() { + let x = Some(5); + // the easiest cases + let _ = x.and_then(Some); + let _ = x.and_then(|o| Some(o + 1)); + // and an easy counter-example + let _ = x.and_then(|o| if o < 32 { Some(o) } else { None }); + + // Different type + let x: Result = Ok(1); + let _ = x.and_then(Ok); +} + +pub fn foo() -> Option { + let x = Some(String::from("hello")); + Some("hello".to_owned()).and_then(|s| Some(format!("{}{}", s, x?))) +} + +pub fn example2(x: bool) -> Option<&'static str> { + Some("a").and_then(|s| Some(if x { s } else { return None })) +} diff --git a/tests/ui/bind_instead_of_map.stderr b/tests/ui/bind_instead_of_map.stderr new file mode 100644 index 00000000000..24c6b7f9ef3 --- /dev/null +++ b/tests/ui/bind_instead_of_map.stderr @@ -0,0 +1,26 @@ +error: using `Option.and_then(Some)`, which is a no-op + --> $DIR/bind_instead_of_map.rs:8:13 + | +LL | let _ = x.and_then(Some); + | ^^^^^^^^^^^^^^^^ help: use the expression directly: `x` + | +note: the lint level is defined here + --> $DIR/bind_instead_of_map.rs:2:9 + | +LL | #![deny(clippy::bind_instead_of_map)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)` + --> $DIR/bind_instead_of_map.rs:9:13 + | +LL | let _ = x.and_then(|o| Some(o + 1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.map(|o| o + 1)` + +error: using `Result.and_then(Ok)`, which is a no-op + --> $DIR/bind_instead_of_map.rs:15:13 + | +LL | let _ = x.and_then(Ok); + | ^^^^^^^^^^^^^^ help: use the expression directly: `x` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/bind_instead_of_map_multipart.rs b/tests/ui/bind_instead_of_map_multipart.rs new file mode 100644 index 00000000000..91d9d11e3c1 --- /dev/null +++ b/tests/ui/bind_instead_of_map_multipart.rs @@ -0,0 +1,61 @@ +#![deny(clippy::bind_instead_of_map)] +#![allow(clippy::blocks_in_if_conditions)] + +pub fn main() { + let _ = Some("42").and_then(|s| if s.len() < 42 { Some(0) } else { Some(s.len()) }); + let _ = Some("42").and_then(|s| if s.len() < 42 { None } else { Some(s.len()) }); + + let _ = Ok::<_, ()>("42").and_then(|s| if s.len() < 42 { Ok(0) } else { Ok(s.len()) }); + let _ = Ok::<_, ()>("42").and_then(|s| if s.len() < 42 { Err(()) } else { Ok(s.len()) }); + + let _ = Err::<(), _>("42").or_else(|s| if s.len() < 42 { Err(s.len() + 20) } else { Err(s.len()) }); + let _ = Err::<(), _>("42").or_else(|s| if s.len() < 42 { Ok(()) } else { Err(s.len()) }); + + hard_example(); + macro_example(); +} + +fn hard_example() { + Some("42").and_then(|s| { + if { + if s == "43" { + return Some(43); + } + s == "42" + } { + return Some(45); + } + match s.len() { + 10 => Some(2), + 20 => { + if foo() { + return { + if foo() { + return Some(20); + } + println!("foo"); + Some(3) + }; + } + Some(20) + }, + 40 => Some(30), + _ => Some(1), + } + }); +} + +fn foo() -> bool { + true +} + +macro_rules! m { + () => { + Some(10) + }; +} + +fn macro_example() { + let _ = Some("").and_then(|s| if s.len() == 20 { m!() } else { Some(20) }); + let _ = Some("").and_then(|s| if s.len() == 20 { Some(m!()) } else { Some(Some(20)) }); +} diff --git a/tests/ui/bind_instead_of_map_multipart.stderr b/tests/ui/bind_instead_of_map_multipart.stderr new file mode 100644 index 00000000000..50ce2f4051e --- /dev/null +++ b/tests/ui/bind_instead_of_map_multipart.stderr @@ -0,0 +1,73 @@ +error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)` + --> $DIR/bind_instead_of_map_multipart.rs:5:13 + | +LL | let _ = Some("42").and_then(|s| if s.len() < 42 { Some(0) } else { Some(s.len()) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/bind_instead_of_map_multipart.rs:1:9 + | +LL | #![deny(clippy::bind_instead_of_map)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: try this + | +LL | let _ = Some("42").map(|s| if s.len() < 42 { 0 } else { s.len() }); + | ^^^ ^ ^^^^^^^ + +error: using `Result.and_then(|x| Ok(y))`, which is more succinctly expressed as `map(|x| y)` + --> $DIR/bind_instead_of_map_multipart.rs:8:13 + | +LL | let _ = Ok::<_, ()>("42").and_then(|s| if s.len() < 42 { Ok(0) } else { Ok(s.len()) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try this + | +LL | let _ = Ok::<_, ()>("42").map(|s| if s.len() < 42 { 0 } else { s.len() }); + | ^^^ ^ ^^^^^^^ + +error: using `Result.or_else(|x| Err(y))`, which is more succinctly expressed as `map_err(|x| y)` + --> $DIR/bind_instead_of_map_multipart.rs:11:13 + | +LL | let _ = Err::<(), _>("42").or_else(|s| if s.len() < 42 { Err(s.len() + 20) } else { Err(s.len()) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try this + | +LL | let _ = Err::<(), _>("42").map_err(|s| if s.len() < 42 { s.len() + 20 } else { s.len() }); + | ^^^^^^^ ^^^^^^^^^^^^ ^^^^^^^ + +error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)` + --> $DIR/bind_instead_of_map_multipart.rs:19:5 + | +LL | / Some("42").and_then(|s| { +LL | | if { +LL | | if s == "43" { +LL | | return Some(43); +... | +LL | | } +LL | | }); + | |______^ + | +help: try this + | +LL | Some("42").map(|s| { +LL | if { +LL | if s == "43" { +LL | return 43; +LL | } +LL | s == "42" + ... + +error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)` + --> $DIR/bind_instead_of_map_multipart.rs:60:13 + | +LL | let _ = Some("").and_then(|s| if s.len() == 20 { Some(m!()) } else { Some(Some(20)) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try this + | +LL | let _ = Some("").map(|s| if s.len() == 20 { m!() } else { Some(20) }); + | ^^^ ^^^^ ^^^^^^^^ + +error: aborting due to 5 previous errors + diff --git a/tests/ui/blocks_in_if_conditions.fixed b/tests/ui/blocks_in_if_conditions.fixed index 9040552cefc..14562c4d32c 100644 --- a/tests/ui/blocks_in_if_conditions.fixed +++ b/tests/ui/blocks_in_if_conditions.fixed @@ -63,10 +63,10 @@ fn block_in_assert() { let opt = Some(42); assert!(opt .as_ref() - .and_then(|val| { + .map(|val| { let mut v = val * 2; v -= 1; - Some(v * 3) + v * 3 }) .is_some()); } diff --git a/tests/ui/blocks_in_if_conditions.rs b/tests/ui/blocks_in_if_conditions.rs index 2fe409b22d3..bda87650f6d 100644 --- a/tests/ui/blocks_in_if_conditions.rs +++ b/tests/ui/blocks_in_if_conditions.rs @@ -63,10 +63,10 @@ fn block_in_assert() { let opt = Some(42); assert!(opt .as_ref() - .and_then(|val| { + .map(|val| { let mut v = val * 2; v -= 1; - Some(v * 3) + v * 3 }) .is_some()); } diff --git a/tests/ui/option_and_then_some.fixed b/tests/ui/option_and_then_some.fixed deleted file mode 100644 index 035bc1e5465..00000000000 --- a/tests/ui/option_and_then_some.fixed +++ /dev/null @@ -1,25 +0,0 @@ -// run-rustfix -#![deny(clippy::option_and_then_some)] - -// need a main anyway, use it get rid of unused warnings too -pub fn main() { - let x = Some(5); - // the easiest cases - let _ = x; - let _ = x.map(|o| o + 1); - // and an easy counter-example - let _ = x.and_then(|o| if o < 32 { Some(o) } else { None }); - - // Different type - let x: Result = Ok(1); - let _ = x.and_then(Ok); -} - -pub fn foo() -> Option { - let x = Some(String::from("hello")); - Some("hello".to_owned()).and_then(|s| Some(format!("{}{}", s, x?))) -} - -pub fn example2(x: bool) -> Option<&'static str> { - Some("a").and_then(|s| Some(if x { s } else { return None })) -} diff --git a/tests/ui/option_and_then_some.rs b/tests/ui/option_and_then_some.rs deleted file mode 100644 index d49da7813c6..00000000000 --- a/tests/ui/option_and_then_some.rs +++ /dev/null @@ -1,25 +0,0 @@ -// run-rustfix -#![deny(clippy::option_and_then_some)] - -// need a main anyway, use it get rid of unused warnings too -pub fn main() { - let x = Some(5); - // the easiest cases - let _ = x.and_then(Some); - let _ = x.and_then(|o| Some(o + 1)); - // and an easy counter-example - let _ = x.and_then(|o| if o < 32 { Some(o) } else { None }); - - // Different type - let x: Result = Ok(1); - let _ = x.and_then(Ok); -} - -pub fn foo() -> Option { - let x = Some(String::from("hello")); - Some("hello".to_owned()).and_then(|s| Some(format!("{}{}", s, x?))) -} - -pub fn example2(x: bool) -> Option<&'static str> { - Some("a").and_then(|s| Some(if x { s } else { return None })) -} diff --git a/tests/ui/option_and_then_some.stderr b/tests/ui/option_and_then_some.stderr deleted file mode 100644 index 47825491765..00000000000 --- a/tests/ui/option_and_then_some.stderr +++ /dev/null @@ -1,20 +0,0 @@ -error: using `Option.and_then(Some)`, which is a no-op - --> $DIR/option_and_then_some.rs:8:13 - | -LL | let _ = x.and_then(Some); - | ^^^^^^^^^^^^^^^^ help: use the expression directly: `x` - | -note: the lint level is defined here - --> $DIR/option_and_then_some.rs:2:9 - | -LL | #![deny(clippy::option_and_then_some)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)` - --> $DIR/option_and_then_some.rs:9:13 - | -LL | let _ = x.and_then(|o| Some(o + 1)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.map(|o| o + 1)` - -error: aborting due to 2 previous errors - diff --git a/tests/ui/option_map_or_none.fixed b/tests/ui/option_map_or_none.fixed index decbae4e5af..d80c3c7c1b7 100644 --- a/tests/ui/option_map_or_none.fixed +++ b/tests/ui/option_map_or_none.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![allow(clippy::option_and_then_some)] +#![allow(clippy::bind_instead_of_map)] fn main() { let opt = Some(1); diff --git a/tests/ui/option_map_or_none.rs b/tests/ui/option_map_or_none.rs index 0f1d2218d5d..629842419e5 100644 --- a/tests/ui/option_map_or_none.rs +++ b/tests/ui/option_map_or_none.rs @@ -1,6 +1,6 @@ // run-rustfix -#![allow(clippy::option_and_then_some)] +#![allow(clippy::bind_instead_of_map)] fn main() { let opt = Some(1); -- cgit 1.4.1-3-g733a5 From da9b138ec7645d483112c6b20e91ab595326c41d Mon Sep 17 00:00:00 2001 From: flip1995 Date: Tue, 19 May 2020 16:12:03 +0200 Subject: Update test after const_ptr functions are must_use now --- tests/ui/ptr_offset_with_cast.fixed | 12 ++++++------ tests/ui/ptr_offset_with_cast.rs | 12 ++++++------ tests/ui/ptr_offset_with_cast.stderr | 12 ++++++------ 3 files changed, 18 insertions(+), 18 deletions(-) (limited to 'tests') diff --git a/tests/ui/ptr_offset_with_cast.fixed b/tests/ui/ptr_offset_with_cast.fixed index ebdd6c4003d..718e391e8bf 100644 --- a/tests/ui/ptr_offset_with_cast.fixed +++ b/tests/ui/ptr_offset_with_cast.fixed @@ -9,12 +9,12 @@ fn main() { let offset_isize = 1_isize; unsafe { - ptr.add(offset_usize); - ptr.offset(offset_isize as isize); - ptr.offset(offset_u8 as isize); + let _ = ptr.add(offset_usize); + let _ = ptr.offset(offset_isize as isize); + let _ = ptr.offset(offset_u8 as isize); - ptr.wrapping_add(offset_usize); - ptr.wrapping_offset(offset_isize as isize); - ptr.wrapping_offset(offset_u8 as isize); + let _ = ptr.wrapping_add(offset_usize); + let _ = ptr.wrapping_offset(offset_isize as isize); + let _ = ptr.wrapping_offset(offset_u8 as isize); } } diff --git a/tests/ui/ptr_offset_with_cast.rs b/tests/ui/ptr_offset_with_cast.rs index 3416c4b727a..f613742c741 100644 --- a/tests/ui/ptr_offset_with_cast.rs +++ b/tests/ui/ptr_offset_with_cast.rs @@ -9,12 +9,12 @@ fn main() { let offset_isize = 1_isize; unsafe { - ptr.offset(offset_usize as isize); - ptr.offset(offset_isize as isize); - ptr.offset(offset_u8 as isize); + let _ = ptr.offset(offset_usize as isize); + let _ = ptr.offset(offset_isize as isize); + let _ = ptr.offset(offset_u8 as isize); - ptr.wrapping_offset(offset_usize as isize); - ptr.wrapping_offset(offset_isize as isize); - ptr.wrapping_offset(offset_u8 as isize); + let _ = ptr.wrapping_offset(offset_usize as isize); + let _ = ptr.wrapping_offset(offset_isize as isize); + let _ = ptr.wrapping_offset(offset_u8 as isize); } } diff --git a/tests/ui/ptr_offset_with_cast.stderr b/tests/ui/ptr_offset_with_cast.stderr index b5c7a03e277..fd45224ca06 100644 --- a/tests/ui/ptr_offset_with_cast.stderr +++ b/tests/ui/ptr_offset_with_cast.stderr @@ -1,16 +1,16 @@ error: use of `offset` with a `usize` casted to an `isize` - --> $DIR/ptr_offset_with_cast.rs:12:9 + --> $DIR/ptr_offset_with_cast.rs:12:17 | -LL | ptr.offset(offset_usize as isize); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr.add(offset_usize)` +LL | let _ = ptr.offset(offset_usize as isize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr.add(offset_usize)` | = note: `-D clippy::ptr-offset-with-cast` implied by `-D warnings` error: use of `wrapping_offset` with a `usize` casted to an `isize` - --> $DIR/ptr_offset_with_cast.rs:16:9 + --> $DIR/ptr_offset_with_cast.rs:16:17 | -LL | ptr.wrapping_offset(offset_usize as isize); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr.wrapping_add(offset_usize)` +LL | let _ = ptr.wrapping_offset(offset_usize as isize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr.wrapping_add(offset_usize)` error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From ecd0a67b01e13d7a80d2f64bbfa5da1e568367e5 Mon Sep 17 00:00:00 2001 From: Elichai Turkel Date: Wed, 20 May 2020 13:23:51 +0300 Subject: Make match_wild_err_arm pedantic, and update help messages --- clippy_lints/src/lib.rs | 3 +-- clippy_lints/src/matches.rs | 6 +++--- src/lintlist/mod.rs | 2 +- tests/ui/match_wild_err_arm.stderr | 8 ++++---- 4 files changed, 9 insertions(+), 10 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 4d4fff883b3..057d39d4c82 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1141,6 +1141,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS), LintId::of(&matches::MATCH_BOOL), LintId::of(&matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS), + LintId::of(&matches::MATCH_WILD_ERR_ARM), LintId::of(&matches::SINGLE_MATCH_ELSE), LintId::of(&methods::FILTER_MAP), LintId::of(&methods::FILTER_MAP_NEXT), @@ -1285,7 +1286,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&matches::MATCH_OVERLAPPING_ARM), LintId::of(&matches::MATCH_REF_PATS), LintId::of(&matches::MATCH_SINGLE_BINDING), - LintId::of(&matches::MATCH_WILD_ERR_ARM), LintId::of(&matches::SINGLE_MATCH), LintId::of(&matches::WILDCARD_IN_OR_PATTERNS), LintId::of(&mem_discriminant::MEM_DISCRIMINANT_NON_ENUM), @@ -1476,7 +1476,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH), LintId::of(&matches::MATCH_OVERLAPPING_ARM), LintId::of(&matches::MATCH_REF_PATS), - LintId::of(&matches::MATCH_WILD_ERR_ARM), LintId::of(&matches::SINGLE_MATCH), LintId::of(&mem_replace::MEM_REPLACE_OPTION_WITH_NONE), LintId::of(&mem_replace::MEM_REPLACE_WITH_DEFAULT), diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 4106e5013b9..94380acfcfd 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -168,7 +168,7 @@ declare_clippy_lint! { /// **What it does:** Checks for arm which matches all errors with `Err(_)` /// and take drastic actions like `panic!`. /// - /// **Why is this bad?** It is generally a bad practice, just like + /// **Why is this bad?** It is generally a bad practice, similar to /// catching all exceptions in java with `catch(Exception)` /// /// **Known problems:** None. @@ -182,7 +182,7 @@ declare_clippy_lint! { /// } /// ``` pub MATCH_WILD_ERR_ARM, - style, + pedantic, "a `match` with `Err(_)` arm and take drastic actions" } @@ -711,7 +711,7 @@ fn check_wild_err_arm(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>]) arm.pat.span, &format!("`Err({})` matches all errors", &ident_bind_name), None, - "match each error separately or use the error output", + "match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable", ); } } diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 0bf46491d31..8211a57b564 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1195,7 +1195,7 @@ pub static ref ALL_LINTS: Vec = vec![ }, Lint { name: "match_wild_err_arm", - group: "style", + group: "pedantic", desc: "a `match` with `Err(_)` arm and take drastic actions", deprecation: None, module: "matches", diff --git a/tests/ui/match_wild_err_arm.stderr b/tests/ui/match_wild_err_arm.stderr index 20d4c933418..6a2a02987de 100644 --- a/tests/ui/match_wild_err_arm.stderr +++ b/tests/ui/match_wild_err_arm.stderr @@ -5,7 +5,7 @@ LL | Err(_) => panic!("err"), | ^^^^^^ | = note: `-D clippy::match-wild-err-arm` implied by `-D warnings` - = note: match each error separately or use the error output + = note: match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable error: `Err(_)` matches all errors --> $DIR/match_wild_err_arm.rs:17:9 @@ -13,7 +13,7 @@ error: `Err(_)` matches all errors LL | Err(_) => panic!(), | ^^^^^^ | - = note: match each error separately or use the error output + = note: match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable error: `Err(_)` matches all errors --> $DIR/match_wild_err_arm.rs:23:9 @@ -21,7 +21,7 @@ error: `Err(_)` matches all errors LL | Err(_) => { | ^^^^^^ | - = note: match each error separately or use the error output + = note: match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable error: `Err(_e)` matches all errors --> $DIR/match_wild_err_arm.rs:31:9 @@ -29,7 +29,7 @@ error: `Err(_e)` matches all errors LL | Err(_e) => panic!(), | ^^^^^^^ | - = note: match each error separately or use the error output + = note: match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From 2db7f1abf84699605a5863887484cbf587db3eb1 Mon Sep 17 00:00:00 2001 From: Elichai Turkel Date: Wed, 20 May 2020 16:46:30 +0300 Subject: Update future-not-send stderr output --- tests/ui/future_not_send.stderr | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/tests/ui/future_not_send.stderr b/tests/ui/future_not_send.stderr index d1863701bfe..b59dbb3e76c 100644 --- a/tests/ui/future_not_send.stderr +++ b/tests/ui/future_not_send.stderr @@ -47,17 +47,32 @@ error: future cannot be sent between threads safely --> $DIR/future_not_send.rs:20:63 | LL | async fn private_future2(rc: Rc<[u8]>, cell: &Cell) -> bool { - | ^^^^ + | ^^^^ future returned by `private_future2` is not `Send` | +note: captured value is not `Send` + --> $DIR/future_not_send.rs:20:26 + | +LL | async fn private_future2(rc: Rc<[u8]>, cell: &Cell) -> bool { + | ^^ has type `std::rc::Rc<[u8]>` which is not `Send` = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send` +note: captured value is not `Send` + --> $DIR/future_not_send.rs:20:40 + | +LL | async fn private_future2(rc: Rc<[u8]>, cell: &Cell) -> bool { + | ^^^^ has type `&std::cell::Cell` which is not `Send` = note: `std::cell::Cell` doesn't implement `std::marker::Sync` error: future cannot be sent between threads safely --> $DIR/future_not_send.rs:24:43 | LL | pub async fn public_future2(rc: Rc<[u8]>) {} - | ^ + | ^ future returned by `public_future2` is not `Send` | +note: captured value is not `Send` + --> $DIR/future_not_send.rs:24:29 + | +LL | pub async fn public_future2(rc: Rc<[u8]>) {} + | ^^ has type `std::rc::Rc<[u8]>` which is not `Send` = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send` error: future cannot be sent between threads safely @@ -117,8 +132,13 @@ error: future cannot be sent between threads safely --> $DIR/future_not_send.rs:66:34 | LL | async fn unclear_future(t: T) {} - | ^ + | ^ future returned by `unclear_future` is not `Send` | +note: captured value is not `Send` + --> $DIR/future_not_send.rs:66:28 + | +LL | async fn unclear_future(t: T) {} + | ^ has type `T` which is not `Send` = note: `T` doesn't implement `std::marker::Send` error: aborting due to 8 previous errors -- cgit 1.4.1-3-g733a5 From bd9b09e29396697874f63d82926b36fa154caa1f Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Mon, 18 May 2020 20:54:09 +0200 Subject: Adapt compile-test to run tests for cargo lints --- tests/compile-test.rs | 140 ++++++++++++++++++++++++-------- tests/ui-cargo/update-all-references.sh | 18 ++++ tests/ui-cargo/update-references.sh | 38 +++++++++ 3 files changed, 164 insertions(+), 32 deletions(-) create mode 100755 tests/ui-cargo/update-all-references.sh create mode 100755 tests/ui-cargo/update-references.sh (limited to 'tests') diff --git a/tests/compile-test.rs b/tests/compile-test.rs index a3df9d5ccbd..91b9c73c9d4 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -101,49 +101,124 @@ fn run_mode(cfg: &mut compiletest::Config) { compiletest::run_tests(&cfg); } -#[allow(clippy::identity_conversion)] -fn run_ui_toml_tests(config: &compiletest::Config, mut tests: Vec) -> Result { - let mut result = true; - let opts = compiletest::test_opts(config); - for dir in fs::read_dir(&config.src_base)? { - let dir = dir?; - if !dir.file_type()?.is_dir() { - continue; - } - let dir_path = dir.path(); - set_var("CARGO_MANIFEST_DIR", &dir_path); - for file in fs::read_dir(&dir_path)? { - let file = file?; - let file_path = file.path(); - if file.file_type()?.is_dir() { +fn run_ui_toml(config: &mut compiletest::Config) { + fn run_tests(config: &compiletest::Config, mut tests: Vec) -> Result { + let mut result = true; + let opts = compiletest::test_opts(config); + for dir in fs::read_dir(&config.src_base)? { + let dir = dir?; + if !dir.file_type()?.is_dir() { continue; } - if file_path.extension() != Some(OsStr::new("rs")) { - continue; + let dir_path = dir.path(); + set_var("CARGO_MANIFEST_DIR", &dir_path); + for file in fs::read_dir(&dir_path)? { + let file = file?; + let file_path = file.path(); + if file.file_type()?.is_dir() { + continue; + } + if file_path.extension() != Some(OsStr::new("rs")) { + continue; + } + let paths = compiletest::common::TestPaths { + file: file_path, + base: config.src_base.clone(), + relative_dir: dir_path.file_name().unwrap().into(), + }; + let test_name = compiletest::make_test_name(&config, &paths); + let index = tests + .iter() + .position(|test| test.desc.name == test_name) + .expect("The test should be in there"); + result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?; } - let paths = compiletest::common::TestPaths { - file: file_path, - base: config.src_base.clone(), - relative_dir: dir_path.file_name().unwrap().into(), - }; - let test_name = compiletest::make_test_name(&config, &paths); - let index = tests - .iter() - .position(|test| test.desc.name == test_name) - .expect("The test should be in there"); - result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?; } + Ok(result) } - Ok(result) -} -fn run_ui_toml(config: &mut compiletest::Config) { config.mode = TestMode::Ui; config.src_base = Path::new("tests").join("ui-toml").canonicalize().unwrap(); let tests = compiletest::make_tests(&config); - let res = run_ui_toml_tests(&config, tests); + let res = run_tests(&config, tests); + match res { + Ok(true) => {}, + Ok(false) => panic!("Some tests failed"), + Err(e) => { + println!("I/O failure during tests: {:?}", e); + }, + } +} + +fn run_ui_cargo(config: &mut compiletest::Config) { + fn run_tests( + config: &compiletest::Config, + filter: &Option, + mut tests: Vec, + ) -> Result { + let mut result = true; + let opts = compiletest::test_opts(config); + + for dir in fs::read_dir(&config.src_base)? { + let dir = dir?; + if !dir.file_type()?.is_dir() { + continue; + } + + // Use the filter if provided + let dir_path = dir.path(); + match &filter { + Some(name) if !dir_path.ends_with(name) => continue, + _ => {}, + } + + for case in &["pass", "fail"] { + let tail: PathBuf = [case, "src"].iter().collect(); + let src_path = dir_path.join(tail); + env::set_current_dir(&src_path)?; + + for file in fs::read_dir(&src_path)? { + let file = file?; + if file.file_type()?.is_dir() { + continue; + } + + // Search for the main file to avoid running a test for each file in the project + let file_path = file.path(); + match file_path.file_name().and_then(OsStr::to_str) { + Some("main.rs") => {}, + _ => continue, + } + + let paths = compiletest::common::TestPaths { + file: file_path, + base: config.src_base.clone(), + relative_dir: src_path.strip_prefix(&config.src_base).unwrap().into(), + }; + let test_name = compiletest::make_test_name(&config, &paths); + let index = tests + .iter() + .position(|test| test.desc.name == test_name) + .expect("The test should be in there"); + result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?; + } + } + } + Ok(result) + } + + config.mode = TestMode::Ui; + config.src_base = Path::new("tests").join("ui-cargo").canonicalize().unwrap(); + + let tests = compiletest::make_tests(&config); + + let current_dir = env::current_dir().unwrap(); + let filter = env::var("TESTNAME").ok(); + let res = run_tests(&config, &filter, tests); + env::set_current_dir(current_dir).unwrap(); + match res { Ok(true) => {}, Ok(false) => panic!("Some tests failed"), @@ -165,4 +240,5 @@ fn compile_test() { let mut config = default_config(); run_mode(&mut config); run_ui_toml(&mut config); + run_ui_cargo(&mut config); } diff --git a/tests/ui-cargo/update-all-references.sh b/tests/ui-cargo/update-all-references.sh new file mode 100755 index 00000000000..7028b251ea0 --- /dev/null +++ b/tests/ui-cargo/update-all-references.sh @@ -0,0 +1,18 @@ +#!/bin/bash +# +# A script to update the references for all tests. The idea is that +# you do a run, which will generate files in the build directory +# containing the (normalized) actual output of the compiler. You then +# run this script, which will copy those files over. If you find +# yourself manually editing a foo.stderr file, you're doing it wrong. +# +# See all `update-references.sh`, if you just want to update a single test. + +if [[ "$1" == "--help" || "$1" == "-h" ]]; then + echo "usage: $0" +fi + +BUILD_DIR=$PWD/target/debug/test_build_base +MY_DIR=$(dirname "$0") +cd "$MY_DIR" || exit +find . -name '*.rs' -exec ./update-references.sh "$BUILD_DIR" {} + diff --git a/tests/ui-cargo/update-references.sh b/tests/ui-cargo/update-references.sh new file mode 100755 index 00000000000..50d42678734 --- /dev/null +++ b/tests/ui-cargo/update-references.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +# A script to update the references for particular tests. The idea is +# that you do a run, which will generate files in the build directory +# containing the (normalized) actual output of the compiler. This +# script will then copy that output and replace the "expected output" +# files. You can then commit the changes. +# +# If you find yourself manually editing a foo.stderr file, you're +# doing it wrong. + +if [[ "$1" == "--help" || "$1" == "-h" || "$1" == "" || "$2" == "" ]]; then + echo "usage: $0 " + echo "" + echo "For example:" + echo " $0 ../../../build/x86_64-apple-darwin/test/ui *.rs */*.rs" +fi + +MYDIR=$(dirname "$0") + +BUILD_DIR="$1" +shift + +while [[ "$1" != "" ]]; do + STDERR_NAME="${1/%.rs/.stderr}" + STDOUT_NAME="${1/%.rs/.stdout}" + shift + if [[ -f "$BUILD_DIR"/"$STDOUT_NAME" ]] && \ + ! (cmp -s -- "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"); then + echo updating "$MYDIR"/"$STDOUT_NAME" + cp "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME" + fi + if [[ -f "$BUILD_DIR"/"$STDERR_NAME" ]] && \ + ! (cmp -s -- "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"); then + echo updating "$MYDIR"/"$STDERR_NAME" + cp "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME" + fi +done -- cgit 1.4.1-3-g733a5 From 96af3e83601a6cd37544e70a7a816c36cc9871f5 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Mon, 18 May 2020 20:55:12 +0200 Subject: Add test for wildcard_dependencies --- tests/ui-cargo/wildcard_dependencies/fail/Cargo.toml | 6 ++++++ tests/ui-cargo/wildcard_dependencies/fail/src/main.rs | 3 +++ tests/ui-cargo/wildcard_dependencies/fail/src/main.stderr | 6 ++++++ tests/ui-cargo/wildcard_dependencies/pass/Cargo.toml | 6 ++++++ tests/ui-cargo/wildcard_dependencies/pass/src/main.rs | 3 +++ 5 files changed, 24 insertions(+) create mode 100644 tests/ui-cargo/wildcard_dependencies/fail/Cargo.toml create mode 100644 tests/ui-cargo/wildcard_dependencies/fail/src/main.rs create mode 100644 tests/ui-cargo/wildcard_dependencies/fail/src/main.stderr create mode 100644 tests/ui-cargo/wildcard_dependencies/pass/Cargo.toml create mode 100644 tests/ui-cargo/wildcard_dependencies/pass/src/main.rs (limited to 'tests') diff --git a/tests/ui-cargo/wildcard_dependencies/fail/Cargo.toml b/tests/ui-cargo/wildcard_dependencies/fail/Cargo.toml new file mode 100644 index 00000000000..9558dd68091 --- /dev/null +++ b/tests/ui-cargo/wildcard_dependencies/fail/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "wildcard_dependencies" +version = "0.1.0" + +[dependencies] +regex = "*" diff --git a/tests/ui-cargo/wildcard_dependencies/fail/src/main.rs b/tests/ui-cargo/wildcard_dependencies/fail/src/main.rs new file mode 100644 index 00000000000..3491ccb0d47 --- /dev/null +++ b/tests/ui-cargo/wildcard_dependencies/fail/src/main.rs @@ -0,0 +1,3 @@ +#![warn(clippy::wildcard_dependencies)] + +fn main() {} diff --git a/tests/ui-cargo/wildcard_dependencies/fail/src/main.stderr b/tests/ui-cargo/wildcard_dependencies/fail/src/main.stderr new file mode 100644 index 00000000000..9e65d2f9942 --- /dev/null +++ b/tests/ui-cargo/wildcard_dependencies/fail/src/main.stderr @@ -0,0 +1,6 @@ +error: wildcard dependency for `regex` + | + = note: `-D clippy::wildcard-dependencies` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui-cargo/wildcard_dependencies/pass/Cargo.toml b/tests/ui-cargo/wildcard_dependencies/pass/Cargo.toml new file mode 100644 index 00000000000..062e441622a --- /dev/null +++ b/tests/ui-cargo/wildcard_dependencies/pass/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "wildcard_dependencies" +version = "0.1.0" + +[dependencies] +regex = "1" diff --git a/tests/ui-cargo/wildcard_dependencies/pass/src/main.rs b/tests/ui-cargo/wildcard_dependencies/pass/src/main.rs new file mode 100644 index 00000000000..3491ccb0d47 --- /dev/null +++ b/tests/ui-cargo/wildcard_dependencies/pass/src/main.rs @@ -0,0 +1,3 @@ +#![warn(clippy::wildcard_dependencies)] + +fn main() {} -- cgit 1.4.1-3-g733a5 From bc93f7052e4a76d62e2aa5f11649e662cb46c7ce Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Mon, 18 May 2020 20:56:33 +0200 Subject: Add test for cargo_common_metadata Fix missing `authors` entry in the provided example --- clippy_lints/src/cargo_common_metadata.rs | 1 + tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml | 3 +++ tests/ui-cargo/cargo_common_metadata/fail/src/main.rs | 3 +++ .../cargo_common_metadata/fail/src/main.stderr | 18 ++++++++++++++++++ tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml | 10 ++++++++++ tests/ui-cargo/cargo_common_metadata/pass/src/main.rs | 3 +++ 6 files changed, 38 insertions(+) create mode 100644 tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml create mode 100644 tests/ui-cargo/cargo_common_metadata/fail/src/main.rs create mode 100644 tests/ui-cargo/cargo_common_metadata/fail/src/main.stderr create mode 100644 tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml create mode 100644 tests/ui-cargo/cargo_common_metadata/pass/src/main.rs (limited to 'tests') diff --git a/clippy_lints/src/cargo_common_metadata.rs b/clippy_lints/src/cargo_common_metadata.rs index 782da249808..16b46423c8f 100644 --- a/clippy_lints/src/cargo_common_metadata.rs +++ b/clippy_lints/src/cargo_common_metadata.rs @@ -23,6 +23,7 @@ declare_clippy_lint! { /// [package] /// name = "clippy" /// version = "0.0.212" + /// authors = ["Someone "] /// 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/tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml b/tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml new file mode 100644 index 00000000000..8346bf05778 --- /dev/null +++ b/tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "cargo_common_metadata" +version = "0.1.0" diff --git a/tests/ui-cargo/cargo_common_metadata/fail/src/main.rs b/tests/ui-cargo/cargo_common_metadata/fail/src/main.rs new file mode 100644 index 00000000000..c67166fc4b0 --- /dev/null +++ b/tests/ui-cargo/cargo_common_metadata/fail/src/main.rs @@ -0,0 +1,3 @@ +#![warn(clippy::cargo_common_metadata)] + +fn main() {} diff --git a/tests/ui-cargo/cargo_common_metadata/fail/src/main.stderr b/tests/ui-cargo/cargo_common_metadata/fail/src/main.stderr new file mode 100644 index 00000000000..c8ae6c820df --- /dev/null +++ b/tests/ui-cargo/cargo_common_metadata/fail/src/main.stderr @@ -0,0 +1,18 @@ +error: package `cargo_common_metadata` is missing `package.authors` metadata + | + = note: `-D clippy::cargo-common-metadata` implied by `-D warnings` + +error: package `cargo_common_metadata` is missing `package.description` metadata + +error: package `cargo_common_metadata` is missing `either package.license or package.license_file` metadata + +error: package `cargo_common_metadata` is missing `package.repository` metadata + +error: package `cargo_common_metadata` is missing `package.readme` metadata + +error: package `cargo_common_metadata` is missing `package.keywords` metadata + +error: package `cargo_common_metadata` is missing `package.categories` metadata + +error: aborting due to 7 previous errors + diff --git a/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml b/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml new file mode 100644 index 00000000000..f99c126fabf --- /dev/null +++ b/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "cargo_common_metadata" +version = "0.1.0" +authors = ["Random person from the Internet "] +description = "A test package for the cargo_common_metadata lint" +repository = "https://github.com/someone/cargo_common_metadata" +readme = "README.md" +license = "MIT OR Apache-2.0" +keywords = ["metadata", "lint", "clippy"] +categories = ["development-tools::testing"] diff --git a/tests/ui-cargo/cargo_common_metadata/pass/src/main.rs b/tests/ui-cargo/cargo_common_metadata/pass/src/main.rs new file mode 100644 index 00000000000..c67166fc4b0 --- /dev/null +++ b/tests/ui-cargo/cargo_common_metadata/pass/src/main.rs @@ -0,0 +1,3 @@ +#![warn(clippy::cargo_common_metadata)] + +fn main() {} -- cgit 1.4.1-3-g733a5 From 7a0eccbd8a719af00b027b0ea85c576d9cbed750 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Mon, 18 May 2020 20:58:02 +0200 Subject: Add test for multiple_crate_versions Make the output of the lint deterministic by sorting the versions --- clippy_lints/src/multiple_crate_versions.rs | 4 +++- tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml | 7 +++++++ tests/ui-cargo/multiple_crate_versions/fail/src/main.rs | 3 +++ tests/ui-cargo/multiple_crate_versions/fail/src/main.stderr | 6 ++++++ tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml | 7 +++++++ tests/ui-cargo/multiple_crate_versions/pass/src/main.rs | 3 +++ 6 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml create mode 100644 tests/ui-cargo/multiple_crate_versions/fail/src/main.rs create mode 100644 tests/ui-cargo/multiple_crate_versions/fail/src/main.stderr create mode 100644 tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml create mode 100644 tests/ui-cargo/multiple_crate_versions/pass/src/main.rs (limited to 'tests') diff --git a/clippy_lints/src/multiple_crate_versions.rs b/clippy_lints/src/multiple_crate_versions.rs index ed85d0315bd..c4decfc9401 100644 --- a/clippy_lints/src/multiple_crate_versions.rs +++ b/clippy_lints/src/multiple_crate_versions.rs @@ -54,7 +54,9 @@ impl LateLintPass<'_, '_> for MultipleCrateVersions { let group: Vec = group.collect(); if group.len() > 1 { - let versions = group.into_iter().map(|p| p.version).join(", "); + let mut versions: Vec<_> = group.into_iter().map(|p| p.version).collect(); + versions.sort(); + let versions = versions.iter().join(", "); span_lint( cx, diff --git a/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml b/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml new file mode 100644 index 00000000000..05ffde839dc --- /dev/null +++ b/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "multiple_crate_versions" +version = "0.1.0" + +[dependencies] +ctrlc = "=3.1.0" +ansi_term = "=0.11.0" diff --git a/tests/ui-cargo/multiple_crate_versions/fail/src/main.rs b/tests/ui-cargo/multiple_crate_versions/fail/src/main.rs new file mode 100644 index 00000000000..4bc61dd6299 --- /dev/null +++ b/tests/ui-cargo/multiple_crate_versions/fail/src/main.rs @@ -0,0 +1,3 @@ +#![warn(clippy::multiple_crate_versions)] + +fn main() {} diff --git a/tests/ui-cargo/multiple_crate_versions/fail/src/main.stderr b/tests/ui-cargo/multiple_crate_versions/fail/src/main.stderr new file mode 100644 index 00000000000..4f668599be9 --- /dev/null +++ b/tests/ui-cargo/multiple_crate_versions/fail/src/main.stderr @@ -0,0 +1,6 @@ +error: multiple versions for dependency `winapi`: 0.2.8, 0.3.8 + | + = note: `-D clippy::multiple-crate-versions` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml b/tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml new file mode 100644 index 00000000000..cad32b9233f --- /dev/null +++ b/tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "cargo_common_metadata" +version = "0.1.0" + +[dependencies] +regex = "1.3.7" +serde = "1.0.110" diff --git a/tests/ui-cargo/multiple_crate_versions/pass/src/main.rs b/tests/ui-cargo/multiple_crate_versions/pass/src/main.rs new file mode 100644 index 00000000000..4bc61dd6299 --- /dev/null +++ b/tests/ui-cargo/multiple_crate_versions/pass/src/main.rs @@ -0,0 +1,3 @@ +#![warn(clippy::multiple_crate_versions)] + +fn main() {} -- cgit 1.4.1-3-g733a5 From 7ff71199df911b462800cf6bda7ac32879ba7eb1 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Thu, 21 May 2020 14:46:04 +0200 Subject: Address comments from PR review --- clippy_dev/src/new_lint.rs | 1 + tests/compile-test.rs | 4 ++-- tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml | 1 + tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml | 1 + tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml | 1 + tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml | 1 + tests/ui-cargo/wildcard_dependencies/fail/Cargo.toml | 1 + tests/ui-cargo/wildcard_dependencies/pass/Cargo.toml | 1 + 8 files changed, 9 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 843beaf3238..80713ab569f 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -139,6 +139,7 @@ fn get_manifest_contents(lint_name: &str, hint: &str) -> String { [package] name = "{}" version = "0.1.0" +publish = false "#, hint, lint_name ) diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 91b9c73c9d4..232b966f69a 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -147,7 +147,7 @@ fn run_ui_toml(config: &mut compiletest::Config) { Ok(true) => {}, Ok(false) => panic!("Some tests failed"), Err(e) => { - println!("I/O failure during tests: {:?}", e); + panic!("I/O failure during tests: {:?}", e); }, } } @@ -223,7 +223,7 @@ fn run_ui_cargo(config: &mut compiletest::Config) { Ok(true) => {}, Ok(false) => panic!("Some tests failed"), Err(e) => { - println!("I/O failure during tests: {:?}", e); + panic!("I/O failure during tests: {:?}", e); }, } } diff --git a/tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml b/tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml index 8346bf05778..c64adcf7c01 100644 --- a/tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml +++ b/tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml @@ -1,3 +1,4 @@ [package] name = "cargo_common_metadata" version = "0.1.0" +publish = false diff --git a/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml b/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml index f99c126fabf..c8233f328bb 100644 --- a/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml +++ b/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "cargo_common_metadata" version = "0.1.0" +publish = false authors = ["Random person from the Internet "] description = "A test package for the cargo_common_metadata lint" repository = "https://github.com/someone/cargo_common_metadata" diff --git a/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml b/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml index 05ffde839dc..3a94b723f3f 100644 --- a/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml +++ b/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "multiple_crate_versions" version = "0.1.0" +publish = false [dependencies] ctrlc = "=3.1.0" diff --git a/tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml b/tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml index cad32b9233f..a9b06420b33 100644 --- a/tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml +++ b/tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "cargo_common_metadata" version = "0.1.0" +publish = false [dependencies] regex = "1.3.7" diff --git a/tests/ui-cargo/wildcard_dependencies/fail/Cargo.toml b/tests/ui-cargo/wildcard_dependencies/fail/Cargo.toml index 9558dd68091..fd2a3414856 100644 --- a/tests/ui-cargo/wildcard_dependencies/fail/Cargo.toml +++ b/tests/ui-cargo/wildcard_dependencies/fail/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "wildcard_dependencies" version = "0.1.0" +publish = false [dependencies] regex = "*" diff --git a/tests/ui-cargo/wildcard_dependencies/pass/Cargo.toml b/tests/ui-cargo/wildcard_dependencies/pass/Cargo.toml index 062e441622a..38cb139146e 100644 --- a/tests/ui-cargo/wildcard_dependencies/pass/Cargo.toml +++ b/tests/ui-cargo/wildcard_dependencies/pass/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "wildcard_dependencies" version = "0.1.0" +publish = false [dependencies] regex = "1" -- cgit 1.4.1-3-g733a5 From f9013ff197a693798f0532f88bab0ae591d5ff82 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Thu, 21 May 2020 15:34:48 +0200 Subject: Relax fs layout so that multiple pass/fail manifests are possible --- doc/adding_lints.md | 11 ++++++++--- tests/compile-test.rs | 10 +++++++--- 2 files changed, 15 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/doc/adding_lints.md b/doc/adding_lints.md index 75768681db9..b3f5a62d553 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -43,7 +43,7 @@ case), and we don't need type information so it will have an early pass type (category will default to nursery if not provided). This command will create two files: `tests/ui/foo_functions.rs` and `clippy_lints/src/foo_functions.rs`, as well as run `cargo dev update_lints` to register the new lint. For cargo lints, -two project hierarchies (fail/pass) will be created under `tests/ui-cargo`. +two project hierarchies (fail/pass) will be created by default under `tests/ui-cargo`. Next, we'll open up these files and add our lint! @@ -110,12 +110,17 @@ specific lint you are creating/editing. ### Cargo lints For cargo lints, the process of testing differs in that we are interested in -the contents of the `Cargo.toml` files. If our new lint is named e.g. `foo_categories`, -after running `cargo dev new_lint` we will find two new manifest files: +the `Cargo.toml` manifest file. We also need a minimal crate associated +with that manifest. + +If our new lint is named e.g. `foo_categories`, after running `cargo dev new_lint` +we will find by default two new crates, each with its manifest file: * `tests/ui-cargo/foo_categories/fail/Cargo.toml`: this file should cause the new lint to raise an error. * `tests/ui-cargo/foo_categories/pass/Cargo.toml`: this file should not trigger the lint. +If you need more cases, you can copy one of those crates (under `foo_categories`) and rename it. + The process of generating the `.stderr` file is the same, and prepending the `TESTNAME` variable to `cargo uitest` works too, but the script to update the references is in another path: `tests/ui-cargo/update-all-references.sh`. diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 232b966f69a..a5de8429390 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -174,9 +174,13 @@ fn run_ui_cargo(config: &mut compiletest::Config) { _ => {}, } - for case in &["pass", "fail"] { - let tail: PathBuf = [case, "src"].iter().collect(); - let src_path = dir_path.join(tail); + for case in fs::read_dir(&dir_path)? { + let case = case?; + if !case.file_type()?.is_dir() { + continue; + } + + let src_path = case.path().join("src"); env::set_current_dir(&src_path)?; for file in fs::read_dir(&src_path)? { -- cgit 1.4.1-3-g733a5 From a578bed69ac2a9b33fcb871f9ad7dbf02355cb82 Mon Sep 17 00:00:00 2001 From: Andy Russell Date: Mon, 18 May 2020 18:35:49 -0400 Subject: new_without_default: do not suggest deriving --- clippy_lints/src/new_without_default.rs | 118 +++++++------------------------- tests/ui/new_without_default.rs | 11 +++ tests/ui/new_without_default.stderr | 35 ++++++++-- 3 files changed, 64 insertions(+), 100 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index a599667b8d8..3b88e4c4cb1 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -1,27 +1,20 @@ use crate::utils::paths; use crate::utils::sugg::DiagnosticBuilderExt; -use crate::utils::{get_trait_def_id, implements_trait, return_ty, same_tys, span_lint_hir_and_then}; +use crate::utils::{get_trait_def_id, return_ty, same_tys, span_lint_hir_and_then}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::def_id::DefId; use rustc_hir::HirIdSet; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::Ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::source_map::Span; declare_clippy_lint! { /// **What it does:** Checks for types with a `fn new() -> Self` method and no /// implementation of /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html). /// - /// It detects both the case when a manual - /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) - /// implementation is required and also when it can be created with - /// `#[derive(Default)]` - /// /// **Why is this bad?** The user might expect to be able to use /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) as the /// type can be constructed without arguments. @@ -40,46 +33,17 @@ declare_clippy_lint! { /// } /// ``` /// - /// Instead, use: + /// To fix the lint, and a `Default` implementation that delegates to `new`: /// /// ```ignore /// struct Foo(Bar); /// /// impl Default for Foo { /// fn default() -> Self { - /// Foo(Bar::new()) + /// Foo::new() /// } /// } /// ``` - /// - /// Or, if - /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) - /// can be derived by `#[derive(Default)]`: - /// - /// ```rust - /// struct Foo; - /// - /// impl Foo { - /// fn new() -> Self { - /// Foo - /// } - /// } - /// ``` - /// - /// Instead, use: - /// - /// ```rust - /// #[derive(Default)] - /// struct Foo; - /// - /// impl Foo { - /// fn new() -> Self { - /// Foo - /// } - /// } - /// ``` - /// - /// You can also have `new()` call `Default::default()`. pub NEW_WITHOUT_DEFAULT, style, "`fn new() -> Self` method without `Default` implementation" @@ -158,46 +122,25 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NewWithoutDefault { } } - if let Some(sp) = can_derive_default(self_ty, cx, default_trait_id) { - span_lint_hir_and_then( - cx, - NEW_WITHOUT_DEFAULT, - id, - impl_item.span, - &format!( - "you should consider deriving a `Default` implementation for `{}`", - self_ty - ), - |diag| { - diag.suggest_item_with_attr( - cx, - sp, - "try this", - "#[derive(Default)]", - Applicability::MaybeIncorrect, - ); - }); - } else { - span_lint_hir_and_then( - cx, - NEW_WITHOUT_DEFAULT, - id, - impl_item.span, - &format!( - "you should consider adding a `Default` implementation for `{}`", - self_ty - ), - |diag| { - diag.suggest_prepend_item( - cx, - item.span, - "try this", - &create_new_without_default_suggest_msg(self_ty), - Applicability::MaybeIncorrect, - ); - }, - ); - } + span_lint_hir_and_then( + cx, + NEW_WITHOUT_DEFAULT, + id, + impl_item.span, + &format!( + "you should consider adding a `Default` implementation for `{}`", + self_ty + ), + |diag| { + diag.suggest_prepend_item( + cx, + item.span, + "try this", + &create_new_without_default_suggest_msg(self_ty), + Applicability::MaybeIncorrect, + ); + }, + ); } } } @@ -217,18 +160,3 @@ fn create_new_without_default_suggest_msg(ty: Ty<'_>) -> String { }} }}", ty) } - -fn can_derive_default<'t, 'c>(ty: Ty<'t>, cx: &LateContext<'c, 't>, default_trait_id: DefId) -> Option { - match ty.kind { - ty::Adt(adt_def, substs) if adt_def.is_struct() => { - for field in adt_def.all_fields() { - let f_ty = field.ty(cx.tcx, substs); - if !implements_trait(cx, f_ty, default_trait_id, &[]) { - return None; - } - } - Some(cx.tcx.def_span(adt_def.did)) - }, - _ => None, - } -} diff --git a/tests/ui/new_without_default.rs b/tests/ui/new_without_default.rs index 781ea7bb152..3b6041823d8 100644 --- a/tests/ui/new_without_default.rs +++ b/tests/ui/new_without_default.rs @@ -148,4 +148,15 @@ impl AllowDerive { } } +pub struct NewNotEqualToDerive { + foo: i32, +} + +impl NewNotEqualToDerive { + // This `new` implementation is not equal to a derived `Default`, so do not suggest deriving. + pub fn new() -> Self { + NewNotEqualToDerive { foo: 1 } + } +} + fn main() {} diff --git a/tests/ui/new_without_default.stderr b/tests/ui/new_without_default.stderr index 5e485d40663..e529e441eb7 100644 --- a/tests/ui/new_without_default.stderr +++ b/tests/ui/new_without_default.stderr @@ -1,4 +1,4 @@ -error: you should consider deriving a `Default` implementation for `Foo` +error: you should consider adding a `Default` implementation for `Foo` --> $DIR/new_without_default.rs:8:5 | LL | / pub fn new() -> Foo { @@ -9,10 +9,14 @@ LL | | } = note: `-D clippy::new-without-default` implied by `-D warnings` help: try this | -LL | #[derive(Default)] +LL | impl Default for Foo { +LL | fn default() -> Self { +LL | Self::new() +LL | } +LL | } | -error: you should consider deriving a `Default` implementation for `Bar` +error: you should consider adding a `Default` implementation for `Bar` --> $DIR/new_without_default.rs:16:5 | LL | / pub fn new() -> Self { @@ -22,7 +26,11 @@ LL | | } | help: try this | -LL | #[derive(Default)] +LL | impl Default for Bar { +LL | fn default() -> Self { +LL | Self::new() +LL | } +LL | } | error: you should consider adding a `Default` implementation for `LtKo<'c>` @@ -42,5 +50,22 @@ LL | } LL | } | -error: aborting due to 3 previous errors +error: you should consider adding a `Default` implementation for `NewNotEqualToDerive` + --> $DIR/new_without_default.rs:157:5 + | +LL | / pub fn new() -> Self { +LL | | NewNotEqualToDerive { foo: 1 } +LL | | } + | |_____^ + | +help: try this + | +LL | impl Default for NewNotEqualToDerive { +LL | fn default() -> Self { +LL | Self::new() +LL | } +LL | } + | + +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From 29d043683e6f70b22ae34596b4cb9ae07274c28b Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Fri, 22 May 2020 19:09:24 +0200 Subject: option_option test case #4298 --- tests/ui/option_option.rs | 25 +++++++++++++++++++++++++ tests/ui/option_option.stderr | 8 +++++++- 2 files changed, 32 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/ui/option_option.rs b/tests/ui/option_option.rs index 904c50e1403..a2617a13eca 100644 --- a/tests/ui/option_option.rs +++ b/tests/ui/option_option.rs @@ -60,3 +60,28 @@ fn main() { // The lint allows this let expr = Some(Some(true)); } + +extern crate serde; +mod issue_4298 { + use serde::{Deserialize, Deserializer, Serialize}; + use std::borrow::Cow; + + #[derive(Serialize, Deserialize)] + struct Foo<'a> { + #[serde(deserialize_with = "func")] + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(default)] + #[serde(borrow)] + // FIXME: should not lint here + #[allow(clippy::option_option)] + foo: Option>>, + } + + #[allow(clippy::option_option)] + fn func<'a, D>(_: D) -> Result>>, D::Error> + where + D: Deserializer<'a>, + { + Ok(Some(Some(Cow::Borrowed("hi")))) + } +} diff --git a/tests/ui/option_option.stderr b/tests/ui/option_option.stderr index 79db186d7ea..0cd4c96eb4f 100644 --- a/tests/ui/option_option.stderr +++ b/tests/ui/option_option.stderr @@ -58,5 +58,11 @@ error: consider using `Option` instead of `Option>` or a custom enu LL | Struct { x: Option> }, | ^^^^^^^^^^^^^^^^^^ -error: aborting due to 9 previous errors +error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases + --> $DIR/option_option.rs:77:14 + | +LL | foo: Option>>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 10 previous errors -- cgit 1.4.1-3-g733a5 From cff5cff2f3a6687dfaf12b92762e70545e0abefe Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 22 May 2020 22:30:28 +0200 Subject: Make the name of the crate available in cargo UI tests --- clippy_dev/src/new_lint.rs | 17 ++++++++++++----- tests/ui-cargo/cargo_common_metadata/fail/src/main.rs | 1 + tests/ui-cargo/cargo_common_metadata/pass/src/main.rs | 1 + tests/ui-cargo/multiple_crate_versions/fail/src/main.rs | 1 + tests/ui-cargo/multiple_crate_versions/pass/src/main.rs | 1 + tests/ui-cargo/wildcard_dependencies/fail/src/main.rs | 1 + tests/ui-cargo/wildcard_dependencies/pass/src/main.rs | 1 + 7 files changed, 18 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 08a2e0c0918..c0b2dac2f60 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -76,7 +76,8 @@ fn create_test(lint: &LintData) -> io::Result<()> { path.push("src"); fs::create_dir(&path)?; - write_file(path.join("main.rs"), get_test_file_contents(lint_name))?; + let header = format!("// compile-flags: --crate-name={}", lint_name); + write_file(path.join("main.rs"), get_test_file_contents(lint_name, Some(&header)))?; Ok(()) } @@ -90,7 +91,7 @@ fn create_test(lint: &LintData) -> io::Result<()> { create_project_layout(lint.name, &test_dir, "pass", "This file should not trigger the lint") } else { let test_path = format!("tests/ui/{}.rs", lint.name); - let test_contents = get_test_file_contents(lint.name); + let test_contents = get_test_file_contents(lint.name, None); write_file(lint.project_root.join(test_path), test_contents) } } @@ -119,8 +120,8 @@ fn to_camel_case(name: &str) -> String { .collect() } -fn get_test_file_contents(lint_name: &str) -> String { - format!( +fn get_test_file_contents(lint_name: &str, header_commands: Option<&str>) -> String { + let mut contents = format!( "#![warn(clippy::{})] fn main() {{ @@ -128,7 +129,13 @@ fn main() {{ }} ", lint_name - ) + ); + + if let Some(header) = header_commands { + contents = format!("{}\n{}", header, contents); + } + + contents } fn get_manifest_contents(lint_name: &str, hint: &str) -> String { diff --git a/tests/ui-cargo/cargo_common_metadata/fail/src/main.rs b/tests/ui-cargo/cargo_common_metadata/fail/src/main.rs index c67166fc4b0..27841e18aa9 100644 --- a/tests/ui-cargo/cargo_common_metadata/fail/src/main.rs +++ b/tests/ui-cargo/cargo_common_metadata/fail/src/main.rs @@ -1,3 +1,4 @@ +// compile-flags: --crate-name=cargo_common_metadata #![warn(clippy::cargo_common_metadata)] fn main() {} diff --git a/tests/ui-cargo/cargo_common_metadata/pass/src/main.rs b/tests/ui-cargo/cargo_common_metadata/pass/src/main.rs index c67166fc4b0..27841e18aa9 100644 --- a/tests/ui-cargo/cargo_common_metadata/pass/src/main.rs +++ b/tests/ui-cargo/cargo_common_metadata/pass/src/main.rs @@ -1,3 +1,4 @@ +// compile-flags: --crate-name=cargo_common_metadata #![warn(clippy::cargo_common_metadata)] fn main() {} diff --git a/tests/ui-cargo/multiple_crate_versions/fail/src/main.rs b/tests/ui-cargo/multiple_crate_versions/fail/src/main.rs index 4bc61dd6299..1b2d3ec9459 100644 --- a/tests/ui-cargo/multiple_crate_versions/fail/src/main.rs +++ b/tests/ui-cargo/multiple_crate_versions/fail/src/main.rs @@ -1,3 +1,4 @@ +// compile-flags: --crate-name=multiple_crate_versions #![warn(clippy::multiple_crate_versions)] fn main() {} diff --git a/tests/ui-cargo/multiple_crate_versions/pass/src/main.rs b/tests/ui-cargo/multiple_crate_versions/pass/src/main.rs index 4bc61dd6299..1b2d3ec9459 100644 --- a/tests/ui-cargo/multiple_crate_versions/pass/src/main.rs +++ b/tests/ui-cargo/multiple_crate_versions/pass/src/main.rs @@ -1,3 +1,4 @@ +// compile-flags: --crate-name=multiple_crate_versions #![warn(clippy::multiple_crate_versions)] fn main() {} diff --git a/tests/ui-cargo/wildcard_dependencies/fail/src/main.rs b/tests/ui-cargo/wildcard_dependencies/fail/src/main.rs index 3491ccb0d47..581babfeacb 100644 --- a/tests/ui-cargo/wildcard_dependencies/fail/src/main.rs +++ b/tests/ui-cargo/wildcard_dependencies/fail/src/main.rs @@ -1,3 +1,4 @@ +// compile-flags: --crate-name=wildcard_dependencies #![warn(clippy::wildcard_dependencies)] fn main() {} diff --git a/tests/ui-cargo/wildcard_dependencies/pass/src/main.rs b/tests/ui-cargo/wildcard_dependencies/pass/src/main.rs index 3491ccb0d47..581babfeacb 100644 --- a/tests/ui-cargo/wildcard_dependencies/pass/src/main.rs +++ b/tests/ui-cargo/wildcard_dependencies/pass/src/main.rs @@ -1,3 +1,4 @@ +// compile-flags: --crate-name=wildcard_dependencies #![warn(clippy::wildcard_dependencies)] fn main() {} -- cgit 1.4.1-3-g733a5 From 8642fc97dd1a9b4f0291726c47ec97d15599d74d Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 22 May 2020 22:39:19 +0200 Subject: multiple_crate_versions: skip dev and build deps --- clippy_lints/src/multiple_crate_versions.rs | 59 +++++++++++++++++----- .../5041_allow_dev_build/Cargo.toml | 17 +++++++ .../5041_allow_dev_build/src/main.rs | 4 ++ 3 files changed, 66 insertions(+), 14 deletions(-) create mode 100644 tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/Cargo.toml create mode 100644 tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/src/main.rs (limited to 'tests') diff --git a/clippy_lints/src/multiple_crate_versions.rs b/clippy_lints/src/multiple_crate_versions.rs index c4decfc9401..b24ec897ef5 100644 --- a/clippy_lints/src/multiple_crate_versions.rs +++ b/clippy_lints/src/multiple_crate_versions.rs @@ -1,11 +1,14 @@ //! lint on multiple versions of a crate being used use crate::utils::{run_lints, span_lint}; +use rustc_hir::def_id::LOCAL_CRATE; use rustc_hir::{Crate, CRATE_HIR_ID}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::DUMMY_SP; +use cargo_metadata::{DependencyKind, MetadataCommand, Node, Package, PackageId}; +use if_chain::if_chain; use itertools::Itertools; declare_clippy_lint! { @@ -34,37 +37,65 @@ declare_clippy_lint! { declare_lint_pass!(MultipleCrateVersions => [MULTIPLE_CRATE_VERSIONS]); impl LateLintPass<'_, '_> for MultipleCrateVersions { + #[allow(clippy::find_map)] fn check_crate(&mut self, cx: &LateContext<'_, '_>, _: &Crate<'_>) { if !run_lints(cx, &[MULTIPLE_CRATE_VERSIONS], CRATE_HIR_ID) { return; } - let metadata = if let Ok(metadata) = cargo_metadata::MetadataCommand::new().exec() { + let metadata = if let Ok(metadata) = MetadataCommand::new().exec() { metadata } else { span_lint(cx, MULTIPLE_CRATE_VERSIONS, DUMMY_SP, "could not read cargo metadata"); - return; }; + let local_name = cx.tcx.crate_name(LOCAL_CRATE).as_str(); let mut packages = metadata.packages; packages.sort_by(|a, b| a.name.cmp(&b.name)); - for (name, group) in &packages.into_iter().group_by(|p| p.name.clone()) { - let group: Vec = group.collect(); + if_chain! { + if let Some(resolve) = &metadata.resolve; + if let Some(local_id) = packages.iter().find(|p| p.name == *local_name).map(|p| &p.id); + then { + for (name, group) in &packages.iter().group_by(|p| p.name.clone()) { + let group: Vec<&Package> = group.collect(); + + if group.len() <= 1 { + continue; + } - if group.len() > 1 { - let mut versions: Vec<_> = group.into_iter().map(|p| p.version).collect(); - versions.sort(); - let versions = versions.iter().join(", "); + if group.iter().all(|p| is_normal_dep(&resolve.nodes, local_id, &p.id)) { + let mut versions: Vec<_> = group.into_iter().map(|p| &p.version).collect(); + versions.sort(); + let versions = versions.iter().join(", "); - span_lint( - cx, - MULTIPLE_CRATE_VERSIONS, - DUMMY_SP, - &format!("multiple versions for dependency `{}`: {}", name, versions), - ); + span_lint( + cx, + MULTIPLE_CRATE_VERSIONS, + DUMMY_SP, + &format!("multiple versions for dependency `{}`: {}", name, versions), + ); + } + } } } } } + +fn is_normal_dep(nodes: &[Node], local_id: &PackageId, dep_id: &PackageId) -> bool { + fn depends_on(node: &Node, dep_id: &PackageId) -> bool { + node.deps.iter().any(|dep| { + dep.pkg == *dep_id + && dep + .dep_kinds + .iter() + .any(|info| matches!(info.kind, DependencyKind::Normal)) + }) + } + + nodes + .iter() + .filter(|node| depends_on(node, dep_id)) + .any(|node| node.id == *local_id || is_normal_dep(nodes, local_id, &node.id)) +} diff --git a/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/Cargo.toml b/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/Cargo.toml new file mode 100644 index 00000000000..72731fbc75d --- /dev/null +++ b/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/Cargo.toml @@ -0,0 +1,17 @@ +# Should not lint for dev or build dependencies. See issue 5041. + +[package] +name = "multiple_crate_versions" +version = "0.1.0" +publish = false + +# One of the versions of winapi is only a dev dependency: allowed +[dependencies] +ctrlc = "=3.1.0" +[dev-dependencies] +ansi_term = "=0.11.0" + +# Both versions of winapi are a build dependency: allowed +[build-dependencies] +ctrlc = "=3.1.0" +ansi_term = "=0.11.0" diff --git a/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/src/main.rs b/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/src/main.rs new file mode 100644 index 00000000000..1b2d3ec9459 --- /dev/null +++ b/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/src/main.rs @@ -0,0 +1,4 @@ +// compile-flags: --crate-name=multiple_crate_versions +#![warn(clippy::multiple_crate_versions)] + +fn main() {} -- cgit 1.4.1-3-g733a5 From 4f8909fad986dda68a9dcd172eaa362b6fce105b Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sat, 9 May 2020 21:28:31 +0200 Subject: Extend `useless_conversion` lint with TryFrom --- clippy_lints/src/useless_conversion.rs | 47 ++++++++++++++++++++++++++-------- clippy_lints/src/utils/paths.rs | 1 + tests/ui/useless_conversion.stderr | 20 +++++++-------- tests/ui/useless_conversion_try.rs | 25 ++++++++++++++++++ tests/ui/useless_conversion_try.stderr | 39 ++++++++++++++++++++++++++++ 5 files changed, 112 insertions(+), 20 deletions(-) create mode 100644 tests/ui/useless_conversion_try.rs create mode 100644 tests/ui/useless_conversion_try.stderr (limited to 'tests') diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 95921518986..0b080d9be2c 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,13 +1,16 @@ use crate::utils::{ - match_def_path, match_trait_method, paths, same_tys, snippet, snippet_with_macro_callsite, span_lint_and_sugg, + is_type_diagnostic_item, match_def_path, match_trait_method, paths, same_tys, snippet, snippet_with_macro_callsite, + span_lint_and_help, span_lint_and_sugg, }; +use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, HirId, MatchSource}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_clippy_lint! { - /// **What it does:** Checks for `Into`/`From`/`IntoIter` calls that useless converts + /// **What it does:** Checks for `Into`, `From`, `TryFrom`,`IntoIter` calls that useless converts /// to the same type as caller. /// /// **Why is this bad?** Redundant code. @@ -26,7 +29,7 @@ declare_clippy_lint! { /// ``` pub USELESS_CONVERSION, complexity, - "calls to `Into`/`From`/`IntoIter` that performs useless conversions to the same type" + "calls to `Into`, `From`, `TryFrom`, `IntoIter` that performs useless conversions to the same type" } #[derive(Default)] @@ -68,7 +71,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - "useless conversion", + "Useless conversion to the same type", "consider removing `.into()`", sugg, Applicability::MachineApplicable, // snippet @@ -84,7 +87,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - "useless conversion", + "Useless conversion to the same type", "consider removing `.into_iter()`", sugg, Applicability::MachineApplicable, // snippet @@ -94,11 +97,35 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { }, ExprKind::Call(ref path, ref args) => { - if let ExprKind::Path(ref qpath) = path.kind { - if let Some(def_id) = cx.tables.qpath_res(qpath, path.hir_id).opt_def_id() { + if_chain! { + if args.len() == 1; + if let ExprKind::Path(ref qpath) = path.kind; + if let Some(def_id) = cx.tables.qpath_res(qpath, path.hir_id).opt_def_id(); + let a = cx.tables.expr_ty(e); + let b = cx.tables.expr_ty(&args[0]); + + then { + if_chain! { + if match_def_path(cx, def_id, &paths::TRY_FROM); + if is_type_diagnostic_item(cx, a, sym!(result_type)); + if let ty::Adt(_, substs) = a.kind; + if let Some(a_type) = substs.types().nth(0); + if same_tys(cx, a_type, b); + + then { + let hint = format!("consider removing `{}()`", snippet(cx, path.span, "TryFrom::try_from")); + span_lint_and_help( + cx, + USELESS_CONVERSION, + e.span, + "Useless conversion to the same type", + None, + &hint, + ); + } + } + if match_def_path(cx, def_id, &paths::FROM_FROM) { - let a = cx.tables.expr_ty(e); - let b = cx.tables.expr_ty(&args[0]); if same_tys(cx, a, b) { let sugg = snippet(cx, args[0].span.source_callsite(), "").into_owned(); let sugg_msg = @@ -107,7 +134,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - "useless conversion", + "Useless conversion to the same type", &sugg_msg, sugg, Applicability::MachineApplicable, // snippet diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index b3ad2ad9d99..e00d726282a 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -128,6 +128,7 @@ pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned" pub const TO_STRING: [&str; 3] = ["alloc", "string", "ToString"]; pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"]; pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"]; +pub const TRY_FROM: [&str; 4] = ["core", "convert", "TryFrom", "try_from"]; pub const TRY_FROM_ERROR: [&str; 4] = ["std", "ops", "Try", "from_error"]; pub const TRY_INTO_RESULT: [&str; 4] = ["std", "ops", "Try", "into_result"]; pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"]; diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index 7df3507edfd..0b2947f7d62 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -1,4 +1,4 @@ -error: useless conversion +error: Useless conversion to the same type --> $DIR/useless_conversion.rs:6:13 | LL | let _ = T::from(val); @@ -10,55 +10,55 @@ note: the lint level is defined here LL | #![deny(clippy::useless_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: useless conversion +error: Useless conversion to the same type --> $DIR/useless_conversion.rs:7:5 | LL | val.into() | ^^^^^^^^^^ help: consider removing `.into()`: `val` -error: useless conversion +error: Useless conversion to the same type --> $DIR/useless_conversion.rs:19:22 | LL | let _: i32 = 0i32.into(); | ^^^^^^^^^^^ help: consider removing `.into()`: `0i32` -error: useless conversion +error: Useless conversion to the same type --> $DIR/useless_conversion.rs:51:21 | LL | let _: String = "foo".to_string().into(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `"foo".to_string()` -error: useless conversion +error: Useless conversion to the same type --> $DIR/useless_conversion.rs:52:21 | LL | let _: String = From::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `From::from()`: `"foo".to_string()` -error: useless conversion +error: Useless conversion to the same type --> $DIR/useless_conversion.rs:53:13 | LL | let _ = String::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `"foo".to_string()` -error: useless conversion +error: Useless conversion to the same type --> $DIR/useless_conversion.rs:54:13 | LL | let _ = String::from(format!("A: {:04}", 123)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `format!("A: {:04}", 123)` -error: useless conversion +error: Useless conversion to the same type --> $DIR/useless_conversion.rs:55:13 | LL | let _ = "".lines().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()` -error: useless conversion +error: Useless conversion to the same type --> $DIR/useless_conversion.rs:56:13 | LL | let _ = vec![1, 2, 3].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![1, 2, 3].into_iter()` -error: useless conversion +error: Useless conversion to the same type --> $DIR/useless_conversion.rs:57:21 | LL | let _: String = format!("Hello {}", "world").into(); diff --git a/tests/ui/useless_conversion_try.rs b/tests/ui/useless_conversion_try.rs new file mode 100644 index 00000000000..abf0c891b52 --- /dev/null +++ b/tests/ui/useless_conversion_try.rs @@ -0,0 +1,25 @@ +#![deny(clippy::useless_conversion)] + +use std::convert::TryFrom; + +fn test_generic(val: T) -> T { + T::try_from(val).unwrap() +} + +fn test_generic2 + Into, U: From>(val: T) { + let _ = U::try_from(val).unwrap(); +} + +fn main() { + test_generic(10i32); + test_generic2::(10i32); + + let _: String = TryFrom::try_from("foo").unwrap(); + let _ = String::try_from("foo").unwrap(); + #[allow(clippy::useless_conversion)] + let _ = String::try_from("foo").unwrap(); + + let _: String = TryFrom::try_from("foo".to_string()).unwrap(); + let _ = String::try_from("foo".to_string()).unwrap(); + let _ = String::try_from(format!("A: {:04}", 123)).unwrap(); +} diff --git a/tests/ui/useless_conversion_try.stderr b/tests/ui/useless_conversion_try.stderr new file mode 100644 index 00000000000..b3cb01fbe32 --- /dev/null +++ b/tests/ui/useless_conversion_try.stderr @@ -0,0 +1,39 @@ +error: Useless conversion to the same type + --> $DIR/useless_conversion_try.rs:6:5 + | +LL | T::try_from(val).unwrap() + | ^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/useless_conversion_try.rs:1:9 + | +LL | #![deny(clippy::useless_conversion)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: consider removing `T::try_from()` + +error: Useless conversion to the same type + --> $DIR/useless_conversion_try.rs:22:21 + | +LL | let _: String = TryFrom::try_from("foo".to_string()).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider removing `TryFrom::try_from()` + +error: Useless conversion to the same type + --> $DIR/useless_conversion_try.rs:23:13 + | +LL | let _ = String::try_from("foo".to_string()).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider removing `String::try_from()` + +error: Useless conversion to the same type + --> $DIR/useless_conversion_try.rs:24:13 + | +LL | let _ = String::try_from(format!("A: {:04}", 123)).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider removing `String::try_from()` + +error: aborting due to 4 previous errors + -- cgit 1.4.1-3-g733a5 From 705bfdcc467c0ddd7eb61d3adb24809b27bae891 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Fri, 22 May 2020 11:46:17 +0200 Subject: Extend `useless_conversion` lint with TryInto --- clippy_lints/src/useless_conversion.rs | 38 +++++++++++++++++++++++++++------- clippy_lints/src/utils/paths.rs | 1 + src/lintlist/mod.rs | 2 +- tests/ui/useless_conversion_try.rs | 17 +++++++++++---- tests/ui/useless_conversion_try.stderr | 38 +++++++++++++++++++++++++++------- 5 files changed, 77 insertions(+), 19 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 0b080d9be2c..1645c5777b2 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -10,8 +10,8 @@ use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_clippy_lint! { - /// **What it does:** Checks for `Into`, `From`, `TryFrom`,`IntoIter` calls that useless converts - /// to the same type as caller. + /// **What it does:** Checks for `Into`, `TryInto`, `From`, `TryFrom`,`IntoIter` calls + /// that useless converts to the same type as caller. /// /// **Why is this bad?** Redundant code. /// @@ -29,7 +29,7 @@ declare_clippy_lint! { /// ``` pub USELESS_CONVERSION, complexity, - "calls to `Into`, `From`, `TryFrom`, `IntoIter` that performs useless conversions to the same type" + "calls to `Into`, `TryInto`, `From`, `TryFrom`, `IntoIter` that performs useless conversions to the same type" } #[derive(Default)] @@ -39,6 +39,7 @@ pub struct UselessConversion { impl_lint_pass!(UselessConversion => [USELESS_CONVERSION]); +#[allow(clippy::too_many_lines)] impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr<'_>) { if e.span.from_expansion() { @@ -66,7 +67,6 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { let b = cx.tables.expr_ty(&args[0]); if same_tys(cx, a, b) { let sugg = snippet_with_macro_callsite(cx, args[0].span, "").to_string(); - span_lint_and_sugg( cx, USELESS_CONVERSION, @@ -94,6 +94,27 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { ); } } + if match_trait_method(cx, e, &paths::TRY_INTO_TRAIT) && &*name.ident.as_str() == "try_into" { + if_chain! { + let a = cx.tables.expr_ty(e); + let b = cx.tables.expr_ty(&args[0]); + if is_type_diagnostic_item(cx, a, sym!(result_type)); + if let ty::Adt(_, substs) = a.kind; + if let Some(a_type) = substs.types().next(); + if same_tys(cx, a_type, b); + + then { + span_lint_and_help( + cx, + USELESS_CONVERSION, + e.span, + "Useless conversion to the same type", + None, + "consider removing `.try_into()`", + ); + } + } + } }, ExprKind::Call(ref path, ref args) => { @@ -109,7 +130,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { if match_def_path(cx, def_id, &paths::TRY_FROM); if is_type_diagnostic_item(cx, a, sym!(result_type)); if let ty::Adt(_, substs) = a.kind; - if let Some(a_type) = substs.types().nth(0); + if let Some(a_type) = substs.types().next(); if same_tys(cx, a_type, b); then { @@ -125,8 +146,11 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { } } - if match_def_path(cx, def_id, &paths::FROM_FROM) { - if same_tys(cx, a, b) { + if_chain! { + if match_def_path(cx, def_id, &paths::FROM_FROM); + if same_tys(cx, a, b); + + then { let sugg = snippet(cx, args[0].span.source_callsite(), "").into_owned(); let sugg_msg = format!("consider removing `{}()`", snippet(cx, path.span, "From::from")); diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index e00d726282a..779da7e6bf2 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -131,6 +131,7 @@ pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"]; pub const TRY_FROM: [&str; 4] = ["core", "convert", "TryFrom", "try_from"]; pub const TRY_FROM_ERROR: [&str; 4] = ["std", "ops", "Try", "from_error"]; pub const TRY_INTO_RESULT: [&str; 4] = ["std", "ops", "Try", "into_result"]; +pub const TRY_INTO_TRAIT: [&str; 3] = ["core", "convert", "TryInto"]; pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"]; pub const VEC_AS_MUT_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_mut_slice"]; pub const VEC_AS_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_slice"]; diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 8211a57b564..f63301c7db0 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2421,7 +2421,7 @@ pub static ref ALL_LINTS: Vec = vec![ Lint { name: "useless_conversion", group: "complexity", - desc: "calls to `Into`/`From`/`IntoIter` that performs useless conversions to the same type", + desc: "calls to `Into`, `TryInto`, `From`, `TryFrom`, `IntoIter` that performs useless conversions to the same type", deprecation: None, module: "useless_conversion", }, diff --git a/tests/ui/useless_conversion_try.rs b/tests/ui/useless_conversion_try.rs index abf0c891b52..ab4f960edb7 100644 --- a/tests/ui/useless_conversion_try.rs +++ b/tests/ui/useless_conversion_try.rs @@ -1,12 +1,16 @@ #![deny(clippy::useless_conversion)] -use std::convert::TryFrom; +use std::convert::{TryFrom, TryInto}; fn test_generic(val: T) -> T { - T::try_from(val).unwrap() + let _ = T::try_from(val).unwrap(); + val.try_into().unwrap() } fn test_generic2 + Into, U: From>(val: T) { + // ok + let _: i32 = val.try_into().unwrap(); + let _: U = val.try_into().unwrap(); let _ = U::try_from(val).unwrap(); } @@ -14,12 +18,17 @@ fn main() { test_generic(10i32); test_generic2::(10i32); + let _: String = "foo".try_into().unwrap(); let _: String = TryFrom::try_from("foo").unwrap(); let _ = String::try_from("foo").unwrap(); #[allow(clippy::useless_conversion)] - let _ = String::try_from("foo").unwrap(); - + { + let _ = String::try_from("foo").unwrap(); + let _: String = "foo".try_into().unwrap(); + } + let _: String = "foo".to_string().try_into().unwrap(); let _: String = TryFrom::try_from("foo".to_string()).unwrap(); let _ = String::try_from("foo".to_string()).unwrap(); let _ = String::try_from(format!("A: {:04}", 123)).unwrap(); + let _: String = format!("Hello {}", "world").try_into().unwrap(); } diff --git a/tests/ui/useless_conversion_try.stderr b/tests/ui/useless_conversion_try.stderr index b3cb01fbe32..5afb5dc45d3 100644 --- a/tests/ui/useless_conversion_try.stderr +++ b/tests/ui/useless_conversion_try.stderr @@ -1,8 +1,8 @@ error: Useless conversion to the same type - --> $DIR/useless_conversion_try.rs:6:5 + --> $DIR/useless_conversion_try.rs:6:13 | -LL | T::try_from(val).unwrap() - | ^^^^^^^^^^^^^^^^ +LL | let _ = T::try_from(val).unwrap(); + | ^^^^^^^^^^^^^^^^ | note: the lint level is defined here --> $DIR/useless_conversion_try.rs:1:9 @@ -12,7 +12,23 @@ LL | #![deny(clippy::useless_conversion)] = help: consider removing `T::try_from()` error: Useless conversion to the same type - --> $DIR/useless_conversion_try.rs:22:21 + --> $DIR/useless_conversion_try.rs:7:5 + | +LL | val.try_into().unwrap() + | ^^^^^^^^^^^^^^ + | + = help: consider removing `.try_into()` + +error: Useless conversion to the same type + --> $DIR/useless_conversion_try.rs:29:21 + | +LL | let _: String = "foo".to_string().try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider removing `.try_into()` + +error: Useless conversion to the same type + --> $DIR/useless_conversion_try.rs:30:21 | LL | let _: String = TryFrom::try_from("foo".to_string()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -20,7 +36,7 @@ LL | let _: String = TryFrom::try_from("foo".to_string()).unwrap(); = help: consider removing `TryFrom::try_from()` error: Useless conversion to the same type - --> $DIR/useless_conversion_try.rs:23:13 + --> $DIR/useless_conversion_try.rs:31:13 | LL | let _ = String::try_from("foo".to_string()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -28,12 +44,20 @@ LL | let _ = String::try_from("foo".to_string()).unwrap(); = help: consider removing `String::try_from()` error: Useless conversion to the same type - --> $DIR/useless_conversion_try.rs:24:13 + --> $DIR/useless_conversion_try.rs:32:13 | LL | let _ = String::try_from(format!("A: {:04}", 123)).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider removing `String::try_from()` -error: aborting due to 4 previous errors +error: Useless conversion to the same type + --> $DIR/useless_conversion_try.rs:33:21 + | +LL | let _: String = format!("Hello {}", "world").try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider removing `.try_into()` + +error: aborting due to 7 previous errors -- cgit 1.4.1-3-g733a5 From 60d38ee1dde4344daa5fdf716eef78b45f483c7e Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sat, 23 May 2020 22:07:03 +0200 Subject: reversed_empty_ranges: add suggestion for &slice[N..N] --- clippy_lints/src/ranges.rs | 30 ++++++++++++++++++------- tests/ui/reversed_empty_ranges_fixable.fixed | 7 +++++- tests/ui/reversed_empty_ranges_fixable.rs | 7 +++++- tests/ui/reversed_empty_ranges_fixable.stderr | 16 ++++++++----- tests/ui/reversed_empty_ranges_unfixable.rs | 1 - tests/ui/reversed_empty_ranges_unfixable.stderr | 10 ++------- 6 files changed, 47 insertions(+), 24 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 83c6faac041..1eb26d97ed4 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -241,14 +241,14 @@ fn check_inclusive_range_minus_one(cx: &LateContext<'_, '_>, expr: &Expr<'_>) { } fn check_reversed_empty_range(cx: &LateContext<'_, '_>, expr: &Expr<'_>) { - fn inside_indexing_expr(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { - matches!( - get_parent_expr(cx, expr), - Some(Expr { + fn inside_indexing_expr<'a>(cx: &'a LateContext<'_, '_>, expr: &Expr<'_>) -> Option<&'a Expr<'a>> { + match get_parent_expr(cx, expr) { + parent_expr @ Some(Expr { kind: ExprKind::Index(..), .. - }) - ) + }) => parent_expr, + _ => None, + } } fn is_empty_range(limits: RangeLimits, ordering: Ordering) -> bool { @@ -267,18 +267,32 @@ fn check_reversed_empty_range(cx: &LateContext<'_, '_>, expr: &Expr<'_>) { if let Some(ordering) = Constant::partial_cmp(cx.tcx, ty, &start_idx, &end_idx); if is_empty_range(limits, ordering); then { - if inside_indexing_expr(cx, expr) { + if let Some(parent_expr) = inside_indexing_expr(cx, expr) { let (reason, outcome) = if ordering == Ordering::Equal { ("empty", "always yield an empty slice") } else { ("reversed", "panic at run-time") }; - span_lint( + span_lint_and_then( cx, REVERSED_EMPTY_RANGES, expr.span, &format!("this range is {} and using it to index a slice will {}", reason, outcome), + |diag| { + if_chain! { + if ordering == Ordering::Equal; + if let ty::Slice(slice_ty) = cx.tables.expr_ty(parent_expr).kind; + then { + diag.span_suggestion( + parent_expr.span, + "if you want an empty slice, use", + format!("[] as &[{}]", slice_ty), + Applicability::MaybeIncorrect + ); + } + } + } ); } else { span_lint_and_then( diff --git a/tests/ui/reversed_empty_ranges_fixable.fixed b/tests/ui/reversed_empty_ranges_fixable.fixed index ee2cbc3cf54..332c0427ef6 100644 --- a/tests/ui/reversed_empty_ranges_fixable.fixed +++ b/tests/ui/reversed_empty_ranges_fixable.fixed @@ -4,18 +4,23 @@ const ANSWER: i32 = 42; fn main() { + let arr = [1, 2, 3, 4, 5]; + + // These should be linted: + (21..=42).rev().for_each(|x| println!("{}", x)); let _ = (21..ANSWER).rev().filter(|x| x % 2 == 0).take(10).collect::>(); for _ in (-42..=-21).rev() {} for _ in (21u32..42u32).rev() {} + let _ = &[] as &[i32]; + // These should be ignored as they are not empty ranges: (21..=42).for_each(|x| println!("{}", x)); (21..42).for_each(|x| println!("{}", x)); - let arr = [1, 2, 3, 4, 5]; let _ = &arr[1..=3]; let _ = &arr[1..3]; diff --git a/tests/ui/reversed_empty_ranges_fixable.rs b/tests/ui/reversed_empty_ranges_fixable.rs index 6ed5ca6daa0..901ec8bcc09 100644 --- a/tests/ui/reversed_empty_ranges_fixable.rs +++ b/tests/ui/reversed_empty_ranges_fixable.rs @@ -4,18 +4,23 @@ const ANSWER: i32 = 42; fn main() { + let arr = [1, 2, 3, 4, 5]; + + // These should be linted: + (42..=21).for_each(|x| println!("{}", x)); let _ = (ANSWER..21).filter(|x| x % 2 == 0).take(10).collect::>(); for _ in -21..=-42 {} for _ in 42u32..21u32 {} + let _ = &arr[3..3]; + // These should be ignored as they are not empty ranges: (21..=42).for_each(|x| println!("{}", x)); (21..42).for_each(|x| println!("{}", x)); - let arr = [1, 2, 3, 4, 5]; let _ = &arr[1..=3]; let _ = &arr[1..3]; diff --git a/tests/ui/reversed_empty_ranges_fixable.stderr b/tests/ui/reversed_empty_ranges_fixable.stderr index 97933b8ff85..9a646fd9939 100644 --- a/tests/ui/reversed_empty_ranges_fixable.stderr +++ b/tests/ui/reversed_empty_ranges_fixable.stderr @@ -1,5 +1,5 @@ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_fixable.rs:7:5 + --> $DIR/reversed_empty_ranges_fixable.rs:11:5 | LL | (42..=21).for_each(|x| println!("{}", x)); | ^^^^^^^^^ @@ -11,7 +11,7 @@ LL | (21..=42).rev().for_each(|x| println!("{}", x)); | ^^^^^^^^^^^^^^^ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_fixable.rs:8:13 + --> $DIR/reversed_empty_ranges_fixable.rs:12:13 | LL | let _ = (ANSWER..21).filter(|x| x % 2 == 0).take(10).collect::>(); | ^^^^^^^^^^^^ @@ -22,7 +22,7 @@ LL | let _ = (21..ANSWER).rev().filter(|x| x % 2 == 0).take(10).collect:: $DIR/reversed_empty_ranges_fixable.rs:10:14 + --> $DIR/reversed_empty_ranges_fixable.rs:14:14 | LL | for _ in -21..=-42 {} | ^^^^^^^^^ @@ -33,7 +33,7 @@ LL | for _ in (-42..=-21).rev() {} | ^^^^^^^^^^^^^^^^^ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_fixable.rs:11:14 + --> $DIR/reversed_empty_ranges_fixable.rs:15:14 | LL | for _ in 42u32..21u32 {} | ^^^^^^^^^^^^ @@ -43,5 +43,11 @@ help: consider using the following if you are attempting to iterate over this ra LL | for _ in (21u32..42u32).rev() {} | ^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 4 previous errors +error: this range is empty and using it to index a slice will always yield an empty slice + --> $DIR/reversed_empty_ranges_fixable.rs:17:18 + | +LL | let _ = &arr[3..3]; + | ----^^^^- help: if you want an empty slice, use: `[] as &[i32]` + +error: aborting due to 5 previous errors diff --git a/tests/ui/reversed_empty_ranges_unfixable.rs b/tests/ui/reversed_empty_ranges_unfixable.rs index c9ca4c47668..561a35625f0 100644 --- a/tests/ui/reversed_empty_ranges_unfixable.rs +++ b/tests/ui/reversed_empty_ranges_unfixable.rs @@ -9,7 +9,6 @@ fn main() { let arr = [1, 2, 3, 4, 5]; let _ = &arr[3usize..=1usize]; let _ = &arr[SOME_NUM..1]; - let _ = &arr[3..3]; for _ in ANSWER..ANSWER {} } diff --git a/tests/ui/reversed_empty_ranges_unfixable.stderr b/tests/ui/reversed_empty_ranges_unfixable.stderr index 12e5483ecdf..240188cbb46 100644 --- a/tests/ui/reversed_empty_ranges_unfixable.stderr +++ b/tests/ui/reversed_empty_ranges_unfixable.stderr @@ -18,17 +18,11 @@ error: this range is reversed and using it to index a slice will panic at run-ti LL | let _ = &arr[SOME_NUM..1]; | ^^^^^^^^^^^ -error: this range is empty and using it to index a slice will always yield an empty slice - --> $DIR/reversed_empty_ranges_unfixable.rs:12:18 - | -LL | let _ = &arr[3..3]; - | ^^^^ - error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_unfixable.rs:14:14 + --> $DIR/reversed_empty_ranges_unfixable.rs:13:14 | LL | for _ in ANSWER..ANSWER {} | ^^^^^^^^^^^^^^ -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From 6bd9cd99a3da53bdda4530dde9f737a843de6c91 Mon Sep 17 00:00:00 2001 From: Jeremy Stucki Date: Wed, 21 Aug 2019 21:18:43 +0200 Subject: Add tests --- tests/ui/or_fun_call.rs | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'tests') diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 7599b945a91..522f31b72d0 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -95,6 +95,15 @@ fn test_or_with_ctors() { let b = "b".to_string(); let _ = Some(Bar("a".to_string(), Duration::from_secs(1))) .or(Some(Bar(b, Duration::from_secs(2)))); + + let vec = vec!["foo"]; + let _ = opt.ok_or(vec.len()); + + let array = ["foo"]; + let _ = opt.ok_or(array.len()); + + let slice = &["foo"][..]; + let _ = opt.ok_or(slice.len()); } // Issue 4514 - early return -- cgit 1.4.1-3-g733a5 From d9f55322cccf1e1ca1b996f8431f7ff8836d5d55 Mon Sep 17 00:00:00 2001 From: Jeremy Stucki Date: Mon, 25 May 2020 21:38:46 +0200 Subject: Update ui test --- tests/ui/or_fun_call.fixed | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'tests') diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 8ea03fe4261..7bb08797ef3 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -95,6 +95,15 @@ fn test_or_with_ctors() { let b = "b".to_string(); let _ = Some(Bar("a".to_string(), Duration::from_secs(1))) .or(Some(Bar(b, Duration::from_secs(2)))); + + let vec = vec!["foo"]; + let _ = opt.ok_or(vec.len()); + + let array = ["foo"]; + let _ = opt.ok_or(array.len()); + + let slice = &["foo"][..]; + let _ = opt.ok_or(slice.len()); } // Issue 4514 - early return -- cgit 1.4.1-3-g733a5 From a1824e187cb6d17e48e2ff039810551540a9b826 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Mon, 25 May 2020 23:09:06 +0200 Subject: ptr_arg: honor `allow` attr on arguments --- clippy_lints/src/ptr.rs | 10 +++++++++- clippy_lints/src/utils/sugg.rs | 2 +- tests/ui/ptr_arg.rs | 32 +++++++++++++++++++++++++++++++- 3 files changed, 41 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 2cdf9671419..4eac571f966 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -2,7 +2,7 @@ use crate::utils::ptr::get_spans; use crate::utils::{ - is_type_diagnostic_item, match_qpath, match_type, paths, snippet_opt, span_lint, span_lint_and_sugg, + is_allowed, is_type_diagnostic_item, match_qpath, match_type, paths, snippet_opt, span_lint, span_lint_and_sugg, span_lint_and_then, walk_ptrs_hir_ty, }; use if_chain::if_chain; @@ -150,8 +150,16 @@ fn check_fn(cx: &LateContext<'_, '_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_ let fn_def_id = cx.tcx.hir().local_def_id(fn_id); let sig = cx.tcx.fn_sig(fn_def_id); let fn_ty = sig.skip_binder(); + let body = opt_body_id.map(|id| cx.tcx.hir().body(id)); for (idx, (arg, ty)) in decl.inputs.iter().zip(fn_ty.inputs()).enumerate() { + // Honor the allow attribute on parameters. See issue 5644. + if let Some(body) = &body { + if is_allowed(cx, PTR_ARG, body.params[idx].hir_id) { + continue; + } + } + if let ty::Ref(_, ty, Mutability::Not) = ty.kind { if is_type_diagnostic_item(cx, ty, sym!(vec_type)) { let mut ty_snippet = None; diff --git a/clippy_lints/src/utils/sugg.rs b/clippy_lints/src/utils/sugg.rs index 4ebe2e2852f..73758b7eeb7 100644 --- a/clippy_lints/src/utils/sugg.rs +++ b/clippy_lints/src/utils/sugg.rs @@ -530,7 +530,7 @@ pub trait DiagnosticBuilderExt<'a, T: LintContext> { /// Suggest to add an item before another. /// - /// The item should not be indented (expect for inner indentation). + /// The item should not be indented (except for inner indentation). /// /// # Example /// diff --git a/tests/ui/ptr_arg.rs b/tests/ui/ptr_arg.rs index 30f39e9b063..541225e6351 100644 --- a/tests/ui/ptr_arg.rs +++ b/tests/ui/ptr_arg.rs @@ -71,7 +71,6 @@ fn false_positive_capacity_too(x: &String) -> String { #[allow(dead_code)] fn test_cow_with_ref(c: &Cow<[i32]>) {} -#[allow(dead_code)] fn test_cow(c: Cow<[i32]>) { let _c = c; } @@ -84,3 +83,34 @@ trait Foo2 { impl Foo2 for String { fn do_string(&self) {} } + +// Check that the allow attribute on parameters is honored +mod issue_5644 { + use std::borrow::Cow; + + fn allowed( + #[allow(clippy::ptr_arg)] _v: &Vec, + #[allow(clippy::ptr_arg)] _s: &String, + #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>, + ) { + } + + struct S {} + impl S { + fn allowed( + #[allow(clippy::ptr_arg)] _v: &Vec, + #[allow(clippy::ptr_arg)] _s: &String, + #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>, + ) { + } + } + + trait T { + fn allowed( + #[allow(clippy::ptr_arg)] _v: &Vec, + #[allow(clippy::ptr_arg)] _s: &String, + #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>, + ) { + } + } +} -- cgit 1.4.1-3-g733a5 From 67167be1679c60eefa2c314c5e4a2b673d5eef11 Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Sun, 17 May 2020 18:14:43 +0200 Subject: Make empty_line_after_outer_attr an early lint --- clippy_lints/Cargo.toml | 4 ++ clippy_lints/src/attrs.rs | 75 ++++++++++++++---------- tests/compile-test.rs | 2 +- tests/ui/auxiliary/proc_macro_attr.rs | 37 ++++++++++++ tests/ui/empty_line_after_outer_attribute.rs | 19 +++++- tests/ui/empty_line_after_outer_attribute.stderr | 12 ++-- 6 files changed, 109 insertions(+), 40 deletions(-) create mode 100644 tests/ui/auxiliary/proc_macro_attr.rs (limited to 'tests') diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 1c0be727834..043a79f2001 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -33,5 +33,9 @@ semver = "0.9.0" # see url = { version = "2.1.0", features = ["serde"] } +[dev-dependencies] +quote = "*" +syn = { version = "*", features = ["full"] } + [features] deny-warnings = [] diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 64abc9fdc71..41f125d4839 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -248,7 +248,6 @@ declare_lint_pass!(Attributes => [ INLINE_ALWAYS, DEPRECATED_SEMVER, USELESS_ATTRIBUTE, - EMPTY_LINE_AFTER_OUTER_ATTR, UNKNOWN_CLIPPY_LINTS, ]); @@ -480,36 +479,6 @@ fn check_attrs(cx: &LateContext<'_, '_>, span: Span, name: Name, attrs: &[Attrib } for attr in attrs { - let attr_item = if let AttrKind::Normal(ref attr) = attr.kind { - attr - } else { - continue; - }; - - if attr.style == AttrStyle::Outer { - if attr_item.args.inner_tokens().is_empty() || !is_present_in_source(cx, attr.span) { - return; - } - - let begin_of_attr_to_item = Span::new(attr.span.lo(), span.lo(), span.ctxt()); - let end_of_attr_to_item = Span::new(attr.span.hi(), span.lo(), span.ctxt()); - - if let Some(snippet) = snippet_opt(cx, end_of_attr_to_item) { - let lines = snippet.split('\n').collect::>(); - let lines = without_block_comments(lines); - - if lines.iter().filter(|l| l.trim().is_empty()).count() > 2 { - span_lint( - cx, - EMPTY_LINE_AFTER_OUTER_ATTR, - begin_of_attr_to_item, - "Found an empty line after an outer attribute. \ - Perhaps you forgot to add a `!` to make it an inner attribute?", - ); - } - } - } - if let Some(values) = attr.meta_item_list() { if values.len() != 1 || !attr.check_name(sym!(inline)) { continue; @@ -551,15 +520,57 @@ fn is_word(nmi: &NestedMetaItem, expected: Symbol) -> bool { } } -declare_lint_pass!(EarlyAttributes => [DEPRECATED_CFG_ATTR, MISMATCHED_TARGET_OS]); +declare_lint_pass!(EarlyAttributes => [ + DEPRECATED_CFG_ATTR, + MISMATCHED_TARGET_OS, + EMPTY_LINE_AFTER_OUTER_ATTR, +]); impl EarlyLintPass for EarlyAttributes { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &rustc_ast::ast::Item) { + check_empty_line_after_outer_attr(cx, item); + } + fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) { check_deprecated_cfg_attr(cx, attr); check_mismatched_target_os(cx, attr); } } +fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::ast::Item) { + for attr in &item.attrs { + let attr_item = if let AttrKind::Normal(ref attr) = attr.kind { + attr + } else { + return; + }; + + if attr.style == AttrStyle::Outer { + if attr_item.args.inner_tokens().is_empty() || !is_present_in_source(cx, attr.span) { + return; + } + + let begin_of_attr_to_item = Span::new(attr.span.lo(), item.span.lo(), item.span.ctxt()); + let end_of_attr_to_item = Span::new(attr.span.hi(), item.span.lo(), item.span.ctxt()); + + if let Some(snippet) = snippet_opt(cx, end_of_attr_to_item) { + let lines = snippet.split('\n').collect::>(); + let lines = without_block_comments(lines); + + if lines.iter().filter(|l| l.trim().is_empty()).count() > 2 { + span_lint( + cx, + EMPTY_LINE_AFTER_OUTER_ATTR, + begin_of_attr_to_item, + "Found an empty line after an outer attribute. \ + Perhaps you forgot to add a `!` to make it an inner attribute?", + ); + } + } + } + } +} + fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute) { if_chain! { // check cfg_attr diff --git a/tests/compile-test.rs b/tests/compile-test.rs index a5de8429390..2758b9a7e76 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -38,7 +38,7 @@ fn clippy_driver_path() -> PathBuf { // as what we manually pass to `cargo` invocation fn third_party_crates() -> String { use std::collections::HashMap; - static CRATES: &[&str] = &["serde", "serde_derive", "regex", "clippy_lints"]; + static CRATES: &[&str] = &["serde", "serde_derive", "regex", "clippy_lints", "syn", "quote"]; let dep_dir = cargo::TARGET_LIB.join("deps"); let mut crates: HashMap<&str, PathBuf> = HashMap::with_capacity(CRATES.len()); for entry in fs::read_dir(dep_dir).unwrap() { diff --git a/tests/ui/auxiliary/proc_macro_attr.rs b/tests/ui/auxiliary/proc_macro_attr.rs new file mode 100644 index 00000000000..e6626d57a77 --- /dev/null +++ b/tests/ui/auxiliary/proc_macro_attr.rs @@ -0,0 +1,37 @@ +// no-prefer-dynamic + +#![crate_type = "proc-macro"] +#![feature(repr128, proc_macro_hygiene, proc_macro_quote)] +#![allow(clippy::useless_conversion)] + +extern crate proc_macro; +extern crate quote; +extern crate syn; + +use proc_macro::TokenStream; +use quote::{quote, quote_spanned}; +use syn::parse_macro_input; +use syn::{parse_quote, ItemTrait, TraitItem}; + +#[proc_macro_attribute] +pub fn fake_async_trait(_args: TokenStream, input: TokenStream) -> TokenStream { + let mut item = parse_macro_input!(input as ItemTrait); + for inner in &mut item.items { + if let TraitItem::Method(method) = inner { + let sig = &method.sig; + let block = &mut method.default; + if let Some(block) = block { + let brace = block.brace_token; + + let my_block = quote_spanned!( brace.span => { + // Should not trigger `empty_line_after_outer_attr` + #[crate_type = "lib"] + #sig #block + Vec::new() + }); + *block = parse_quote!(#my_block); + } + } + } + TokenStream::from(quote!(#item)) +} diff --git a/tests/ui/empty_line_after_outer_attribute.rs b/tests/ui/empty_line_after_outer_attribute.rs index 5343dff9da1..3e92bca986a 100644 --- a/tests/ui/empty_line_after_outer_attribute.rs +++ b/tests/ui/empty_line_after_outer_attribute.rs @@ -1,8 +1,12 @@ +// aux-build:proc_macro_attr.rs #![warn(clippy::empty_line_after_outer_attr)] #![allow(clippy::assertions_on_constants)] #![feature(custom_inner_attributes)] #![rustfmt::skip] +#[macro_use] +extern crate proc_macro_attr; + // This should produce a warning #[crate_type = "lib"] @@ -93,4 +97,17 @@ pub struct S; /* test */ pub struct T; -fn main() { } +// This should not produce a warning +// See https://github.com/rust-lang/rust-clippy/issues/5567 +#[fake_async_trait] +pub trait Bazz { + fn foo() -> Vec { + let _i = ""; + + + + vec![] + } +} + +fn main() {} diff --git a/tests/ui/empty_line_after_outer_attribute.stderr b/tests/ui/empty_line_after_outer_attribute.stderr index d8c9786541f..bf753a732f0 100644 --- a/tests/ui/empty_line_after_outer_attribute.stderr +++ b/tests/ui/empty_line_after_outer_attribute.stderr @@ -1,5 +1,5 @@ error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:7:1 + --> $DIR/empty_line_after_outer_attribute.rs:11:1 | LL | / #[crate_type = "lib"] LL | | @@ -10,7 +10,7 @@ LL | | fn with_one_newline_and_comment() { assert!(true) } = note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings` error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:19:1 + --> $DIR/empty_line_after_outer_attribute.rs:23:1 | LL | / #[crate_type = "lib"] LL | | @@ -18,7 +18,7 @@ LL | | fn with_one_newline() { assert!(true) } | |_ error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:24:1 + --> $DIR/empty_line_after_outer_attribute.rs:28:1 | LL | / #[crate_type = "lib"] LL | | @@ -27,7 +27,7 @@ LL | | fn with_two_newlines() { assert!(true) } | |_ error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:31:1 + --> $DIR/empty_line_after_outer_attribute.rs:35:1 | LL | / #[crate_type = "lib"] LL | | @@ -35,7 +35,7 @@ LL | | enum Baz { | |_ error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:39:1 + --> $DIR/empty_line_after_outer_attribute.rs:43:1 | LL | / #[crate_type = "lib"] LL | | @@ -43,7 +43,7 @@ LL | | struct Foo { | |_ error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:47:1 + --> $DIR/empty_line_after_outer_attribute.rs:51:1 | LL | / #[crate_type = "lib"] LL | | -- cgit 1.4.1-3-g733a5 From 1801841ae554a7778666c4c1085393b32eccf74d Mon Sep 17 00:00:00 2001 From: ThibsG Date: Tue, 26 May 2020 18:40:42 +0200 Subject: Add test cases for broader coverage --- clippy_lints/src/useless_conversion.rs | 10 +++++----- tests/ui/useless_conversion.stderr | 20 ++++++++++---------- tests/ui/useless_conversion_try.rs | 8 ++++++++ tests/ui/useless_conversion_try.stderr | 32 ++++++++++++++++++++++++-------- 4 files changed, 47 insertions(+), 23 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 1645c5777b2..7fa97b24699 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -71,7 +71,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - "Useless conversion to the same type", + "useless conversion to the same type", "consider removing `.into()`", sugg, Applicability::MachineApplicable, // snippet @@ -87,7 +87,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - "Useless conversion to the same type", + "useless conversion to the same type", "consider removing `.into_iter()`", sugg, Applicability::MachineApplicable, // snippet @@ -108,7 +108,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - "Useless conversion to the same type", + "useless conversion to the same type", None, "consider removing `.try_into()`", ); @@ -139,7 +139,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - "Useless conversion to the same type", + "useless conversion to the same type", None, &hint, ); @@ -158,7 +158,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - "Useless conversion to the same type", + "useless conversion to the same type", &sugg_msg, sugg, Applicability::MachineApplicable, // snippet diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index 0b2947f7d62..84ec5370278 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -1,4 +1,4 @@ -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion.rs:6:13 | LL | let _ = T::from(val); @@ -10,55 +10,55 @@ note: the lint level is defined here LL | #![deny(clippy::useless_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion.rs:7:5 | LL | val.into() | ^^^^^^^^^^ help: consider removing `.into()`: `val` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion.rs:19:22 | LL | let _: i32 = 0i32.into(); | ^^^^^^^^^^^ help: consider removing `.into()`: `0i32` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion.rs:51:21 | LL | let _: String = "foo".to_string().into(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `"foo".to_string()` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion.rs:52:21 | LL | let _: String = From::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `From::from()`: `"foo".to_string()` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion.rs:53:13 | LL | let _ = String::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `"foo".to_string()` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion.rs:54:13 | LL | let _ = String::from(format!("A: {:04}", 123)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `format!("A: {:04}", 123)` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion.rs:55:13 | LL | let _ = "".lines().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion.rs:56:13 | LL | let _ = vec![1, 2, 3].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![1, 2, 3].into_iter()` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion.rs:57:21 | LL | let _: String = format!("Hello {}", "world").into(); diff --git a/tests/ui/useless_conversion_try.rs b/tests/ui/useless_conversion_try.rs index ab4f960edb7..3787ea99144 100644 --- a/tests/ui/useless_conversion_try.rs +++ b/tests/ui/useless_conversion_try.rs @@ -31,4 +31,12 @@ fn main() { let _ = String::try_from("foo".to_string()).unwrap(); let _ = String::try_from(format!("A: {:04}", 123)).unwrap(); let _: String = format!("Hello {}", "world").try_into().unwrap(); + let _: String = "".to_owned().try_into().unwrap(); + let _: String = match String::from("_").try_into() { + Ok(a) => a, + Err(_) => "".into(), + }; + // FIXME this is a false negative + #[allow(clippy::cmp_owned)] + if String::from("a") == TryInto::::try_into(String::from("a")).unwrap() {} } diff --git a/tests/ui/useless_conversion_try.stderr b/tests/ui/useless_conversion_try.stderr index 5afb5dc45d3..b765727c168 100644 --- a/tests/ui/useless_conversion_try.stderr +++ b/tests/ui/useless_conversion_try.stderr @@ -1,4 +1,4 @@ -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion_try.rs:6:13 | LL | let _ = T::try_from(val).unwrap(); @@ -11,7 +11,7 @@ LL | #![deny(clippy::useless_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: consider removing `T::try_from()` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion_try.rs:7:5 | LL | val.try_into().unwrap() @@ -19,7 +19,7 @@ LL | val.try_into().unwrap() | = help: consider removing `.try_into()` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion_try.rs:29:21 | LL | let _: String = "foo".to_string().try_into().unwrap(); @@ -27,7 +27,7 @@ LL | let _: String = "foo".to_string().try_into().unwrap(); | = help: consider removing `.try_into()` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion_try.rs:30:21 | LL | let _: String = TryFrom::try_from("foo".to_string()).unwrap(); @@ -35,7 +35,7 @@ LL | let _: String = TryFrom::try_from("foo".to_string()).unwrap(); | = help: consider removing `TryFrom::try_from()` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion_try.rs:31:13 | LL | let _ = String::try_from("foo".to_string()).unwrap(); @@ -43,7 +43,7 @@ LL | let _ = String::try_from("foo".to_string()).unwrap(); | = help: consider removing `String::try_from()` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion_try.rs:32:13 | LL | let _ = String::try_from(format!("A: {:04}", 123)).unwrap(); @@ -51,7 +51,7 @@ LL | let _ = String::try_from(format!("A: {:04}", 123)).unwrap(); | = help: consider removing `String::try_from()` -error: Useless conversion to the same type +error: useless conversion to the same type --> $DIR/useless_conversion_try.rs:33:21 | LL | let _: String = format!("Hello {}", "world").try_into().unwrap(); @@ -59,5 +59,21 @@ LL | let _: String = format!("Hello {}", "world").try_into().unwrap(); | = help: consider removing `.try_into()` -error: aborting due to 7 previous errors +error: useless conversion to the same type + --> $DIR/useless_conversion_try.rs:34:21 + | +LL | let _: String = "".to_owned().try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider removing `.try_into()` + +error: useless conversion to the same type + --> $DIR/useless_conversion_try.rs:35:27 + | +LL | let _: String = match String::from("_").try_into() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider removing `.try_into()` + +error: aborting due to 9 previous errors -- cgit 1.4.1-3-g733a5 From 3089c3b3077fa8ae0b6f68c5f56650bf726e3298 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Wed, 27 May 2020 13:55:57 +0200 Subject: rustup https://github.com/rust-lang/rust/pull/72342, allow unused_crate_dependencies --- tests/ui/cognitive_complexity.rs | 2 +- tests/ui/cognitive_complexity_attr_used.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/tests/ui/cognitive_complexity.rs b/tests/ui/cognitive_complexity.rs index 1d3fe405521..912e6788afd 100644 --- a/tests/ui/cognitive_complexity.rs +++ b/tests/ui/cognitive_complexity.rs @@ -1,6 +1,6 @@ #![allow(clippy::all)] #![warn(clippy::cognitive_complexity)] -#![allow(unused)] +#![allow(unused, unused_crate_dependencies)] #[rustfmt::skip] fn main() { diff --git a/tests/ui/cognitive_complexity_attr_used.rs b/tests/ui/cognitive_complexity_attr_used.rs index 403eff566ed..771a26fc9a8 100644 --- a/tests/ui/cognitive_complexity_attr_used.rs +++ b/tests/ui/cognitive_complexity_attr_used.rs @@ -1,5 +1,5 @@ -#![warn(clippy::cognitive_complexity)] -#![warn(unused)] +#![warn(unused, clippy::cognitive_complexity)] +#![allow(unused_crate_dependencies)] fn main() { kaboom(); -- cgit 1.4.1-3-g733a5 From 64a05f56c33d4754808ef85e634f72a9053c56fd Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Thu, 28 May 2020 00:36:15 +0200 Subject: len_zero: skip ranges if feature `range_is_empty` is not enabled --- clippy_lints/src/len_zero.rs | 17 ++++++++++++++++- tests/ui/len_zero.fixed | 8 ++++++++ tests/ui/len_zero.rs | 8 ++++++++ tests/ui/len_zero_ranges.fixed | 14 ++++++++++++++ tests/ui/len_zero_ranges.rs | 14 ++++++++++++++ tests/ui/len_zero_ranges.stderr | 10 ++++++++++ 6 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 tests/ui/len_zero_ranges.fixed create mode 100644 tests/ui/len_zero_ranges.rs create mode 100644 tests/ui/len_zero_ranges.stderr (limited to 'tests') diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 2ec0b5a8d6f..f5bfede75a7 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -1,4 +1,4 @@ -use crate::utils::{get_item_name, snippet_with_applicability, span_lint, span_lint_and_sugg, walk_ptrs_ty}; +use crate::utils::{get_item_name, higher, snippet_with_applicability, span_lint, span_lint_and_sugg, walk_ptrs_ty}; use rustc_ast::ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -259,6 +259,17 @@ fn check_len( /// Checks if this type has an `is_empty` method. fn has_is_empty(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { + /// Special case ranges until `range_is_empty` is stabilized. See issue 3807. + fn should_skip_range(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { + higher::range(cx, expr).map_or(false, |_| { + !cx.tcx + .features() + .declared_lib_features + .iter() + .any(|(name, _)| name.as_str() == "range_is_empty") + }) + } + /// 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 { @@ -284,6 +295,10 @@ fn has_is_empty(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { }) } + if should_skip_range(cx, expr) { + return false; + } + let ty = &walk_ptrs_ty(cx.tables.expr_ty(expr)); match ty.kind { ty::Dynamic(ref tt, ..) => { diff --git a/tests/ui/len_zero.fixed b/tests/ui/len_zero.fixed index 624e5ef8fcf..a29b832eb60 100644 --- a/tests/ui/len_zero.fixed +++ b/tests/ui/len_zero.fixed @@ -141,3 +141,11 @@ fn main() { fn test_slice(b: &[u8]) { if !b.is_empty() {} } + +mod issue_3807 { + // Avoid suggesting changes to ranges if the user did not enable `range_is_empty`. + // See https://github.com/rust-lang/rust/issues/48111#issuecomment-445132965 + fn no_suggestion() { + let _ = (0..42).len() == 0; + } +} diff --git a/tests/ui/len_zero.rs b/tests/ui/len_zero.rs index 7fba971cfd8..8fd0093f4fd 100644 --- a/tests/ui/len_zero.rs +++ b/tests/ui/len_zero.rs @@ -141,3 +141,11 @@ fn main() { fn test_slice(b: &[u8]) { if b.len() != 0 {} } + +mod issue_3807 { + // Avoid suggesting changes to ranges if the user did not enable `range_is_empty`. + // See https://github.com/rust-lang/rust/issues/48111#issuecomment-445132965 + fn no_suggestion() { + let _ = (0..42).len() == 0; + } +} diff --git a/tests/ui/len_zero_ranges.fixed b/tests/ui/len_zero_ranges.fixed new file mode 100644 index 00000000000..7da26f8ff4d --- /dev/null +++ b/tests/ui/len_zero_ranges.fixed @@ -0,0 +1,14 @@ +// run-rustfix + +#![feature(range_is_empty)] +#![warn(clippy::len_zero)] +#![allow(unused)] + +mod issue_3807 { + // With the feature enabled, `is_empty` should be suggested + fn suggestion_is_fine() { + let _ = (0..42).is_empty(); + } +} + +fn main() {} diff --git a/tests/ui/len_zero_ranges.rs b/tests/ui/len_zero_ranges.rs new file mode 100644 index 00000000000..be7b4244bc0 --- /dev/null +++ b/tests/ui/len_zero_ranges.rs @@ -0,0 +1,14 @@ +// run-rustfix + +#![feature(range_is_empty)] +#![warn(clippy::len_zero)] +#![allow(unused)] + +mod issue_3807 { + // With the feature enabled, `is_empty` should be suggested + fn suggestion_is_fine() { + let _ = (0..42).len() == 0; + } +} + +fn main() {} diff --git a/tests/ui/len_zero_ranges.stderr b/tests/ui/len_zero_ranges.stderr new file mode 100644 index 00000000000..6e5fa41fb08 --- /dev/null +++ b/tests/ui/len_zero_ranges.stderr @@ -0,0 +1,10 @@ +error: length comparison to zero + --> $DIR/len_zero_ranges.rs:10:17 + | +LL | let _ = (0..42).len() == 0; + | ^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(0..42).is_empty()` + | + = note: `-D clippy::len-zero` implied by `-D warnings` + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From b92cc8a08d74fb412bc444a4361df51b0c95401c Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Fri, 29 May 2020 22:46:05 +0200 Subject: add testcase that no longer ICEs Fixes #3969 --- tests/ui/crashes/ice-3969.rs | 51 ++++++++++++++++++++++++++++++++++++++++ tests/ui/crashes/ice-3969.stderr | 22 +++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 tests/ui/crashes/ice-3969.rs create mode 100644 tests/ui/crashes/ice-3969.stderr (limited to 'tests') diff --git a/tests/ui/crashes/ice-3969.rs b/tests/ui/crashes/ice-3969.rs new file mode 100644 index 00000000000..4feab7910b7 --- /dev/null +++ b/tests/ui/crashes/ice-3969.rs @@ -0,0 +1,51 @@ +// https://github.com/rust-lang/rust-clippy/issues/3969 +// used to crash: error: internal compiler error: +// src/librustc_traits/normalize_erasing_regions.rs:43: could not fully normalize `::Item test from rustc ./ui/trivial-bounds/trivial-bounds-inconsistent.rs + +// Check that tautalogically false bounds are accepted, and are used +// in type inference. +#![feature(trivial_bounds)] +#![allow(unused)] + +trait A {} + +impl A for i32 {} + +struct Dst { + x: X, +} + +struct TwoStrs(str, str) +where + str: Sized; + +fn unsized_local() +where + for<'a> Dst: Sized, +{ + let x: Dst = *(Box::new(Dst { x: 1 }) as Box>); +} + +fn return_str() -> str +where + str: Sized, +{ + *"Sized".to_string().into_boxed_str() +} + +fn use_op(s: String) -> String +where + String: ::std::ops::Neg, +{ + -s +} + +fn use_for() +where + i32: Iterator, +{ + for _ in 2i32 {} +} + +fn main() {} diff --git a/tests/ui/crashes/ice-3969.stderr b/tests/ui/crashes/ice-3969.stderr new file mode 100644 index 00000000000..923db0664a7 --- /dev/null +++ b/tests/ui/crashes/ice-3969.stderr @@ -0,0 +1,22 @@ +error: trait objects without an explicit `dyn` are deprecated + --> $DIR/ice-3969.rs:25:17 + | +LL | for<'a> Dst: Sized, + | ^^^^^^ help: use `dyn`: `dyn A + 'a` + | + = note: `-D bare-trait-objects` implied by `-D warnings` + +error: trait objects without an explicit `dyn` are deprecated + --> $DIR/ice-3969.rs:27:16 + | +LL | let x: Dst = *(Box::new(Dst { x: 1 }) as Box>); + | ^ help: use `dyn`: `dyn A` + +error: trait objects without an explicit `dyn` are deprecated + --> $DIR/ice-3969.rs:27:57 + | +LL | let x: Dst = *(Box::new(Dst { x: 1 }) as Box>); + | ^ help: use `dyn`: `dyn A` + +error: aborting due to 3 previous errors + -- cgit 1.4.1-3-g733a5 From 5faab874f9f8655c8f284944b5acdede5c088af4 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Sat, 23 May 2020 00:07:09 +0200 Subject: new lint: vec_resize_to_zero --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 +++ clippy_lints/src/utils/paths.rs | 1 + clippy_lints/src/vec_resize_to_zero.rs | 59 ++++++++++++++++++++++++++++++++++ src/lintlist/mod.rs | 7 ++++ tests/ui/vec_resize_to_zero.rs | 15 +++++++++ tests/ui/vec_resize_to_zero.stderr | 13 ++++++++ 7 files changed, 101 insertions(+) create mode 100644 clippy_lints/src/vec_resize_to_zero.rs create mode 100644 tests/ui/vec_resize_to_zero.rs create mode 100644 tests/ui/vec_resize_to_zero.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ac9057199f..f7dae3dcfff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1630,6 +1630,7 @@ Released 2018-09-13 [`useless_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_transmute [`useless_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec [`vec_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_box +[`vec_resize_to_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_resize_to_zero [`verbose_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_bit_mask [`verbose_file_reads`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_file_reads [`vtable_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#vtable_address_comparisons diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 902f3d56c1e..4f0ecab393d 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -325,6 +325,7 @@ mod unwrap; mod use_self; mod useless_conversion; mod vec; +mod vec_resize_to_zero; mod verbose_file_reads; mod wildcard_dependencies; mod wildcard_imports; @@ -847,6 +848,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &utils::internal_lints::OUTER_EXPN_EXPN_DATA, &utils::internal_lints::PRODUCE_ICE, &vec::USELESS_VEC, + &vec_resize_to_zero::VEC_RESIZE_TO_ZERO, &verbose_file_reads::VERBOSE_FILE_READS, &wildcard_dependencies::WILDCARD_DEPENDENCIES, &wildcard_imports::ENUM_GLOB_USE, @@ -1062,6 +1064,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box manual_non_exhaustive::ManualNonExhaustive); store.register_late_pass(|| box manual_async_fn::ManualAsyncFn); store.register_early_pass(|| box redundant_field_names::RedundantFieldNames); + store.register_late_pass(|| box vec_resize_to_zero::VecResizeToZero); let single_char_binding_names_threshold = conf.single_char_binding_names_threshold; store.register_early_pass(move || box non_expressive_names::NonExpressiveNames { single_char_binding_names_threshold, @@ -1430,6 +1433,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unwrap::UNNECESSARY_UNWRAP), LintId::of(&useless_conversion::USELESS_CONVERSION), LintId::of(&vec::USELESS_VEC), + 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), @@ -1677,6 +1681,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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::perf", Some("clippy_perf"), vec![ diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 779da7e6bf2..3b7e9739211 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -138,5 +138,6 @@ pub const VEC_AS_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_slice"]; pub const VEC_DEQUE: [&str; 4] = ["alloc", "collections", "vec_deque", "VecDeque"]; 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 WEAK_ARC: [&str; 3] = ["alloc", "sync", "Weak"]; pub const WEAK_RC: [&str; 3] = ["alloc", "rc", "Weak"]; diff --git a/clippy_lints/src/vec_resize_to_zero.rs b/clippy_lints/src/vec_resize_to_zero.rs new file mode 100644 index 00000000000..86cbfa8203d --- /dev/null +++ b/clippy_lints/src/vec_resize_to_zero.rs @@ -0,0 +1,59 @@ +use crate::utils::span_lint_and_then; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Spanned; + +use crate::utils::{match_def_path, paths}; +use rustc_ast::ast::LitKind; +use rustc_hir as hir; + +declare_clippy_lint! { + /// **What it does:** Finds occurences of `Vec::resize(0, an_int)` + /// + /// **Why is this bad?** This is probably an argument inversion mistake. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// vec!(1, 2, 3, 4, 5).resize(0, 5) + /// ``` + pub VEC_RESIZE_TO_ZERO, + correctness, + "emptying a vector with `resize(0, an_int)` instead of `clear()` is probably an argument inversion mistake" +} + +declare_lint_pass!(VecResizeToZero => [VEC_RESIZE_TO_ZERO]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for VecResizeToZero { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + if let hir::ExprKind::MethodCall(path_segment, _, ref args) = expr.kind; + if let Some(method_def_id) = cx.tables.type_dependent_def_id(expr.hir_id); + if match_def_path(cx, method_def_id, &paths::VEC_RESIZE) && args.len() == 3; + if let ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = args[1].kind; + if let ExprKind::Lit(Spanned { node: LitKind::Int(..), .. }) = args[2].kind; + then { + let method_call_span = expr.span.with_lo(path_segment.ident.span.lo()); + span_lint_and_then( + cx, + VEC_RESIZE_TO_ZERO, + expr.span, + "emptying a vector with `resize`", + |db| { + db.help("the arguments may be inverted..."); + db.span_suggestion( + method_call_span, + "...or you can empty the vector with", + "clear()".to_string(), + Applicability::MaybeIncorrect, + ); + }, + ); + } + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index f63301c7db0..1e94ca00c14 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2460,6 +2460,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "types", }, + Lint { + name: "vec_resize_to_zero", + group: "correctness", + desc: "emptying a vector with `resize(0, an_int)` instead of `clear()` is probably an argument inversion mistake", + deprecation: None, + module: "vec_resize_to_zero", + }, Lint { name: "verbose_bit_mask", group: "style", diff --git a/tests/ui/vec_resize_to_zero.rs b/tests/ui/vec_resize_to_zero.rs new file mode 100644 index 00000000000..0263e2f5f20 --- /dev/null +++ b/tests/ui/vec_resize_to_zero.rs @@ -0,0 +1,15 @@ +#![warn(clippy::vec_resize_to_zero)] + +fn main() { + // applicable here + vec![1, 2, 3, 4, 5].resize(0, 5); + + // not applicable + vec![1, 2, 3, 4, 5].resize(2, 5); + + // applicable here, but only implemented for integer litterals for now + vec!["foo", "bar", "baz"].resize(0, "bar"); + + // not applicable + vec!["foo", "bar", "baz"].resize(2, "bar") +} diff --git a/tests/ui/vec_resize_to_zero.stderr b/tests/ui/vec_resize_to_zero.stderr new file mode 100644 index 00000000000..feb846298c6 --- /dev/null +++ b/tests/ui/vec_resize_to_zero.stderr @@ -0,0 +1,13 @@ +error: emptying a vector with `resize` + --> $DIR/vec_resize_to_zero.rs:5:5 + | +LL | vec![1, 2, 3, 4, 5].resize(0, 5); + | ^^^^^^^^^^^^^^^^^^^^------------ + | | + | help: ...or you can empty the vector with: `clear()` + | + = note: `-D clippy::vec-resize-to-zero` implied by `-D warnings` + = help: the arguments may be inverted... + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From 37381d33a4761a064311dd95fbc54b5da6ad3766 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sun, 31 May 2020 14:05:57 +0200 Subject: Fix sync fallout --- clippy_lints/src/needless_pass_by_value.rs | 8 +------- clippy_lints/src/write.rs | 13 ++++++------- tests/compile-test.rs | 7 ++++--- 3 files changed, 11 insertions(+), 17 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 218b0d27f74..9c508fc0e4a 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -173,13 +173,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue { !preds.is_empty() && { let ty_empty_region = cx.tcx.mk_imm_ref(cx.tcx.lifetimes.re_root_empty, ty); preds.iter().all(|t| { - let ty_params = &t - .skip_binder() - .trait_ref - .substs - .iter() - .skip(1) - .collect::>(); + let ty_params = &t.skip_binder().trait_ref.substs.iter().skip(1).collect::>(); implements_trait(cx, ty_empty_region, t.def_id(), ty_params) }) }, diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index dfa6223f1b9..5f794598052 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -279,13 +279,12 @@ impl EarlyLintPass for Write { if let (Some(fmt_str), expr) = self.check_tts(cx, &mac.args.inner_tokens(), true) { if fmt_str.symbol == Symbol::intern("") { let mut applicability = Applicability::MachineApplicable; - let suggestion = expr.map_or_else( - move || { - applicability = Applicability::HasPlaceholders; - Cow::Borrowed("v") - }, - move |expr| snippet_with_applicability(cx, expr.span, "v", &mut applicability), - ); + let suggestion = if let Some(e) = expr { + snippet_with_applicability(cx, e.span, "v", &mut applicability) + } else { + applicability = Applicability::HasPlaceholders; + Cow::Borrowed("v") + }; span_lint_and_sugg( cx, diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 1c4914a470c..7bd5f09f333 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -153,9 +153,6 @@ fn run_ui_toml(config: &mut compiletest::Config) { } fn run_ui_cargo(config: &mut compiletest::Config) { - if cargo::is_rustc_test_suite() { - return; - } fn run_tests( config: &compiletest::Config, filter: &Option, @@ -216,6 +213,10 @@ fn run_ui_cargo(config: &mut compiletest::Config) { Ok(result) } + if cargo::is_rustc_test_suite() { + return; + } + config.mode = TestMode::Ui; config.src_base = Path::new("tests").join("ui-cargo").canonicalize().unwrap(); -- cgit 1.4.1-3-g733a5 From 380d941a045dc213ae28807d74fc32d1b1841e22 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Mon, 26 Aug 2019 19:35:25 +0200 Subject: Adapt stderr and fixed files --- tests/ui/unit_arg.fixed | 29 +++++++++--- tests/ui/unit_arg.rs | 10 ++++- tests/ui/unit_arg.stderr | 112 ++++++++++++++++++++++++++++++++++++----------- 3 files changed, 118 insertions(+), 33 deletions(-) (limited to 'tests') diff --git a/tests/ui/unit_arg.fixed b/tests/ui/unit_arg.fixed index a739cf7ad81..67c6bdf8873 100644 --- a/tests/ui/unit_arg.fixed +++ b/tests/ui/unit_arg.fixed @@ -1,6 +1,6 @@ // run-rustfix #![warn(clippy::unit_arg)] -#![allow(unused_braces, clippy::no_effect, unused_must_use)] +#![allow(clippy::no_effect, unused_must_use, unused_variables)] use std::fmt::Debug; @@ -21,13 +21,21 @@ impl Bar { } fn bad() { - foo(()); - foo(()); - foo(()); - foo(()); - foo3((), 2, 2); + {}; foo(()); + { + 1; + }; foo(()); + foo(1); foo(()); + { + foo(1); + foo(2); + }; foo(()); + {}; foo3((), 2, 2); let b = Bar; - b.bar(()); + { + 1; + }; b.bar(()); + foo(0); foo(1); taking_multiple_units((), ()); } fn ok() { @@ -58,6 +66,13 @@ mod issue_2945 { } } +#[allow(dead_code)] +fn returning_expr() -> Option<()> { + foo(1); Some(()) +} + +fn taking_multiple_units(a: (), b: ()) {} + fn main() { bad(); ok(); diff --git a/tests/ui/unit_arg.rs b/tests/ui/unit_arg.rs index d90c49f79de..c6e465b2e4c 100644 --- a/tests/ui/unit_arg.rs +++ b/tests/ui/unit_arg.rs @@ -1,6 +1,6 @@ // run-rustfix #![warn(clippy::unit_arg)] -#![allow(unused_braces, clippy::no_effect, unused_must_use)] +#![allow(clippy::no_effect, unused_must_use, unused_variables)] use std::fmt::Debug; @@ -35,6 +35,7 @@ fn bad() { b.bar({ 1; }); + taking_multiple_units(foo(0), foo(1)); } fn ok() { @@ -65,6 +66,13 @@ mod issue_2945 { } } +#[allow(dead_code)] +fn returning_expr() -> Option<()> { + Some(foo(1)) +} + +fn taking_multiple_units(a: (), b: ()) {} + fn main() { bad(); ok(); diff --git a/tests/ui/unit_arg.stderr b/tests/ui/unit_arg.stderr index 21ccc684ea9..ce9ab2f1271 100644 --- a/tests/ui/unit_arg.stderr +++ b/tests/ui/unit_arg.stderr @@ -1,79 +1,141 @@ error: passing a unit value to a function - --> $DIR/unit_arg.rs:24:9 + --> $DIR/unit_arg.rs:24:5 | LL | foo({}); - | ^^ + | ^^^^^^^ | = note: `-D clippy::unit-arg` implied by `-D warnings` -help: if you intended to pass a unit value, use a unit literal instead +help: move the expressions in front of the call... + | +LL | {}; foo({}); + | ^^^ +help: ...and use unit literals instead | LL | foo(()); | ^^ error: passing a unit value to a function - --> $DIR/unit_arg.rs:25:9 + --> $DIR/unit_arg.rs:25:5 | -LL | foo({ - | _________^ +LL | / foo({ LL | | 1; LL | | }); - | |_____^ + | |______^ + | +help: move the expressions in front of the call... + | +LL | { +LL | 1; +LL | }; foo({ | -help: if you intended to pass a unit value, use a unit literal instead +help: ...and use unit literals instead | LL | foo(()); | ^^ error: passing a unit value to a function - --> $DIR/unit_arg.rs:28:9 + --> $DIR/unit_arg.rs:28:5 | LL | foo(foo(1)); - | ^^^^^^ + | ^^^^^^^^^^^ | -help: if you intended to pass a unit value, use a unit literal instead +help: move the expressions in front of the call... + | +LL | foo(1); foo(foo(1)); + | ^^^^^^^ +help: ...and use unit literals instead | LL | foo(()); | ^^ error: passing a unit value to a function - --> $DIR/unit_arg.rs:29:9 + --> $DIR/unit_arg.rs:29:5 | -LL | foo({ - | _________^ +LL | / foo({ LL | | foo(1); LL | | foo(2); LL | | }); - | |_____^ + | |______^ + | +help: move the expressions in front of the call... + | +LL | { +LL | foo(1); +LL | foo(2); +LL | }; foo({ | -help: if you intended to pass a unit value, use a unit literal instead +help: ...and use unit literals instead | LL | foo(()); | ^^ error: passing a unit value to a function - --> $DIR/unit_arg.rs:33:10 + --> $DIR/unit_arg.rs:33:5 | LL | foo3({}, 2, 2); - | ^^ + | ^^^^^^^^^^^^^^ + | +help: move the expressions in front of the call... | -help: if you intended to pass a unit value, use a unit literal instead +LL | {}; foo3({}, 2, 2); + | ^^^ +help: ...and use unit literals instead | LL | foo3((), 2, 2); | ^^ error: passing a unit value to a function - --> $DIR/unit_arg.rs:35:11 + --> $DIR/unit_arg.rs:35:5 | -LL | b.bar({ - | ___________^ +LL | / b.bar({ LL | | 1; LL | | }); - | |_____^ + | |______^ + | +help: move the expressions in front of the call... | -help: if you intended to pass a unit value, use a unit literal instead +LL | { +LL | 1; +LL | }; b.bar({ + | +help: ...and use unit literals instead | LL | b.bar(()); | ^^ -error: aborting due to 6 previous errors +error: passing a unit value to a function + --> $DIR/unit_arg.rs:38:5 + | +LL | taking_multiple_units(foo(0), foo(1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: move the expressions in front of the call... + | +LL | foo(0); foo(1); taking_multiple_units(foo(0), foo(1)); + | ^^^^^^^^^^^^^^^ +help: ...and use unit literals instead + | +LL | taking_multiple_units((), foo(1)); + | ^^ +help: ...and use unit literals instead + | +LL | taking_multiple_units(foo(0), ()); + | ^^ + +error: passing a unit value to a function + --> $DIR/unit_arg.rs:71:5 + | +LL | Some(foo(1)) + | ^^^^^^^^^^^^ + | +help: move the expressions in front of the call... + | +LL | foo(1); Some(foo(1)) + | ^^^^^^^ +help: ...and use unit literals instead + | +LL | Some(()) + | ^^ + +error: aborting due to 8 previous errors -- cgit 1.4.1-3-g733a5 From 6d15a149640e5647ce232690d54b540346fa1641 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Mon, 17 Feb 2020 18:12:01 +0100 Subject: Update test files --- tests/ui/unit_arg.fixed | 79 ------------------------ tests/ui/unit_arg.rs | 15 ++++- tests/ui/unit_arg.stderr | 152 ++++++++++++++++++++++++++++++++++------------- 3 files changed, 125 insertions(+), 121 deletions(-) delete mode 100644 tests/ui/unit_arg.fixed (limited to 'tests') diff --git a/tests/ui/unit_arg.fixed b/tests/ui/unit_arg.fixed deleted file mode 100644 index 67c6bdf8873..00000000000 --- a/tests/ui/unit_arg.fixed +++ /dev/null @@ -1,79 +0,0 @@ -// run-rustfix -#![warn(clippy::unit_arg)] -#![allow(clippy::no_effect, unused_must_use, unused_variables)] - -use std::fmt::Debug; - -fn foo(t: T) { - println!("{:?}", t); -} - -fn foo3(t1: T1, t2: T2, t3: T3) { - println!("{:?}, {:?}, {:?}", t1, t2, t3); -} - -struct Bar; - -impl Bar { - fn bar(&self, t: T) { - println!("{:?}", t); - } -} - -fn bad() { - {}; foo(()); - { - 1; - }; foo(()); - foo(1); foo(()); - { - foo(1); - foo(2); - }; foo(()); - {}; foo3((), 2, 2); - let b = Bar; - { - 1; - }; b.bar(()); - foo(0); foo(1); taking_multiple_units((), ()); -} - -fn ok() { - foo(()); - foo(1); - foo({ 1 }); - foo3("a", 3, vec![3]); - let b = Bar; - b.bar({ 1 }); - b.bar(()); - question_mark(); -} - -fn question_mark() -> Result<(), ()> { - Ok(Ok(())?)?; - Ok(Ok(()))??; - Ok(()) -} - -#[allow(dead_code)] -mod issue_2945 { - fn unit_fn() -> Result<(), i32> { - Ok(()) - } - - fn fallible() -> Result<(), i32> { - Ok(unit_fn()?) - } -} - -#[allow(dead_code)] -fn returning_expr() -> Option<()> { - foo(1); Some(()) -} - -fn taking_multiple_units(a: (), b: ()) {} - -fn main() { - bad(); - ok(); -} diff --git a/tests/ui/unit_arg.rs b/tests/ui/unit_arg.rs index c6e465b2e4c..7d1b99fedc9 100644 --- a/tests/ui/unit_arg.rs +++ b/tests/ui/unit_arg.rs @@ -1,4 +1,3 @@ -// run-rustfix #![warn(clippy::unit_arg)] #![allow(clippy::no_effect, unused_must_use, unused_variables)] @@ -36,6 +35,20 @@ fn bad() { 1; }); taking_multiple_units(foo(0), foo(1)); + taking_multiple_units(foo(0), { + foo(1); + foo(2); + }); + taking_multiple_units( + { + foo(0); + foo(1); + }, + { + foo(2); + foo(3); + }, + ); } fn ok() { diff --git a/tests/ui/unit_arg.stderr b/tests/ui/unit_arg.stderr index ce9ab2f1271..145b3c62b06 100644 --- a/tests/ui/unit_arg.stderr +++ b/tests/ui/unit_arg.stderr @@ -1,55 +1,59 @@ error: passing a unit value to a function - --> $DIR/unit_arg.rs:24:5 + --> $DIR/unit_arg.rs:23:5 | LL | foo({}); | ^^^^^^^ | = note: `-D clippy::unit-arg` implied by `-D warnings` -help: move the expressions in front of the call... +help: move the expression in front of the call... | -LL | {}; foo({}); - | ^^^ -help: ...and use unit literals instead +LL | {}; + | +help: ...and use a unit literal instead | LL | foo(()); | ^^ error: passing a unit value to a function - --> $DIR/unit_arg.rs:25:5 + --> $DIR/unit_arg.rs:24:5 | LL | / foo({ LL | | 1; LL | | }); | |______^ | -help: move the expressions in front of the call... +help: remove the semicolon from the last statement in the block + | +LL | 1 + | +help: or move the expression in front of the call... | LL | { LL | 1; -LL | }; foo({ +LL | }; | -help: ...and use unit literals instead +help: ...and use a unit literal instead | LL | foo(()); | ^^ error: passing a unit value to a function - --> $DIR/unit_arg.rs:28:5 + --> $DIR/unit_arg.rs:27:5 | LL | foo(foo(1)); | ^^^^^^^^^^^ | -help: move the expressions in front of the call... +help: move the expression in front of the call... | -LL | foo(1); foo(foo(1)); - | ^^^^^^^ -help: ...and use unit literals instead +LL | foo(1); + | +help: ...and use a unit literal instead | LL | foo(()); | ^^ error: passing a unit value to a function - --> $DIR/unit_arg.rs:29:5 + --> $DIR/unit_arg.rs:28:5 | LL | / foo({ LL | | foo(1); @@ -57,85 +61,151 @@ LL | | foo(2); LL | | }); | |______^ | -help: move the expressions in front of the call... +help: remove the semicolon from the last statement in the block + | +LL | foo(2) + | +help: or move the expression in front of the call... | LL | { LL | foo(1); LL | foo(2); -LL | }; foo({ +LL | }; | -help: ...and use unit literals instead +help: ...and use a unit literal instead | LL | foo(()); | ^^ error: passing a unit value to a function - --> $DIR/unit_arg.rs:33:5 + --> $DIR/unit_arg.rs:32:5 | LL | foo3({}, 2, 2); | ^^^^^^^^^^^^^^ | -help: move the expressions in front of the call... +help: move the expression in front of the call... | -LL | {}; foo3({}, 2, 2); - | ^^^ -help: ...and use unit literals instead +LL | {}; + | +help: ...and use a unit literal instead | LL | foo3((), 2, 2); | ^^ error: passing a unit value to a function - --> $DIR/unit_arg.rs:35:5 + --> $DIR/unit_arg.rs:34:5 | LL | / b.bar({ LL | | 1; LL | | }); | |______^ | -help: move the expressions in front of the call... +help: remove the semicolon from the last statement in the block + | +LL | 1 + | +help: or move the expression in front of the call... | LL | { LL | 1; -LL | }; b.bar({ +LL | }; | -help: ...and use unit literals instead +help: ...and use a unit literal instead | LL | b.bar(()); | ^^ -error: passing a unit value to a function - --> $DIR/unit_arg.rs:38:5 +error: passing unit values to a function + --> $DIR/unit_arg.rs:37:5 | LL | taking_multiple_units(foo(0), foo(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: move the expressions in front of the call... | -LL | foo(0); foo(1); taking_multiple_units(foo(0), foo(1)); - | ^^^^^^^^^^^^^^^ +LL | foo(0); +LL | foo(1); + | help: ...and use unit literals instead | -LL | taking_multiple_units((), foo(1)); - | ^^ +LL | taking_multiple_units((), ()); + | ^^ ^^ + +error: passing unit values to a function + --> $DIR/unit_arg.rs:38:5 + | +LL | / taking_multiple_units(foo(0), { +LL | | foo(1); +LL | | foo(2); +LL | | }); + | |______^ + | +help: remove the semicolon from the last statement in the block + | +LL | foo(2) + | +help: or move the expressions in front of the call... + | +LL | foo(0); +LL | { +LL | foo(1); +LL | foo(2); +LL | }; + | help: ...and use unit literals instead | -LL | taking_multiple_units(foo(0), ()); - | ^^ +LL | taking_multiple_units((), ()); + | ^^ ^^ + +error: passing unit values to a function + --> $DIR/unit_arg.rs:42:5 + | +LL | / taking_multiple_units( +LL | | { +LL | | foo(0); +LL | | foo(1); +... | +LL | | }, +LL | | ); + | |_____^ + | +help: remove the semicolon from the last statement in the block + | +LL | foo(1) + | +help: remove the semicolon from the last statement in the block + | +LL | foo(3) + | +help: or move the expressions in front of the call... + | +LL | { +LL | foo(0); +LL | foo(1); +LL | }; +LL | { +LL | foo(2); + ... +help: ...and use unit literals instead + | +LL | (), +LL | (), + | error: passing a unit value to a function - --> $DIR/unit_arg.rs:71:5 + --> $DIR/unit_arg.rs:84:5 | LL | Some(foo(1)) | ^^^^^^^^^^^^ | -help: move the expressions in front of the call... +help: move the expression in front of the call... | -LL | foo(1); Some(foo(1)) - | ^^^^^^^ -help: ...and use unit literals instead +LL | foo(1); + | +help: ...and use a unit literal instead | LL | Some(()) | ^^ -error: aborting due to 8 previous errors +error: aborting due to 10 previous errors -- cgit 1.4.1-3-g733a5 From 77dd0ea62aa6a2af70da4c5e05de064eee182a6c Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sun, 31 May 2020 19:29:36 +0200 Subject: Add tests for empty blocks --- tests/ui/unit_arg.rs | 2 -- tests/ui/unit_arg.stderr | 46 ++++++------------------------- tests/ui/unit_arg_empty_blocks.rs | 26 ++++++++++++++++++ tests/ui/unit_arg_empty_blocks.stderr | 51 +++++++++++++++++++++++++++++++++++ 4 files changed, 85 insertions(+), 40 deletions(-) create mode 100644 tests/ui/unit_arg_empty_blocks.rs create mode 100644 tests/ui/unit_arg_empty_blocks.stderr (limited to 'tests') diff --git a/tests/ui/unit_arg.rs b/tests/ui/unit_arg.rs index 7d1b99fedc9..2992abae775 100644 --- a/tests/ui/unit_arg.rs +++ b/tests/ui/unit_arg.rs @@ -20,7 +20,6 @@ impl Bar { } fn bad() { - foo({}); foo({ 1; }); @@ -29,7 +28,6 @@ fn bad() { foo(1); foo(2); }); - foo3({}, 2, 2); let b = Bar; b.bar({ 1; diff --git a/tests/ui/unit_arg.stderr b/tests/ui/unit_arg.stderr index 145b3c62b06..56f6a855dfa 100644 --- a/tests/ui/unit_arg.stderr +++ b/tests/ui/unit_arg.stderr @@ -1,27 +1,12 @@ error: passing a unit value to a function --> $DIR/unit_arg.rs:23:5 | -LL | foo({}); - | ^^^^^^^ - | - = note: `-D clippy::unit-arg` implied by `-D warnings` -help: move the expression in front of the call... - | -LL | {}; - | -help: ...and use a unit literal instead - | -LL | foo(()); - | ^^ - -error: passing a unit value to a function - --> $DIR/unit_arg.rs:24:5 - | LL | / foo({ LL | | 1; LL | | }); | |______^ | + = note: `-D clippy::unit-arg` implied by `-D warnings` help: remove the semicolon from the last statement in the block | LL | 1 @@ -38,7 +23,7 @@ LL | foo(()); | ^^ error: passing a unit value to a function - --> $DIR/unit_arg.rs:27:5 + --> $DIR/unit_arg.rs:26:5 | LL | foo(foo(1)); | ^^^^^^^^^^^ @@ -53,7 +38,7 @@ LL | foo(()); | ^^ error: passing a unit value to a function - --> $DIR/unit_arg.rs:28:5 + --> $DIR/unit_arg.rs:27:5 | LL | / foo({ LL | | foo(1); @@ -80,21 +65,6 @@ LL | foo(()); error: passing a unit value to a function --> $DIR/unit_arg.rs:32:5 | -LL | foo3({}, 2, 2); - | ^^^^^^^^^^^^^^ - | -help: move the expression in front of the call... - | -LL | {}; - | -help: ...and use a unit literal instead - | -LL | foo3((), 2, 2); - | ^^ - -error: passing a unit value to a function - --> $DIR/unit_arg.rs:34:5 - | LL | / b.bar({ LL | | 1; LL | | }); @@ -116,7 +86,7 @@ LL | b.bar(()); | ^^ error: passing unit values to a function - --> $DIR/unit_arg.rs:37:5 + --> $DIR/unit_arg.rs:35:5 | LL | taking_multiple_units(foo(0), foo(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -132,7 +102,7 @@ LL | taking_multiple_units((), ()); | ^^ ^^ error: passing unit values to a function - --> $DIR/unit_arg.rs:38:5 + --> $DIR/unit_arg.rs:36:5 | LL | / taking_multiple_units(foo(0), { LL | | foo(1); @@ -158,7 +128,7 @@ LL | taking_multiple_units((), ()); | ^^ ^^ error: passing unit values to a function - --> $DIR/unit_arg.rs:42:5 + --> $DIR/unit_arg.rs:40:5 | LL | / taking_multiple_units( LL | | { @@ -193,7 +163,7 @@ LL | (), | error: passing a unit value to a function - --> $DIR/unit_arg.rs:84:5 + --> $DIR/unit_arg.rs:82:5 | LL | Some(foo(1)) | ^^^^^^^^^^^^ @@ -207,5 +177,5 @@ help: ...and use a unit literal instead LL | Some(()) | ^^ -error: aborting due to 10 previous errors +error: aborting due to 8 previous errors diff --git a/tests/ui/unit_arg_empty_blocks.rs b/tests/ui/unit_arg_empty_blocks.rs new file mode 100644 index 00000000000..18a31eb3dee --- /dev/null +++ b/tests/ui/unit_arg_empty_blocks.rs @@ -0,0 +1,26 @@ +#![warn(clippy::unit_arg)] +#![allow(clippy::no_effect, unused_must_use, unused_variables)] + +use std::fmt::Debug; + +fn foo(t: T) { + println!("{:?}", t); +} + +fn foo3(t1: T1, t2: T2, t3: T3) { + println!("{:?}, {:?}, {:?}", t1, t2, t3); +} + +fn bad() { + foo({}); + foo3({}, 2, 2); + taking_two_units({}, foo(0)); + taking_three_units({}, foo(0), foo(1)); +} + +fn taking_two_units(a: (), b: ()) {} +fn taking_three_units(a: (), b: (), c: ()) {} + +fn main() { + bad(); +} diff --git a/tests/ui/unit_arg_empty_blocks.stderr b/tests/ui/unit_arg_empty_blocks.stderr new file mode 100644 index 00000000000..bb58483584b --- /dev/null +++ b/tests/ui/unit_arg_empty_blocks.stderr @@ -0,0 +1,51 @@ +error: passing a unit value to a function + --> $DIR/unit_arg_empty_blocks.rs:15:5 + | +LL | foo({}); + | ^^^^--^ + | | + | help: use a unit literal instead: `()` + | + = note: `-D clippy::unit-arg` implied by `-D warnings` + +error: passing a unit value to a function + --> $DIR/unit_arg_empty_blocks.rs:16:5 + | +LL | foo3({}, 2, 2); + | ^^^^^--^^^^^^^ + | | + | help: use a unit literal instead: `()` + +error: passing unit values to a function + --> $DIR/unit_arg_empty_blocks.rs:17:5 + | +LL | taking_two_units({}, foo(0)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: move the expression in front of the call... + | +LL | foo(0); + | +help: ...and use unit literals instead + | +LL | taking_two_units((), ()); + | ^^ ^^ + +error: passing unit values to a function + --> $DIR/unit_arg_empty_blocks.rs:18:5 + | +LL | taking_three_units({}, foo(0), foo(1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: move the expressions in front of the call... + | +LL | foo(0); +LL | foo(1); + | +help: ...and use unit literals instead + | +LL | taking_three_units((), (), ()); + | ^^ ^^ ^^ + +error: aborting due to 4 previous errors + -- cgit 1.4.1-3-g733a5 From 14e9100543166e48acd0ea00233249d2cddf09c2 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Mon, 25 May 2020 00:41:13 +0200 Subject: cargo-ui tests: check that /src exists before processing test --- tests/compile-test.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 7bd5f09f333..194354b291f 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -181,8 +181,15 @@ fn run_ui_cargo(config: &mut compiletest::Config) { } let src_path = case.path().join("src"); - env::set_current_dir(&src_path)?; + // When switching between branches, if the previous branch had a test + // that the current branch does not have, the directory is not removed + // because an ignored Cargo.lock file exists. + if !src_path.exists() { + continue; + } + + env::set_current_dir(&src_path)?; for file in fs::read_dir(&src_path)? { let file = file?; if file.file_type()?.is_dir() { -- cgit 1.4.1-3-g733a5 From 7e843515d9525b6389c3fc1bcfa6ae046c1351dc Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Thu, 14 May 2020 15:06:05 -0700 Subject: Created lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 4 ++++ clippy_lints/src/sort_by_key_reverse.rs | 28 ++++++++++++++++++++++++++++ src/lintlist/mod.rs | 7 +++++++ tests/ui/sort_by_key_reverse.rs | 5 +++++ 5 files changed, 45 insertions(+) create mode 100644 clippy_lints/src/sort_by_key_reverse.rs create mode 100644 tests/ui/sort_by_key_reverse.rs (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index f7dae3dcfff..c00f84bdb85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1555,6 +1555,7 @@ Released 2018-09-13 [`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else [`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next [`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization +[`sort_by_key_reverse`]: https://rust-lang.github.io/rust-clippy/master/index.html#sort_by_key_reverse [`str_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_to_string [`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add [`string_add_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add_assign diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 38cfa212d9f..f51855badff 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -304,6 +304,7 @@ mod serde_api; mod shadow; mod single_component_path_imports; mod slow_vector_initialization; +mod sort_by_key_reverse; mod strings; mod suspicious_trait_impl; mod swap; @@ -779,6 +780,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &shadow::SHADOW_UNRELATED, &single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, &slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, + &sort_by_key_reverse::SORT_BY_KEY_REVERSE, &strings::STRING_ADD, &strings::STRING_ADD_ASSIGN, &strings::STRING_LIT_AS_BYTES, @@ -1391,6 +1393,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&serde_api::SERDE_API_MISUSE), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), + LintId::of(&sort_by_key_reverse::SORT_BY_KEY_REVERSE), LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), @@ -1592,6 +1595,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ranges::RANGE_ZIP_WITH_LEN), LintId::of(&reference::DEREF_ADDROF), LintId::of(&reference::REF_IN_DEREF), + LintId::of(&sort_by_key_reverse::SORT_BY_KEY_REVERSE), LintId::of(&swap::MANUAL_SWAP), LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT), LintId::of(&transmute::CROSSPOINTER_TRANSMUTE), diff --git a/clippy_lints/src/sort_by_key_reverse.rs b/clippy_lints/src/sort_by_key_reverse.rs new file mode 100644 index 00000000000..65830afd0f8 --- /dev/null +++ b/clippy_lints/src/sort_by_key_reverse.rs @@ -0,0 +1,28 @@ +use rustc_lint::{LateLintPass, LateContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_hir::*; + +declare_clippy_lint! { + /// **What it does:** + /// + /// **Why is this bad?** + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // example code where clippy issues a warning + /// ``` + /// Use instead: + /// ```rust + /// // example code which does not raise clippy warning + /// ``` + pub SORT_BY_KEY_REVERSE, + complexity, + "default lint description" +} + +declare_lint_pass!(SortByKeyReverse => [SORT_BY_KEY_REVERSE]); + +impl LateLintPass<'_, '_> for SortByKeyReverse {} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 69578732898..1b82f34c863 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1984,6 +1984,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "slow_vector_initialization", }, + Lint { + name: "sort_by_key_reverse", + group: "complexity", + desc: "default lint description", + deprecation: None, + module: "sort_by_key_reverse", + }, Lint { name: "string_add", group: "restriction", diff --git a/tests/ui/sort_by_key_reverse.rs b/tests/ui/sort_by_key_reverse.rs new file mode 100644 index 00000000000..2338dc6e594 --- /dev/null +++ b/tests/ui/sort_by_key_reverse.rs @@ -0,0 +1,5 @@ +#![warn(clippy::sort_by_key_reverse)] + +fn main() { + // test code goes here +} -- cgit 1.4.1-3-g733a5 From 24847ea53e332853597aca2c7dfe48a9f3be1de8 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Sat, 16 May 2020 13:50:33 -0700 Subject: Attempted start at sort_by_key_reverse lint --- clippy_lints/src/sort_by_key_reverse.rs | 71 +++++++++++++++++++++++++++++++-- src/lintlist/mod.rs | 2 +- tests/ui/sort_by_key_reverse.fixed | 0 tests/ui/sort_by_key_reverse.rs | 3 +- tests/ui/sort_by_key_reverse.stderr | 0 5 files changed, 70 insertions(+), 6 deletions(-) create mode 100644 tests/ui/sort_by_key_reverse.fixed create mode 100644 tests/ui/sort_by_key_reverse.stderr (limited to 'tests') diff --git a/clippy_lints/src/sort_by_key_reverse.rs b/clippy_lints/src/sort_by_key_reverse.rs index 65830afd0f8..7d7097a8125 100644 --- a/clippy_lints/src/sort_by_key_reverse.rs +++ b/clippy_lints/src/sort_by_key_reverse.rs @@ -1,28 +1,91 @@ +use crate::utils::{match_type, span_lint_and_sugg}; +use crate::utils::paths; +use crate::utils::sugg::Sugg; +use if_chain::if_chain; +use rustc_errors::Applicability; use rustc_lint::{LateLintPass, LateContext}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_hir::*; declare_clippy_lint! { /// **What it does:** + /// Detects when people use `Vec::sort_by` and pass in a function + /// which compares the second argument to the first. /// /// **Why is this bad?** + /// It is more clear to use `Vec::sort_by_key` and `std::cmp::Reverse` /// /// **Known problems:** None. /// /// **Example:** /// /// ```rust - /// // example code where clippy issues a warning + /// vec.sort_by(|a, b| b.foo().cmp(&a.foo())); /// ``` /// Use instead: /// ```rust - /// // example code which does not raise clippy warning + /// vec.sort_by_key(|e| Reverse(e.foo())); /// ``` pub SORT_BY_KEY_REVERSE, complexity, - "default lint description" + "Use of `Vec::sort_by` when `Vec::sort_by_key` would be clearer" } declare_lint_pass!(SortByKeyReverse => [SORT_BY_KEY_REVERSE]); -impl LateLintPass<'_, '_> for SortByKeyReverse {} +struct LintTrigger { + vec_name: String, + closure_arg: String, + closure_reverse_body: String, + unstable: bool, +} + +fn detect_lint(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option { + if_chain! { + if let ExprKind::MethodCall(name_ident, _, args) = &expr.kind; + if let name = name_ident.ident.name.to_ident_string(); + if name == "sort_by" || name == "sort_unstable_by"; + if let [vec, Expr { kind: ExprKind::Closure(_, closure_decl, closure_body_id, _, _), .. }] = args; + if closure_decl.inputs.len() == 2; + if match_type(cx, &cx.tables.expr_ty(vec), &paths::VEC); + then { + let vec_name = Sugg::hir(cx, &args[0], "..").to_string(); + let unstable = name == "sort_unstable_by"; + Some(LintTrigger { vec_name, unstable, closure_arg: "e".to_string(), closure_reverse_body: "e".to_string() }) + } else { + None + } + } +} + +impl LateLintPass<'_, '_> for SortByKeyReverse { + fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr<'_>) { + println!("{:?}", expr); + span_lint_and_sugg( + cx, + SORT_BY_KEY_REVERSE, + expr.span, + "use Vec::sort_by_key here instead", + "try", + String::from("being a better person"), + Applicability::MachineApplicable, + ); + if let Some(trigger) = detect_lint(cx, expr) { + span_lint_and_sugg( + cx, + SORT_BY_KEY_REVERSE, + expr.span, + "use Vec::sort_by_key here instead", + "try", + format!( + "{}.sort{}_by_key(|{}| Reverse({}))", + trigger.vec_name, + if trigger.unstable { "_unstable" } else { "" }, + trigger.closure_arg, + trigger.closure_reverse_body, + ), + Applicability::MachineApplicable, + ); + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 1b82f34c863..b5d9ef0110e 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1987,7 +1987,7 @@ pub static ref ALL_LINTS: Vec = vec![ Lint { name: "sort_by_key_reverse", group: "complexity", - desc: "default lint description", + desc: "Use of `Vec::sort_by` when `Vec::sort_by_key` would be clearer", deprecation: None, module: "sort_by_key_reverse", }, diff --git a/tests/ui/sort_by_key_reverse.fixed b/tests/ui/sort_by_key_reverse.fixed new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/ui/sort_by_key_reverse.rs b/tests/ui/sort_by_key_reverse.rs index 2338dc6e594..c0350f243c7 100644 --- a/tests/ui/sort_by_key_reverse.rs +++ b/tests/ui/sort_by_key_reverse.rs @@ -1,5 +1,6 @@ #![warn(clippy::sort_by_key_reverse)] fn main() { - // test code goes here + let mut vec = vec![3, 6, 1, 2, 5]; + vec.sort_by(|a, b| b.cmp(a)); } diff --git a/tests/ui/sort_by_key_reverse.stderr b/tests/ui/sort_by_key_reverse.stderr new file mode 100644 index 00000000000..e69de29bb2d -- cgit 1.4.1-3-g733a5 From 8590ab4d46de4eb43e7ebd42cb2f13b0064573e6 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Mon, 18 May 2020 21:48:35 -0700 Subject: More progress towards sort_by_key_reverse lint --- clippy_lints/src/lib.rs | 1 + clippy_lints/src/sort_by_key_reverse.rs | 131 +++++++++++++++++++++++++++----- tests/ui/sort_by_key_reverse.fixed | 9 +++ tests/ui/sort_by_key_reverse.rs | 5 +- tests/ui/sort_by_key_reverse.stderr | 22 ++++++ 5 files changed, 149 insertions(+), 19 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f51855badff..e7a4c1ecaa9 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -998,6 +998,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box ptr_offset_with_cast::PtrOffsetWithCast); store.register_late_pass(|| box redundant_clone::RedundantClone); store.register_late_pass(|| box slow_vector_initialization::SlowVectorInit); + store.register_late_pass(|| box sort_by_key_reverse::SortByKeyReverse); store.register_late_pass(|| box types::RefToMut); store.register_late_pass(|| box assertions_on_constants::AssertionsOnConstants); store.register_late_pass(|| box missing_const_for_fn::MissingConstForFn); diff --git a/clippy_lints/src/sort_by_key_reverse.rs b/clippy_lints/src/sort_by_key_reverse.rs index 7d7097a8125..d70391999a0 100644 --- a/clippy_lints/src/sort_by_key_reverse.rs +++ b/clippy_lints/src/sort_by_key_reverse.rs @@ -1,11 +1,12 @@ -use crate::utils::{match_type, span_lint_and_sugg}; +use crate::utils; use crate::utils::paths; use crate::utils::sugg::Sugg; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_lint::{LateLintPass, LateContext}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_hir::*; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::Ident; declare_clippy_lint! { /// **What it does:** @@ -40,18 +41,122 @@ struct LintTrigger { unstable: bool, } +/// Detect if the two expressions are mirrored (identical, except one +/// contains a and the other replaces it with b) +fn mirrored_exprs(cx: &LateContext<'_, '_>, a_expr: &Expr<'_>, a_ident: &Ident, b_expr: &Expr<'_>, b_ident: &Ident) -> bool { + match (&a_expr.kind, &b_expr.kind) { + // Two boxes with mirrored contents + (ExprKind::Box(left_expr), ExprKind::Box(right_expr)) => mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), + // Two arrays with mirrored contents + (ExprKind::Array(left_exprs), ExprKind::Array(right_exprs)) + => left_exprs.iter().zip(right_exprs.iter()).all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), + // The two exprs are function calls. + // Check to see that the function itself and its arguments are mirrored + (ExprKind::Call(left_expr, left_args), ExprKind::Call(right_expr, right_args)) + => mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident) + && left_args.iter().zip(right_args.iter()).all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), + // The two exprs are method calls. + // Check to see that the function is the same and the arguments are mirrored + // This is enough because the receiver of the method is listed in the arguments + (ExprKind::MethodCall(left_segment, _, left_args), ExprKind::MethodCall(right_segment, _, right_args)) + => left_segment.ident == right_segment.ident + && left_args.iter().zip(right_args.iter()).all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), + // Two tuples with mirrored contents + (ExprKind::Tup(left_exprs), ExprKind::Tup(right_exprs)) + => left_exprs.iter().zip(right_exprs.iter()).all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), + // Two binary ops, which are the same operation and which have mirrored arguments + (ExprKind::Binary(left_op, left_left, left_right), ExprKind::Binary(right_op, right_left, right_right)) + => left_op.node == right_op.node + && mirrored_exprs(cx, left_left, a_ident, right_left, b_ident) + && mirrored_exprs(cx, left_right, a_ident, right_right, b_ident), + // Two unary ops, which are the same operation and which have the same argument + (ExprKind::Unary(left_op, left_expr), ExprKind::Unary(right_op, right_expr)) + => left_op == right_op && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), + // The two exprs are literals of some kind + (ExprKind::Lit(left_lit), ExprKind::Lit(right_lit)) => left_lit.node == right_lit.node, + (ExprKind::Cast(left_expr, _), ExprKind::Cast(right_expr, _)) + => mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), + (ExprKind::DropTemps(left), ExprKind::DropTemps(right)) => mirrored_exprs(cx, left, a_ident, right, b_ident), + (ExprKind::Block(left, _), ExprKind::Block(right, _)) => mirrored_blocks(cx, left, a_ident, right, b_ident), + (ExprKind::Field(left_expr, left_ident), ExprKind::Field(right_expr, right_ident)) + => left_ident.name == right_ident.name && mirrored_exprs(cx, left_expr, a_ident, right_expr, right_ident), + // The two exprs are `a` and `b`, directly + (ExprKind::Path(QPath::Resolved(_, Path { segments: &[PathSegment { ident: left_ident, .. }], .. },)), + ExprKind::Path(QPath::Resolved(_, Path { segments: &[PathSegment { ident: right_ident, .. }], .. },)), + ) => &left_ident == a_ident && &right_ident == b_ident, + // The two exprs are Paths to the same name (which is neither a nor b) + (ExprKind::Path(QPath::Resolved(_, Path { segments: left_segments, .. })), + ExprKind::Path(QPath::Resolved(_, Path { segments: right_segments, .. }))) + => left_segments.iter().zip(right_segments.iter()).all(|(left, right)| left.ident == right.ident) + && left_segments.iter().all(|seg| &seg.ident != a_ident && &seg.ident != b_ident), + // Matching expressions, but one or both is borrowed + (ExprKind::AddrOf(left_kind, Mutability::Not, left_expr), ExprKind::AddrOf(right_kind, Mutability::Not, right_expr)) + => left_kind == right_kind && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), + (_, ExprKind::AddrOf(_, Mutability::Not, right_expr)) + => mirrored_exprs(cx, a_expr, a_ident, right_expr, b_ident), + (ExprKind::AddrOf(_, Mutability::Not, left_expr), _) + => mirrored_exprs(cx, left_expr, a_ident, b_expr, b_ident), + // _ => false, + (left, right) => { + println!("{:?}\n{:?}", left, right); + false + }, + } +} + +/// Detect if the two blocks are mirrored (identical, except one +/// contains a and the other replaces it with b) +fn mirrored_blocks(cx: &LateContext<'_, '_>, a_block: &Block<'_>, a_ident: &Ident, b_block: &Block<'_>, b_ident: &Ident) -> bool { + match (a_block, b_block) { + (Block { stmts: left_stmts, expr: left_expr, .. }, + Block { stmts: right_stmts, expr: right_expr, .. }) + => left_stmts.iter().zip(right_stmts.iter()).all(|(left, right)| match (&left.kind, &right.kind) { + (StmtKind::Expr(left_expr), StmtKind::Expr(right_expr)) => mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), + (StmtKind::Semi(left_expr), StmtKind::Semi(right_expr)) => mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), + (StmtKind::Item(left_item), StmtKind::Item(right_item)) => left_item.id == right_item.id, + (StmtKind::Local(left), StmtKind::Local(right)) => mirrored_locals(cx, left, a_ident, right, b_ident), + _ => false, + }) && match (left_expr, right_expr) { + (None, None) => true, + (Some(left), Some(right)) => mirrored_exprs(cx, left, a_ident, right, b_ident), + _ => false, + }, + } +} + +/// Check that the two "Local"s (let statements) are equal +fn mirrored_locals(cx: &LateContext<'_, '_>, a_local: &Local<'_>, a_ident: &Ident, b_local: &Local<'_>, b_ident: &Ident) -> bool { + match (a_local, b_local) { + (Local { pat: left_pat, init: left_expr, .. }, Local { pat: right_pat, init: right_expr, .. }) + => match (left_expr, right_expr) { + (None, None) => true, + (Some(left), Some(right)) => mirrored_exprs(cx, left, a_ident, right, b_ident), + _ => false, + }, + } +} + fn detect_lint(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option { if_chain! { if let ExprKind::MethodCall(name_ident, _, args) = &expr.kind; if let name = name_ident.ident.name.to_ident_string(); if name == "sort_by" || name == "sort_unstable_by"; - if let [vec, Expr { kind: ExprKind::Closure(_, closure_decl, closure_body_id, _, _), .. }] = args; - if closure_decl.inputs.len() == 2; - if match_type(cx, &cx.tables.expr_ty(vec), &paths::VEC); + if let [vec, Expr { kind: ExprKind::Closure(_, _, closure_body_id, _, _), .. }] = args; + if utils::match_type(cx, &cx.tables.expr_ty(vec), &paths::VEC); + if let closure_body = cx.tcx.hir().body(*closure_body_id); + if let &[ + Param { pat: Pat { kind: PatKind::Binding(_, _, a_ident, _), .. }, ..}, + Param { pat: Pat { kind: PatKind::Binding(_, _, b_ident, _), .. }, .. } + ] = &closure_body.params; + if let ExprKind::MethodCall(method_path, _, [ref b_expr, ref a_expr]) = &closure_body.value.kind; + if method_path.ident.name.to_ident_string() == "cmp"; + if mirrored_exprs(&cx, &a_expr, &a_ident, &b_expr, &b_ident); then { let vec_name = Sugg::hir(cx, &args[0], "..").to_string(); let unstable = name == "sort_unstable_by"; - Some(LintTrigger { vec_name, unstable, closure_arg: "e".to_string(), closure_reverse_body: "e".to_string() }) + let closure_arg = a_ident.name.to_ident_string(); + let closure_reverse_body = Sugg::hir(cx, &a_expr, "..").to_string(); + Some(LintTrigger { vec_name, unstable, closure_arg, closure_reverse_body }) } else { None } @@ -60,18 +165,8 @@ fn detect_lint(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option impl LateLintPass<'_, '_> for SortByKeyReverse { fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr<'_>) { - println!("{:?}", expr); - span_lint_and_sugg( - cx, - SORT_BY_KEY_REVERSE, - expr.span, - "use Vec::sort_by_key here instead", - "try", - String::from("being a better person"), - Applicability::MachineApplicable, - ); if let Some(trigger) = detect_lint(cx, expr) { - span_lint_and_sugg( + utils::span_lint_and_sugg( cx, SORT_BY_KEY_REVERSE, expr.span, diff --git a/tests/ui/sort_by_key_reverse.fixed b/tests/ui/sort_by_key_reverse.fixed index e69de29bb2d..4b18a073e1a 100644 --- a/tests/ui/sort_by_key_reverse.fixed +++ b/tests/ui/sort_by_key_reverse.fixed @@ -0,0 +1,9 @@ +// run-rustfix +#![warn(clippy::sort_by_key_reverse)] + +fn main() { + let mut vec: Vec = vec![3, 6, 1, 2, 5]; + vec.sort_by_key(|a| Reverse(a)); + vec.sort_by_key(|a| Reverse(&(a+5).abs())); + vec.sort_by_key(|a| Reverse(&-a)); +} diff --git a/tests/ui/sort_by_key_reverse.rs b/tests/ui/sort_by_key_reverse.rs index c0350f243c7..f4fb70b7b1d 100644 --- a/tests/ui/sort_by_key_reverse.rs +++ b/tests/ui/sort_by_key_reverse.rs @@ -1,6 +1,9 @@ +// run-rustfix #![warn(clippy::sort_by_key_reverse)] fn main() { - let mut vec = vec![3, 6, 1, 2, 5]; + let mut vec: Vec = vec![3, 6, 1, 2, 5]; vec.sort_by(|a, b| b.cmp(a)); + vec.sort_by(|a, b| (b + 5).abs().cmp(&(a+5).abs())); + vec.sort_by(|a, b| (-b).cmp(&-a)); } diff --git a/tests/ui/sort_by_key_reverse.stderr b/tests/ui/sort_by_key_reverse.stderr index e69de29bb2d..36a28c04b1c 100644 --- a/tests/ui/sort_by_key_reverse.stderr +++ b/tests/ui/sort_by_key_reverse.stderr @@ -0,0 +1,22 @@ +error: use Vec::sort_by_key here instead + --> $DIR/sort_by_key_reverse.rs:6:5 + | +LL | vec.sort_by(|a, b| b.cmp(a)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| Reverse(a))` + | + = note: `-D clippy::sort-by-key-reverse` implied by `-D warnings` + +error: use Vec::sort_by_key here instead + --> $DIR/sort_by_key_reverse.rs:7:5 + | +LL | vec.sort_by(|a, b| (b + 5).abs().cmp(&(a+5).abs())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| Reverse(&(a+5).abs()))` + +error: use Vec::sort_by_key here instead + --> $DIR/sort_by_key_reverse.rs:8:5 + | +LL | vec.sort_by(|a, b| (-b).cmp(&-a)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| Reverse(&-a))` + +error: aborting due to 3 previous errors + -- cgit 1.4.1-3-g733a5 From 943cb94dce8fca6f3a3f7f011a2a2f9f0a665b97 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Tue, 19 May 2020 22:57:27 -0700 Subject: Passes all tests now! --- clippy_lints/src/sort_by_key_reverse.rs | 72 ++++++++++----------------------- tests/ui/sort_by_key_reverse.fixed | 12 ++++-- tests/ui/sort_by_key_reverse.rs | 8 +++- tests/ui/sort_by_key_reverse.stderr | 14 +++---- 4 files changed, 45 insertions(+), 61 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/sort_by_key_reverse.rs b/clippy_lints/src/sort_by_key_reverse.rs index d70391999a0..31629a1dbc1 100644 --- a/clippy_lints/src/sort_by_key_reverse.rs +++ b/clippy_lints/src/sort_by_key_reverse.rs @@ -53,8 +53,12 @@ fn mirrored_exprs(cx: &LateContext<'_, '_>, a_expr: &Expr<'_>, a_ident: &Ident, // The two exprs are function calls. // Check to see that the function itself and its arguments are mirrored (ExprKind::Call(left_expr, left_args), ExprKind::Call(right_expr, right_args)) - => mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident) - && left_args.iter().zip(right_args.iter()).all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), + => { + // println!("{:?}\n{:?}\n", left_expr, left_args); + // println!("{:?}\n{:?}\n", right_expr, right_args); + mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident) + && left_args.iter().zip(right_args.iter()).all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)) + }, // The two exprs are method calls. // Check to see that the function is the same and the arguments are mirrored // This is enough because the receiver of the method is listed in the arguments @@ -74,21 +78,17 @@ fn mirrored_exprs(cx: &LateContext<'_, '_>, a_expr: &Expr<'_>, a_ident: &Ident, => left_op == right_op && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), // The two exprs are literals of some kind (ExprKind::Lit(left_lit), ExprKind::Lit(right_lit)) => left_lit.node == right_lit.node, - (ExprKind::Cast(left_expr, _), ExprKind::Cast(right_expr, _)) + (ExprKind::Cast(left_expr, left_ty), ExprKind::Cast(right_expr, right_ty)) => mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), (ExprKind::DropTemps(left), ExprKind::DropTemps(right)) => mirrored_exprs(cx, left, a_ident, right, b_ident), - (ExprKind::Block(left, _), ExprKind::Block(right, _)) => mirrored_blocks(cx, left, a_ident, right, b_ident), (ExprKind::Field(left_expr, left_ident), ExprKind::Field(right_expr, right_ident)) => left_ident.name == right_ident.name && mirrored_exprs(cx, left_expr, a_ident, right_expr, right_ident), - // The two exprs are `a` and `b`, directly - (ExprKind::Path(QPath::Resolved(_, Path { segments: &[PathSegment { ident: left_ident, .. }], .. },)), - ExprKind::Path(QPath::Resolved(_, Path { segments: &[PathSegment { ident: right_ident, .. }], .. },)), - ) => &left_ident == a_ident && &right_ident == b_ident, - // The two exprs are Paths to the same name (which is neither a nor b) + // Two paths: either one is a and the other is b, or they're identical to each other (ExprKind::Path(QPath::Resolved(_, Path { segments: left_segments, .. })), ExprKind::Path(QPath::Resolved(_, Path { segments: right_segments, .. }))) - => left_segments.iter().zip(right_segments.iter()).all(|(left, right)| left.ident == right.ident) - && left_segments.iter().all(|seg| &seg.ident != a_ident && &seg.ident != b_ident), + => (left_segments.iter().zip(right_segments.iter()).all(|(left, right)| left.ident == right.ident) + && left_segments.iter().all(|seg| &seg.ident != a_ident && &seg.ident != b_ident)) + || (left_segments.len() == 1 && &left_segments[0].ident == a_ident && right_segments.len() == 1 && &right_segments[0].ident == b_ident), // Matching expressions, but one or both is borrowed (ExprKind::AddrOf(left_kind, Mutability::Not, left_expr), ExprKind::AddrOf(right_kind, Mutability::Not, right_expr)) => left_kind == right_kind && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), @@ -96,43 +96,11 @@ fn mirrored_exprs(cx: &LateContext<'_, '_>, a_expr: &Expr<'_>, a_ident: &Ident, => mirrored_exprs(cx, a_expr, a_ident, right_expr, b_ident), (ExprKind::AddrOf(_, Mutability::Not, left_expr), _) => mirrored_exprs(cx, left_expr, a_ident, b_expr, b_ident), - // _ => false, - (left, right) => { - println!("{:?}\n{:?}", left, right); - false - }, - } -} - -/// Detect if the two blocks are mirrored (identical, except one -/// contains a and the other replaces it with b) -fn mirrored_blocks(cx: &LateContext<'_, '_>, a_block: &Block<'_>, a_ident: &Ident, b_block: &Block<'_>, b_ident: &Ident) -> bool { - match (a_block, b_block) { - (Block { stmts: left_stmts, expr: left_expr, .. }, - Block { stmts: right_stmts, expr: right_expr, .. }) - => left_stmts.iter().zip(right_stmts.iter()).all(|(left, right)| match (&left.kind, &right.kind) { - (StmtKind::Expr(left_expr), StmtKind::Expr(right_expr)) => mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), - (StmtKind::Semi(left_expr), StmtKind::Semi(right_expr)) => mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), - (StmtKind::Item(left_item), StmtKind::Item(right_item)) => left_item.id == right_item.id, - (StmtKind::Local(left), StmtKind::Local(right)) => mirrored_locals(cx, left, a_ident, right, b_ident), - _ => false, - }) && match (left_expr, right_expr) { - (None, None) => true, - (Some(left), Some(right)) => mirrored_exprs(cx, left, a_ident, right, b_ident), - _ => false, - }, - } -} - -/// Check that the two "Local"s (let statements) are equal -fn mirrored_locals(cx: &LateContext<'_, '_>, a_local: &Local<'_>, a_ident: &Ident, b_local: &Local<'_>, b_ident: &Ident) -> bool { - match (a_local, b_local) { - (Local { pat: left_pat, init: left_expr, .. }, Local { pat: right_pat, init: right_expr, .. }) - => match (left_expr, right_expr) { - (None, None) => true, - (Some(left), Some(right)) => mirrored_exprs(cx, left, a_ident, right, b_ident), - _ => false, - }, + _ => false, + // (left, right) => { + // println!("{:?}\n{:?}", left, right); + // false + // }, } } @@ -154,8 +122,12 @@ fn detect_lint(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option then { let vec_name = Sugg::hir(cx, &args[0], "..").to_string(); let unstable = name == "sort_unstable_by"; - let closure_arg = a_ident.name.to_ident_string(); - let closure_reverse_body = Sugg::hir(cx, &a_expr, "..").to_string(); + let closure_arg = format!("&{}", b_ident.name.to_ident_string()); + let closure_reverse_body = Sugg::hir(cx, &b_expr, "..").to_string(); + // Get rid of parentheses, because they aren't needed anymore + // while closure_reverse_body.chars().next() == Some('(') && closure_reverse_body.chars().last() == Some(')') { + // closure_reverse_body = String::from(&closure_reverse_body[1..closure_reverse_body.len()-1]); + // } Some(LintTrigger { vec_name, unstable, closure_arg, closure_reverse_body }) } else { None diff --git a/tests/ui/sort_by_key_reverse.fixed b/tests/ui/sort_by_key_reverse.fixed index 4b18a073e1a..d536dc385d5 100644 --- a/tests/ui/sort_by_key_reverse.fixed +++ b/tests/ui/sort_by_key_reverse.fixed @@ -1,9 +1,15 @@ // run-rustfix #![warn(clippy::sort_by_key_reverse)] +use std::cmp::Reverse; + +fn id(x: isize) -> isize { + x +} + fn main() { let mut vec: Vec = vec![3, 6, 1, 2, 5]; - vec.sort_by_key(|a| Reverse(a)); - vec.sort_by_key(|a| Reverse(&(a+5).abs())); - vec.sort_by_key(|a| Reverse(&-a)); + vec.sort_by_key(|&b| Reverse(b)); + vec.sort_by_key(|&b| Reverse((b + 5).abs())); + vec.sort_by_key(|&b| Reverse(id(-b))); } diff --git a/tests/ui/sort_by_key_reverse.rs b/tests/ui/sort_by_key_reverse.rs index f4fb70b7b1d..9c42d401755 100644 --- a/tests/ui/sort_by_key_reverse.rs +++ b/tests/ui/sort_by_key_reverse.rs @@ -1,9 +1,15 @@ // run-rustfix #![warn(clippy::sort_by_key_reverse)] +use std::cmp::Reverse; + +fn id(x: isize) -> isize { + x +} + fn main() { let mut vec: Vec = vec![3, 6, 1, 2, 5]; vec.sort_by(|a, b| b.cmp(a)); vec.sort_by(|a, b| (b + 5).abs().cmp(&(a+5).abs())); - vec.sort_by(|a, b| (-b).cmp(&-a)); + vec.sort_by(|a, b| id(-b).cmp(&id(-a))); } diff --git a/tests/ui/sort_by_key_reverse.stderr b/tests/ui/sort_by_key_reverse.stderr index 36a28c04b1c..3d26ddae78a 100644 --- a/tests/ui/sort_by_key_reverse.stderr +++ b/tests/ui/sort_by_key_reverse.stderr @@ -1,22 +1,22 @@ error: use Vec::sort_by_key here instead - --> $DIR/sort_by_key_reverse.rs:6:5 + --> $DIR/sort_by_key_reverse.rs:12:5 | LL | vec.sort_by(|a, b| b.cmp(a)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| Reverse(a))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(b))` | = note: `-D clippy::sort-by-key-reverse` implied by `-D warnings` error: use Vec::sort_by_key here instead - --> $DIR/sort_by_key_reverse.rs:7:5 + --> $DIR/sort_by_key_reverse.rs:13:5 | LL | vec.sort_by(|a, b| (b + 5).abs().cmp(&(a+5).abs())); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| Reverse(&(a+5).abs()))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse((b + 5).abs()))` error: use Vec::sort_by_key here instead - --> $DIR/sort_by_key_reverse.rs:8:5 + --> $DIR/sort_by_key_reverse.rs:14:5 | -LL | vec.sort_by(|a, b| (-b).cmp(&-a)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| Reverse(&-a))` +LL | vec.sort_by(|a, b| id(-b).cmp(&id(-a))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(id(-b)))` error: aborting due to 3 previous errors -- cgit 1.4.1-3-g733a5 From 955a25ee7db234a8ab697176a433070702aabe59 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Wed, 20 May 2020 09:23:00 -0700 Subject: Added negative test cases and ran cargo dev fmt --- clippy_lints/src/sort_by_key_reverse.rs | 125 +++++++++++++++++++++----------- tests/ui/sort_by_key_reverse.fixed | 7 ++ tests/ui/sort_by_key_reverse.rs | 9 ++- tests/ui/sort_by_key_reverse.stderr | 4 +- 4 files changed, 100 insertions(+), 45 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/sort_by_key_reverse.rs b/clippy_lints/src/sort_by_key_reverse.rs index 31629a1dbc1..ea850955db1 100644 --- a/clippy_lints/src/sort_by_key_reverse.rs +++ b/clippy_lints/src/sort_by_key_reverse.rs @@ -3,7 +3,7 @@ use crate::utils::paths; use crate::utils::sugg::Sugg; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::*; +use rustc_hir::{Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::Ident; @@ -43,64 +43,105 @@ struct LintTrigger { /// Detect if the two expressions are mirrored (identical, except one /// contains a and the other replaces it with b) -fn mirrored_exprs(cx: &LateContext<'_, '_>, a_expr: &Expr<'_>, a_ident: &Ident, b_expr: &Expr<'_>, b_ident: &Ident) -> bool { +fn mirrored_exprs( + cx: &LateContext<'_, '_>, + a_expr: &Expr<'_>, + a_ident: &Ident, + b_expr: &Expr<'_>, + b_ident: &Ident, +) -> bool { match (&a_expr.kind, &b_expr.kind) { // Two boxes with mirrored contents - (ExprKind::Box(left_expr), ExprKind::Box(right_expr)) => mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), + (ExprKind::Box(left_expr), ExprKind::Box(right_expr)) => { + mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident) + }, // Two arrays with mirrored contents - (ExprKind::Array(left_exprs), ExprKind::Array(right_exprs)) - => left_exprs.iter().zip(right_exprs.iter()).all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), + (ExprKind::Array(left_exprs), ExprKind::Array(right_exprs)) => left_exprs + .iter() + .zip(right_exprs.iter()) + .all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), // The two exprs are function calls. // Check to see that the function itself and its arguments are mirrored - (ExprKind::Call(left_expr, left_args), ExprKind::Call(right_expr, right_args)) - => { - // println!("{:?}\n{:?}\n", left_expr, left_args); - // println!("{:?}\n{:?}\n", right_expr, right_args); - mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident) - && left_args.iter().zip(right_args.iter()).all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)) - }, + (ExprKind::Call(left_expr, left_args), ExprKind::Call(right_expr, right_args)) => { + mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident) + && left_args + .iter() + .zip(right_args.iter()) + .all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)) + }, // The two exprs are method calls. // Check to see that the function is the same and the arguments are mirrored // This is enough because the receiver of the method is listed in the arguments - (ExprKind::MethodCall(left_segment, _, left_args), ExprKind::MethodCall(right_segment, _, right_args)) - => left_segment.ident == right_segment.ident - && left_args.iter().zip(right_args.iter()).all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), + (ExprKind::MethodCall(left_segment, _, left_args), ExprKind::MethodCall(right_segment, _, right_args)) => { + left_segment.ident == right_segment.ident + && left_args + .iter() + .zip(right_args.iter()) + .all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)) + }, // Two tuples with mirrored contents - (ExprKind::Tup(left_exprs), ExprKind::Tup(right_exprs)) - => left_exprs.iter().zip(right_exprs.iter()).all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), + (ExprKind::Tup(left_exprs), ExprKind::Tup(right_exprs)) => left_exprs + .iter() + .zip(right_exprs.iter()) + .all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), // Two binary ops, which are the same operation and which have mirrored arguments - (ExprKind::Binary(left_op, left_left, left_right), ExprKind::Binary(right_op, right_left, right_right)) - => left_op.node == right_op.node + (ExprKind::Binary(left_op, left_left, left_right), ExprKind::Binary(right_op, right_left, right_right)) => { + left_op.node == right_op.node && mirrored_exprs(cx, left_left, a_ident, right_left, b_ident) - && mirrored_exprs(cx, left_right, a_ident, right_right, b_ident), + && mirrored_exprs(cx, left_right, a_ident, right_right, b_ident) + }, // Two unary ops, which are the same operation and which have the same argument - (ExprKind::Unary(left_op, left_expr), ExprKind::Unary(right_op, right_expr)) - => left_op == right_op && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), + (ExprKind::Unary(left_op, left_expr), ExprKind::Unary(right_op, right_expr)) => { + left_op == right_op && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident) + }, // The two exprs are literals of some kind (ExprKind::Lit(left_lit), ExprKind::Lit(right_lit)) => left_lit.node == right_lit.node, - (ExprKind::Cast(left_expr, left_ty), ExprKind::Cast(right_expr, right_ty)) - => mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), - (ExprKind::DropTemps(left), ExprKind::DropTemps(right)) => mirrored_exprs(cx, left, a_ident, right, b_ident), - (ExprKind::Field(left_expr, left_ident), ExprKind::Field(right_expr, right_ident)) - => left_ident.name == right_ident.name && mirrored_exprs(cx, left_expr, a_ident, right_expr, right_ident), + (ExprKind::Cast(left, _), ExprKind::Cast(right, _)) => mirrored_exprs(cx, left, a_ident, right, b_ident), + (ExprKind::DropTemps(left_block), ExprKind::DropTemps(right_block)) => { + mirrored_exprs(cx, left_block, a_ident, right_block, b_ident) + }, + (ExprKind::Field(left_expr, left_ident), ExprKind::Field(right_expr, right_ident)) => { + left_ident.name == right_ident.name && mirrored_exprs(cx, left_expr, a_ident, right_expr, right_ident) + }, // Two paths: either one is a and the other is b, or they're identical to each other - (ExprKind::Path(QPath::Resolved(_, Path { segments: left_segments, .. })), - ExprKind::Path(QPath::Resolved(_, Path { segments: right_segments, .. }))) - => (left_segments.iter().zip(right_segments.iter()).all(|(left, right)| left.ident == right.ident) - && left_segments.iter().all(|seg| &seg.ident != a_ident && &seg.ident != b_ident)) - || (left_segments.len() == 1 && &left_segments[0].ident == a_ident && right_segments.len() == 1 && &right_segments[0].ident == b_ident), + ( + ExprKind::Path(QPath::Resolved( + _, + Path { + segments: left_segments, + .. + }, + )), + ExprKind::Path(QPath::Resolved( + _, + Path { + segments: right_segments, + .. + }, + )), + ) => { + (left_segments + .iter() + .zip(right_segments.iter()) + .all(|(left, right)| left.ident == right.ident) + && left_segments + .iter() + .all(|seg| &seg.ident != a_ident && &seg.ident != b_ident)) + || (left_segments.len() == 1 + && &left_segments[0].ident == a_ident + && right_segments.len() == 1 + && &right_segments[0].ident == b_ident) + }, // Matching expressions, but one or both is borrowed - (ExprKind::AddrOf(left_kind, Mutability::Not, left_expr), ExprKind::AddrOf(right_kind, Mutability::Not, right_expr)) - => left_kind == right_kind && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), - (_, ExprKind::AddrOf(_, Mutability::Not, right_expr)) - => mirrored_exprs(cx, a_expr, a_ident, right_expr, b_ident), - (ExprKind::AddrOf(_, Mutability::Not, left_expr), _) - => mirrored_exprs(cx, left_expr, a_ident, b_expr, b_ident), + ( + ExprKind::AddrOf(left_kind, Mutability::Not, left_expr), + ExprKind::AddrOf(right_kind, Mutability::Not, right_expr), + ) => left_kind == right_kind && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), + (_, ExprKind::AddrOf(_, Mutability::Not, right_expr)) => { + mirrored_exprs(cx, a_expr, a_ident, right_expr, b_ident) + }, + (ExprKind::AddrOf(_, Mutability::Not, left_expr), _) => mirrored_exprs(cx, left_expr, a_ident, b_expr, b_ident), _ => false, - // (left, right) => { - // println!("{:?}\n{:?}", left, right); - // false - // }, } } diff --git a/tests/ui/sort_by_key_reverse.fixed b/tests/ui/sort_by_key_reverse.fixed index d536dc385d5..722675a6b71 100644 --- a/tests/ui/sort_by_key_reverse.fixed +++ b/tests/ui/sort_by_key_reverse.fixed @@ -12,4 +12,11 @@ fn main() { vec.sort_by_key(|&b| Reverse(b)); vec.sort_by_key(|&b| Reverse((b + 5).abs())); vec.sort_by_key(|&b| Reverse(id(-b))); + // Negative examples (shouldn't be changed) + let c = &7; + vec.sort_by(|a, b| (b - a).cmp(&(a - b))); + vec.sort_by(|_, b| b.cmp(&5)); + vec.sort_by(|_, b| b.cmp(c)); + vec.sort_by(|a, _| a.cmp(c)); + vec.sort_by(|a, b| a.cmp(b)); } diff --git a/tests/ui/sort_by_key_reverse.rs b/tests/ui/sort_by_key_reverse.rs index 9c42d401755..601621ffa9f 100644 --- a/tests/ui/sort_by_key_reverse.rs +++ b/tests/ui/sort_by_key_reverse.rs @@ -10,6 +10,13 @@ fn id(x: isize) -> isize { fn main() { let mut vec: Vec = vec![3, 6, 1, 2, 5]; vec.sort_by(|a, b| b.cmp(a)); - vec.sort_by(|a, b| (b + 5).abs().cmp(&(a+5).abs())); + vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); vec.sort_by(|a, b| id(-b).cmp(&id(-a))); + // Negative examples (shouldn't be changed) + let c = &7; + vec.sort_by(|a, b| (b - a).cmp(&(a - b))); + vec.sort_by(|_, b| b.cmp(&5)); + vec.sort_by(|_, b| b.cmp(c)); + vec.sort_by(|a, _| a.cmp(c)); + vec.sort_by(|a, b| a.cmp(b)); } diff --git a/tests/ui/sort_by_key_reverse.stderr b/tests/ui/sort_by_key_reverse.stderr index 3d26ddae78a..b757c8a6176 100644 --- a/tests/ui/sort_by_key_reverse.stderr +++ b/tests/ui/sort_by_key_reverse.stderr @@ -9,8 +9,8 @@ LL | vec.sort_by(|a, b| b.cmp(a)); error: use Vec::sort_by_key here instead --> $DIR/sort_by_key_reverse.rs:13: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()))` +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/sort_by_key_reverse.rs:14:5 -- cgit 1.4.1-3-g733a5 From 059e8edd15401d5544260e4058731dc8818578d5 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Sun, 24 May 2020 19:45:41 -0700 Subject: Detect also a non-reversed comparison --- clippy_lints/src/lib.rs | 10 +- clippy_lints/src/sort_by_key.rs | 207 ++++++++++++++++++++++++++++++++ clippy_lints/src/sort_by_key_reverse.rs | 199 ------------------------------ tests/ui/sort_by_key.fixed | 26 ++++ tests/ui/sort_by_key.rs | 26 ++++ tests/ui/sort_by_key.stderr | 48 ++++++++ tests/ui/sort_by_key_reverse.fixed | 22 ---- tests/ui/sort_by_key_reverse.rs | 22 ---- tests/ui/sort_by_key_reverse.stderr | 22 ---- 9 files changed, 312 insertions(+), 270 deletions(-) create mode 100644 clippy_lints/src/sort_by_key.rs delete mode 100644 clippy_lints/src/sort_by_key_reverse.rs create mode 100644 tests/ui/sort_by_key.fixed create mode 100644 tests/ui/sort_by_key.rs create mode 100644 tests/ui/sort_by_key.stderr delete mode 100644 tests/ui/sort_by_key_reverse.fixed delete mode 100644 tests/ui/sort_by_key_reverse.rs delete mode 100644 tests/ui/sort_by_key_reverse.stderr (limited to 'tests') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index e7a4c1ecaa9..9e826316f21 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -304,7 +304,7 @@ mod serde_api; mod shadow; mod single_component_path_imports; mod slow_vector_initialization; -mod sort_by_key_reverse; +mod sort_by_key; mod strings; mod suspicious_trait_impl; mod swap; @@ -780,7 +780,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &shadow::SHADOW_UNRELATED, &single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, &slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, - &sort_by_key_reverse::SORT_BY_KEY_REVERSE, + &sort_by_key::SORT_BY_KEY, &strings::STRING_ADD, &strings::STRING_ADD_ASSIGN, &strings::STRING_LIT_AS_BYTES, @@ -998,7 +998,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box ptr_offset_with_cast::PtrOffsetWithCast); store.register_late_pass(|| box redundant_clone::RedundantClone); store.register_late_pass(|| box slow_vector_initialization::SlowVectorInit); - store.register_late_pass(|| box sort_by_key_reverse::SortByKeyReverse); + store.register_late_pass(|| box sort_by_key::SortByKey); store.register_late_pass(|| box types::RefToMut); store.register_late_pass(|| box assertions_on_constants::AssertionsOnConstants); store.register_late_pass(|| box missing_const_for_fn::MissingConstForFn); @@ -1394,7 +1394,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&serde_api::SERDE_API_MISUSE), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), - LintId::of(&sort_by_key_reverse::SORT_BY_KEY_REVERSE), + LintId::of(&sort_by_key::SORT_BY_KEY), LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), @@ -1596,7 +1596,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ranges::RANGE_ZIP_WITH_LEN), LintId::of(&reference::DEREF_ADDROF), LintId::of(&reference::REF_IN_DEREF), - LintId::of(&sort_by_key_reverse::SORT_BY_KEY_REVERSE), + LintId::of(&sort_by_key::SORT_BY_KEY), LintId::of(&swap::MANUAL_SWAP), LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT), LintId::of(&transmute::CROSSPOINTER_TRANSMUTE), diff --git a/clippy_lints/src/sort_by_key.rs b/clippy_lints/src/sort_by_key.rs new file mode 100644 index 00000000000..109845a28f4 --- /dev/null +++ b/clippy_lints/src/sort_by_key.rs @@ -0,0 +1,207 @@ +use crate::utils; +use crate::utils::paths; +use crate::utils::sugg::Sugg; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::Ident; + +declare_clippy_lint! { + /// **What it does:** + /// Detects when people use `Vec::sort_by` and pass in a function + /// which compares the two arguments, either directly or indirectly. + /// + /// **Why is this bad?** + /// It is more clear to use `Vec::sort_by_key` (or + /// `Vec::sort_by_key` and `std::cmp::Reverse` if necessary) than + /// using + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// vec.sort_by(|a, b| a.foo().cmp(b.foo())); + /// ``` + /// Use instead: + /// ```rust + /// vec.sort_by_key(|a| a.foo()); + /// ``` + pub SORT_BY_KEY, + complexity, + "Use of `Vec::sort_by` when `Vec::sort_by_key` would be clearer" +} + +declare_lint_pass!(SortByKey => [SORT_BY_KEY]); + +struct LintTrigger { + vec_name: String, + closure_arg: String, + closure_body: String, + unstable: bool, +} + +/// Detect if the two expressions are mirrored (identical, except one +/// contains a and the other replaces it with b) +fn mirrored_exprs( + cx: &LateContext<'_, '_>, + a_expr: &Expr<'_>, + a_ident: &Ident, + b_expr: &Expr<'_>, + b_ident: &Ident, +) -> bool { + match (&a_expr.kind, &b_expr.kind) { + // Two boxes with mirrored contents + (ExprKind::Box(left_expr), ExprKind::Box(right_expr)) => { + mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident) + }, + // Two arrays with mirrored contents + (ExprKind::Array(left_exprs), ExprKind::Array(right_exprs)) => left_exprs + .iter() + .zip(right_exprs.iter()) + .all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), + // The two exprs are function calls. + // Check to see that the function itself and its arguments are mirrored + (ExprKind::Call(left_expr, left_args), ExprKind::Call(right_expr, right_args)) => { + mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident) + && left_args + .iter() + .zip(right_args.iter()) + .all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)) + }, + // The two exprs are method calls. + // Check to see that the function is the same and the arguments are mirrored + // This is enough because the receiver of the method is listed in the arguments + (ExprKind::MethodCall(left_segment, _, left_args), ExprKind::MethodCall(right_segment, _, right_args)) => { + left_segment.ident == right_segment.ident + && left_args + .iter() + .zip(right_args.iter()) + .all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)) + }, + // Two tuples with mirrored contents + (ExprKind::Tup(left_exprs), ExprKind::Tup(right_exprs)) => left_exprs + .iter() + .zip(right_exprs.iter()) + .all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), + // Two binary ops, which are the same operation and which have mirrored arguments + (ExprKind::Binary(left_op, left_left, left_right), ExprKind::Binary(right_op, right_left, right_right)) => { + left_op.node == right_op.node + && mirrored_exprs(cx, left_left, a_ident, right_left, b_ident) + && mirrored_exprs(cx, left_right, a_ident, right_right, b_ident) + }, + // Two unary ops, which are the same operation and which have the same argument + (ExprKind::Unary(left_op, left_expr), ExprKind::Unary(right_op, right_expr)) => { + left_op == right_op && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident) + }, + // The two exprs are literals of some kind + (ExprKind::Lit(left_lit), ExprKind::Lit(right_lit)) => left_lit.node == right_lit.node, + (ExprKind::Cast(left, _), ExprKind::Cast(right, _)) => mirrored_exprs(cx, left, a_ident, right, b_ident), + (ExprKind::DropTemps(left_block), ExprKind::DropTemps(right_block)) => { + mirrored_exprs(cx, left_block, a_ident, right_block, b_ident) + }, + (ExprKind::Field(left_expr, left_ident), ExprKind::Field(right_expr, right_ident)) => { + left_ident.name == right_ident.name && mirrored_exprs(cx, left_expr, a_ident, right_expr, right_ident) + }, + // Two paths: either one is a and the other is b, or they're identical to each other + ( + ExprKind::Path(QPath::Resolved( + _, + Path { + segments: left_segments, + .. + }, + )), + ExprKind::Path(QPath::Resolved( + _, + Path { + segments: right_segments, + .. + }, + )), + ) => { + (left_segments + .iter() + .zip(right_segments.iter()) + .all(|(left, right)| left.ident == right.ident) + && left_segments + .iter() + .all(|seg| &seg.ident != a_ident && &seg.ident != b_ident)) + || (left_segments.len() == 1 + && &left_segments[0].ident == a_ident + && right_segments.len() == 1 + && &right_segments[0].ident == b_ident) + }, + // Matching expressions, but one or both is borrowed + ( + ExprKind::AddrOf(left_kind, Mutability::Not, left_expr), + ExprKind::AddrOf(right_kind, Mutability::Not, right_expr), + ) => left_kind == right_kind && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), + (_, ExprKind::AddrOf(_, Mutability::Not, right_expr)) => { + mirrored_exprs(cx, a_expr, a_ident, right_expr, b_ident) + }, + (ExprKind::AddrOf(_, Mutability::Not, left_expr), _) => mirrored_exprs(cx, left_expr, a_ident, b_expr, b_ident), + _ => false, + } +} + +fn detect_lint(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option { + if_chain! { + if let ExprKind::MethodCall(name_ident, _, args) = &expr.kind; + if let name = name_ident.ident.name.to_ident_string(); + if name == "sort_by" || name == "sort_unstable_by"; + if let [vec, Expr { kind: ExprKind::Closure(_, _, closure_body_id, _, _), .. }] = args; + if utils::match_type(cx, &cx.tables.expr_ty(vec), &paths::VEC); + if let closure_body = cx.tcx.hir().body(*closure_body_id); + if let &[ + Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..}, + Param { pat: Pat { kind: PatKind::Binding(_, _, right_ident, _), .. }, .. } + ] = &closure_body.params; + if let ExprKind::MethodCall(method_path, _, [ref left_expr, ref right_expr]) = &closure_body.value.kind; + if method_path.ident.name.to_ident_string() == "cmp"; + then { + let (closure_body, closure_arg) = if mirrored_exprs( + &cx, + &left_expr, + &left_ident, + &right_expr, + &right_ident + ) { + (Sugg::hir(cx, &left_expr, "..").to_string(), left_ident.name.to_string()) + } else if mirrored_exprs(&cx, &left_expr, &right_ident, &right_expr, &left_ident) { + (format!("Reverse({})", Sugg::hir(cx, &left_expr, "..").to_string()), right_ident.name.to_string()) + } else { + return None; + }; + let vec_name = Sugg::hir(cx, &args[0], "..").to_string(); + let unstable = name == "sort_unstable_by"; + Some(LintTrigger { vec_name, unstable, closure_arg, closure_body }) + } else { + None + } + } +} + +impl LateLintPass<'_, '_> for SortByKey { + fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr<'_>) { + if let Some(trigger) = detect_lint(cx, expr) { + utils::span_lint_and_sugg( + cx, + SORT_BY_KEY, + expr.span, + "use Vec::sort_by_key here instead", + "try", + format!( + "{}.sort{}_by_key(|&{}| {})", + trigger.vec_name, + if trigger.unstable { "_unstable" } else { "" }, + trigger.closure_arg, + trigger.closure_body, + ), + Applicability::MachineApplicable, + ); + } + } +} diff --git a/clippy_lints/src/sort_by_key_reverse.rs b/clippy_lints/src/sort_by_key_reverse.rs deleted file mode 100644 index ea850955db1..00000000000 --- a/clippy_lints/src/sort_by_key_reverse.rs +++ /dev/null @@ -1,199 +0,0 @@ -use crate::utils; -use crate::utils::paths; -use crate::utils::sugg::Sugg; -use if_chain::if_chain; -use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, QPath}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::symbol::Ident; - -declare_clippy_lint! { - /// **What it does:** - /// Detects when people use `Vec::sort_by` and pass in a function - /// which compares the second argument to the first. - /// - /// **Why is this bad?** - /// It is more clear to use `Vec::sort_by_key` and `std::cmp::Reverse` - /// - /// **Known problems:** None. - /// - /// **Example:** - /// - /// ```rust - /// vec.sort_by(|a, b| b.foo().cmp(&a.foo())); - /// ``` - /// Use instead: - /// ```rust - /// vec.sort_by_key(|e| Reverse(e.foo())); - /// ``` - pub SORT_BY_KEY_REVERSE, - complexity, - "Use of `Vec::sort_by` when `Vec::sort_by_key` would be clearer" -} - -declare_lint_pass!(SortByKeyReverse => [SORT_BY_KEY_REVERSE]); - -struct LintTrigger { - vec_name: String, - closure_arg: String, - closure_reverse_body: String, - unstable: bool, -} - -/// Detect if the two expressions are mirrored (identical, except one -/// contains a and the other replaces it with b) -fn mirrored_exprs( - cx: &LateContext<'_, '_>, - a_expr: &Expr<'_>, - a_ident: &Ident, - b_expr: &Expr<'_>, - b_ident: &Ident, -) -> bool { - match (&a_expr.kind, &b_expr.kind) { - // Two boxes with mirrored contents - (ExprKind::Box(left_expr), ExprKind::Box(right_expr)) => { - mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident) - }, - // Two arrays with mirrored contents - (ExprKind::Array(left_exprs), ExprKind::Array(right_exprs)) => left_exprs - .iter() - .zip(right_exprs.iter()) - .all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), - // The two exprs are function calls. - // Check to see that the function itself and its arguments are mirrored - (ExprKind::Call(left_expr, left_args), ExprKind::Call(right_expr, right_args)) => { - mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident) - && left_args - .iter() - .zip(right_args.iter()) - .all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)) - }, - // The two exprs are method calls. - // Check to see that the function is the same and the arguments are mirrored - // This is enough because the receiver of the method is listed in the arguments - (ExprKind::MethodCall(left_segment, _, left_args), ExprKind::MethodCall(right_segment, _, right_args)) => { - left_segment.ident == right_segment.ident - && left_args - .iter() - .zip(right_args.iter()) - .all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)) - }, - // Two tuples with mirrored contents - (ExprKind::Tup(left_exprs), ExprKind::Tup(right_exprs)) => left_exprs - .iter() - .zip(right_exprs.iter()) - .all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), - // Two binary ops, which are the same operation and which have mirrored arguments - (ExprKind::Binary(left_op, left_left, left_right), ExprKind::Binary(right_op, right_left, right_right)) => { - left_op.node == right_op.node - && mirrored_exprs(cx, left_left, a_ident, right_left, b_ident) - && mirrored_exprs(cx, left_right, a_ident, right_right, b_ident) - }, - // Two unary ops, which are the same operation and which have the same argument - (ExprKind::Unary(left_op, left_expr), ExprKind::Unary(right_op, right_expr)) => { - left_op == right_op && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident) - }, - // The two exprs are literals of some kind - (ExprKind::Lit(left_lit), ExprKind::Lit(right_lit)) => left_lit.node == right_lit.node, - (ExprKind::Cast(left, _), ExprKind::Cast(right, _)) => mirrored_exprs(cx, left, a_ident, right, b_ident), - (ExprKind::DropTemps(left_block), ExprKind::DropTemps(right_block)) => { - mirrored_exprs(cx, left_block, a_ident, right_block, b_ident) - }, - (ExprKind::Field(left_expr, left_ident), ExprKind::Field(right_expr, right_ident)) => { - left_ident.name == right_ident.name && mirrored_exprs(cx, left_expr, a_ident, right_expr, right_ident) - }, - // Two paths: either one is a and the other is b, or they're identical to each other - ( - ExprKind::Path(QPath::Resolved( - _, - Path { - segments: left_segments, - .. - }, - )), - ExprKind::Path(QPath::Resolved( - _, - Path { - segments: right_segments, - .. - }, - )), - ) => { - (left_segments - .iter() - .zip(right_segments.iter()) - .all(|(left, right)| left.ident == right.ident) - && left_segments - .iter() - .all(|seg| &seg.ident != a_ident && &seg.ident != b_ident)) - || (left_segments.len() == 1 - && &left_segments[0].ident == a_ident - && right_segments.len() == 1 - && &right_segments[0].ident == b_ident) - }, - // Matching expressions, but one or both is borrowed - ( - ExprKind::AddrOf(left_kind, Mutability::Not, left_expr), - ExprKind::AddrOf(right_kind, Mutability::Not, right_expr), - ) => left_kind == right_kind && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), - (_, ExprKind::AddrOf(_, Mutability::Not, right_expr)) => { - mirrored_exprs(cx, a_expr, a_ident, right_expr, b_ident) - }, - (ExprKind::AddrOf(_, Mutability::Not, left_expr), _) => mirrored_exprs(cx, left_expr, a_ident, b_expr, b_ident), - _ => false, - } -} - -fn detect_lint(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option { - if_chain! { - if let ExprKind::MethodCall(name_ident, _, args) = &expr.kind; - if let name = name_ident.ident.name.to_ident_string(); - if name == "sort_by" || name == "sort_unstable_by"; - if let [vec, Expr { kind: ExprKind::Closure(_, _, closure_body_id, _, _), .. }] = args; - if utils::match_type(cx, &cx.tables.expr_ty(vec), &paths::VEC); - if let closure_body = cx.tcx.hir().body(*closure_body_id); - if let &[ - Param { pat: Pat { kind: PatKind::Binding(_, _, a_ident, _), .. }, ..}, - Param { pat: Pat { kind: PatKind::Binding(_, _, b_ident, _), .. }, .. } - ] = &closure_body.params; - if let ExprKind::MethodCall(method_path, _, [ref b_expr, ref a_expr]) = &closure_body.value.kind; - if method_path.ident.name.to_ident_string() == "cmp"; - if mirrored_exprs(&cx, &a_expr, &a_ident, &b_expr, &b_ident); - then { - let vec_name = Sugg::hir(cx, &args[0], "..").to_string(); - let unstable = name == "sort_unstable_by"; - let closure_arg = format!("&{}", b_ident.name.to_ident_string()); - let closure_reverse_body = Sugg::hir(cx, &b_expr, "..").to_string(); - // Get rid of parentheses, because they aren't needed anymore - // while closure_reverse_body.chars().next() == Some('(') && closure_reverse_body.chars().last() == Some(')') { - // closure_reverse_body = String::from(&closure_reverse_body[1..closure_reverse_body.len()-1]); - // } - Some(LintTrigger { vec_name, unstable, closure_arg, closure_reverse_body }) - } else { - None - } - } -} - -impl LateLintPass<'_, '_> for SortByKeyReverse { - fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr<'_>) { - if let Some(trigger) = detect_lint(cx, expr) { - utils::span_lint_and_sugg( - cx, - SORT_BY_KEY_REVERSE, - expr.span, - "use Vec::sort_by_key here instead", - "try", - format!( - "{}.sort{}_by_key(|{}| Reverse({}))", - trigger.vec_name, - if trigger.unstable { "_unstable" } else { "" }, - trigger.closure_arg, - trigger.closure_reverse_body, - ), - Applicability::MachineApplicable, - ); - } - } -} diff --git a/tests/ui/sort_by_key.fixed b/tests/ui/sort_by_key.fixed new file mode 100644 index 00000000000..f6535c8d8f5 --- /dev/null +++ b/tests/ui/sort_by_key.fixed @@ -0,0 +1,26 @@ +// run-rustfix +#![warn(clippy::sort_by_key)] + +use std::cmp::Reverse; + +fn id(x: isize) -> isize { + x +} + +fn main() { + let mut vec: Vec = vec![3, 6, 1, 2, 5]; + // Forward examples + vec.sort_by_key(|&a| a); + vec.sort_by_key(|&a| (a + 5).abs()); + vec.sort_by_key(|&a| id(-a)); + // Reverse examples + vec.sort_by_key(|&b| Reverse(b)); + vec.sort_by_key(|&b| Reverse((b + 5).abs())); + vec.sort_by_key(|&b| Reverse(id(-b))); + // Negative examples (shouldn't be changed) + let c = &7; + vec.sort_by(|a, b| (b - a).cmp(&(a - b))); + vec.sort_by(|_, b| b.cmp(&5)); + vec.sort_by(|_, b| b.cmp(c)); + vec.sort_by(|a, _| a.cmp(c)); +} diff --git a/tests/ui/sort_by_key.rs b/tests/ui/sort_by_key.rs new file mode 100644 index 00000000000..953c573d406 --- /dev/null +++ b/tests/ui/sort_by_key.rs @@ -0,0 +1,26 @@ +// run-rustfix +#![warn(clippy::sort_by_key_reverse)] + +use std::cmp::Reverse; + +fn id(x: isize) -> isize { + x +} + +fn main() { + let mut vec: Vec = vec![3, 6, 1, 2, 5]; + // Forward examples + vec.sort_by(|a, b| a.cmp(b)); + vec.sort_by(|a, b| (a + 5).abs().cmp(&(b + 5).abs())); + vec.sort_by(|a, b| id(-a).cmp(&id(-b))); + // Reverse examples + vec.sort_by(|a, b| b.cmp(a)); + vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); + vec.sort_by(|a, b| id(-b).cmp(&id(-a))); + // Negative examples (shouldn't be changed) + let c = &7; + vec.sort_by(|a, b| (b - a).cmp(&(a - b))); + vec.sort_by(|_, b| b.cmp(&5)); + vec.sort_by(|_, b| b.cmp(c)); + vec.sort_by(|a, _| a.cmp(c)); +} diff --git a/tests/ui/sort_by_key.stderr b/tests/ui/sort_by_key.stderr new file mode 100644 index 00000000000..fa6a9a0fb10 --- /dev/null +++ b/tests/ui/sort_by_key.stderr @@ -0,0 +1,48 @@ +error: use Vec::sort_by_key here instead + --> $DIR/sort_by_key.rs:13:5 + | +LL | vec.sort_by(|a, b| a.cmp(b)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&a| a)` + | + = note: `-D clippy::sort-by-key` implied by `-D warnings` + +error: use Vec::sort_by_key here instead + --> $DIR/sort_by_key.rs:14: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/sort_by_key.rs:15:5 + | +LL | vec.sort_by(|a, b| id(-a).cmp(&id(-b))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&a| id(-a))` + +error: use Vec::sort_by_key here instead + --> $DIR/sort_by_key.rs:17:5 + | +LL | vec.sort_by(|a, b| b.cmp(a)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(b))` + +error: use Vec::sort_by_key here instead + --> $DIR/sort_by_key.rs:18: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/sort_by_key.rs:19:5 + | +LL | vec.sort_by(|a, b| id(-b).cmp(&id(-a))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(id(-b)))` + +error: unknown clippy lint: clippy::sort_by_key_reverse + --> $DIR/sort_by_key.rs:2:9 + | +LL | #![warn(clippy::sort_by_key_reverse)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::sort_by_key` + | + = note: `-D clippy::unknown-clippy-lints` implied by `-D warnings` + +error: aborting due to 7 previous errors + diff --git a/tests/ui/sort_by_key_reverse.fixed b/tests/ui/sort_by_key_reverse.fixed deleted file mode 100644 index 722675a6b71..00000000000 --- a/tests/ui/sort_by_key_reverse.fixed +++ /dev/null @@ -1,22 +0,0 @@ -// run-rustfix -#![warn(clippy::sort_by_key_reverse)] - -use std::cmp::Reverse; - -fn id(x: isize) -> isize { - x -} - -fn main() { - let mut vec: Vec = vec![3, 6, 1, 2, 5]; - vec.sort_by_key(|&b| Reverse(b)); - vec.sort_by_key(|&b| Reverse((b + 5).abs())); - vec.sort_by_key(|&b| Reverse(id(-b))); - // Negative examples (shouldn't be changed) - let c = &7; - vec.sort_by(|a, b| (b - a).cmp(&(a - b))); - vec.sort_by(|_, b| b.cmp(&5)); - vec.sort_by(|_, b| b.cmp(c)); - vec.sort_by(|a, _| a.cmp(c)); - vec.sort_by(|a, b| a.cmp(b)); -} diff --git a/tests/ui/sort_by_key_reverse.rs b/tests/ui/sort_by_key_reverse.rs deleted file mode 100644 index 601621ffa9f..00000000000 --- a/tests/ui/sort_by_key_reverse.rs +++ /dev/null @@ -1,22 +0,0 @@ -// run-rustfix -#![warn(clippy::sort_by_key_reverse)] - -use std::cmp::Reverse; - -fn id(x: isize) -> isize { - x -} - -fn main() { - let mut vec: Vec = vec![3, 6, 1, 2, 5]; - vec.sort_by(|a, b| b.cmp(a)); - vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); - vec.sort_by(|a, b| id(-b).cmp(&id(-a))); - // Negative examples (shouldn't be changed) - let c = &7; - vec.sort_by(|a, b| (b - a).cmp(&(a - b))); - vec.sort_by(|_, b| b.cmp(&5)); - vec.sort_by(|_, b| b.cmp(c)); - vec.sort_by(|a, _| a.cmp(c)); - vec.sort_by(|a, b| a.cmp(b)); -} diff --git a/tests/ui/sort_by_key_reverse.stderr b/tests/ui/sort_by_key_reverse.stderr deleted file mode 100644 index b757c8a6176..00000000000 --- a/tests/ui/sort_by_key_reverse.stderr +++ /dev/null @@ -1,22 +0,0 @@ -error: use Vec::sort_by_key here instead - --> $DIR/sort_by_key_reverse.rs:12:5 - | -LL | vec.sort_by(|a, b| b.cmp(a)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(b))` - | - = note: `-D clippy::sort-by-key-reverse` implied by `-D warnings` - -error: use Vec::sort_by_key here instead - --> $DIR/sort_by_key_reverse.rs:13: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/sort_by_key_reverse.rs:14:5 - | -LL | vec.sort_by(|a, b| id(-b).cmp(&id(-a))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(id(-b)))` - -error: aborting due to 3 previous errors - -- cgit 1.4.1-3-g733a5 From 07886a97640b89f72b70805f519bd9d42d7d1c4e Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Sun, 24 May 2020 20:05:58 -0700 Subject: Detect also when works --- clippy_lints/src/sort_by_key.rs | 49 +++++++++++++++++++++++++++++++++++------ tests/ui/sort_by_key.fixed | 2 +- tests/ui/sort_by_key.stderr | 4 ++-- 3 files changed, 45 insertions(+), 10 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/sort_by_key.rs b/clippy_lints/src/sort_by_key.rs index 109845a28f4..f720d14473a 100644 --- a/clippy_lints/src/sort_by_key.rs +++ b/clippy_lints/src/sort_by_key.rs @@ -3,7 +3,7 @@ use crate::utils::paths; use crate::utils::sugg::Sugg; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, QPath}; +use rustc_hir::{Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::Ident; @@ -16,7 +16,7 @@ declare_clippy_lint! { /// **Why is this bad?** /// It is more clear to use `Vec::sort_by_key` (or /// `Vec::sort_by_key` and `std::cmp::Reverse` if necessary) than - /// using + /// using /// /// **Known problems:** None. /// @@ -36,7 +36,17 @@ declare_clippy_lint! { declare_lint_pass!(SortByKey => [SORT_BY_KEY]); -struct LintTrigger { +enum LintTrigger { + Sort(SortDetection), + SortByKey(SortByKeyDetection), +} + +struct SortDetection { + vec_name: String, + unstable: bool, +} + +struct SortByKeyDetection { vec_name: String, closure_arg: String, closure_body: String, @@ -177,7 +187,18 @@ fn detect_lint(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option }; let vec_name = Sugg::hir(cx, &args[0], "..").to_string(); let unstable = name == "sort_unstable_by"; - Some(LintTrigger { vec_name, unstable, closure_arg, closure_body }) + if_chain! { + if let ExprKind::Path(QPath::Resolved(_, Path { + segments: [PathSegment { ident: left_name, .. }], .. + })) = &left_expr.kind; + if left_name == left_ident; + then { + Some(LintTrigger::Sort(SortDetection { vec_name, unstable })) + } + else { + Some(LintTrigger::SortByKey(SortByKeyDetection { vec_name, unstable, closure_arg, closure_body })) + } + } } else { None } @@ -186,8 +207,8 @@ fn detect_lint(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option impl LateLintPass<'_, '_> for SortByKey { fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr<'_>) { - if let Some(trigger) = detect_lint(cx, expr) { - utils::span_lint_and_sugg( + match detect_lint(cx, expr) { + Some(LintTrigger::SortByKey(trigger)) => utils::span_lint_and_sugg( cx, SORT_BY_KEY, expr.span, @@ -201,7 +222,21 @@ impl LateLintPass<'_, '_> for SortByKey { trigger.closure_body, ), Applicability::MachineApplicable, - ); + ), + Some(LintTrigger::Sort(trigger)) => utils::span_lint_and_sugg( + cx, + SORT_BY_KEY, + expr.span, + "use Vec::sort here instead", + "try", + format!( + "{}.sort{}()", + trigger.vec_name, + if trigger.unstable { "_unstable" } else { "" }, + ), + Applicability::MachineApplicable, + ), + None => {}, } } } diff --git a/tests/ui/sort_by_key.fixed b/tests/ui/sort_by_key.fixed index f6535c8d8f5..bb88df1a56c 100644 --- a/tests/ui/sort_by_key.fixed +++ b/tests/ui/sort_by_key.fixed @@ -10,7 +10,7 @@ fn id(x: isize) -> isize { fn main() { let mut vec: Vec = vec![3, 6, 1, 2, 5]; // Forward examples - vec.sort_by_key(|&a| a); + vec.sort(); vec.sort_by_key(|&a| (a + 5).abs()); vec.sort_by_key(|&a| id(-a)); // Reverse examples diff --git a/tests/ui/sort_by_key.stderr b/tests/ui/sort_by_key.stderr index fa6a9a0fb10..291fd5500f7 100644 --- a/tests/ui/sort_by_key.stderr +++ b/tests/ui/sort_by_key.stderr @@ -1,8 +1,8 @@ -error: use Vec::sort_by_key here instead +error: use Vec::sort here instead --> $DIR/sort_by_key.rs:13:5 | LL | vec.sort_by(|a, b| a.cmp(b)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&a| a)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort()` | = note: `-D clippy::sort-by-key` implied by `-D warnings` -- cgit 1.4.1-3-g733a5 From 015ab9f9259d58a48c171276f6e7190528f1a9ad Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Thu, 28 May 2020 18:18:25 -0700 Subject: Renamed to --- clippy_lints/src/lib.rs | 10 +- clippy_lints/src/sort_by_key.rs | 242 -------------------------------- clippy_lints/src/unnecessary_sort_by.rs | 242 ++++++++++++++++++++++++++++++++ tests/ui/sort_by_key.fixed | 26 ---- tests/ui/sort_by_key.rs | 26 ---- tests/ui/sort_by_key.stderr | 48 ------- tests/ui/unnecessary_sort_by.fixed | 26 ++++ tests/ui/unnecessary_sort_by.rs | 26 ++++ tests/ui/unnecessary_sort_by.stderr | 48 +++++++ 9 files changed, 347 insertions(+), 347 deletions(-) delete mode 100644 clippy_lints/src/sort_by_key.rs create mode 100644 clippy_lints/src/unnecessary_sort_by.rs delete mode 100644 tests/ui/sort_by_key.fixed delete mode 100644 tests/ui/sort_by_key.rs delete mode 100644 tests/ui/sort_by_key.stderr create mode 100644 tests/ui/unnecessary_sort_by.fixed create mode 100644 tests/ui/unnecessary_sort_by.rs create mode 100644 tests/ui/unnecessary_sort_by.stderr (limited to 'tests') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 9e826316f21..46df743b5bf 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -304,7 +304,7 @@ mod serde_api; mod shadow; mod single_component_path_imports; mod slow_vector_initialization; -mod sort_by_key; +mod unnecessary_sort_by; mod strings; mod suspicious_trait_impl; mod swap; @@ -780,7 +780,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &shadow::SHADOW_UNRELATED, &single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, &slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, - &sort_by_key::SORT_BY_KEY, + &unnecessary_sort_by::UNNECESSARY_SORT_BY, &strings::STRING_ADD, &strings::STRING_ADD_ASSIGN, &strings::STRING_LIT_AS_BYTES, @@ -998,7 +998,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box ptr_offset_with_cast::PtrOffsetWithCast); store.register_late_pass(|| box redundant_clone::RedundantClone); store.register_late_pass(|| box slow_vector_initialization::SlowVectorInit); - store.register_late_pass(|| box sort_by_key::SortByKey); + store.register_late_pass(|| box unnecessary_sort_by::UnnecessarySortBy); store.register_late_pass(|| box types::RefToMut); store.register_late_pass(|| box assertions_on_constants::AssertionsOnConstants); store.register_late_pass(|| box missing_const_for_fn::MissingConstForFn); @@ -1394,7 +1394,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&serde_api::SERDE_API_MISUSE), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), - LintId::of(&sort_by_key::SORT_BY_KEY), + LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), @@ -1596,7 +1596,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ranges::RANGE_ZIP_WITH_LEN), LintId::of(&reference::DEREF_ADDROF), LintId::of(&reference::REF_IN_DEREF), - LintId::of(&sort_by_key::SORT_BY_KEY), + LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), LintId::of(&swap::MANUAL_SWAP), LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT), LintId::of(&transmute::CROSSPOINTER_TRANSMUTE), diff --git a/clippy_lints/src/sort_by_key.rs b/clippy_lints/src/sort_by_key.rs deleted file mode 100644 index f720d14473a..00000000000 --- a/clippy_lints/src/sort_by_key.rs +++ /dev/null @@ -1,242 +0,0 @@ -use crate::utils; -use crate::utils::paths; -use crate::utils::sugg::Sugg; -use if_chain::if_chain; -use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::symbol::Ident; - -declare_clippy_lint! { - /// **What it does:** - /// Detects when people use `Vec::sort_by` and pass in a function - /// which compares the two arguments, either directly or indirectly. - /// - /// **Why is this bad?** - /// It is more clear to use `Vec::sort_by_key` (or - /// `Vec::sort_by_key` and `std::cmp::Reverse` if necessary) than - /// using - /// - /// **Known problems:** None. - /// - /// **Example:** - /// - /// ```rust - /// vec.sort_by(|a, b| a.foo().cmp(b.foo())); - /// ``` - /// Use instead: - /// ```rust - /// vec.sort_by_key(|a| a.foo()); - /// ``` - pub SORT_BY_KEY, - complexity, - "Use of `Vec::sort_by` when `Vec::sort_by_key` would be clearer" -} - -declare_lint_pass!(SortByKey => [SORT_BY_KEY]); - -enum LintTrigger { - Sort(SortDetection), - SortByKey(SortByKeyDetection), -} - -struct SortDetection { - vec_name: String, - unstable: bool, -} - -struct SortByKeyDetection { - vec_name: String, - closure_arg: String, - closure_body: String, - unstable: bool, -} - -/// Detect if the two expressions are mirrored (identical, except one -/// contains a and the other replaces it with b) -fn mirrored_exprs( - cx: &LateContext<'_, '_>, - a_expr: &Expr<'_>, - a_ident: &Ident, - b_expr: &Expr<'_>, - b_ident: &Ident, -) -> bool { - match (&a_expr.kind, &b_expr.kind) { - // Two boxes with mirrored contents - (ExprKind::Box(left_expr), ExprKind::Box(right_expr)) => { - mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident) - }, - // Two arrays with mirrored contents - (ExprKind::Array(left_exprs), ExprKind::Array(right_exprs)) => left_exprs - .iter() - .zip(right_exprs.iter()) - .all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), - // The two exprs are function calls. - // Check to see that the function itself and its arguments are mirrored - (ExprKind::Call(left_expr, left_args), ExprKind::Call(right_expr, right_args)) => { - mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident) - && left_args - .iter() - .zip(right_args.iter()) - .all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)) - }, - // The two exprs are method calls. - // Check to see that the function is the same and the arguments are mirrored - // This is enough because the receiver of the method is listed in the arguments - (ExprKind::MethodCall(left_segment, _, left_args), ExprKind::MethodCall(right_segment, _, right_args)) => { - left_segment.ident == right_segment.ident - && left_args - .iter() - .zip(right_args.iter()) - .all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)) - }, - // Two tuples with mirrored contents - (ExprKind::Tup(left_exprs), ExprKind::Tup(right_exprs)) => left_exprs - .iter() - .zip(right_exprs.iter()) - .all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), - // Two binary ops, which are the same operation and which have mirrored arguments - (ExprKind::Binary(left_op, left_left, left_right), ExprKind::Binary(right_op, right_left, right_right)) => { - left_op.node == right_op.node - && mirrored_exprs(cx, left_left, a_ident, right_left, b_ident) - && mirrored_exprs(cx, left_right, a_ident, right_right, b_ident) - }, - // Two unary ops, which are the same operation and which have the same argument - (ExprKind::Unary(left_op, left_expr), ExprKind::Unary(right_op, right_expr)) => { - left_op == right_op && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident) - }, - // The two exprs are literals of some kind - (ExprKind::Lit(left_lit), ExprKind::Lit(right_lit)) => left_lit.node == right_lit.node, - (ExprKind::Cast(left, _), ExprKind::Cast(right, _)) => mirrored_exprs(cx, left, a_ident, right, b_ident), - (ExprKind::DropTemps(left_block), ExprKind::DropTemps(right_block)) => { - mirrored_exprs(cx, left_block, a_ident, right_block, b_ident) - }, - (ExprKind::Field(left_expr, left_ident), ExprKind::Field(right_expr, right_ident)) => { - left_ident.name == right_ident.name && mirrored_exprs(cx, left_expr, a_ident, right_expr, right_ident) - }, - // Two paths: either one is a and the other is b, or they're identical to each other - ( - ExprKind::Path(QPath::Resolved( - _, - Path { - segments: left_segments, - .. - }, - )), - ExprKind::Path(QPath::Resolved( - _, - Path { - segments: right_segments, - .. - }, - )), - ) => { - (left_segments - .iter() - .zip(right_segments.iter()) - .all(|(left, right)| left.ident == right.ident) - && left_segments - .iter() - .all(|seg| &seg.ident != a_ident && &seg.ident != b_ident)) - || (left_segments.len() == 1 - && &left_segments[0].ident == a_ident - && right_segments.len() == 1 - && &right_segments[0].ident == b_ident) - }, - // Matching expressions, but one or both is borrowed - ( - ExprKind::AddrOf(left_kind, Mutability::Not, left_expr), - ExprKind::AddrOf(right_kind, Mutability::Not, right_expr), - ) => left_kind == right_kind && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), - (_, ExprKind::AddrOf(_, Mutability::Not, right_expr)) => { - mirrored_exprs(cx, a_expr, a_ident, right_expr, b_ident) - }, - (ExprKind::AddrOf(_, Mutability::Not, left_expr), _) => mirrored_exprs(cx, left_expr, a_ident, b_expr, b_ident), - _ => false, - } -} - -fn detect_lint(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option { - if_chain! { - if let ExprKind::MethodCall(name_ident, _, args) = &expr.kind; - if let name = name_ident.ident.name.to_ident_string(); - if name == "sort_by" || name == "sort_unstable_by"; - if let [vec, Expr { kind: ExprKind::Closure(_, _, closure_body_id, _, _), .. }] = args; - if utils::match_type(cx, &cx.tables.expr_ty(vec), &paths::VEC); - if let closure_body = cx.tcx.hir().body(*closure_body_id); - if let &[ - Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..}, - Param { pat: Pat { kind: PatKind::Binding(_, _, right_ident, _), .. }, .. } - ] = &closure_body.params; - if let ExprKind::MethodCall(method_path, _, [ref left_expr, ref right_expr]) = &closure_body.value.kind; - if method_path.ident.name.to_ident_string() == "cmp"; - then { - let (closure_body, closure_arg) = if mirrored_exprs( - &cx, - &left_expr, - &left_ident, - &right_expr, - &right_ident - ) { - (Sugg::hir(cx, &left_expr, "..").to_string(), left_ident.name.to_string()) - } else if mirrored_exprs(&cx, &left_expr, &right_ident, &right_expr, &left_ident) { - (format!("Reverse({})", Sugg::hir(cx, &left_expr, "..").to_string()), right_ident.name.to_string()) - } else { - return None; - }; - 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; - then { - Some(LintTrigger::Sort(SortDetection { vec_name, unstable })) - } - else { - Some(LintTrigger::SortByKey(SortByKeyDetection { vec_name, unstable, closure_arg, closure_body })) - } - } - } else { - None - } - } -} - -impl LateLintPass<'_, '_> for SortByKey { - fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr<'_>) { - match detect_lint(cx, expr) { - Some(LintTrigger::SortByKey(trigger)) => utils::span_lint_and_sugg( - cx, - SORT_BY_KEY, - expr.span, - "use Vec::sort_by_key here instead", - "try", - format!( - "{}.sort{}_by_key(|&{}| {})", - trigger.vec_name, - if trigger.unstable { "_unstable" } else { "" }, - trigger.closure_arg, - trigger.closure_body, - ), - Applicability::MachineApplicable, - ), - Some(LintTrigger::Sort(trigger)) => utils::span_lint_and_sugg( - cx, - SORT_BY_KEY, - expr.span, - "use Vec::sort here instead", - "try", - format!( - "{}.sort{}()", - trigger.vec_name, - if trigger.unstable { "_unstable" } else { "" }, - ), - Applicability::MachineApplicable, - ), - None => {}, - } - } -} diff --git a/clippy_lints/src/unnecessary_sort_by.rs b/clippy_lints/src/unnecessary_sort_by.rs new file mode 100644 index 00000000000..c0858ec4c88 --- /dev/null +++ b/clippy_lints/src/unnecessary_sort_by.rs @@ -0,0 +1,242 @@ +use crate::utils; +use crate::utils::paths; +use crate::utils::sugg::Sugg; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::Ident; + +declare_clippy_lint! { + /// **What it does:** + /// Detects when people use `Vec::sort_by` and pass in a function + /// which compares the two arguments, either directly or indirectly. + /// + /// **Why is this bad?** + /// It is more clear to use `Vec::sort_by_key` (or `Vec::sort` if + /// possible) than to use `Vec::sort_by` and and a more complicated + /// closure. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// vec.sort_by(|a, b| a.foo().cmp(b.foo())); + /// ``` + /// Use instead: + /// ```rust + /// vec.sort_by_key(|a| a.foo()); + /// ``` + pub UNNECESSARY_SORT_BY, + complexity, + "Use of `Vec::sort_by` when `Vec::sort_by_key` or `Vec::sort` would be clearer" +} + +declare_lint_pass!(UnnecessarySortBy => [UNNECESSARY_SORT_BY]); + +enum LintTrigger { + Sort(SortDetection), + SortByKey(SortByKeyDetection), +} + +struct SortDetection { + vec_name: String, + unstable: bool, +} + +struct SortByKeyDetection { + vec_name: String, + closure_arg: String, + closure_body: String, + unstable: bool, +} + +/// Detect if the two expressions are mirrored (identical, except one +/// contains a and the other replaces it with b) +fn mirrored_exprs( + cx: &LateContext<'_, '_>, + a_expr: &Expr<'_>, + a_ident: &Ident, + b_expr: &Expr<'_>, + b_ident: &Ident, +) -> bool { + match (&a_expr.kind, &b_expr.kind) { + // Two boxes with mirrored contents + (ExprKind::Box(left_expr), ExprKind::Box(right_expr)) => { + mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident) + }, + // Two arrays with mirrored contents + (ExprKind::Array(left_exprs), ExprKind::Array(right_exprs)) => left_exprs + .iter() + .zip(right_exprs.iter()) + .all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), + // The two exprs are function calls. + // Check to see that the function itself and its arguments are mirrored + (ExprKind::Call(left_expr, left_args), ExprKind::Call(right_expr, right_args)) => { + mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident) + && left_args + .iter() + .zip(right_args.iter()) + .all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)) + }, + // The two exprs are method calls. + // Check to see that the function is the same and the arguments are mirrored + // This is enough because the receiver of the method is listed in the arguments + (ExprKind::MethodCall(left_segment, _, left_args), ExprKind::MethodCall(right_segment, _, right_args)) => { + left_segment.ident == right_segment.ident + && left_args + .iter() + .zip(right_args.iter()) + .all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)) + }, + // Two tuples with mirrored contents + (ExprKind::Tup(left_exprs), ExprKind::Tup(right_exprs)) => left_exprs + .iter() + .zip(right_exprs.iter()) + .all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident)), + // Two binary ops, which are the same operation and which have mirrored arguments + (ExprKind::Binary(left_op, left_left, left_right), ExprKind::Binary(right_op, right_left, right_right)) => { + left_op.node == right_op.node + && mirrored_exprs(cx, left_left, a_ident, right_left, b_ident) + && mirrored_exprs(cx, left_right, a_ident, right_right, b_ident) + }, + // Two unary ops, which are the same operation and which have the same argument + (ExprKind::Unary(left_op, left_expr), ExprKind::Unary(right_op, right_expr)) => { + left_op == right_op && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident) + }, + // The two exprs are literals of some kind + (ExprKind::Lit(left_lit), ExprKind::Lit(right_lit)) => left_lit.node == right_lit.node, + (ExprKind::Cast(left, _), ExprKind::Cast(right, _)) => mirrored_exprs(cx, left, a_ident, right, b_ident), + (ExprKind::DropTemps(left_block), ExprKind::DropTemps(right_block)) => { + mirrored_exprs(cx, left_block, a_ident, right_block, b_ident) + }, + (ExprKind::Field(left_expr, left_ident), ExprKind::Field(right_expr, right_ident)) => { + left_ident.name == right_ident.name && mirrored_exprs(cx, left_expr, a_ident, right_expr, right_ident) + }, + // Two paths: either one is a and the other is b, or they're identical to each other + ( + ExprKind::Path(QPath::Resolved( + _, + Path { + segments: left_segments, + .. + }, + )), + ExprKind::Path(QPath::Resolved( + _, + Path { + segments: right_segments, + .. + }, + )), + ) => { + (left_segments + .iter() + .zip(right_segments.iter()) + .all(|(left, right)| left.ident == right.ident) + && left_segments + .iter() + .all(|seg| &seg.ident != a_ident && &seg.ident != b_ident)) + || (left_segments.len() == 1 + && &left_segments[0].ident == a_ident + && right_segments.len() == 1 + && &right_segments[0].ident == b_ident) + }, + // Matching expressions, but one or both is borrowed + ( + ExprKind::AddrOf(left_kind, Mutability::Not, left_expr), + ExprKind::AddrOf(right_kind, Mutability::Not, right_expr), + ) => left_kind == right_kind && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident), + (_, ExprKind::AddrOf(_, Mutability::Not, right_expr)) => { + mirrored_exprs(cx, a_expr, a_ident, right_expr, b_ident) + }, + (ExprKind::AddrOf(_, Mutability::Not, left_expr), _) => mirrored_exprs(cx, left_expr, a_ident, b_expr, b_ident), + _ => false, + } +} + +fn detect_lint(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option { + if_chain! { + if let ExprKind::MethodCall(name_ident, _, args) = &expr.kind; + if let name = name_ident.ident.name.to_ident_string(); + if name == "sort_by" || name == "sort_unstable_by"; + if let [vec, Expr { kind: ExprKind::Closure(_, _, closure_body_id, _, _), .. }] = args; + if utils::match_type(cx, &cx.tables.expr_ty(vec), &paths::VEC); + if let closure_body = cx.tcx.hir().body(*closure_body_id); + if let &[ + Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..}, + Param { pat: Pat { kind: PatKind::Binding(_, _, right_ident, _), .. }, .. } + ] = &closure_body.params; + if let ExprKind::MethodCall(method_path, _, [ref left_expr, ref right_expr]) = &closure_body.value.kind; + if method_path.ident.name.to_ident_string() == "cmp"; + then { + let (closure_body, closure_arg) = if mirrored_exprs( + &cx, + &left_expr, + &left_ident, + &right_expr, + &right_ident + ) { + (Sugg::hir(cx, &left_expr, "..").to_string(), left_ident.name.to_string()) + } else if mirrored_exprs(&cx, &left_expr, &right_ident, &right_expr, &left_ident) { + (format!("Reverse({})", Sugg::hir(cx, &left_expr, "..").to_string()), right_ident.name.to_string()) + } else { + return None; + }; + 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; + then { + Some(LintTrigger::Sort(SortDetection { vec_name, unstable })) + } + else { + Some(LintTrigger::SortByKey(SortByKeyDetection { vec_name, unstable, closure_arg, closure_body })) + } + } + } else { + None + } + } +} + +impl LateLintPass<'_, '_> for UnnecessarySortBy { + fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &Expr<'_>) { + match detect_lint(cx, expr) { + Some(LintTrigger::SortByKey(trigger)) => utils::span_lint_and_sugg( + cx, + UNNECESSARY_SORT_BY, + expr.span, + "use Vec::sort_by_key here instead", + "try", + format!( + "{}.sort{}_by_key(|&{}| {})", + trigger.vec_name, + if trigger.unstable { "_unstable" } else { "" }, + trigger.closure_arg, + trigger.closure_body, + ), + Applicability::MachineApplicable, + ), + Some(LintTrigger::Sort(trigger)) => utils::span_lint_and_sugg( + cx, + UNNECESSARY_SORT_BY, + expr.span, + "use Vec::sort here instead", + "try", + format!( + "{}.sort{}()", + trigger.vec_name, + if trigger.unstable { "_unstable" } else { "" }, + ), + Applicability::MachineApplicable, + ), + None => {}, + } + } +} diff --git a/tests/ui/sort_by_key.fixed b/tests/ui/sort_by_key.fixed deleted file mode 100644 index bb88df1a56c..00000000000 --- a/tests/ui/sort_by_key.fixed +++ /dev/null @@ -1,26 +0,0 @@ -// run-rustfix -#![warn(clippy::sort_by_key)] - -use std::cmp::Reverse; - -fn id(x: isize) -> isize { - x -} - -fn main() { - let mut vec: Vec = vec![3, 6, 1, 2, 5]; - // Forward examples - vec.sort(); - vec.sort_by_key(|&a| (a + 5).abs()); - vec.sort_by_key(|&a| id(-a)); - // Reverse examples - vec.sort_by_key(|&b| Reverse(b)); - vec.sort_by_key(|&b| Reverse((b + 5).abs())); - vec.sort_by_key(|&b| Reverse(id(-b))); - // Negative examples (shouldn't be changed) - let c = &7; - vec.sort_by(|a, b| (b - a).cmp(&(a - b))); - vec.sort_by(|_, b| b.cmp(&5)); - vec.sort_by(|_, b| b.cmp(c)); - vec.sort_by(|a, _| a.cmp(c)); -} diff --git a/tests/ui/sort_by_key.rs b/tests/ui/sort_by_key.rs deleted file mode 100644 index 953c573d406..00000000000 --- a/tests/ui/sort_by_key.rs +++ /dev/null @@ -1,26 +0,0 @@ -// run-rustfix -#![warn(clippy::sort_by_key_reverse)] - -use std::cmp::Reverse; - -fn id(x: isize) -> isize { - x -} - -fn main() { - let mut vec: Vec = vec![3, 6, 1, 2, 5]; - // Forward examples - vec.sort_by(|a, b| a.cmp(b)); - vec.sort_by(|a, b| (a + 5).abs().cmp(&(b + 5).abs())); - vec.sort_by(|a, b| id(-a).cmp(&id(-b))); - // Reverse examples - vec.sort_by(|a, b| b.cmp(a)); - vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); - vec.sort_by(|a, b| id(-b).cmp(&id(-a))); - // Negative examples (shouldn't be changed) - let c = &7; - vec.sort_by(|a, b| (b - a).cmp(&(a - b))); - vec.sort_by(|_, b| b.cmp(&5)); - vec.sort_by(|_, b| b.cmp(c)); - vec.sort_by(|a, _| a.cmp(c)); -} diff --git a/tests/ui/sort_by_key.stderr b/tests/ui/sort_by_key.stderr deleted file mode 100644 index 291fd5500f7..00000000000 --- a/tests/ui/sort_by_key.stderr +++ /dev/null @@ -1,48 +0,0 @@ -error: use Vec::sort here instead - --> $DIR/sort_by_key.rs:13:5 - | -LL | vec.sort_by(|a, b| a.cmp(b)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort()` - | - = note: `-D clippy::sort-by-key` implied by `-D warnings` - -error: use Vec::sort_by_key here instead - --> $DIR/sort_by_key.rs:14: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/sort_by_key.rs:15:5 - | -LL | vec.sort_by(|a, b| id(-a).cmp(&id(-b))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&a| id(-a))` - -error: use Vec::sort_by_key here instead - --> $DIR/sort_by_key.rs:17:5 - | -LL | vec.sort_by(|a, b| b.cmp(a)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(b))` - -error: use Vec::sort_by_key here instead - --> $DIR/sort_by_key.rs:18: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/sort_by_key.rs:19:5 - | -LL | vec.sort_by(|a, b| id(-b).cmp(&id(-a))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(id(-b)))` - -error: unknown clippy lint: clippy::sort_by_key_reverse - --> $DIR/sort_by_key.rs:2:9 - | -LL | #![warn(clippy::sort_by_key_reverse)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::sort_by_key` - | - = note: `-D clippy::unknown-clippy-lints` implied by `-D warnings` - -error: aborting due to 7 previous errors - diff --git a/tests/ui/unnecessary_sort_by.fixed b/tests/ui/unnecessary_sort_by.fixed new file mode 100644 index 00000000000..bb88df1a56c --- /dev/null +++ b/tests/ui/unnecessary_sort_by.fixed @@ -0,0 +1,26 @@ +// run-rustfix +#![warn(clippy::sort_by_key)] + +use std::cmp::Reverse; + +fn id(x: isize) -> isize { + x +} + +fn main() { + let mut vec: Vec = vec![3, 6, 1, 2, 5]; + // Forward examples + vec.sort(); + vec.sort_by_key(|&a| (a + 5).abs()); + vec.sort_by_key(|&a| id(-a)); + // Reverse examples + vec.sort_by_key(|&b| Reverse(b)); + vec.sort_by_key(|&b| Reverse((b + 5).abs())); + vec.sort_by_key(|&b| Reverse(id(-b))); + // Negative examples (shouldn't be changed) + let c = &7; + vec.sort_by(|a, b| (b - a).cmp(&(a - b))); + vec.sort_by(|_, b| b.cmp(&5)); + vec.sort_by(|_, b| b.cmp(c)); + vec.sort_by(|a, _| a.cmp(c)); +} diff --git a/tests/ui/unnecessary_sort_by.rs b/tests/ui/unnecessary_sort_by.rs new file mode 100644 index 00000000000..953c573d406 --- /dev/null +++ b/tests/ui/unnecessary_sort_by.rs @@ -0,0 +1,26 @@ +// run-rustfix +#![warn(clippy::sort_by_key_reverse)] + +use std::cmp::Reverse; + +fn id(x: isize) -> isize { + x +} + +fn main() { + let mut vec: Vec = vec![3, 6, 1, 2, 5]; + // Forward examples + vec.sort_by(|a, b| a.cmp(b)); + vec.sort_by(|a, b| (a + 5).abs().cmp(&(b + 5).abs())); + vec.sort_by(|a, b| id(-a).cmp(&id(-b))); + // Reverse examples + vec.sort_by(|a, b| b.cmp(a)); + vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); + vec.sort_by(|a, b| id(-b).cmp(&id(-a))); + // Negative examples (shouldn't be changed) + let c = &7; + vec.sort_by(|a, b| (b - a).cmp(&(a - b))); + vec.sort_by(|_, b| b.cmp(&5)); + vec.sort_by(|_, b| b.cmp(c)); + vec.sort_by(|a, _| a.cmp(c)); +} diff --git a/tests/ui/unnecessary_sort_by.stderr b/tests/ui/unnecessary_sort_by.stderr new file mode 100644 index 00000000000..291fd5500f7 --- /dev/null +++ b/tests/ui/unnecessary_sort_by.stderr @@ -0,0 +1,48 @@ +error: use Vec::sort here instead + --> $DIR/sort_by_key.rs:13:5 + | +LL | vec.sort_by(|a, b| a.cmp(b)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort()` + | + = note: `-D clippy::sort-by-key` implied by `-D warnings` + +error: use Vec::sort_by_key here instead + --> $DIR/sort_by_key.rs:14: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/sort_by_key.rs:15:5 + | +LL | vec.sort_by(|a, b| id(-a).cmp(&id(-b))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&a| id(-a))` + +error: use Vec::sort_by_key here instead + --> $DIR/sort_by_key.rs:17:5 + | +LL | vec.sort_by(|a, b| b.cmp(a)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(b))` + +error: use Vec::sort_by_key here instead + --> $DIR/sort_by_key.rs:18: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/sort_by_key.rs:19:5 + | +LL | vec.sort_by(|a, b| id(-b).cmp(&id(-a))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(id(-b)))` + +error: unknown clippy lint: clippy::sort_by_key_reverse + --> $DIR/sort_by_key.rs:2:9 + | +LL | #![warn(clippy::sort_by_key_reverse)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::sort_by_key` + | + = note: `-D clippy::unknown-clippy-lints` implied by `-D warnings` + +error: aborting due to 7 previous errors + -- cgit 1.4.1-3-g733a5 From 20cb512e81ad03a014b40c377a01fdebaea66963 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Sun, 31 May 2020 12:06:32 -0700 Subject: Updated test cases and formatted --- clippy_lints/src/lib.rs | 2 +- tests/ui/unnecessary_sort_by.fixed | 1 - tests/ui/unnecessary_sort_by.rs | 1 - tests/ui/unnecessary_sort_by.stderr | 24 ++++++++---------------- 4 files changed, 9 insertions(+), 19 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 46df743b5bf..fd832d11577 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -304,7 +304,6 @@ mod serde_api; mod shadow; mod single_component_path_imports; mod slow_vector_initialization; -mod unnecessary_sort_by; mod strings; mod suspicious_trait_impl; mod swap; @@ -319,6 +318,7 @@ mod try_err; mod types; mod unicode; mod unnamed_address; +mod unnecessary_sort_by; mod unsafe_removed_from_name; mod unused_io_amount; mod unused_self; diff --git a/tests/ui/unnecessary_sort_by.fixed b/tests/ui/unnecessary_sort_by.fixed index bb88df1a56c..4521ae38d49 100644 --- a/tests/ui/unnecessary_sort_by.fixed +++ b/tests/ui/unnecessary_sort_by.fixed @@ -1,5 +1,4 @@ // run-rustfix -#![warn(clippy::sort_by_key)] use std::cmp::Reverse; diff --git a/tests/ui/unnecessary_sort_by.rs b/tests/ui/unnecessary_sort_by.rs index 953c573d406..fdb5a823369 100644 --- a/tests/ui/unnecessary_sort_by.rs +++ b/tests/ui/unnecessary_sort_by.rs @@ -1,5 +1,4 @@ // run-rustfix -#![warn(clippy::sort_by_key_reverse)] use std::cmp::Reverse; diff --git a/tests/ui/unnecessary_sort_by.stderr b/tests/ui/unnecessary_sort_by.stderr index 291fd5500f7..b6365c1709d 100644 --- a/tests/ui/unnecessary_sort_by.stderr +++ b/tests/ui/unnecessary_sort_by.stderr @@ -1,48 +1,40 @@ error: use Vec::sort here instead - --> $DIR/sort_by_key.rs:13:5 + --> $DIR/unnecessary_sort_by.rs:12:5 | LL | vec.sort_by(|a, b| a.cmp(b)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort()` | - = note: `-D clippy::sort-by-key` implied by `-D warnings` + = note: `-D clippy::unnecessary-sort-by` implied by `-D warnings` error: use Vec::sort_by_key here instead - --> $DIR/sort_by_key.rs:14:5 + --> $DIR/unnecessary_sort_by.rs:13: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/sort_by_key.rs:15:5 + --> $DIR/unnecessary_sort_by.rs:14:5 | LL | vec.sort_by(|a, b| id(-a).cmp(&id(-b))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&a| id(-a))` error: use Vec::sort_by_key here instead - --> $DIR/sort_by_key.rs:17:5 + --> $DIR/unnecessary_sort_by.rs:16:5 | LL | vec.sort_by(|a, b| b.cmp(a)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(b))` error: use Vec::sort_by_key here instead - --> $DIR/sort_by_key.rs:18:5 + --> $DIR/unnecessary_sort_by.rs:17: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/sort_by_key.rs:19:5 + --> $DIR/unnecessary_sort_by.rs:18:5 | LL | vec.sort_by(|a, b| id(-b).cmp(&id(-a))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(id(-b)))` -error: unknown clippy lint: clippy::sort_by_key_reverse - --> $DIR/sort_by_key.rs:2:9 - | -LL | #![warn(clippy::sort_by_key_reverse)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::sort_by_key` - | - = note: `-D clippy::unknown-clippy-lints` implied by `-D warnings` - -error: aborting due to 7 previous errors +error: aborting due to 6 previous errors -- cgit 1.4.1-3-g733a5 From 32fde0b5116b3a1115d11c49a9bf2af2ebdd5773 Mon Sep 17 00:00:00 2001 From: Ericko Samudera Date: Mon, 25 May 2020 23:22:01 +0700 Subject: New lint: iter_next_slice --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 3 ++ clippy_lints/src/loops.rs | 26 +++++------ clippy_lints/src/methods/mod.rs | 84 ++++++++++++++++++++++++++++++++++- clippy_lints/src/needless_continue.rs | 2 +- src/lintlist/mod.rs | 7 +++ tests/ui/into_iter_on_ref.fixed | 2 + tests/ui/into_iter_on_ref.rs | 2 + tests/ui/into_iter_on_ref.stderr | 8 +++- tests/ui/iter_next_slice.fixed | 24 ++++++++++ tests/ui/iter_next_slice.rs | 24 ++++++++++ tests/ui/iter_next_slice.stderr | 28 ++++++++++++ tests/ui/needless_collect.fixed | 2 +- tests/ui/needless_collect.stderr | 16 +++---- 14 files changed, 204 insertions(+), 25 deletions(-) create mode 100644 tests/ui/iter_next_slice.fixed create mode 100644 tests/ui/iter_next_slice.rs create mode 100644 tests/ui/iter_next_slice.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ac9057199f..714e25a32ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1401,6 +1401,7 @@ Released 2018-09-13 [`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements [`iter_cloned_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_cloned_collect [`iter_next_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_loop +[`iter_next_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_slice [`iter_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth [`iter_nth_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth_zero [`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 057d39d4c82..7c16dbd8f26 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -669,6 +669,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::INTO_ITER_ON_REF, &methods::ITERATOR_STEP_BY_ZERO, &methods::ITER_CLONED_COLLECT, + &methods::ITER_NEXT_SLICE, &methods::ITER_NTH, &methods::ITER_NTH_ZERO, &methods::ITER_SKIP_NEXT, @@ -1303,6 +1304,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::INTO_ITER_ON_REF), LintId::of(&methods::ITERATOR_STEP_BY_ZERO), LintId::of(&methods::ITER_CLONED_COLLECT), + LintId::of(&methods::ITER_NEXT_SLICE), LintId::of(&methods::ITER_NTH), LintId::of(&methods::ITER_NTH_ZERO), LintId::of(&methods::ITER_SKIP_NEXT), @@ -1483,6 +1485,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 38a5829b3f7..dbe41823a9c 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -27,7 +27,7 @@ use rustc_middle::middle::region; use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use rustc_span::BytePos; +use rustc_span::symbol::Symbol; use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, Place, PlaceBase}; use std::iter::{once, Iterator}; use std::mem; @@ -2381,32 +2381,32 @@ fn check_needless_collect<'a, 'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'a, ' match_type(cx, ty, &paths::BTREEMAP) || is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) { if method.ident.name == sym!(len) { - let span = shorten_needless_collect_span(expr); + let span = shorten_span(expr, sym!(collect)); span_lint_and_sugg( cx, NEEDLESS_COLLECT, span, NEEDLESS_COLLECT_MSG, "replace with", - ".count()".to_string(), + "count()".to_string(), Applicability::MachineApplicable, ); } if method.ident.name == sym!(is_empty) { - let span = shorten_needless_collect_span(expr); + let span = shorten_span(expr, sym!(iter)); span_lint_and_sugg( cx, NEEDLESS_COLLECT, span, NEEDLESS_COLLECT_MSG, "replace with", - ".next().is_none()".to_string(), + "get(0).is_none()".to_string(), Applicability::MachineApplicable, ); } if method.ident.name == sym!(contains) { let contains_arg = snippet(cx, args[1].span, "??"); - let span = shorten_needless_collect_span(expr); + let span = shorten_span(expr, sym!(collect)); span_lint_and_then( cx, NEEDLESS_COLLECT, @@ -2422,7 +2422,7 @@ fn check_needless_collect<'a, 'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'a, ' span, "replace with", format!( - ".any(|{}| x == {})", + "any(|{}| x == {})", arg, pred ), Applicability::MachineApplicable, @@ -2435,13 +2435,13 @@ fn check_needless_collect<'a, 'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'a, ' } } -fn shorten_needless_collect_span(expr: &Expr<'_>) -> Span { - if_chain! { - if let ExprKind::MethodCall(_, _, ref args) = expr.kind; - if let ExprKind::MethodCall(_, ref span, _) = args[0].kind; - then { - return expr.span.with_lo(span.lo() - BytePos(1)); +fn shorten_span(expr: &Expr<'_>, target_fn_name: Symbol) -> Span { + let mut current_expr = expr; + while let ExprKind::MethodCall(ref path, ref span, ref args) = current_expr.kind { + if path.ident.name == target_fn_name { + return expr.span.with_lo(span.lo()); } + current_expr = &args[0]; } unreachable!() } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 32b3b7f7947..7cb04d4d81c 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -26,7 +26,7 @@ use rustc_span::symbol::{sym, SymbolStr}; use crate::consts::{constant, Constant}; use crate::utils::usage::mutated_variables; use crate::utils::{ - get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, implements_trait, in_macro, is_copy, + get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro, is_copy, is_ctor_or_promotable_const_function, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls, method_chain_args, paths, remove_blocks, return_ty, same_tys, single_segment_path, snippet, snippet_with_applicability, @@ -1242,6 +1242,32 @@ declare_clippy_lint! { "using `as_ref().map(Deref::deref)`, which is more succinctly expressed as `as_deref()`" } +declare_clippy_lint! { + /// **What it does:** Checks for usage of `iter().next()` on a Slice or an Array + /// + /// **Why is this bad?** These can be shortened into `.get()` + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// # let a = [1, 2, 3]; + /// # let b = vec![1, 2, 3]; + /// a[2..].iter().next(); + /// b.iter().next(); + /// ``` + /// should be written as: + /// ```rust + /// # let a = [1, 2, 3]; + /// # let b = vec![1, 2, 3]; + /// a.get(2); + /// b.get(0); + /// ``` + pub ITER_NEXT_SLICE, + style, + "using `.iter().next()` on a sliced array, which can be shortened to just `.get()`" +} + declare_lint_pass!(Methods => [ UNWRAP_USED, EXPECT_USED, @@ -1273,6 +1299,7 @@ declare_lint_pass!(Methods => [ FIND_MAP, MAP_FLATTEN, ITERATOR_STEP_BY_ZERO, + ITER_NEXT_SLICE, ITER_NTH, ITER_NTH_ZERO, ITER_SKIP_NEXT, @@ -1320,6 +1347,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods { }, ["next", "filter"] => lint_filter_next(cx, expr, arg_lists[1]), ["next", "skip_while"] => lint_skip_while_next(cx, expr, arg_lists[1]), + ["next", "iter"] => lint_iter_next(cx, expr, arg_lists[1]), ["map", "filter"] => lint_filter_map(cx, expr, arg_lists[1], arg_lists[0]), ["map", "filter_map"] => lint_filter_map_map(cx, expr, arg_lists[1], arg_lists[0]), ["next", "filter_map"] => lint_filter_map_next(cx, expr, arg_lists[1]), @@ -2184,6 +2212,60 @@ fn lint_step_by<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &hir::Expr<'_>, args } } +fn lint_iter_next<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr<'_>, iter_args: &'tcx [hir::Expr<'_>]) { + let caller_expr = &iter_args[0]; + + // Skip lint if the `iter().next()` expression is a for loop argument, + // since it is already covered by `&loops::ITER_NEXT_LOOP` + let mut parent_expr_opt = get_parent_expr(cx, expr); + while let Some(parent_expr) = parent_expr_opt { + if higher::for_loop(parent_expr).is_some() { + return; + } + parent_expr_opt = get_parent_expr(cx, parent_expr); + } + + if derefs_to_slice(cx, caller_expr, cx.tables.expr_ty(caller_expr)).is_some() { + // caller is a Slice + if_chain! { + if let hir::ExprKind::Index(ref caller_var, ref index_expr) = &caller_expr.kind; + if let Some(higher::Range { start: Some(start_expr), end: None, limits: ast::RangeLimits::HalfOpen }) + = higher::range(cx, index_expr); + if let hir::ExprKind::Lit(ref start_lit) = &start_expr.kind; + if let ast::LitKind::Int(start_idx, _) = start_lit.node; + then { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + ITER_NEXT_SLICE, + expr.span, + "Using `.iter().next()` on a Slice without end index.", + "try calling", + format!("{}.get({})", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability), start_idx), + applicability, + ); + } + } + } else if is_type_diagnostic_item(cx, cx.tables.expr_ty(caller_expr), sym!(vec_type)) + || matches!(&walk_ptrs_ty(cx.tables.expr_ty(caller_expr)).kind, ty::Array(_, _)) + { + // caller is a Vec or an Array + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + ITER_NEXT_SLICE, + expr.span, + "Using `.iter().next()` on an array", + "try calling", + format!( + "{}.get(0)", + snippet_with_applicability(cx, caller_expr.span, "..", &mut applicability) + ), + applicability, + ); + } +} + fn lint_iter_nth<'a, 'tcx>( cx: &LateContext<'a, 'tcx>, expr: &hir::Expr<'_>, diff --git a/clippy_lints/src/needless_continue.rs b/clippy_lints/src/needless_continue.rs index 28183810df4..a971d041ca6 100644 --- a/clippy_lints/src/needless_continue.rs +++ b/clippy_lints/src/needless_continue.rs @@ -424,7 +424,7 @@ fn erode_from_back(s: &str) -> String { } fn span_of_first_expr_in_block(block: &ast::Block) -> Option { - block.stmts.iter().next().map(|stmt| stmt.span) + block.stmts.get(0).map(|stmt| stmt.span) } #[cfg(test)] diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 8211a57b564..79da1f3702e 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -934,6 +934,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "loops", }, + Lint { + name: "iter_next_slice", + group: "style", + desc: "using `.iter().next()` on a sliced array, which can be shortened to just `.get()`", + deprecation: None, + module: "methods", + }, Lint { name: "iter_nth", group: "perf", diff --git a/tests/ui/into_iter_on_ref.fixed b/tests/ui/into_iter_on_ref.fixed index c30d23de3f8..7f92d0dbdc9 100644 --- a/tests/ui/into_iter_on_ref.fixed +++ b/tests/ui/into_iter_on_ref.fixed @@ -40,4 +40,6 @@ fn main() { let _ = (&HashSet::::new()).iter(); //~ WARN equivalent to .iter() let _ = std::path::Path::new("12/34").iter(); //~ WARN equivalent to .iter() let _ = std::path::PathBuf::from("12/34").iter(); //~ ERROR equivalent to .iter() + + let _ = (&[1, 2, 3]).iter().next(); //~ WARN equivalent to .iter() } diff --git a/tests/ui/into_iter_on_ref.rs b/tests/ui/into_iter_on_ref.rs index 94bc1689619..416056d3fdb 100644 --- a/tests/ui/into_iter_on_ref.rs +++ b/tests/ui/into_iter_on_ref.rs @@ -40,4 +40,6 @@ fn main() { let _ = (&HashSet::::new()).into_iter(); //~ WARN equivalent to .iter() let _ = std::path::Path::new("12/34").into_iter(); //~ WARN equivalent to .iter() let _ = std::path::PathBuf::from("12/34").into_iter(); //~ ERROR equivalent to .iter() + + let _ = (&[1, 2, 3]).into_iter().next(); //~ WARN equivalent to .iter() } diff --git a/tests/ui/into_iter_on_ref.stderr b/tests/ui/into_iter_on_ref.stderr index 80e2d104f82..1cd6400b019 100644 --- a/tests/ui/into_iter_on_ref.stderr +++ b/tests/ui/into_iter_on_ref.stderr @@ -156,5 +156,11 @@ error: this `.into_iter()` call is equivalent to `.iter()` and will not move the LL | let _ = std::path::PathBuf::from("12/34").into_iter(); //~ ERROR equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: aborting due to 26 previous errors +error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `array` + --> $DIR/into_iter_on_ref.rs:44:26 + | +LL | let _ = (&[1, 2, 3]).into_iter().next(); //~ WARN equivalent to .iter() + | ^^^^^^^^^ help: call directly: `iter` + +error: aborting due to 27 previous errors diff --git a/tests/ui/iter_next_slice.fixed b/tests/ui/iter_next_slice.fixed new file mode 100644 index 00000000000..79c1db87ac3 --- /dev/null +++ b/tests/ui/iter_next_slice.fixed @@ -0,0 +1,24 @@ +// run-rustfix +#![warn(clippy::iter_next_slice)] + +fn main() { + // test code goes here + let s = [1, 2, 3]; + let v = vec![1, 2, 3]; + + s.get(0); + // Should be replaced by s.get(0) + + s.get(2); + // Should be replaced by s.get(2) + + v.get(5); + // Should be replaced by v.get(5) + + v.get(0); + // Should be replaced by v.get(0) + + let o = Some(5); + o.iter().next(); + // Shouldn't be linted since this is not a Slice or an Array +} diff --git a/tests/ui/iter_next_slice.rs b/tests/ui/iter_next_slice.rs new file mode 100644 index 00000000000..ef9a55f3d99 --- /dev/null +++ b/tests/ui/iter_next_slice.rs @@ -0,0 +1,24 @@ +// run-rustfix +#![warn(clippy::iter_next_slice)] + +fn main() { + // test code goes here + let s = [1, 2, 3]; + let v = vec![1, 2, 3]; + + s.iter().next(); + // Should be replaced by s.get(0) + + s[2..].iter().next(); + // Should be replaced by s.get(2) + + v[5..].iter().next(); + // Should be replaced by v.get(5) + + v.iter().next(); + // Should be replaced by v.get(0) + + let o = Some(5); + o.iter().next(); + // Shouldn't be linted since this is not a Slice or an Array +} diff --git a/tests/ui/iter_next_slice.stderr b/tests/ui/iter_next_slice.stderr new file mode 100644 index 00000000000..bbf61df0cda --- /dev/null +++ b/tests/ui/iter_next_slice.stderr @@ -0,0 +1,28 @@ +error: Using `.iter().next()` on an array + --> $DIR/iter_next_slice.rs:9:5 + | +LL | s.iter().next(); + | ^^^^^^^^^^^^^^^ help: try calling: `s.get(0)` + | + = note: `-D clippy::iter-next-slice` implied by `-D warnings` + +error: Using `.iter().next()` on a Slice without end index. + --> $DIR/iter_next_slice.rs:12:5 + | +LL | s[2..].iter().next(); + | ^^^^^^^^^^^^^^^^^^^^ help: try calling: `s.get(2)` + +error: Using `.iter().next()` on a Slice without end index. + --> $DIR/iter_next_slice.rs:15:5 + | +LL | v[5..].iter().next(); + | ^^^^^^^^^^^^^^^^^^^^ help: try calling: `v.get(5)` + +error: Using `.iter().next()` on an array + --> $DIR/iter_next_slice.rs:18:5 + | +LL | v.iter().next(); + | ^^^^^^^^^^^^^^^ help: try calling: `v.get(0)` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/needless_collect.fixed b/tests/ui/needless_collect.fixed index b4227eaf2f8..be37dc16b9a 100644 --- a/tests/ui/needless_collect.fixed +++ b/tests/ui/needless_collect.fixed @@ -9,7 +9,7 @@ use std::collections::{BTreeSet, HashMap, HashSet}; fn main() { let sample = [1; 5]; let len = sample.iter().count(); - if sample.iter().next().is_none() { + if sample.get(0).is_none() { // Empty } sample.iter().cloned().any(|x| x == 1); diff --git a/tests/ui/needless_collect.stderr b/tests/ui/needless_collect.stderr index 8884c8e1612..9113aad90dd 100644 --- a/tests/ui/needless_collect.stderr +++ b/tests/ui/needless_collect.stderr @@ -1,28 +1,28 @@ error: avoid using `collect()` when not needed - --> $DIR/needless_collect.rs:11:28 + --> $DIR/needless_collect.rs:11:29 | LL | let len = sample.iter().collect::>().len(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `.count()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `count()` | = note: `-D clippy::needless-collect` implied by `-D warnings` error: avoid using `collect()` when not needed - --> $DIR/needless_collect.rs:12:21 + --> $DIR/needless_collect.rs:12:15 | LL | if sample.iter().collect::>().is_empty() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `.next().is_none()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `get(0).is_none()` error: avoid using `collect()` when not needed - --> $DIR/needless_collect.rs:15:27 + --> $DIR/needless_collect.rs:15:28 | LL | sample.iter().cloned().collect::>().contains(&1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `.any(|x| x == 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == 1)` error: avoid using `collect()` when not needed - --> $DIR/needless_collect.rs:16:34 + --> $DIR/needless_collect.rs:16:35 | LL | sample.iter().map(|x| (x, x)).collect::>().len(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `.count()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `count()` error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From 9a5baed482b68e0d9806e19eb9e8676d7ff3e1c2 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Sun, 31 May 2020 15:09:12 -0700 Subject: Implement suggestions from phansch --- clippy_lints/src/unnecessary_sort_by.rs | 41 ++++++++++++++++++++++++++------- tests/ui/unnecessary_sort_by.fixed | 7 +++--- tests/ui/unnecessary_sort_by.rs | 7 +++--- tests/ui/unnecessary_sort_by.stderr | 26 +++++++++++++-------- 4 files changed, 57 insertions(+), 24 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/unnecessary_sort_by.rs b/clippy_lints/src/unnecessary_sort_by.rs index c0858ec4c88..33d8331c292 100644 --- a/clippy_lints/src/unnecessary_sort_by.rs +++ b/clippy_lints/src/unnecessary_sort_by.rs @@ -18,15 +18,25 @@ declare_clippy_lint! { /// possible) than to use `Vec::sort_by` and and a more complicated /// closure. /// - /// **Known problems:** None. + /// **Known problems:** + /// If the suggested `Vec::sort_by_key` uses Reverse and it isn't + /// imported by a use statement in the current frame, then a `use` + /// statement that imports it will need to be added (which this lint + /// can't do). /// /// **Example:** /// /// ```rust - /// vec.sort_by(|a, b| a.foo().cmp(b.foo())); + /// # struct A; + /// # impl A { fn foo(&self) {} } + /// # let mut vec: Vec = Vec::new(); + /// vec.sort_by(|a, b| a.foo().cmp(&b.foo())); /// ``` /// Use instead: /// ```rust + /// # struct A; + /// # impl A { fn foo(&self) {} } + /// # let mut vec: Vec = Vec::new(); /// vec.sort_by_key(|a| a.foo()); /// ``` pub UNNECESSARY_SORT_BY, @@ -50,6 +60,7 @@ struct SortByKeyDetection { vec_name: String, closure_arg: String, closure_body: String, + reverse: bool, unstable: bool, } @@ -172,16 +183,16 @@ fn detect_lint(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option if let ExprKind::MethodCall(method_path, _, [ref left_expr, ref right_expr]) = &closure_body.value.kind; if method_path.ident.name.to_ident_string() == "cmp"; then { - let (closure_body, closure_arg) = if mirrored_exprs( + let (closure_body, closure_arg, reverse) = if mirrored_exprs( &cx, &left_expr, &left_ident, &right_expr, &right_ident ) { - (Sugg::hir(cx, &left_expr, "..").to_string(), left_ident.name.to_string()) + (Sugg::hir(cx, &left_expr, "..").to_string(), left_ident.name.to_string(), false) } else if mirrored_exprs(&cx, &left_expr, &right_ident, &right_expr, &left_ident) { - (format!("Reverse({})", Sugg::hir(cx, &left_expr, "..").to_string()), right_ident.name.to_string()) + (Sugg::hir(cx, &left_expr, "..").to_string(), right_ident.name.to_string(), true) } else { return None; }; @@ -196,7 +207,13 @@ fn detect_lint(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option Some(LintTrigger::Sort(SortDetection { vec_name, unstable })) } else { - Some(LintTrigger::SortByKey(SortByKeyDetection { vec_name, unstable, closure_arg, closure_body })) + Some(LintTrigger::SortByKey(SortByKeyDetection { + vec_name, + unstable, + closure_arg, + closure_body, + reverse + })) } } } else { @@ -219,9 +236,17 @@ impl LateLintPass<'_, '_> for UnnecessarySortBy { trigger.vec_name, if trigger.unstable { "_unstable" } else { "" }, trigger.closure_arg, - trigger.closure_body, + if trigger.reverse { + format!("Reverse({})", trigger.closure_body) + } else { + trigger.closure_body.to_string() + }, ), - Applicability::MachineApplicable, + if trigger.reverse { + Applicability::MaybeIncorrect + } else { + Applicability::MachineApplicable + }, ), Some(LintTrigger::Sort(trigger)) => utils::span_lint_and_sugg( cx, diff --git a/tests/ui/unnecessary_sort_by.fixed b/tests/ui/unnecessary_sort_by.fixed index 4521ae38d49..779fd57707a 100644 --- a/tests/ui/unnecessary_sort_by.fixed +++ b/tests/ui/unnecessary_sort_by.fixed @@ -10,16 +10,17 @@ fn main() { let mut vec: Vec = vec![3, 6, 1, 2, 5]; // Forward examples vec.sort(); + vec.sort_unstable(); vec.sort_by_key(|&a| (a + 5).abs()); - vec.sort_by_key(|&a| id(-a)); + vec.sort_unstable_by_key(|&a| id(-a)); // Reverse examples vec.sort_by_key(|&b| Reverse(b)); vec.sort_by_key(|&b| Reverse((b + 5).abs())); - vec.sort_by_key(|&b| Reverse(id(-b))); + vec.sort_unstable_by_key(|&b| Reverse(id(-b))); // Negative examples (shouldn't be changed) let c = &7; vec.sort_by(|a, b| (b - a).cmp(&(a - b))); vec.sort_by(|_, b| b.cmp(&5)); vec.sort_by(|_, b| b.cmp(c)); - vec.sort_by(|a, _| a.cmp(c)); + vec.sort_unstable_by(|a, _| a.cmp(c)); } diff --git a/tests/ui/unnecessary_sort_by.rs b/tests/ui/unnecessary_sort_by.rs index fdb5a823369..0485a5630af 100644 --- a/tests/ui/unnecessary_sort_by.rs +++ b/tests/ui/unnecessary_sort_by.rs @@ -10,16 +10,17 @@ fn main() { let mut vec: Vec = vec![3, 6, 1, 2, 5]; // Forward examples vec.sort_by(|a, b| a.cmp(b)); + vec.sort_unstable_by(|a, b| a.cmp(b)); vec.sort_by(|a, b| (a + 5).abs().cmp(&(b + 5).abs())); - vec.sort_by(|a, b| id(-a).cmp(&id(-b))); + vec.sort_unstable_by(|a, b| id(-a).cmp(&id(-b))); // Reverse examples vec.sort_by(|a, b| b.cmp(a)); vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); - vec.sort_by(|a, b| id(-b).cmp(&id(-a))); + vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a))); // Negative examples (shouldn't be changed) let c = &7; vec.sort_by(|a, b| (b - a).cmp(&(a - b))); vec.sort_by(|_, b| b.cmp(&5)); vec.sort_by(|_, b| b.cmp(c)); - vec.sort_by(|a, _| a.cmp(c)); + vec.sort_unstable_by(|a, _| a.cmp(c)); } diff --git a/tests/ui/unnecessary_sort_by.stderr b/tests/ui/unnecessary_sort_by.stderr index b6365c1709d..903b6e5099c 100644 --- a/tests/ui/unnecessary_sort_by.stderr +++ b/tests/ui/unnecessary_sort_by.stderr @@ -6,35 +6,41 @@ LL | vec.sort_by(|a, b| a.cmp(b)); | = note: `-D clippy::unnecessary-sort-by` implied by `-D warnings` -error: use Vec::sort_by_key here instead +error: use Vec::sort here instead --> $DIR/unnecessary_sort_by.rs:13: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:14: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:14:5 + --> $DIR/unnecessary_sort_by.rs:15:5 | -LL | vec.sort_by(|a, b| id(-a).cmp(&id(-b))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&a| id(-a))` +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:16:5 + --> $DIR/unnecessary_sort_by.rs:17:5 | LL | vec.sort_by(|a, b| b.cmp(a)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(b))` 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_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:18:5 + --> $DIR/unnecessary_sort_by.rs:19:5 | -LL | vec.sort_by(|a, b| id(-b).cmp(&id(-a))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(id(-b)))` +LL | vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|&b| Reverse(id(-b)))` -error: aborting due to 6 previous errors +error: aborting due to 7 previous errors -- cgit 1.4.1-3-g733a5 From ae0ce2255aea7e896cbfc0330c9d4f17ed66b55f Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Mon, 1 Jun 2020 09:58:42 +0200 Subject: Add regression test for string_lit_as_bytes issue --- tests/ui/string_lit_as_bytes.fixed | 2 ++ tests/ui/string_lit_as_bytes.rs | 2 ++ tests/ui/string_lit_as_bytes.stderr | 4 ++-- 3 files changed, 6 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/ui/string_lit_as_bytes.fixed b/tests/ui/string_lit_as_bytes.fixed index 7ad272ade5f..ccf8f61c4a9 100644 --- a/tests/ui/string_lit_as_bytes.fixed +++ b/tests/ui/string_lit_as_bytes.fixed @@ -14,6 +14,8 @@ fn str_lit_as_bytes() { let strify = stringify!(foobar).as_bytes(); + let current_version = env!("CARGO_PKG_VERSION").as_bytes(); + let includestr = include_bytes!("entry_unfixable.rs"); let _ = b"string with newline\t\n"; diff --git a/tests/ui/string_lit_as_bytes.rs b/tests/ui/string_lit_as_bytes.rs index 1bf4538b7c9..178df08e249 100644 --- a/tests/ui/string_lit_as_bytes.rs +++ b/tests/ui/string_lit_as_bytes.rs @@ -14,6 +14,8 @@ fn str_lit_as_bytes() { let strify = stringify!(foobar).as_bytes(); + let current_version = env!("CARGO_PKG_VERSION").as_bytes(); + let includestr = include_str!("entry_unfixable.rs").as_bytes(); let _ = "string with newline\t\n".as_bytes(); diff --git a/tests/ui/string_lit_as_bytes.stderr b/tests/ui/string_lit_as_bytes.stderr index ff6e3346dfc..99c512354d5 100644 --- a/tests/ui/string_lit_as_bytes.stderr +++ b/tests/ui/string_lit_as_bytes.stderr @@ -13,13 +13,13 @@ LL | let bs = r###"raw string with 3# plus " ""###.as_bytes(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a byte string literal instead: `br###"raw string with 3# plus " ""###` error: calling `as_bytes()` on `include_str!(..)` - --> $DIR/string_lit_as_bytes.rs:17:22 + --> $DIR/string_lit_as_bytes.rs:19:22 | LL | let includestr = include_str!("entry_unfixable.rs").as_bytes(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `include_bytes!(..)` instead: `include_bytes!("entry_unfixable.rs")` error: calling `as_bytes()` on a string literal - --> $DIR/string_lit_as_bytes.rs:19:13 + --> $DIR/string_lit_as_bytes.rs:21:13 | LL | let _ = "string with newline/t/n".as_bytes(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a byte string literal instead: `b"string with newline/t/n"` -- cgit 1.4.1-3-g733a5 From 861b897c54200becd6767ad6e091abef61f15344 Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Mon, 1 Jun 2020 10:20:17 +0200 Subject: Add regression test for endless loop This was fixed in pulldown_cmark 0.7.1, specifically https://github.com/raphlinus/pulldown-cmark/pull/438 --- clippy_lints/Cargo.toml | 2 +- tests/ui/crashes/regressions.rs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 98391732d18..e959c1a6511 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -21,7 +21,7 @@ cargo_metadata = "0.9.1" if_chain = "1.0.0" itertools = "0.9" lazy_static = "1.0.2" -pulldown-cmark = { version = "0.7", default-features = false } +pulldown-cmark = { version = "0.7.1", default-features = false } quine-mc_cluskey = "0.2.2" regex-syntax = "0.6" serde = { version = "1.0", features = ["derive"] } diff --git a/tests/ui/crashes/regressions.rs b/tests/ui/crashes/regressions.rs index 623ae51f9f0..3d5063d1a3a 100644 --- a/tests/ui/crashes/regressions.rs +++ b/tests/ui/crashes/regressions.rs @@ -6,4 +6,8 @@ pub fn foo(bar: *const u8) { println!("{:#p}", bar); } +// Regression test for https://github.com/rust-lang/rust-clippy/issues/4917 +/// Date: Wed, 3 Jun 2020 09:04:24 +0700 Subject: Fix false negative of checked_conversion lint --- clippy_lints/src/checked_conversions.rs | 78 +++++++++++------------ tests/ui/checked_conversions.fixed | 106 ++++++++++++-------------------- tests/ui/checked_conversions.rs | 106 ++++++++++++-------------------- tests/ui/checked_conversions.stderr | 98 +++++++++++++++++++++-------- tests/ui/checked_conversions.stdout | 0 5 files changed, 188 insertions(+), 200 deletions(-) delete mode 100644 tests/ui/checked_conversions.stdout (limited to 'tests') diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index d9776dd50a8..e845ef99c7c 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -58,24 +58,18 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CheckedConversions { } }; - if_chain! { - if let Some(cv) = result; - if let Some(to_type) = cv.to_type; - - then { + if let Some(cv) = result { + if let Some(to_type) = cv.to_type { let mut applicability = Applicability::MachineApplicable; - let snippet = snippet_with_applicability(cx, cv.expr_to_cast.span, "_", &mut - applicability); + let snippet = snippet_with_applicability(cx, cv.expr_to_cast.span, "_", &mut applicability); span_lint_and_sugg( cx, CHECKED_CONVERSIONS, item.span, "Checked cast can be simplified.", "try", - format!("{}::try_from({}).is_ok()", - to_type, - snippet), - applicability + format!("{}::try_from({}).is_ok()", to_type, snippet), + applicability, ); } } @@ -184,7 +178,7 @@ fn check_upper_bound<'tcx>(expr: &'tcx Expr<'tcx>) -> Option> { if_chain! { if let ExprKind::Binary(ref op, ref left, ref right) = &expr.kind; if let Some((candidate, check)) = normalize_le_ge(op, left, right); - if let Some((from, to)) = get_types_from_cast(check, MAX_VALUE, INTS); + if let Some((from, to)) = get_types_from_cast(check, INTS, "max_value", "MAX"); then { Conversion::try_new(candidate, from, to) @@ -224,7 +218,7 @@ fn check_lower_bound_zero<'a>(candidate: &'a Expr<'_>, check: &'a Expr<'_>) -> O /// Check for `expr >= (to_type::MIN as from_type)` fn check_lower_bound_min<'a>(candidate: &'a Expr<'_>, check: &'a Expr<'_>) -> Option> { - if let Some((from, to)) = get_types_from_cast(check, MIN_VALUE, SINTS) { + if let Some((from, to)) = get_types_from_cast(check, SINTS, "min_value", "MIN") { Conversion::try_new(candidate, from, to) } else { None @@ -232,10 +226,16 @@ fn check_lower_bound_min<'a>(candidate: &'a Expr<'_>, check: &'a Expr<'_>) -> Op } /// Tries to extract the from- and to-type from a cast expression -fn get_types_from_cast<'a>(expr: &'a Expr<'_>, func: &'a str, types: &'a [&str]) -> Option<(&'a str, &'a str)> { - // `to_type::maxmin_value() as from_type` +fn get_types_from_cast<'a>( + expr: &'a Expr<'_>, + types: &'a [&str], + func: &'a str, + assoc_const: &'a str, +) -> Option<(&'a str, &'a str)> { + // `to_type::max_value() as from_type` + // or `to_type::MAX as from_type` let call_from_cast: Option<(&Expr<'_>, &str)> = if_chain! { - // to_type::maxmin_value(), from_type + // to_type::max_value(), from_type if let ExprKind::Cast(ref limit, ref from_type) = &expr.kind; if let TyKind::Path(ref from_type_path) = &from_type.kind; if let Some(from_sym) = int_ty_to_sym(from_type_path); @@ -247,17 +247,17 @@ fn get_types_from_cast<'a>(expr: &'a Expr<'_>, func: &'a str, types: &'a [&str]) } }; - // `from_type::from(to_type::maxmin_value())` + // `from_type::from(to_type::max_value())` let limit_from: Option<(&Expr<'_>, &str)> = call_from_cast.or_else(|| { if_chain! { - // `from_type::from, to_type::maxmin_value()` + // `from_type::from, to_type::max_value()` if let ExprKind::Call(ref from_func, ref args) = &expr.kind; - // `to_type::maxmin_value()` + // `to_type::max_value()` if args.len() == 1; if let limit = &args[0]; // `from_type::from` if let ExprKind::Path(ref path) = &from_func.kind; - if let Some(from_sym) = get_implementing_type(path, INTS, FROM); + if let Some(from_sym) = get_implementing_type(path, INTS, "from"); then { Some((limit, from_sym)) @@ -268,22 +268,26 @@ fn get_types_from_cast<'a>(expr: &'a Expr<'_>, func: &'a str, types: &'a [&str]) }); if let Some((limit, from_type)) = limit_from { - if_chain! { - if let ExprKind::Call(ref fun_name, _) = &limit.kind; - // `to_type, maxmin_value` - if let ExprKind::Path(ref path) = &fun_name.kind; - // `to_type` - if let Some(to_type) = get_implementing_type(path, types, func); - - then { - Some((from_type, to_type)) - } else { - None - } + match limit.kind { + // `from_type::from(_)` + ExprKind::Call(path, _) => { + if let ExprKind::Path(ref path) = path.kind { + // `to_type` + if let Some(to_type) = get_implementing_type(path, types, func) { + return Some((from_type, to_type)); + } + } + }, + // `to_type::MAX` + ExprKind::Path(ref path) => { + if let Some(to_type) = get_implementing_type(path, types, assoc_const) { + return Some((from_type, to_type)); + } + }, + _ => {}, } - } else { - None - } + }; + None } /// Gets the type which implements the called function @@ -336,10 +340,6 @@ fn normalize_le_ge<'a>(op: &BinOp, left: &'a Expr<'a>, right: &'a Expr<'a>) -> O } // Constants -const FROM: &str = "from"; -const MAX_VALUE: &str = "max_value"; -const MIN_VALUE: &str = "min_value"; - const UINTS: &[&str] = &["u8", "u16", "u32", "u64", "usize"]; const SINTS: &[&str] = &["i8", "i16", "i32", "i64", "isize"]; const INTS: &[&str] = &["u8", "u16", "u32", "u64", "usize", "i8", "i16", "i32", "i64", "isize"]; diff --git a/tests/ui/checked_conversions.fixed b/tests/ui/checked_conversions.fixed index 7febd6f3761..12290db3dcf 100644 --- a/tests/ui/checked_conversions.fixed +++ b/tests/ui/checked_conversions.fixed @@ -1,106 +1,76 @@ // run-rustfix +#![allow( + clippy::cast_lossless, + // Int::max_value will be deprecated in the future + deprecated, +)] #![warn(clippy::checked_conversions)] -#![allow(clippy::cast_lossless)] -#![allow(dead_code)] + use std::convert::TryFrom; // Positive tests // Signed to unsigned -fn i64_to_u32(value: i64) -> Option { - if u32::try_from(value).is_ok() { - Some(value as u32) - } else { - None - } +pub fn i64_to_u32(value: i64) { + let _ = u32::try_from(value).is_ok(); + let _ = u32::try_from(value).is_ok(); } -fn i64_to_u16(value: i64) -> Option { - if u16::try_from(value).is_ok() { - Some(value as u16) - } else { - None - } +pub fn i64_to_u16(value: i64) { + let _ = u16::try_from(value).is_ok(); + let _ = u16::try_from(value).is_ok(); } -fn isize_to_u8(value: isize) -> Option { - if u8::try_from(value).is_ok() { - Some(value as u8) - } else { - None - } +pub fn isize_to_u8(value: isize) { + let _ = u8::try_from(value).is_ok(); + let _ = u8::try_from(value).is_ok(); } // Signed to signed -fn i64_to_i32(value: i64) -> Option { - if i32::try_from(value).is_ok() { - Some(value as i32) - } else { - None - } +pub fn i64_to_i32(value: i64) { + let _ = i32::try_from(value).is_ok(); + let _ = i32::try_from(value).is_ok(); } -fn i64_to_i16(value: i64) -> Option { - if i16::try_from(value).is_ok() { - Some(value as i16) - } else { - None - } +pub fn i64_to_i16(value: i64) { + let _ = i16::try_from(value).is_ok(); + let _ = i16::try_from(value).is_ok(); } // Unsigned to X -fn u32_to_i32(value: u32) -> Option { - if i32::try_from(value).is_ok() { - Some(value as i32) - } else { - None - } +pub fn u32_to_i32(value: u32) { + let _ = i32::try_from(value).is_ok(); + let _ = i32::try_from(value).is_ok(); } -fn usize_to_isize(value: usize) -> isize { - if isize::try_from(value).is_ok() && value as i32 == 5 { - 5 - } else { - 1 - } +pub fn usize_to_isize(value: usize) { + let _ = isize::try_from(value).is_ok() && value as i32 == 5; + let _ = isize::try_from(value).is_ok() && value as i32 == 5; } -fn u32_to_u16(value: u32) -> isize { - if u16::try_from(value).is_ok() && value as i32 == 5 { - 5 - } else { - 1 - } +pub fn u32_to_u16(value: u32) { + let _ = u16::try_from(value).is_ok() && value as i32 == 5; + let _ = u16::try_from(value).is_ok() && value as i32 == 5; } // Negative tests -fn no_i64_to_i32(value: i64) -> Option { - if value <= (i32::max_value() as i64) && value >= 0 { - Some(value as i32) - } else { - None - } +pub fn no_i64_to_i32(value: i64) { + let _ = value <= (i32::max_value() as i64) && value >= 0; + let _ = value <= (i32::MAX as i64) && value >= 0; } -fn no_isize_to_u8(value: isize) -> Option { - if value <= (u8::max_value() as isize) && value >= (u8::min_value() as isize) { - Some(value as u8) - } else { - None - } +pub fn no_isize_to_u8(value: isize) { + let _ = value <= (u8::max_value() as isize) && value >= (u8::min_value() as isize); + let _ = value <= (u8::MAX as isize) && value >= (u8::MIN as isize); } -fn i8_to_u8(value: i8) -> Option { - if value >= 0 { - Some(value as u8) - } else { - None - } +pub fn i8_to_u8(value: i8) { + let _ = value >= 0; } fn main() {} diff --git a/tests/ui/checked_conversions.rs b/tests/ui/checked_conversions.rs index a643354e243..895a301e821 100644 --- a/tests/ui/checked_conversions.rs +++ b/tests/ui/checked_conversions.rs @@ -1,106 +1,76 @@ // run-rustfix +#![allow( + clippy::cast_lossless, + // Int::max_value will be deprecated in the future + deprecated, +)] #![warn(clippy::checked_conversions)] -#![allow(clippy::cast_lossless)] -#![allow(dead_code)] + use std::convert::TryFrom; // Positive tests // Signed to unsigned -fn i64_to_u32(value: i64) -> Option { - if value <= (u32::max_value() as i64) && value >= 0 { - Some(value as u32) - } else { - None - } +pub fn i64_to_u32(value: i64) { + let _ = value <= (u32::max_value() as i64) && value >= 0; + let _ = value <= (u32::MAX as i64) && value >= 0; } -fn i64_to_u16(value: i64) -> Option { - if value <= i64::from(u16::max_value()) && value >= 0 { - Some(value as u16) - } else { - None - } +pub fn i64_to_u16(value: i64) { + let _ = value <= i64::from(u16::max_value()) && value >= 0; + let _ = value <= i64::from(u16::MAX) && value >= 0; } -fn isize_to_u8(value: isize) -> Option { - if value <= (u8::max_value() as isize) && value >= 0 { - Some(value as u8) - } else { - None - } +pub fn isize_to_u8(value: isize) { + let _ = value <= (u8::max_value() as isize) && value >= 0; + let _ = value <= (u8::MAX as isize) && value >= 0; } // Signed to signed -fn i64_to_i32(value: i64) -> Option { - if value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64) { - Some(value as i32) - } else { - None - } +pub fn i64_to_i32(value: i64) { + let _ = value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64); + let _ = value <= (i32::MAX as i64) && value >= (i32::MIN as i64); } -fn i64_to_i16(value: i64) -> Option { - if value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value()) { - Some(value as i16) - } else { - None - } +pub fn i64_to_i16(value: i64) { + let _ = value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value()); + let _ = value <= i64::from(i16::MAX) && value >= i64::from(i16::MIN); } // Unsigned to X -fn u32_to_i32(value: u32) -> Option { - if value <= i32::max_value() as u32 { - Some(value as i32) - } else { - None - } +pub fn u32_to_i32(value: u32) { + let _ = value <= i32::max_value() as u32; + let _ = value <= i32::MAX as u32; } -fn usize_to_isize(value: usize) -> isize { - if value <= isize::max_value() as usize && value as i32 == 5 { - 5 - } else { - 1 - } +pub fn usize_to_isize(value: usize) { + let _ = value <= isize::max_value() as usize && value as i32 == 5; + let _ = value <= isize::MAX as usize && value as i32 == 5; } -fn u32_to_u16(value: u32) -> isize { - if value <= u16::max_value() as u32 && value as i32 == 5 { - 5 - } else { - 1 - } +pub fn u32_to_u16(value: u32) { + let _ = value <= u16::max_value() as u32 && value as i32 == 5; + let _ = value <= u16::MAX as u32 && value as i32 == 5; } // Negative tests -fn no_i64_to_i32(value: i64) -> Option { - if value <= (i32::max_value() as i64) && value >= 0 { - Some(value as i32) - } else { - None - } +pub fn no_i64_to_i32(value: i64) { + let _ = value <= (i32::max_value() as i64) && value >= 0; + let _ = value <= (i32::MAX as i64) && value >= 0; } -fn no_isize_to_u8(value: isize) -> Option { - if value <= (u8::max_value() as isize) && value >= (u8::min_value() as isize) { - Some(value as u8) - } else { - None - } +pub fn no_isize_to_u8(value: isize) { + let _ = value <= (u8::max_value() as isize) && value >= (u8::min_value() as isize); + let _ = value <= (u8::MAX as isize) && value >= (u8::MIN as isize); } -fn i8_to_u8(value: i8) -> Option { - if value >= 0 { - Some(value as u8) - } else { - None - } +pub fn i8_to_u8(value: i8) { + let _ = value >= 0; } fn main() {} diff --git a/tests/ui/checked_conversions.stderr b/tests/ui/checked_conversions.stderr index f678f009621..648ba3ccd01 100644 --- a/tests/ui/checked_conversions.stderr +++ b/tests/ui/checked_conversions.stderr @@ -1,52 +1,100 @@ error: Checked cast can be simplified. - --> $DIR/checked_conversions.rs:13:8 + --> $DIR/checked_conversions.rs:17:13 | -LL | if value <= (u32::max_value() as i64) && value >= 0 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` +LL | let _ = value <= (u32::max_value() as i64) && value >= 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` | = note: `-D clippy::checked-conversions` implied by `-D warnings` error: Checked cast can be simplified. - --> $DIR/checked_conversions.rs:21:8 + --> $DIR/checked_conversions.rs:18:13 | -LL | if value <= i64::from(u16::max_value()) && value >= 0 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` +LL | let _ = value <= (u32::MAX as i64) && value >= 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` error: Checked cast can be simplified. - --> $DIR/checked_conversions.rs:29:8 + --> $DIR/checked_conversions.rs:22:13 | -LL | if value <= (u8::max_value() as isize) && value >= 0 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` +LL | let _ = value <= i64::from(u16::max_value()) && value >= 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: Checked cast can be simplified. - --> $DIR/checked_conversions.rs:39:8 + --> $DIR/checked_conversions.rs:23:13 | -LL | if value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` +LL | let _ = value <= i64::from(u16::MAX) && value >= 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: Checked cast can be simplified. - --> $DIR/checked_conversions.rs:47:8 + --> $DIR/checked_conversions.rs:27:13 | -LL | if value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value()) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` +LL | let _ = value <= (u8::max_value() as isize) && value >= 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` error: Checked cast can be simplified. - --> $DIR/checked_conversions.rs:57:8 + --> $DIR/checked_conversions.rs:28:13 | -LL | if value <= i32::max_value() as u32 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` +LL | let _ = value <= (u8::MAX as isize) && value >= 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` error: Checked cast can be simplified. - --> $DIR/checked_conversions.rs:65:8 + --> $DIR/checked_conversions.rs:34:13 | -LL | if value <= isize::max_value() as usize && value as i32 == 5 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` +LL | let _ = value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: Checked cast can be simplified. - --> $DIR/checked_conversions.rs:73:8 + --> $DIR/checked_conversions.rs:35:13 | -LL | if value <= u16::max_value() as u32 && value as i32 == 5 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` +LL | let _ = value <= (i32::MAX as i64) && value >= (i32::MIN as i64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` -error: aborting due to 8 previous errors +error: Checked cast can be simplified. + --> $DIR/checked_conversions.rs:39:13 + | +LL | let _ = value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` + +error: Checked cast can be simplified. + --> $DIR/checked_conversions.rs:40:13 + | +LL | let _ = value <= i64::from(i16::MAX) && value >= i64::from(i16::MIN); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` + +error: Checked cast can be simplified. + --> $DIR/checked_conversions.rs:46:13 + | +LL | let _ = value <= i32::max_value() as u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` + +error: Checked cast can be simplified. + --> $DIR/checked_conversions.rs:47:13 + | +LL | let _ = value <= i32::MAX as u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` + +error: Checked cast can be simplified. + --> $DIR/checked_conversions.rs:51:13 + | +LL | let _ = value <= isize::max_value() as usize && value as i32 == 5; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` + +error: Checked cast can be simplified. + --> $DIR/checked_conversions.rs:52:13 + | +LL | let _ = value <= isize::MAX as usize && value as i32 == 5; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` + +error: Checked cast can be simplified. + --> $DIR/checked_conversions.rs:56:13 + | +LL | let _ = value <= u16::max_value() as u32 && value as i32 == 5; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` + +error: Checked cast can be simplified. + --> $DIR/checked_conversions.rs:57:13 + | +LL | let _ = value <= u16::MAX as u32 && value as i32 == 5; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` + +error: aborting due to 16 previous errors diff --git a/tests/ui/checked_conversions.stdout b/tests/ui/checked_conversions.stdout deleted file mode 100644 index e69de29bb2d..00000000000 -- cgit 1.4.1-3-g733a5 From c325c120c21657acb1b131ded41261889e51a62b Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 5 Jun 2020 22:30:14 +0200 Subject: Fix cargo ui tests when running inside rust repo --- clippy_dev/src/new_lint.rs | 2 ++ tests/compile-test.rs | 4 ---- tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml | 2 ++ tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml | 2 ++ .../ui-cargo/multiple_crate_versions/5041_allow_dev_build/Cargo.toml | 2 ++ tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml | 2 ++ tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml | 2 ++ tests/ui-cargo/wildcard_dependencies/fail/Cargo.toml | 2 ++ tests/ui-cargo/wildcard_dependencies/pass/Cargo.toml | 2 ++ 9 files changed, 16 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index c0b2dac2f60..1e032a7bc20 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -147,6 +147,8 @@ fn get_manifest_contents(lint_name: &str, hint: &str) -> String { name = "{}" version = "0.1.0" publish = false + +[workspace] "#, hint, lint_name ) diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 194354b291f..11b3f69a828 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -220,10 +220,6 @@ fn run_ui_cargo(config: &mut compiletest::Config) { Ok(result) } - if cargo::is_rustc_test_suite() { - return; - } - config.mode = TestMode::Ui; config.src_base = Path::new("tests").join("ui-cargo").canonicalize().unwrap(); diff --git a/tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml b/tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml index c64adcf7c01..ae0a6032996 100644 --- a/tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml +++ b/tests/ui-cargo/cargo_common_metadata/fail/Cargo.toml @@ -2,3 +2,5 @@ name = "cargo_common_metadata" version = "0.1.0" publish = false + +[workspace] diff --git a/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml b/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml index c8233f328bb..737e84e963c 100644 --- a/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml +++ b/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml @@ -9,3 +9,5 @@ readme = "README.md" license = "MIT OR Apache-2.0" keywords = ["metadata", "lint", "clippy"] categories = ["development-tools::testing"] + +[workspace] diff --git a/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/Cargo.toml b/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/Cargo.toml index 72731fbc75d..278bebbbd9e 100644 --- a/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/Cargo.toml +++ b/tests/ui-cargo/multiple_crate_versions/5041_allow_dev_build/Cargo.toml @@ -5,6 +5,8 @@ name = "multiple_crate_versions" version = "0.1.0" publish = false +[workspace] + # One of the versions of winapi is only a dev dependency: allowed [dependencies] ctrlc = "=3.1.0" diff --git a/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml b/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml index 3a94b723f3f..4f97b011334 100644 --- a/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml +++ b/tests/ui-cargo/multiple_crate_versions/fail/Cargo.toml @@ -3,6 +3,8 @@ name = "multiple_crate_versions" version = "0.1.0" publish = false +[workspace] + [dependencies] ctrlc = "=3.1.0" ansi_term = "=0.11.0" diff --git a/tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml b/tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml index a9b06420b33..b4b49bb369a 100644 --- a/tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml +++ b/tests/ui-cargo/multiple_crate_versions/pass/Cargo.toml @@ -3,6 +3,8 @@ name = "cargo_common_metadata" version = "0.1.0" publish = false +[workspace] + [dependencies] regex = "1.3.7" serde = "1.0.110" diff --git a/tests/ui-cargo/wildcard_dependencies/fail/Cargo.toml b/tests/ui-cargo/wildcard_dependencies/fail/Cargo.toml index fd2a3414856..3e1a02cbb3c 100644 --- a/tests/ui-cargo/wildcard_dependencies/fail/Cargo.toml +++ b/tests/ui-cargo/wildcard_dependencies/fail/Cargo.toml @@ -3,5 +3,7 @@ name = "wildcard_dependencies" version = "0.1.0" publish = false +[workspace] + [dependencies] regex = "*" diff --git a/tests/ui-cargo/wildcard_dependencies/pass/Cargo.toml b/tests/ui-cargo/wildcard_dependencies/pass/Cargo.toml index 38cb139146e..f844cab09ba 100644 --- a/tests/ui-cargo/wildcard_dependencies/pass/Cargo.toml +++ b/tests/ui-cargo/wildcard_dependencies/pass/Cargo.toml @@ -3,5 +3,7 @@ name = "wildcard_dependencies" version = "0.1.0" publish = false +[workspace] + [dependencies] regex = "1" -- cgit 1.4.1-3-g733a5 From 5bdbc45ae579e7b8f4187bc791abd67924cb626b Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sun, 7 Jun 2020 03:07:48 +0200 Subject: Rustup to rust-lang/rust#71796 --- tests/ui/or_fun_call.fixed | 4 ++-- tests/ui/or_fun_call.stderr | 18 +++++++++++++++--- 2 files changed, 17 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 7bb08797ef3..2045ffdb5f0 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -29,7 +29,7 @@ fn or_fun_call() { with_enum.unwrap_or(Enum::A(5)); let with_const_fn = Some(Duration::from_secs(1)); - with_const_fn.unwrap_or(Duration::from_secs(5)); + with_const_fn.unwrap_or_else(|| Duration::from_secs(5)); let with_constructor = Some(vec![1]); with_constructor.unwrap_or_else(make); @@ -94,7 +94,7 @@ fn test_or_with_ctors() { let b = "b".to_string(); let _ = Some(Bar("a".to_string(), Duration::from_secs(1))) - .or(Some(Bar(b, Duration::from_secs(2)))); + .or_else(|| Some(Bar(b, Duration::from_secs(2)))); let vec = vec!["foo"]; let _ = opt.ok_or(vec.len()); diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index 96d55771e6c..bc5978b538f 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -1,10 +1,16 @@ +error: use of `unwrap_or` followed by a function call + --> $DIR/or_fun_call.rs:32:19 + | +LL | with_const_fn.unwrap_or(Duration::from_secs(5)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| Duration::from_secs(5))` + | + = note: `-D clippy::or-fun-call` implied by `-D warnings` + error: use of `unwrap_or` followed by a function call --> $DIR/or_fun_call.rs:35:22 | LL | with_constructor.unwrap_or(make()); | ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(make)` - | - = note: `-D clippy::or-fun-call` implied by `-D warnings` error: use of `unwrap_or` followed by a call to `new` --> $DIR/or_fun_call.rs:38:5 @@ -78,5 +84,11 @@ error: use of `or` followed by a function call LL | let _ = Some("a".to_string()).or(Some("b".to_string())); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some("b".to_string()))` -error: aborting due to 13 previous errors +error: use of `or` followed by a function call + --> $DIR/or_fun_call.rs:97:10 + | +LL | .or(Some(Bar(b, Duration::from_secs(2)))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some(Bar(b, Duration::from_secs(2))))` + +error: aborting due to 15 previous errors -- cgit 1.4.1-3-g733a5 From 9c205d7b1baa982ae7063d57b18088ecf28df83b Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sat, 6 Jun 2020 21:51:41 +0200 Subject: Rename let_and_return test for consistency with the lint name --- tests/ui/let_and_return.rs | 70 ++++++++++++++++++++++++++++++++++++++++++ tests/ui/let_and_return.stderr | 31 +++++++++++++++++++ tests/ui/let_return.rs | 70 ------------------------------------------ tests/ui/let_return.stderr | 31 ------------------- 4 files changed, 101 insertions(+), 101 deletions(-) create mode 100644 tests/ui/let_and_return.rs create mode 100644 tests/ui/let_and_return.stderr delete mode 100644 tests/ui/let_return.rs delete mode 100644 tests/ui/let_return.stderr (limited to 'tests') diff --git a/tests/ui/let_and_return.rs b/tests/ui/let_and_return.rs new file mode 100644 index 00000000000..23645d48fe7 --- /dev/null +++ b/tests/ui/let_and_return.rs @@ -0,0 +1,70 @@ +#![allow(unused)] +#![warn(clippy::let_and_return)] + +fn test() -> i32 { + let _y = 0; // no warning + let x = 5; + x +} + +fn test_inner() -> i32 { + if true { + let x = 5; + x + } else { + 0 + } +} + +fn test_nowarn_1() -> i32 { + let mut x = 5; + x += 1; + x +} + +fn test_nowarn_2() -> i32 { + let x = 5; + x + 1 +} + +fn test_nowarn_3() -> (i32, i32) { + // this should technically warn, but we do not compare complex patterns + let (x, y) = (5, 9); + (x, y) +} + +fn test_nowarn_4() -> i32 { + // this should technically warn, but not b/c of clippy::let_and_return, but b/c of useless type + let x: i32 = 5; + x +} + +fn test_nowarn_5(x: i16) -> u16 { + #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] + let x = x as u16; + x +} + +// False positive example +trait Decode { + fn decode(d: D) -> Result + where + Self: Sized; +} + +macro_rules! tuple_encode { + ($($x:ident),*) => ( + impl<$($x: Decode),*> Decode for ($($x),*) { + #[inline] + #[allow(non_snake_case)] + fn decode(mut d: D) -> Result { + // Shouldn't trigger lint + Ok(($({let $x = Decode::decode(&mut d)?; $x }),*)) + } + } + ); +} + +tuple_encode!(T0, T1, T2, T3, T4, T5, T6, T7); + +fn main() {} diff --git a/tests/ui/let_and_return.stderr b/tests/ui/let_and_return.stderr new file mode 100644 index 00000000000..eacf948b392 --- /dev/null +++ b/tests/ui/let_and_return.stderr @@ -0,0 +1,31 @@ +error: returning the result of a `let` binding from a block + --> $DIR/let_and_return.rs:7:5 + | +LL | let x = 5; + | ---------- unnecessary `let` binding +LL | x + | ^ + | + = note: `-D clippy::let-and-return` implied by `-D warnings` +help: return the expression directly + | +LL | +LL | 5 + | + +error: returning the result of a `let` binding from a block + --> $DIR/let_and_return.rs:13:9 + | +LL | let x = 5; + | ---------- unnecessary `let` binding +LL | x + | ^ + | +help: return the expression directly + | +LL | +LL | 5 + | + +error: aborting due to 2 previous errors + diff --git a/tests/ui/let_return.rs b/tests/ui/let_return.rs deleted file mode 100644 index 23645d48fe7..00000000000 --- a/tests/ui/let_return.rs +++ /dev/null @@ -1,70 +0,0 @@ -#![allow(unused)] -#![warn(clippy::let_and_return)] - -fn test() -> i32 { - let _y = 0; // no warning - let x = 5; - x -} - -fn test_inner() -> i32 { - if true { - let x = 5; - x - } else { - 0 - } -} - -fn test_nowarn_1() -> i32 { - let mut x = 5; - x += 1; - x -} - -fn test_nowarn_2() -> i32 { - let x = 5; - x + 1 -} - -fn test_nowarn_3() -> (i32, i32) { - // this should technically warn, but we do not compare complex patterns - let (x, y) = (5, 9); - (x, y) -} - -fn test_nowarn_4() -> i32 { - // this should technically warn, but not b/c of clippy::let_and_return, but b/c of useless type - let x: i32 = 5; - x -} - -fn test_nowarn_5(x: i16) -> u16 { - #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] - let x = x as u16; - x -} - -// False positive example -trait Decode { - fn decode(d: D) -> Result - where - Self: Sized; -} - -macro_rules! tuple_encode { - ($($x:ident),*) => ( - impl<$($x: Decode),*> Decode for ($($x),*) { - #[inline] - #[allow(non_snake_case)] - fn decode(mut d: D) -> Result { - // Shouldn't trigger lint - Ok(($({let $x = Decode::decode(&mut d)?; $x }),*)) - } - } - ); -} - -tuple_encode!(T0, T1, T2, T3, T4, T5, T6, T7); - -fn main() {} diff --git a/tests/ui/let_return.stderr b/tests/ui/let_return.stderr deleted file mode 100644 index 128a22c86e3..00000000000 --- a/tests/ui/let_return.stderr +++ /dev/null @@ -1,31 +0,0 @@ -error: returning the result of a `let` binding from a block - --> $DIR/let_return.rs:7:5 - | -LL | let x = 5; - | ---------- unnecessary `let` binding -LL | x - | ^ - | - = note: `-D clippy::let-and-return` implied by `-D warnings` -help: return the expression directly - | -LL | -LL | 5 - | - -error: returning the result of a `let` binding from a block - --> $DIR/let_return.rs:13:9 - | -LL | let x = 5; - | ---------- unnecessary `let` binding -LL | x - | ^ - | -help: return the expression directly - | -LL | -LL | 5 - | - -error: aborting due to 2 previous errors - -- cgit 1.4.1-3-g733a5 From dac8a3c1ca19d2b5934ecbe2ed79ae6c156fd885 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sun, 7 Jun 2020 00:30:39 +0200 Subject: let_and_return: do not lint if last statement borrows --- clippy_lints/src/let_and_return.rs | 61 +++++++++++++++++++++++++++++++++- clippy_lints/src/utils/mod.rs | 2 +- tests/ui/let_and_return.rs | 68 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 129 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/let_and_return.rs b/clippy_lints/src/let_and_return.rs index 8b877f696af..6d3fb317bcf 100644 --- a/clippy_lints/src/let_and_return.rs +++ b/clippy_lints/src/let_and_return.rs @@ -1,8 +1,12 @@ use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Block, ExprKind, PatKind, StmtKind}; +use rustc_hir::def_id::DefId; +use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; +use rustc_hir::{Block, Expr, ExprKind, PatKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; use crate::utils::{in_macro, match_qpath, snippet_opt, span_lint_and_then}; @@ -49,6 +53,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LetReturn { if let PatKind::Binding(.., ident, _) = local.pat.kind; if let ExprKind::Path(qpath) = &retexpr.kind; if match_qpath(qpath, &[&*ident.name.as_str()]); + if !last_statement_borrows(cx, initexpr); if !in_external_macro(cx.sess(), initexpr.span); if !in_external_macro(cx.sess(), retexpr.span); if !in_external_macro(cx.sess(), local.span); @@ -80,3 +85,57 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LetReturn { } } } + +fn last_statement_borrows<'tcx>(cx: &LateContext<'_, 'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + let mut visitor = BorrowVisitor { cx, borrows: false }; + walk_expr(&mut visitor, expr); + visitor.borrows +} + +struct BorrowVisitor<'a, 'tcx> { + cx: &'a LateContext<'a, 'tcx>, + borrows: bool, +} + +impl BorrowVisitor<'_, '_> { + fn fn_def_id(&self, expr: &Expr<'_>) -> Option { + match &expr.kind { + ExprKind::MethodCall(..) => self.cx.tables.type_dependent_def_id(expr.hir_id), + ExprKind::Call( + Expr { + kind: ExprKind::Path(qpath), + .. + }, + .., + ) => self.cx.tables.qpath_res(qpath, expr.hir_id).opt_def_id(), + _ => None, + } + } +} + +impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if self.borrows { + return; + } + + if let Some(def_id) = self.fn_def_id(expr) { + self.borrows = self + .cx + .tcx + .fn_sig(def_id) + .output() + .skip_binder() + .walk() + .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))); + } + + walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 06638e7187b..39410acea4e 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -400,7 +400,7 @@ pub fn method_calls<'tcx>( /// Matches an `Expr` against a chain of methods, and return the matched `Expr`s. /// /// For example, if `expr` represents the `.baz()` in `foo.bar().baz()`, -/// `matched_method_chain(expr, &["bar", "baz"])` will return a `Vec` +/// `method_chain_args(expr, &["bar", "baz"])` will return a `Vec` /// containing the `Expr`s for /// `.bar()` and `.baz()` pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[&str]) -> Option]>> { diff --git a/tests/ui/let_and_return.rs b/tests/ui/let_and_return.rs index 23645d48fe7..09614b8c1ad 100644 --- a/tests/ui/let_and_return.rs +++ b/tests/ui/let_and_return.rs @@ -67,4 +67,72 @@ macro_rules! tuple_encode { tuple_encode!(T0, T1, T2, T3, T4, T5, T6, T7); +mod no_lint_if_stmt_borrows { + mod issue_3792 { + use std::io::{self, BufRead, Stdin}; + + fn read_line() -> String { + let stdin = io::stdin(); + let line = stdin.lock().lines().next().unwrap().unwrap(); + line + } + } + + mod issue_3324 { + use std::cell::RefCell; + use std::rc::{Rc, Weak}; + + fn test(value: Weak>) -> u32 { + let value = value.upgrade().unwrap(); + let ret = value.borrow().baz(); + ret + } + + struct Bar {} + + impl Bar { + fn new() -> Self { + Bar {} + } + fn baz(&self) -> u32 { + 0 + } + } + + fn main() { + let a = Rc::new(RefCell::new(Bar::new())); + let b = Rc::downgrade(&a); + test(b); + } + } + + mod free_function { + struct Inner; + + struct Foo<'a> { + inner: &'a Inner, + } + + impl Drop for Foo<'_> { + fn drop(&mut self) {} + } + + impl Foo<'_> { + fn value(&self) -> i32 { + 42 + } + } + + fn some_foo(inner: &Inner) -> Foo<'_> { + Foo { inner } + } + + fn test() -> i32 { + let x = Inner {}; + let value = some_foo(&x).value(); + value + } + } +} + fn main() {} -- cgit 1.4.1-3-g733a5 From ebfc1da07d2cd1cba87a3df79c5ffbfc0d25618c Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sun, 7 Jun 2020 20:38:28 +0200 Subject: reversed_empty_ranges: don't lint N..N except in for loop arg --- clippy_lints/src/ranges.rs | 62 ++++++++++++------------- tests/ui/reversed_empty_ranges_fixable.fixed | 8 ++-- tests/ui/reversed_empty_ranges_fixable.rs | 8 ++-- tests/ui/reversed_empty_ranges_fixable.stderr | 16 ++----- tests/ui/reversed_empty_ranges_unfixable.rs | 5 +- tests/ui/reversed_empty_ranges_unfixable.stderr | 18 +++---- 6 files changed, 51 insertions(+), 66 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 1eb26d97ed4..45de4d29375 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -241,14 +241,26 @@ fn check_inclusive_range_minus_one(cx: &LateContext<'_, '_>, expr: &Expr<'_>) { } fn check_reversed_empty_range(cx: &LateContext<'_, '_>, expr: &Expr<'_>) { - fn inside_indexing_expr<'a>(cx: &'a LateContext<'_, '_>, expr: &Expr<'_>) -> Option<&'a Expr<'a>> { - match get_parent_expr(cx, expr) { - parent_expr @ Some(Expr { + fn inside_indexing_expr<'a>(cx: &'a LateContext<'_, '_>, expr: &Expr<'_>) -> bool { + matches!( + get_parent_expr(cx, expr), + Some(Expr { kind: ExprKind::Index(..), .. - }) => parent_expr, - _ => None, + }) + ) + } + + fn is_for_loop_arg(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { + let mut cur_expr = expr; + while let Some(parent_expr) = get_parent_expr(cx, cur_expr) { + match higher::for_loop(parent_expr) { + Some((_, args, _)) if args.hir_id == expr.hir_id => return true, + _ => cur_expr = parent_expr, + } } + + false } fn is_empty_range(limits: RangeLimits, ordering: Ordering) -> bool { @@ -267,34 +279,18 @@ fn check_reversed_empty_range(cx: &LateContext<'_, '_>, expr: &Expr<'_>) { if let Some(ordering) = Constant::partial_cmp(cx.tcx, ty, &start_idx, &end_idx); if is_empty_range(limits, ordering); then { - if let Some(parent_expr) = inside_indexing_expr(cx, expr) { - let (reason, outcome) = if ordering == Ordering::Equal { - ("empty", "always yield an empty slice") - } else { - ("reversed", "panic at run-time") - }; - - span_lint_and_then( - cx, - REVERSED_EMPTY_RANGES, - expr.span, - &format!("this range is {} and using it to index a slice will {}", reason, outcome), - |diag| { - if_chain! { - if ordering == Ordering::Equal; - if let ty::Slice(slice_ty) = cx.tables.expr_ty(parent_expr).kind; - then { - diag.span_suggestion( - parent_expr.span, - "if you want an empty slice, use", - format!("[] as &[{}]", slice_ty), - Applicability::MaybeIncorrect - ); - } - } - } - ); - } else { + if inside_indexing_expr(cx, expr) { + // Avoid linting `N..N` as it has proven to be useful, see #5689 and #5628 ... + if ordering != Ordering::Equal { + span_lint( + cx, + REVERSED_EMPTY_RANGES, + expr.span, + "this range is reversed and using it to index a slice will panic at run-time", + ); + } + // ... except in for loop arguments for backwards compatibility with `reverse_range_loop` + } else if ordering != Ordering::Equal || is_for_loop_arg(cx, expr) { span_lint_and_then( cx, REVERSED_EMPTY_RANGES, diff --git a/tests/ui/reversed_empty_ranges_fixable.fixed b/tests/ui/reversed_empty_ranges_fixable.fixed index 332c0427ef6..79e482eec30 100644 --- a/tests/ui/reversed_empty_ranges_fixable.fixed +++ b/tests/ui/reversed_empty_ranges_fixable.fixed @@ -4,8 +4,6 @@ const ANSWER: i32 = 42; fn main() { - let arr = [1, 2, 3, 4, 5]; - // These should be linted: (21..=42).rev().for_each(|x| println!("{}", x)); @@ -14,16 +12,18 @@ fn main() { for _ in (-42..=-21).rev() {} for _ in (21u32..42u32).rev() {} - let _ = &[] as &[i32]; - // These should be ignored as they are not empty ranges: (21..=42).for_each(|x| println!("{}", x)); (21..42).for_each(|x| println!("{}", x)); + let arr = [1, 2, 3, 4, 5]; let _ = &arr[1..=3]; let _ = &arr[1..3]; for _ in 21..=42 {} for _ in 21..42 {} + + // This range is empty but should be ignored, see issue #5689 + let _ = &arr[0..0]; } diff --git a/tests/ui/reversed_empty_ranges_fixable.rs b/tests/ui/reversed_empty_ranges_fixable.rs index 901ec8bcc09..b2e8bf33771 100644 --- a/tests/ui/reversed_empty_ranges_fixable.rs +++ b/tests/ui/reversed_empty_ranges_fixable.rs @@ -4,8 +4,6 @@ const ANSWER: i32 = 42; fn main() { - let arr = [1, 2, 3, 4, 5]; - // These should be linted: (42..=21).for_each(|x| println!("{}", x)); @@ -14,16 +12,18 @@ fn main() { for _ in -21..=-42 {} for _ in 42u32..21u32 {} - let _ = &arr[3..3]; - // These should be ignored as they are not empty ranges: (21..=42).for_each(|x| println!("{}", x)); (21..42).for_each(|x| println!("{}", x)); + let arr = [1, 2, 3, 4, 5]; let _ = &arr[1..=3]; let _ = &arr[1..3]; for _ in 21..=42 {} for _ in 21..42 {} + + // This range is empty but should be ignored, see issue #5689 + let _ = &arr[0..0]; } diff --git a/tests/ui/reversed_empty_ranges_fixable.stderr b/tests/ui/reversed_empty_ranges_fixable.stderr index 9a646fd9939..de83c4f3d63 100644 --- a/tests/ui/reversed_empty_ranges_fixable.stderr +++ b/tests/ui/reversed_empty_ranges_fixable.stderr @@ -1,5 +1,5 @@ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_fixable.rs:11:5 + --> $DIR/reversed_empty_ranges_fixable.rs:9:5 | LL | (42..=21).for_each(|x| println!("{}", x)); | ^^^^^^^^^ @@ -11,7 +11,7 @@ LL | (21..=42).rev().for_each(|x| println!("{}", x)); | ^^^^^^^^^^^^^^^ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_fixable.rs:12:13 + --> $DIR/reversed_empty_ranges_fixable.rs:10:13 | LL | let _ = (ANSWER..21).filter(|x| x % 2 == 0).take(10).collect::>(); | ^^^^^^^^^^^^ @@ -22,7 +22,7 @@ LL | let _ = (21..ANSWER).rev().filter(|x| x % 2 == 0).take(10).collect:: $DIR/reversed_empty_ranges_fixable.rs:14:14 + --> $DIR/reversed_empty_ranges_fixable.rs:12:14 | LL | for _ in -21..=-42 {} | ^^^^^^^^^ @@ -33,7 +33,7 @@ LL | for _ in (-42..=-21).rev() {} | ^^^^^^^^^^^^^^^^^ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_fixable.rs:15:14 + --> $DIR/reversed_empty_ranges_fixable.rs:13:14 | LL | for _ in 42u32..21u32 {} | ^^^^^^^^^^^^ @@ -43,11 +43,5 @@ help: consider using the following if you are attempting to iterate over this ra LL | for _ in (21u32..42u32).rev() {} | ^^^^^^^^^^^^^^^^^^^^ -error: this range is empty and using it to index a slice will always yield an empty slice - --> $DIR/reversed_empty_ranges_fixable.rs:17:18 - | -LL | let _ = &arr[3..3]; - | ----^^^^- help: if you want an empty slice, use: `[] as &[i32]` - -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/reversed_empty_ranges_unfixable.rs b/tests/ui/reversed_empty_ranges_unfixable.rs index 561a35625f0..264d3d1e95a 100644 --- a/tests/ui/reversed_empty_ranges_unfixable.rs +++ b/tests/ui/reversed_empty_ranges_unfixable.rs @@ -4,11 +4,12 @@ const ANSWER: i32 = 42; const SOME_NUM: usize = 3; fn main() { - let _ = (42 + 10..42 + 10).map(|x| x / 2).find(|&x| x == 21); - let arr = [1, 2, 3, 4, 5]; let _ = &arr[3usize..=1usize]; let _ = &arr[SOME_NUM..1]; for _ in ANSWER..ANSWER {} + + // Should not be linted, see issue #5689 + let _ = (42 + 10..42 + 10).map(|x| x / 2).find(|&x| x == 21); } diff --git a/tests/ui/reversed_empty_ranges_unfixable.stderr b/tests/ui/reversed_empty_ranges_unfixable.stderr index 240188cbb46..f23d4eb0f9c 100644 --- a/tests/ui/reversed_empty_ranges_unfixable.stderr +++ b/tests/ui/reversed_empty_ranges_unfixable.stderr @@ -1,28 +1,22 @@ -error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_unfixable.rs:7:13 - | -LL | let _ = (42 + 10..42 + 10).map(|x| x / 2).find(|&x| x == 21); - | ^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::reversed-empty-ranges` implied by `-D warnings` - error: this range is reversed and using it to index a slice will panic at run-time - --> $DIR/reversed_empty_ranges_unfixable.rs:10:18 + --> $DIR/reversed_empty_ranges_unfixable.rs:8:18 | LL | let _ = &arr[3usize..=1usize]; | ^^^^^^^^^^^^^^^ + | + = note: `-D clippy::reversed-empty-ranges` implied by `-D warnings` error: this range is reversed and using it to index a slice will panic at run-time - --> $DIR/reversed_empty_ranges_unfixable.rs:11:18 + --> $DIR/reversed_empty_ranges_unfixable.rs:9:18 | LL | let _ = &arr[SOME_NUM..1]; | ^^^^^^^^^^^ error: this range is empty so it will yield no values - --> $DIR/reversed_empty_ranges_unfixable.rs:13:14 + --> $DIR/reversed_empty_ranges_unfixable.rs:11:14 | LL | for _ in ANSWER..ANSWER {} | ^^^^^^^^^^^^^^ -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors -- cgit 1.4.1-3-g733a5 From 7b6dc7b33dc437a59330ef3f5426102ca60fbf51 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Thu, 19 Mar 2020 14:14:52 +0100 Subject: add `unnested_or_patterns` lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 7 + clippy_lints/src/unnested_or_patterns.rs | 407 +++++++++++++++++++++++ clippy_lints/src/utils/ast_utils.rs | 525 ++++++++++++++++++++++++++++++ clippy_lints/src/utils/hir_utils.rs | 10 +- clippy_lints/src/utils/mod.rs | 3 +- src/lintlist/mod.rs | 7 + tests/ui/neg_cmp_op_on_partial_ord.rs | 1 + tests/ui/neg_cmp_op_on_partial_ord.stderr | 8 +- tests/ui/unnested_or_patterns.fixed | 41 +++ tests/ui/unnested_or_patterns.rs | 41 +++ tests/ui/unnested_or_patterns.stderr | 267 +++++++++++++++ tests/ui/wildcard_enum_match_arm.fixed | 3 +- tests/ui/wildcard_enum_match_arm.rs | 3 +- tests/ui/wildcard_enum_match_arm.stderr | 10 +- 15 files changed, 1314 insertions(+), 20 deletions(-) create mode 100644 clippy_lints/src/unnested_or_patterns.rs create mode 100755 clippy_lints/src/utils/ast_utils.rs create mode 100644 tests/ui/unnested_or_patterns.fixed create mode 100644 tests/ui/unnested_or_patterns.rs create mode 100644 tests/ui/unnested_or_patterns.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index a4a184480fb..adc945a6944 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1683,6 +1683,7 @@ Released 2018-09-13 [`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap [`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern [`unneeded_wildcard_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_wildcard_pattern +[`unnested_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns [`unreachable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreachable [`unreadable_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal [`unsafe_derive_deserialize`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_derive_deserialize diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 6f8923b2660..9809f953d67 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1,5 +1,6 @@ // error-pattern:cargo-clippy +#![feature(bindings_after_at)] #![feature(box_syntax)] #![feature(box_patterns)] #![feature(or_patterns)] @@ -12,6 +13,7 @@ #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![feature(crate_visibility_modifier)] #![feature(concat_idents)] +#![feature(drain_filter)] // FIXME: switch to something more ergonomic here, once available. // (Currently there is no way to opt into sysroot crates without `extern crate`.) @@ -319,6 +321,7 @@ mod types; mod unicode; mod unnamed_address; mod unnecessary_sort_by; +mod unnested_or_patterns; mod unsafe_removed_from_name; mod unused_io_amount; mod unused_self; @@ -836,6 +839,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &unnamed_address::FN_ADDRESS_COMPARISONS, &unnamed_address::VTABLE_ADDRESS_COMPARISONS, &unnecessary_sort_by::UNNECESSARY_SORT_BY, + &unnested_or_patterns::UNNESTED_OR_PATTERNS, &unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, &unused_io_amount::UNUSED_IO_AMOUNT, &unused_self::UNUSED_SELF, @@ -1073,6 +1077,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(move || box non_expressive_names::NonExpressiveNames { single_char_binding_names_threshold, }); + store.register_early_pass(|| box unnested_or_patterns::UnnestedOrPatterns); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1433,6 +1438,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), + LintId::of(&unnested_or_patterns::UNNESTED_OR_PATTERNS), LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), LintId::of(&unwrap::PANICKING_UNWRAP), @@ -1616,6 +1622,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::UNNECESSARY_CAST), LintId::of(&types::VEC_BOX), LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), + LintId::of(&unnested_or_patterns::UNNESTED_OR_PATTERNS), 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/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs new file mode 100644 index 00000000000..2723af03c0b --- /dev/null +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -0,0 +1,407 @@ +#![allow(clippy::wildcard_imports, clippy::enum_glob_use)] + +use crate::utils::ast_utils::{eq_field_pat, eq_id, eq_pat, eq_path}; +use crate::utils::{over, span_lint_and_then}; +use rustc_ast::ast::{self, Pat, PatKind, PatKind::*, DUMMY_NODE_ID}; +use rustc_ast::mut_visit::*; +use rustc_ast::ptr::P; +use rustc_ast_pretty::pprust; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::DUMMY_SP; + +use std::cell::Cell; +use std::mem; + +declare_clippy_lint! { + /// **What it does:** + /// + /// Checks for unnested or-patterns, e.g., `Some(0) | Some(2)` and + /// suggests replacing the pattern with a nested one, `Some(0 | 2)`. + /// + /// Another way to think of this is that it rewrites patterns in + /// *disjunctive normal form (DNF)* into *conjunctive normal form (CNF)*. + /// + /// **Why is this bad?** + /// + /// In the example above, `Some` is repeated, which unncessarily complicates the pattern. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// fn main() { + /// if let Some(0) | Some(2) = Some(0) {} + /// } + /// ``` + /// Use instead: + /// ```rust + /// #![feature(or_patterns)] + /// + /// fn main() { + /// if let Some(0 | 2) = Some(0) {} + /// } + /// ``` + pub UNNESTED_OR_PATTERNS, + complexity, + "unnested or-patterns, e.g., `Foo(Bar) | Foo(Baz) instead of `Foo(Bar | Baz)`" +} + +declare_lint_pass!(UnnestedOrPatterns => [UNNESTED_OR_PATTERNS]); + +impl EarlyLintPass for UnnestedOrPatterns { + fn check_arm(&mut self, cx: &EarlyContext<'_>, a: &ast::Arm) { + lint_unnested_or_patterns(cx, &a.pat); + } + + fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { + if let ast::ExprKind::Let(pat, _) = &e.kind { + lint_unnested_or_patterns(cx, pat); + } + } + + fn check_param(&mut self, cx: &EarlyContext<'_>, p: &ast::Param) { + lint_unnested_or_patterns(cx, &p.pat); + } + + fn check_local(&mut self, cx: &EarlyContext<'_>, l: &ast::Local) { + lint_unnested_or_patterns(cx, &l.pat); + } +} + +fn lint_unnested_or_patterns(cx: &EarlyContext<'_>, pat: &Pat) { + if !cx.sess.opts.unstable_features.is_nightly_build() { + // User cannot do `#![feature(or_patterns)]`, so bail. + return; + } + + if let Ident(.., None) | Lit(_) | Wild | Path(..) | Range(..) | Rest | MacCall(_) = pat.kind { + // This is a leaf pattern, so cloning is unprofitable. + return; + } + + let mut pat = P(pat.clone()); + + // Nix all the paren patterns everywhere so that they aren't in our way. + remove_all_parens(&mut pat); + + // Transform all unnested or-patterns into nested ones, and if there were none, quit. + if !unnest_or_patterns(&mut pat) { + return; + } + + span_lint_and_then(cx, UNNESTED_OR_PATTERNS, pat.span, "unnested or-patterns", |db| { + insert_necessary_parens(&mut pat); + db.span_suggestion_verbose( + pat.span, + "nest the patterns", + pprust::pat_to_string(&pat), + Applicability::MachineApplicable, + ); + }); +} + +/// Remove all `(p)` patterns in `pat`. +fn remove_all_parens(pat: &mut P) { + struct Visitor; + impl MutVisitor for Visitor { + fn visit_pat(&mut self, pat: &mut P) { + noop_visit_pat(pat, self); + let inner = match &mut pat.kind { + Paren(i) => mem::replace(&mut i.kind, Wild), + _ => return, + }; + pat.kind = inner; + } + } + Visitor.visit_pat(pat); +} + +/// Insert parens where necessary according to Rust's precedence rules for patterns. +fn insert_necessary_parens(pat: &mut P) { + struct Visitor; + impl MutVisitor for Visitor { + fn visit_pat(&mut self, pat: &mut P) { + use ast::{BindingMode::*, Mutability::*}; + noop_visit_pat(pat, self); + let target = match &mut pat.kind { + // `i @ a | b`, `box a | b`, and `& mut? a | b`. + Ident(.., Some(p)) | Box(p) | Ref(p, _) if matches!(&p.kind, Or(ps) if ps.len() > 1) => p, + Ref(p, Not) if matches!(p.kind, Ident(ByValue(Mut), ..)) => p, // `&(mut x)` + _ => return, + }; + target.kind = Paren(P(take_pat(target))); + } + } + Visitor.visit_pat(pat); +} + +/// Unnest or-patterns `p0 | ... | p1` in the pattern `pat`. +/// For example, this would transform `Some(0) | FOO | Some(2)` into `Some(0 | 2) | FOO`. +fn unnest_or_patterns(pat: &mut P) -> bool { + struct Visitor { + changed: bool, + } + impl MutVisitor for Visitor { + fn visit_pat(&mut self, p: &mut P) { + // This is a bottom up transformation, so recurse first. + noop_visit_pat(p, self); + + // Don't have an or-pattern? Just quit early on. + let alternatives = match &mut p.kind { + Or(ps) => ps, + _ => return, + }; + + // Collapse or-patterns directly nested in or-patterns. + let mut idx = 0; + let mut this_level_changed = false; + while idx < alternatives.len() { + let inner = if let Or(ps) = &mut alternatives[idx].kind { + mem::take(ps) + } else { + idx += 1; + continue; + }; + this_level_changed = true; + alternatives.splice(idx..=idx, inner); + } + + // Focus on `p_n` and then try to transform all `p_i` where `i > n`. + let mut focus_idx = 0; + while focus_idx < alternatives.len() { + this_level_changed |= transform_with_focus_on_idx(alternatives, focus_idx); + focus_idx += 1; + } + self.changed |= this_level_changed; + + // Deal with `Some(Some(0)) | Some(Some(1))`. + if this_level_changed { + noop_visit_pat(p, self); + } + } + } + + let mut visitor = Visitor { changed: false }; + visitor.visit_pat(pat); + visitor.changed +} + +/// Match `$scrutinee` against `$pat` and extract `$then` from it. +/// Panics if there is no match. +macro_rules! always_pat { + ($scrutinee:expr, $pat:pat => $then:expr) => { + match $scrutinee { + $pat => $then, + _ => unreachable!(), + } + }; +} + +/// Focus on `focus_idx` in `alternatives`, +/// attempting to extend it with elements of the same constructor `C` +/// in `alternatives[focus_idx + 1..]`. +fn transform_with_focus_on_idx(alternatives: &mut Vec>, focus_idx: usize) -> bool { + // Extract the kind; we'll need to make some changes in it. + let mut focus_kind = mem::replace(&mut alternatives[focus_idx].kind, PatKind::Wild); + // We'll focus on `alternatives[focus_idx]`, + // so we're draining from `alternatives[focus_idx + 1..]`. + let start = focus_idx + 1; + + // We're trying to find whatever kind (~"constructor") we found in `alternatives[start..]`. + let changed = match &mut focus_kind { + // These pattern forms are "leafs" and do not have sub-patterns. + // Therefore they are not some form of constructor `C`, + // with which a pattern `C(P0)` may be formed, + // which we would want to join with other `C(Pj)`s. + Ident(.., None) | Lit(_) | Wild | Path(..) | Range(..) | Rest | MacCall(_) + // Dealt with elsewhere. + | Or(_) | Paren(_) => false, + // Transform `box x | ... | box y` into `box (x | y)`. + // + // The cases below until `Slice(...)` deal *singleton* products. + // These patterns have the shape `C(p)`, and not e.g., `C(p0, ..., pn)`. + Box(target) => extend_with_matching( + target, start, alternatives, + |k| matches!(k, Box(_)), + |k| always_pat!(k, Box(p) => p), + ), + // Transform `&m x | ... | &m y` into `&m (x, y)`. + Ref(target, m1) => extend_with_matching( + target, start, alternatives, + |k| matches!(k, Ref(_, m2) if m1 == m2), // Mutabilities must match. + |k| always_pat!(k, Ref(p, _) => p), + ), + // Transform `b @ p0 | ... b @ p1` into `b @ (p0 | p1)`. + Ident(b1, i1, Some(target)) => extend_with_matching( + target, start, alternatives, + // Binding names must match. + |k| matches!(k, Ident(b2, i2, Some(_)) if b1 == b2 && eq_id(*i1, *i2)), + |k| always_pat!(k, Ident(_, _, Some(p)) => p), + ), + // Transform `[pre, x, post] | ... | [pre, y, post]` into `[pre, x | y, post]`. + Slice(ps1) => extend_with_matching_product( + ps1, start, alternatives, + |k, ps1, idx| matches!(k, Slice(ps2) if eq_pre_post(ps1, ps2, idx)), + |k| always_pat!(k, Slice(ps) => ps), + ), + // Transform `(pre, x, post) | ... | (pre, y, post)` into `(pre, x | y, post]`. + Tuple(ps1) => extend_with_matching_product( + ps1, start, alternatives, + |k, ps1, idx| matches!(k, Tuple(ps2) if eq_pre_post(ps1, ps2, idx)), + |k| always_pat!(k, Tuple(ps) => ps), + ), + // Transform `S(pre, x, post) | ... | S(pre, y, post)` into `S(pre, x | y, post]`. + TupleStruct(path1, ps1) => extend_with_matching_product( + ps1, start, alternatives, + |k, ps1, idx| matches!( + k, + TupleStruct(path2, ps2) if eq_path(path1, path2) && eq_pre_post(ps1, ps2, idx) + ), + |k| always_pat!(k, TupleStruct(_, ps) => ps), + ), + // Transform a record pattern `S { fp_0, ..., fp_n }`. + Struct(path1, fps1, rest1) => extend_with_struct_pat(path1, fps1, *rest1, start, alternatives), + }; + + alternatives[focus_idx].kind = focus_kind; + changed +} + +/// Here we focusing on a record pattern `S { fp_0, ..., fp_n }`. +/// In particular, for a record pattern, the order in which the field patterns is irrelevant. +/// So when we fixate on some `ident_k: pat_k`, we try to find `ident_k` in the other pattern +/// and check that all `fp_i` where `i ∈ ((0...n) \ k)` between two patterns are equal. +fn extend_with_struct_pat( + path1: &ast::Path, + fps1: &mut Vec, + rest1: bool, + start: usize, + alternatives: &mut Vec>, +) -> bool { + (0..fps1.len()).any(|idx| { + let pos_in_2 = Cell::new(None); // The element `k`. + let tail_or = drain_matching( + start, + alternatives, + |k| { + matches!(k, Struct(path2, fps2, rest2) + if rest1 == *rest2 // If one struct pattern has `..` so must the other. + && eq_path(path1, path2) + && fps1.len() == fps2.len() + && fps1.iter().enumerate().all(|(idx_1, fp1)| { + if idx_1 == idx { + // In the case of `k`, we merely require identical field names + // so that we will transform into `ident_k: p1_k | p2_k`. + let pos = fps2.iter().position(|fp2| eq_id(fp1.ident, fp2.ident)); + pos_in_2.set(pos); + pos.is_some() + } else { + fps2.iter().any(|fp2| eq_field_pat(fp1, fp2)) + } + })) + }, + // Extract `p2_k`. + |k| always_pat!(k, Struct(_, mut fps, _) => fps.swap_remove(pos_in_2.take().unwrap()).pat), + ); + extend_with_tail_or(&mut fps1[idx].pat, tail_or) + }) +} + +/// Like `extend_with_matching` but for products with > 1 factor, e.g., `C(p_0, ..., p_n)`. +/// Here, the idea is that we fixate on some `p_k` in `C`, +/// allowing it to vary between two `targets` and `ps2` (returned by `extract`), +/// while also requiring `ps1[..n] ~ ps2[..n]` (pre) and `ps1[n + 1..] ~ ps2[n + 1..]` (post), +/// where `~` denotes semantic equality. +fn extend_with_matching_product( + targets: &mut Vec>, + start: usize, + alternatives: &mut Vec>, + predicate: impl Fn(&PatKind, &[P], usize) -> bool, + extract: impl Fn(PatKind) -> Vec>, +) -> bool { + (0..targets.len()).any(|idx| { + let tail_or = drain_matching( + start, + alternatives, + |k| predicate(k, targets, idx), + |k| extract(k).swap_remove(idx), + ); + extend_with_tail_or(&mut targets[idx], tail_or) + }) +} + +/// Extract the pattern from the given one and replace it with `Wild`. +/// This is meant for temporarily swapping out the pattern for manipulation. +fn take_pat(from: &mut Pat) -> Pat { + let dummy = Pat { + id: DUMMY_NODE_ID, + kind: Wild, + span: DUMMY_SP, + }; + mem::replace(from, dummy) +} + +/// Extend `target` as an or-pattern with the alternatives +/// in `tail_or` if there are any and return if there were. +fn extend_with_tail_or(target: &mut Pat, tail_or: Vec>) -> bool { + fn extend(target: &mut Pat, mut tail_or: Vec>) { + match target { + // On an existing or-pattern in the target, append to it. + Pat { kind: Or(ps), .. } => ps.append(&mut tail_or), + // Otherwise convert the target to an or-pattern. + target => { + let mut init_or = vec![P(take_pat(target))]; + init_or.append(&mut tail_or); + target.kind = Or(init_or); + }, + } + } + + let changed = !tail_or.is_empty(); + if changed { + // Extend the target. + extend(target, tail_or); + } + changed +} + +// Extract all inner patterns in `alternatives` matching our `predicate`. +// Only elements beginning with `start` are considered for extraction. +fn drain_matching( + start: usize, + alternatives: &mut Vec>, + predicate: impl Fn(&PatKind) -> bool, + extract: impl Fn(PatKind) -> P, +) -> Vec> { + let mut tail_or = vec![]; + let mut idx = 0; + for pat in alternatives.drain_filter(|p| { + // Check if we should extract, but only if `idx >= start`. + idx += 1; + idx > start && predicate(&p.kind) + }) { + tail_or.push(extract(pat.into_inner().kind)); + } + tail_or +} + +fn extend_with_matching( + target: &mut Pat, + start: usize, + alternatives: &mut Vec>, + predicate: impl Fn(&PatKind) -> bool, + extract: impl Fn(PatKind) -> P, +) -> bool { + extend_with_tail_or(target, drain_matching(start, alternatives, predicate, extract)) +} + +/// Are the patterns in `ps1` and `ps2` equal save for `ps1[idx]` compared to `ps2[idx]`? +fn eq_pre_post(ps1: &[P], ps2: &[P], idx: usize) -> bool { + ps1[idx].is_rest() == ps2[idx].is_rest() // Avoid `[x, ..] | [x, 0]` => `[x, .. | 0]`. + && ps1.len() == ps2.len() + && over(&ps1[..idx], &ps2[..idx], |l, r| eq_pat(l, r)) + && over(&ps1[idx + 1..], &ps2[idx + 1..], |l, r| eq_pat(l, r)) +} diff --git a/clippy_lints/src/utils/ast_utils.rs b/clippy_lints/src/utils/ast_utils.rs new file mode 100755 index 00000000000..69a7b6c051e --- /dev/null +++ b/clippy_lints/src/utils/ast_utils.rs @@ -0,0 +1,525 @@ +//! Utilities for manipulating and extracting information from `rustc_ast::ast`. +//! +//! - The `eq_foobar` functions test for semantic equality but ignores `NodeId`s and `Span`s. + +#![allow(clippy::similar_names, clippy::wildcard_imports, clippy::enum_glob_use)] + +use crate::utils::{both, over}; +use rustc_ast::ast::{self, *}; +use rustc_ast::ptr::P; +use std::mem; + +/// Checks if each element in the first slice is contained within the latter as per `eq_fn`. +pub fn unordered_over(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool { + left.len() == right.len() && left.iter().all(|l| right.iter().any(|r| eq_fn(l, r))) +} + +pub fn eq_id(l: Ident, r: Ident) -> bool { + l.name == r.name +} + +pub fn eq_pat(l: &Pat, r: &Pat) -> bool { + use PatKind::*; + match (&l.kind, &r.kind) { + (Paren(l), _) => eq_pat(l, r), + (_, Paren(r)) => eq_pat(l, r), + (Wild, Wild) | (Rest, Rest) => true, + (Lit(l), Lit(r)) => eq_expr(l, r), + (Ident(b1, i1, s1), Ident(b2, i2, s2)) => b1 == b2 && eq_id(*i1, *i2) && both(s1, s2, |l, r| eq_pat(l, r)), + (Range(lf, lt, le), Range(rf, rt, re)) => { + eq_expr_opt(lf, rf) && eq_expr_opt(lt, rt) && eq_range_end(&le.node, &re.node) + }, + (Box(l), Box(r)) + | (Ref(l, Mutability::Not), Ref(r, Mutability::Not)) + | (Ref(l, Mutability::Mut), Ref(r, Mutability::Mut)) => eq_pat(l, r), + (Tuple(l), Tuple(r)) | (Slice(l), Slice(r)) => over(l, r, |l, r| eq_pat(l, r)), + (Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp), + (TupleStruct(lp, lfs), TupleStruct(rp, rfs)) => eq_path(lp, rp) && over(lfs, rfs, |l, r| eq_pat(l, r)), + (Struct(lp, lfs, lr), Struct(rp, rfs, rr)) => { + lr == rr && eq_path(lp, rp) && unordered_over(lfs, rfs, |lf, rf| eq_field_pat(lf, rf)) + }, + (Or(ls), Or(rs)) => unordered_over(ls, rs, |l, r| eq_pat(l, r)), + (MacCall(l), MacCall(r)) => eq_mac_call(l, r), + _ => false, + } +} + +pub fn eq_range_end(l: &RangeEnd, r: &RangeEnd) -> bool { + match (l, r) { + (RangeEnd::Excluded, RangeEnd::Excluded) => true, + (RangeEnd::Included(l), RangeEnd::Included(r)) => { + matches!(l, RangeSyntax::DotDotEq) == matches!(r, RangeSyntax::DotDotEq) + }, + _ => false, + } +} + +pub fn eq_field_pat(l: &FieldPat, r: &FieldPat) -> bool { + l.is_placeholder == r.is_placeholder + && eq_id(l.ident, r.ident) + && eq_pat(&l.pat, &r.pat) + && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) +} + +pub fn eq_qself(l: &QSelf, r: &QSelf) -> bool { + l.position == r.position && eq_ty(&l.ty, &r.ty) +} + +pub fn eq_path(l: &Path, r: &Path) -> bool { + over(&l.segments, &r.segments, |l, r| eq_path_seg(l, r)) +} + +pub fn eq_path_seg(l: &PathSegment, r: &PathSegment) -> bool { + eq_id(l.ident, r.ident) && both(&l.args, &r.args, |l, r| eq_generic_args(l, r)) +} + +pub fn eq_generic_args(l: &GenericArgs, r: &GenericArgs) -> bool { + match (l, r) { + (GenericArgs::AngleBracketed(l), GenericArgs::AngleBracketed(r)) => { + over(&l.args, &r.args, |l, r| eq_angle_arg(l, r)) + }, + (GenericArgs::Parenthesized(l), GenericArgs::Parenthesized(r)) => { + over(&l.inputs, &r.inputs, |l, r| eq_ty(l, r)) && eq_fn_ret_ty(&l.output, &r.output) + }, + _ => false, + } +} + +pub fn eq_angle_arg(l: &AngleBracketedArg, r: &AngleBracketedArg) -> bool { + match (l, r) { + (AngleBracketedArg::Arg(l), AngleBracketedArg::Arg(r)) => eq_generic_arg(l, r), + (AngleBracketedArg::Constraint(l), AngleBracketedArg::Constraint(r)) => eq_assoc_constraint(l, r), + _ => false, + } +} + +pub fn eq_generic_arg(l: &GenericArg, r: &GenericArg) -> bool { + match (l, r) { + (GenericArg::Lifetime(l), GenericArg::Lifetime(r)) => eq_id(l.ident, r.ident), + (GenericArg::Type(l), GenericArg::Type(r)) => eq_ty(l, r), + (GenericArg::Const(l), GenericArg::Const(r)) => eq_expr(&l.value, &r.value), + _ => false, + } +} + +pub fn eq_expr_opt(l: &Option>, r: &Option>) -> bool { + both(l, r, |l, r| eq_expr(l, r)) +} + +pub fn eq_expr(l: &Expr, r: &Expr) -> bool { + use ExprKind::*; + if !over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) { + return false; + } + match (&l.kind, &r.kind) { + (Paren(l), _) => eq_expr(l, r), + (_, Paren(r)) => eq_expr(l, r), + (Err, Err) => true, + (Box(l), Box(r)) | (Try(l), Try(r)) | (Await(l), Await(r)) => eq_expr(l, r), + (Array(l), Array(r)) | (Tup(l), Tup(r)) => over(l, r, |l, r| eq_expr(l, r)), + (Repeat(le, ls), Repeat(re, rs)) => eq_expr(le, re) && eq_expr(&ls.value, &rs.value), + (Call(lc, la), Call(rc, ra)) => eq_expr(lc, rc) && over(la, ra, |l, r| eq_expr(l, r)), + (MethodCall(lc, la), MethodCall(rc, ra)) => eq_path_seg(lc, rc) && over(la, ra, |l, r| eq_expr(l, r)), + (Binary(lo, ll, lr), Binary(ro, rl, rr)) => lo.node == ro.node && eq_expr(ll, rl) && eq_expr(lr, rr), + (Unary(lo, l), Unary(ro, r)) => mem::discriminant(lo) == mem::discriminant(ro) && eq_expr(l, r), + (Lit(l), Lit(r)) => l.kind == r.kind, + (Cast(l, lt), Cast(r, rt)) | (Type(l, lt), Type(r, rt)) => eq_expr(l, r) && eq_ty(lt, rt), + (Let(lp, le), Let(rp, re)) => eq_pat(lp, rp) && eq_expr(le, re), + (If(lc, lt, le), If(rc, rt, re)) => eq_expr(lc, rc) && eq_block(lt, rt) && eq_expr_opt(le, re), + (While(lc, lt, ll), While(rc, rt, rl)) => eq_label(ll, rl) && eq_expr(lc, rc) && eq_block(lt, rt), + (ForLoop(lp, li, lt, ll), ForLoop(rp, ri, rt, rl)) => { + eq_label(ll, rl) && eq_pat(lp, rp) && eq_expr(li, ri) && eq_block(lt, rt) + }, + (Loop(lt, ll), Loop(rt, rl)) => eq_label(ll, rl) && eq_block(lt, rt), + (Block(lb, ll), Block(rb, rl)) => eq_label(ll, rl) && eq_block(lb, rb), + (TryBlock(l), TryBlock(r)) => eq_block(l, r), + (Yield(l), Yield(r)) | (Ret(l), Ret(r)) => eq_expr_opt(l, r), + (Break(ll, le), Break(rl, re)) => eq_label(ll, rl) && eq_expr_opt(le, re), + (Continue(ll), Continue(rl)) => eq_label(ll, rl), + (Assign(l1, l2, _), Assign(r1, r2, _)) | (Index(l1, l2), Index(r1, r2)) => eq_expr(l1, r1) && eq_expr(l2, r2), + (AssignOp(lo, lp, lv), AssignOp(ro, rp, rv)) => lo.node == ro.node && eq_expr(lp, rp) && eq_expr(lv, rv), + (Field(lp, lf), Field(rp, rf)) => eq_id(*lf, *rf) && eq_expr(lp, rp), + (Match(ls, la), Match(rs, ra)) => eq_expr(ls, rs) && over(la, ra, |l, r| eq_arm(l, r)), + (Closure(lc, la, lm, lf, lb, _), Closure(rc, ra, rm, rf, rb, _)) => { + lc == rc && la.is_async() == ra.is_async() && lm == rm && eq_fn_decl(lf, rf) && eq_expr(lb, rb) + }, + (Async(lc, _, lb), Async(rc, _, rb)) => lc == rc && eq_block(lb, rb), + (Range(lf, lt, ll), Range(rf, rt, rl)) => ll == rl && eq_expr_opt(lf, rf) && eq_expr_opt(lt, rt), + (AddrOf(lbk, lm, le), AddrOf(rbk, rm, re)) => lbk == rbk && lm == rm && eq_expr(le, re), + (Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp), + (MacCall(l), MacCall(r)) => eq_mac_call(l, r), + (Struct(lp, lfs, lb), Struct(rp, rfs, rb)) => { + eq_path(lp, rp) && eq_expr_opt(lb, rb) && unordered_over(lfs, rfs, |l, r| eq_field(l, r)) + }, + _ => false, + } +} + +pub fn eq_field(l: &Field, r: &Field) -> bool { + l.is_placeholder == r.is_placeholder + && eq_id(l.ident, r.ident) + && eq_expr(&l.expr, &r.expr) + && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) +} + +pub fn eq_arm(l: &Arm, r: &Arm) -> bool { + l.is_placeholder == r.is_placeholder + && eq_pat(&l.pat, &r.pat) + && eq_expr(&l.body, &r.body) + && eq_expr_opt(&l.guard, &r.guard) + && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) +} + +pub fn eq_label(l: &Option(A); +pub struct Bar { + a: Foo, + b: Foo, +} + +impl Unpin for Bar +where + Foo: Unpin, + Foo: Unpin, +{ +} + fn main() {} diff --git a/tests/ui/type_repetition_in_bounds.stderr b/tests/ui/type_repetition_in_bounds.stderr index 6a1073a23f6..148c19c7d07 100644 --- a/tests/ui/type_repetition_in_bounds.stderr +++ b/tests/ui/type_repetition_in_bounds.stderr @@ -12,7 +12,7 @@ LL | #![deny(clippy::type_repetition_in_bounds)] = help: consider combining the bounds: `T: Copy + Clone` error: this type has already been used as a bound predicate - --> $DIR/type_repetition_in_bounds.rs:24:5 + --> $DIR/type_repetition_in_bounds.rs:25:5 | LL | Self: Copy + Default + Ord, | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From 2d5930a3da7048d784489f28b44a769880b6ceff Mon Sep 17 00:00:00 2001 From: ThibsG Date: Fri, 3 Jul 2020 11:48:28 +0200 Subject: Don't lint for predicates generated in macros --- clippy_lints/src/trait_bounds.rs | 15 +++++++++----- tests/ui/type_repetition_in_bounds.rs | 37 ++++++++++++++++++++++++++--------- 2 files changed, 38 insertions(+), 14 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 650edbb4b11..0ef70311fb1 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -1,4 +1,5 @@ use crate::utils::{in_macro, snippet, snippet_with_applicability, span_lint_and_help, SpanlessHash}; +use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::{GenericBound, Generics, WherePredicate}; @@ -11,6 +12,8 @@ declare_clippy_lint! { /// **Why is this bad?** Repeating the type for every bound makes the code /// less readable than combining the bounds /// + /// **Known problems:** None. + /// /// **Example:** /// ```rust /// pub fn foo(t: T) where T: Copy, T: Clone {} @@ -53,12 +56,14 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { let mut map = FxHashMap::default(); let mut applicability = Applicability::MaybeIncorrect; for bound in gen.where_clause.predicates { - if let WherePredicate::BoundPredicate(ref p) = bound { - if p.bounds.len() as u64 > self.max_trait_bounds { - return; - } + if_chain! { + if let WherePredicate::BoundPredicate(ref p) = bound; + if p.bounds.len() as u64 <= self.max_trait_bounds; + if !in_macro(p.span); let h = hash(&p.bounded_ty); - if let Some(ref v) = map.insert(h, p.bounds.iter().collect::>()) { + if let Some(ref v) = map.insert(h, p.bounds.iter().collect::>()); + + then { let mut hint_string = format!( "consider combining the bounds: `{}:", snippet(cx, p.bounded_ty.span, "_") diff --git a/tests/ui/type_repetition_in_bounds.rs b/tests/ui/type_repetition_in_bounds.rs index 9f1700567d1..766190f2099 100644 --- a/tests/ui/type_repetition_in_bounds.rs +++ b/tests/ui/type_repetition_in_bounds.rs @@ -37,17 +37,36 @@ where } // Generic distinction (see #4323) -pub struct Foo(A); -pub struct Bar { - a: Foo, - b: Foo, +mod issue4323 { + pub struct Foo(A); + pub struct Bar { + a: Foo, + b: Foo, + } + + impl Unpin for Bar + where + Foo: Unpin, + Foo: Unpin, + { + } } -impl Unpin for Bar -where - Foo: Unpin, - Foo: Unpin, -{ +// Extern macros shouldn't lint (see #4326) +extern crate serde; +mod issue4326 { + use serde::{Deserialize, Serialize}; + + trait Foo {} + impl Foo for String {} + + #[derive(Debug, Serialize, Deserialize)] + struct Bar + where + S: Foo, + { + foo: S, + } } fn main() {} -- cgit 1.4.1-3-g733a5 From c3c402783f1d12852982f9dc734cc4c07b9fce33 Mon Sep 17 00:00:00 2001 From: Robert Sedlacek Date: Mon, 4 Nov 2019 19:39:03 +0100 Subject: Added restriction lint: pattern-type-mismatch --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 4 + clippy_lints/src/pattern_type_mismatch.rs | 276 +++++++++++++++++++++ src/lintlist/mod.rs | 7 + tests/ui/pattern_type_mismatch/mutability.rs | 40 +++ tests/ui/pattern_type_mismatch/mutability.stderr | 19 ++ .../pattern_type_mismatch/pattern_alternatives.rs | 24 ++ .../pattern_alternatives.stderr | 27 ++ tests/ui/pattern_type_mismatch/pattern_structs.rs | 45 ++++ .../pattern_type_mismatch/pattern_structs.stderr | 67 +++++ tests/ui/pattern_type_mismatch/pattern_tuples.rs | 57 +++++ .../ui/pattern_type_mismatch/pattern_tuples.stderr | 83 +++++++ tests/ui/pattern_type_mismatch/syntax.rs | 146 +++++++++++ tests/ui/pattern_type_mismatch/syntax.stderr | 78 ++++++ 14 files changed, 874 insertions(+) create mode 100644 clippy_lints/src/pattern_type_mismatch.rs create mode 100644 tests/ui/pattern_type_mismatch/mutability.rs create mode 100644 tests/ui/pattern_type_mismatch/mutability.stderr create mode 100644 tests/ui/pattern_type_mismatch/pattern_alternatives.rs create mode 100644 tests/ui/pattern_type_mismatch/pattern_alternatives.stderr create mode 100644 tests/ui/pattern_type_mismatch/pattern_structs.rs create mode 100644 tests/ui/pattern_type_mismatch/pattern_structs.stderr create mode 100644 tests/ui/pattern_type_mismatch/pattern_tuples.rs create mode 100644 tests/ui/pattern_type_mismatch/pattern_tuples.stderr create mode 100644 tests/ui/pattern_type_mismatch/syntax.rs create mode 100644 tests/ui/pattern_type_mismatch/syntax.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index b88044d6ce8..ed8f16e65bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1588,6 +1588,7 @@ Released 2018-09-13 [`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap [`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl [`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite +[`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch [`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma [`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence [`print_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_literal diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d31a597b66a..4890349999f 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -268,6 +268,7 @@ mod overflow_check_conditional; mod panic_unimplemented; mod partialeq_ne_impl; mod path_buf_push_overwrite; +pub mod pattern_type_mismatch; mod precedence; mod ptr; mod ptr_offset_with_cast; @@ -741,6 +742,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &panic_unimplemented::UNREACHABLE, &partialeq_ne_impl::PARTIALEQ_NE_IMPL, &path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE, + &pattern_type_mismatch::PATTERN_TYPE_MISMATCH, &precedence::PRECEDENCE, &ptr::CMP_NULL, &ptr::MUT_FROM_REF, @@ -1064,6 +1066,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box unnested_or_patterns::UnnestedOrPatterns); store.register_late_pass(|| box macro_use::MacroUseImports::default()); store.register_late_pass(|| box map_identity::MapIdentity); + store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1097,6 +1100,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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(&shadow::SHADOW_REUSE), LintId::of(&shadow::SHADOW_SAME), LintId::of(&strings::STRING_ADD), diff --git a/clippy_lints/src/pattern_type_mismatch.rs b/clippy_lints/src/pattern_type_mismatch.rs new file mode 100644 index 00000000000..a07279c92b0 --- /dev/null +++ b/clippy_lints/src/pattern_type_mismatch.rs @@ -0,0 +1,276 @@ +use crate::utils::{last_path_segment, span_help_and_lint}; +use rustc::lint::in_external_macro; +use rustc::ty::subst::SubstsRef; +use rustc::ty::{AdtDef, FieldDef, Ty, TyKind, VariantDef}; +use rustc_hir::{ + intravisit, Body, Expr, ExprKind, FieldPat, FnDecl, HirId, LocalSource, MatchSource, Mutability, Pat, PatKind, + QPath, Stmt, StmtKind, +}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for patterns that aren't exact representations of the types + /// they are applied to. + /// + /// **Why is this bad?** It isn't bad in general. But in some contexts it can be desirable + /// because it increases ownership hints in the code, and will guard against some changes + /// in ownership. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust,ignore + /// // Bad + /// let value = &Some(Box::new(23)); + /// match value { + /// Some(inner) => println!("{}", inner), + /// None => println!("none"), + /// } + /// + /// // Good + /// let value = &Some(Box::new(23)); + /// match *value { + /// Some(ref inner) => println!("{}", inner), + /// None => println!("none"), + /// } + /// ``` + pub PATTERN_TYPE_MISMATCH, + restriction, + "type of pattern does not match the expression type" +} + +declare_lint_pass!(PatternTypeMismatch => [PATTERN_TYPE_MISMATCH]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PatternTypeMismatch { + fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx Stmt<'_>) { + if let StmtKind::Local(ref local) = stmt.kind { + if let Some(init) = &local.init { + if let Some(init_ty) = cx.tables.node_type_opt(init.hir_id) { + let pat = &local.pat; + if in_external_macro(cx.sess(), pat.span) { + return; + } + let deref_possible = match local.source { + LocalSource::Normal => DerefPossible::Possible, + _ => DerefPossible::Impossible, + }; + apply_lint(cx, pat, init_ty, deref_possible); + } + } + } + } + + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::Match(ref expr, arms, source) = expr.kind { + match source { + MatchSource::Normal | MatchSource::IfLetDesugar { .. } | MatchSource::WhileLetDesugar => { + if let Some(expr_ty) = cx.tables.node_type_opt(expr.hir_id) { + 'pattern_checks: for arm in arms { + let pat = &arm.pat; + if in_external_macro(cx.sess(), pat.span) { + continue 'pattern_checks; + } + if apply_lint(cx, pat, expr_ty, DerefPossible::Possible) { + break 'pattern_checks; + } + } + } + }, + _ => (), + } + } + } + + fn check_fn( + &mut self, + cx: &LateContext<'a, 'tcx>, + _: intravisit::FnKind<'tcx>, + _: &'tcx FnDecl<'_>, + body: &'tcx Body<'_>, + _: Span, + hir_id: HirId, + ) { + if let Some(fn_sig) = cx.tables.liberated_fn_sigs().get(hir_id) { + for (param, ty) in body.params.iter().zip(fn_sig.inputs().iter()) { + apply_lint(cx, ¶m.pat, ty, DerefPossible::Impossible); + } + } + } +} + +#[derive(Debug, Clone, Copy)] +enum DerefPossible { + Possible, + Impossible, +} + +fn apply_lint<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + pat: &Pat<'_>, + expr_ty: Ty<'tcx>, + deref_possible: DerefPossible, +) -> bool { + let maybe_mismatch = find_first_mismatch(cx, pat, expr_ty, Level::Top); + if let Some((span, mutability, level)) = maybe_mismatch { + span_help_and_lint( + cx, + PATTERN_TYPE_MISMATCH, + span, + "type of pattern does not match the expression type", + &format!( + "{}explicitly match against a `{}` pattern and adjust the enclosed variable bindings", + match (deref_possible, level) { + (DerefPossible::Possible, Level::Top) => "use `*` to dereference the match expression or ", + _ => "", + }, + match mutability { + Mutability::Mut => "&mut _", + Mutability::Not => "&_", + }, + ), + ); + true + } else { + false + } +} + +#[derive(Debug, Copy, Clone)] +enum Level { + Top, + Lower, +} + +#[allow(rustc::usage_of_ty_tykind)] +fn find_first_mismatch<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + pat: &Pat<'_>, + ty: Ty<'tcx>, + level: Level, +) -> Option<(Span, Mutability, Level)> { + if let PatKind::Ref(ref sub_pat, _) = pat.kind { + if let TyKind::Ref(_, sub_ty, _) = ty.kind { + return find_first_mismatch(cx, sub_pat, sub_ty, Level::Lower); + } + } + + if let TyKind::Ref(_, _, mutability) = ty.kind { + if is_non_ref_pattern(&pat.kind) { + return Some((pat.span, mutability, level)); + } + } + + if let PatKind::Struct(ref qpath, ref field_pats, _) = pat.kind { + if let TyKind::Adt(ref adt_def, ref substs_ref) = ty.kind { + if let Some(variant) = get_variant(adt_def, qpath) { + let field_defs = &variant.fields; + return find_first_mismatch_in_struct(cx, field_pats, field_defs, substs_ref); + } + } + } + + if let PatKind::TupleStruct(ref qpath, ref pats, _) = pat.kind { + if let TyKind::Adt(ref adt_def, ref substs_ref) = ty.kind { + if let Some(variant) = get_variant(adt_def, qpath) { + let field_defs = &variant.fields; + let ty_iter = field_defs.iter().map(|field_def| field_def.ty(cx.tcx, substs_ref)); + return find_first_mismatch_in_tuple(cx, pats, ty_iter); + } + } + } + + if let PatKind::Tuple(ref pats, _) = pat.kind { + if let TyKind::Tuple(..) = ty.kind { + return find_first_mismatch_in_tuple(cx, pats, ty.tuple_fields()); + } + } + + if let PatKind::Or(sub_pats) = pat.kind { + for pat in sub_pats { + let maybe_mismatch = find_first_mismatch(cx, pat, ty, level); + if let Some(mismatch) = maybe_mismatch { + return Some(mismatch); + } + } + } + + None +} + +fn get_variant<'a>(adt_def: &'a AdtDef, qpath: &QPath<'_>) -> Option<&'a VariantDef> { + if adt_def.is_struct() { + if let Some(variant) = adt_def.variants.iter().next() { + return Some(variant); + } + } + + if adt_def.is_enum() { + let pat_ident = last_path_segment(qpath).ident; + for variant in &adt_def.variants { + if variant.ident == pat_ident { + return Some(variant); + } + } + } + + None +} + +fn find_first_mismatch_in_tuple<'a, 'tcx, I>( + cx: &LateContext<'a, 'tcx>, + pats: &[&Pat<'_>], + ty_iter_src: I, +) -> Option<(Span, Mutability, Level)> +where + I: IntoIterator>, +{ + let mut field_tys = ty_iter_src.into_iter(); + 'fields: for pat in pats { + let field_ty = if let Some(ty) = field_tys.next() { + ty + } else { + break 'fields; + }; + + let maybe_mismatch = find_first_mismatch(cx, pat, field_ty, Level::Lower); + if let Some(mismatch) = maybe_mismatch { + return Some(mismatch); + } + } + + None +} + +fn find_first_mismatch_in_struct<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + field_pats: &[FieldPat<'_>], + field_defs: &[FieldDef], + substs_ref: SubstsRef<'tcx>, +) -> Option<(Span, Mutability, Level)> { + for field_pat in field_pats { + 'definitions: for field_def in field_defs { + if field_pat.ident == field_def.ident { + let field_ty = field_def.ty(cx.tcx, substs_ref); + let pat = &field_pat.pat; + let maybe_mismatch = find_first_mismatch(cx, pat, field_ty, Level::Lower); + if let Some(mismatch) = maybe_mismatch { + return Some(mismatch); + } + break 'definitions; + } + } + } + + None +} + +fn is_non_ref_pattern(pat_kind: &PatKind<'_>) -> bool { + match pat_kind { + PatKind::Struct(..) | PatKind::Tuple(..) | PatKind::TupleStruct(..) | PatKind::Path(..) => true, + PatKind::Or(sub_pats) => sub_pats.iter().any(|pat| is_non_ref_pattern(&pat.kind)), + _ => false, + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index a2998d74130..6402efc2521 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1697,6 +1697,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "path_buf_push_overwrite", }, + Lint { + name: "pattern_type_mismatch", + group: "restriction", + desc: "type of pattern does not match the expression type", + deprecation: None, + module: "pattern_type_mismatch", + }, Lint { name: "possible_missing_comma", group: "correctness", diff --git a/tests/ui/pattern_type_mismatch/mutability.rs b/tests/ui/pattern_type_mismatch/mutability.rs new file mode 100644 index 00000000000..9b4f2f1f579 --- /dev/null +++ b/tests/ui/pattern_type_mismatch/mutability.rs @@ -0,0 +1,40 @@ +#![allow(clippy::all)] +#![warn(clippy::pattern_type_mismatch)] + +fn main() {} + +fn should_lint() { + let value = &Some(23); + match value { + Some(_) => (), + _ => (), + } + + let value = &mut Some(23); + match value { + Some(_) => (), + _ => (), + } +} + +fn should_not_lint() { + let value = &Some(23); + match value { + &Some(_) => (), + _ => (), + } + match *value { + Some(_) => (), + _ => (), + } + + let value = &mut Some(23); + match value { + &mut Some(_) => (), + _ => (), + } + match *value { + Some(_) => (), + _ => (), + } +} diff --git a/tests/ui/pattern_type_mismatch/mutability.stderr b/tests/ui/pattern_type_mismatch/mutability.stderr new file mode 100644 index 00000000000..3421d568365 --- /dev/null +++ b/tests/ui/pattern_type_mismatch/mutability.stderr @@ -0,0 +1,19 @@ +error: type of pattern does not match the expression type + --> $DIR/mutability.rs:9:9 + | +LL | Some(_) => (), + | ^^^^^^^ + | + = note: `-D clippy::pattern-type-mismatch` implied by `-D warnings` + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/mutability.rs:15:9 + | +LL | Some(_) => (), + | ^^^^^^^ + | + = help: use `*` to dereference the match expression or explicitly match against a `&mut _` pattern and adjust the enclosed variable bindings + +error: aborting due to 2 previous errors + diff --git a/tests/ui/pattern_type_mismatch/pattern_alternatives.rs b/tests/ui/pattern_type_mismatch/pattern_alternatives.rs new file mode 100644 index 00000000000..065ea9fb9b5 --- /dev/null +++ b/tests/ui/pattern_type_mismatch/pattern_alternatives.rs @@ -0,0 +1,24 @@ +#![allow(clippy::all)] +#![warn(clippy::pattern_type_mismatch)] + +fn main() {} + +fn alternatives() { + enum Value<'a> { + Unused, + A(&'a Option), + B, + } + let ref_value = &Value::A(&Some(23)); + + // not ok + if let Value::B | Value::A(_) = ref_value {} + if let &Value::B | &Value::A(Some(_)) = ref_value {} + if let Value::B | Value::A(Some(_)) = *ref_value {} + + // ok + if let &Value::B | &Value::A(_) = ref_value {} + if let Value::B | Value::A(_) = *ref_value {} + if let &Value::B | &Value::A(&Some(_)) = ref_value {} + if let Value::B | Value::A(&Some(_)) = *ref_value {} +} diff --git a/tests/ui/pattern_type_mismatch/pattern_alternatives.stderr b/tests/ui/pattern_type_mismatch/pattern_alternatives.stderr new file mode 100644 index 00000000000..d285c93782c --- /dev/null +++ b/tests/ui/pattern_type_mismatch/pattern_alternatives.stderr @@ -0,0 +1,27 @@ +error: type of pattern does not match the expression type + --> $DIR/pattern_alternatives.rs:15:12 + | +LL | if let Value::B | Value::A(_) = ref_value {} + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::pattern-type-mismatch` implied by `-D warnings` + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_alternatives.rs:16:34 + | +LL | if let &Value::B | &Value::A(Some(_)) = ref_value {} + | ^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_alternatives.rs:17:32 + | +LL | if let Value::B | Value::A(Some(_)) = *ref_value {} + | ^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: aborting due to 3 previous errors + diff --git a/tests/ui/pattern_type_mismatch/pattern_structs.rs b/tests/ui/pattern_type_mismatch/pattern_structs.rs new file mode 100644 index 00000000000..417b1c107c5 --- /dev/null +++ b/tests/ui/pattern_type_mismatch/pattern_structs.rs @@ -0,0 +1,45 @@ +#![allow(clippy::all)] +#![warn(clippy::pattern_type_mismatch)] + +fn main() {} + +fn struct_types() { + struct Struct<'a> { + ref_inner: &'a Option, + } + let ref_value = &Struct { ref_inner: &Some(42) }; + + // not ok + let Struct { .. } = ref_value; + if let &Struct { ref_inner: Some(_) } = ref_value {} + if let Struct { ref_inner: Some(_) } = *ref_value {} + + // ok + let &Struct { .. } = ref_value; + let Struct { .. } = *ref_value; + if let &Struct { ref_inner: &Some(_) } = ref_value {} + if let Struct { ref_inner: &Some(_) } = *ref_value {} +} + +fn struct_enum_variants() { + enum StructEnum<'a> { + Empty, + Var { inner_ref: &'a Option }, + } + let ref_value = &StructEnum::Var { inner_ref: &Some(42) }; + + // not ok + if let StructEnum::Var { .. } = ref_value {} + if let StructEnum::Var { inner_ref: Some(_) } = ref_value {} + if let &StructEnum::Var { inner_ref: Some(_) } = ref_value {} + if let StructEnum::Var { inner_ref: Some(_) } = *ref_value {} + if let StructEnum::Empty = ref_value {} + + // ok + if let &StructEnum::Var { .. } = ref_value {} + if let StructEnum::Var { .. } = *ref_value {} + if let &StructEnum::Var { inner_ref: &Some(_) } = ref_value {} + if let StructEnum::Var { inner_ref: &Some(_) } = *ref_value {} + if let &StructEnum::Empty = ref_value {} + if let StructEnum::Empty = *ref_value {} +} diff --git a/tests/ui/pattern_type_mismatch/pattern_structs.stderr b/tests/ui/pattern_type_mismatch/pattern_structs.stderr new file mode 100644 index 00000000000..d428e85b0c9 --- /dev/null +++ b/tests/ui/pattern_type_mismatch/pattern_structs.stderr @@ -0,0 +1,67 @@ +error: type of pattern does not match the expression type + --> $DIR/pattern_structs.rs:13:9 + | +LL | let Struct { .. } = ref_value; + | ^^^^^^^^^^^^^ + | + = note: `-D clippy::pattern-type-mismatch` implied by `-D warnings` + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_structs.rs:14:33 + | +LL | if let &Struct { ref_inner: Some(_) } = ref_value {} + | ^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_structs.rs:15:32 + | +LL | if let Struct { ref_inner: Some(_) } = *ref_value {} + | ^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_structs.rs:32:12 + | +LL | if let StructEnum::Var { .. } = ref_value {} + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_structs.rs:33:12 + | +LL | if let StructEnum::Var { inner_ref: Some(_) } = ref_value {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_structs.rs:34:42 + | +LL | if let &StructEnum::Var { inner_ref: Some(_) } = ref_value {} + | ^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_structs.rs:35:41 + | +LL | if let StructEnum::Var { inner_ref: Some(_) } = *ref_value {} + | ^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_structs.rs:36:12 + | +LL | if let StructEnum::Empty = ref_value {} + | ^^^^^^^^^^^^^^^^^ + | + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: aborting due to 8 previous errors + diff --git a/tests/ui/pattern_type_mismatch/pattern_tuples.rs b/tests/ui/pattern_type_mismatch/pattern_tuples.rs new file mode 100644 index 00000000000..19504a051d8 --- /dev/null +++ b/tests/ui/pattern_type_mismatch/pattern_tuples.rs @@ -0,0 +1,57 @@ +#![allow(clippy::all)] +#![warn(clippy::pattern_type_mismatch)] + +fn main() {} + +fn tuple_types() { + struct TupleStruct<'a>(&'a Option); + let ref_value = &TupleStruct(&Some(42)); + + // not ok + let TupleStruct(_) = ref_value; + if let &TupleStruct(Some(_)) = ref_value {} + if let TupleStruct(Some(_)) = *ref_value {} + + // ok + let &TupleStruct(_) = ref_value; + let TupleStruct(_) = *ref_value; + if let &TupleStruct(&Some(_)) = ref_value {} + if let TupleStruct(&Some(_)) = *ref_value {} +} + +fn tuple_enum_variants() { + enum TupleEnum<'a> { + Empty, + Var(&'a Option), + } + let ref_value = &TupleEnum::Var(&Some(42)); + + // not ok + if let TupleEnum::Var(_) = ref_value {} + if let &TupleEnum::Var(Some(_)) = ref_value {} + if let TupleEnum::Var(Some(_)) = *ref_value {} + if let TupleEnum::Empty = ref_value {} + + // ok + if let &TupleEnum::Var(_) = ref_value {} + if let TupleEnum::Var(_) = *ref_value {} + if let &TupleEnum::Var(&Some(_)) = ref_value {} + if let TupleEnum::Var(&Some(_)) = *ref_value {} + if let &TupleEnum::Empty = ref_value {} + if let TupleEnum::Empty = *ref_value {} +} + +fn plain_tuples() { + let ref_value = &(&Some(23), &Some(42)); + + // not ok + let (_a, _b) = ref_value; + if let &(_a, Some(_)) = ref_value {} + if let (_a, Some(_)) = *ref_value {} + + // ok + let &(_a, _b) = ref_value; + let (_a, _b) = *ref_value; + if let &(_a, &Some(_)) = ref_value {} + if let (_a, &Some(_)) = *ref_value {} +} diff --git a/tests/ui/pattern_type_mismatch/pattern_tuples.stderr b/tests/ui/pattern_type_mismatch/pattern_tuples.stderr new file mode 100644 index 00000000000..edd0074d00d --- /dev/null +++ b/tests/ui/pattern_type_mismatch/pattern_tuples.stderr @@ -0,0 +1,83 @@ +error: type of pattern does not match the expression type + --> $DIR/pattern_tuples.rs:11:9 + | +LL | let TupleStruct(_) = ref_value; + | ^^^^^^^^^^^^^^ + | + = note: `-D clippy::pattern-type-mismatch` implied by `-D warnings` + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_tuples.rs:12:25 + | +LL | if let &TupleStruct(Some(_)) = ref_value {} + | ^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_tuples.rs:13:24 + | +LL | if let TupleStruct(Some(_)) = *ref_value {} + | ^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_tuples.rs:30:12 + | +LL | if let TupleEnum::Var(_) = ref_value {} + | ^^^^^^^^^^^^^^^^^ + | + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_tuples.rs:31:28 + | +LL | if let &TupleEnum::Var(Some(_)) = ref_value {} + | ^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_tuples.rs:32:27 + | +LL | if let TupleEnum::Var(Some(_)) = *ref_value {} + | ^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_tuples.rs:33:12 + | +LL | if let TupleEnum::Empty = ref_value {} + | ^^^^^^^^^^^^^^^^ + | + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_tuples.rs:48:9 + | +LL | let (_a, _b) = ref_value; + | ^^^^^^^^ + | + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_tuples.rs:49:18 + | +LL | if let &(_a, Some(_)) = ref_value {} + | ^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/pattern_tuples.rs:50:17 + | +LL | if let (_a, Some(_)) = *ref_value {} + | ^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: aborting due to 10 previous errors + diff --git a/tests/ui/pattern_type_mismatch/syntax.rs b/tests/ui/pattern_type_mismatch/syntax.rs new file mode 100644 index 00000000000..e89917c41e8 --- /dev/null +++ b/tests/ui/pattern_type_mismatch/syntax.rs @@ -0,0 +1,146 @@ +#![allow(clippy::all)] +#![warn(clippy::pattern_type_mismatch)] + +fn main() {} + +fn syntax_match() { + let ref_value = &Some(&Some(42)); + + // not ok + match ref_value { + Some(_) => (), + None => (), + } + + // ok + match ref_value { + &Some(_) => (), + &None => (), + } + match *ref_value { + Some(_) => (), + None => (), + } +} + +fn syntax_if_let() { + let ref_value = &Some(42); + + // not ok + if let Some(_) = ref_value {} + + // ok + if let &Some(_) = ref_value {} + if let Some(_) = *ref_value {} +} + +fn syntax_while_let() { + let ref_value = &Some(42); + + // not ok + while let Some(_) = ref_value { + break; + } + + // ok + while let &Some(_) = ref_value { + break; + } + while let Some(_) = *ref_value { + break; + } +} + +fn syntax_for() { + let ref_value = &Some(23); + let slice = &[(2, 3), (4, 2)]; + + // not ok + for (_a, _b) in slice.iter() {} + + // ok + for &(_a, _b) in slice.iter() {} +} + +fn syntax_let() { + let ref_value = &(2, 3); + + // not ok + let (_n, _m) = ref_value; + + // ok + let &(_n, _m) = ref_value; + let (_n, _m) = *ref_value; +} + +fn syntax_fn() { + // not ok + fn foo((_a, _b): &(i32, i32)) {} + + // ok + fn foo_ok_1(&(_a, _b): &(i32, i32)) {} +} + +fn syntax_closure() { + fn foo(f: F) + where + F: FnOnce(&(i32, i32)), + { + } + + // not ok + foo(|(_a, _b)| ()); + + // ok + foo(|&(_a, _b)| ()); +} + +fn macro_with_expression() { + macro_rules! matching_macro { + ($e:expr) => { + $e + }; + } + let value = &Some(23); + + // not ok + matching_macro!(match value { + Some(_) => (), + _ => (), + }); + + // ok + matching_macro!(match value { + &Some(_) => (), + _ => (), + }); + matching_macro!(match *value { + Some(_) => (), + _ => (), + }); +} + +fn macro_expansion() { + macro_rules! matching_macro { + ($e:expr) => { + // not ok + match $e { + Some(_) => (), + _ => (), + } + + // ok + match $e { + &Some(_) => (), + _ => (), + } + match *$e { + Some(_) => (), + _ => (), + } + }; + } + + let value = &Some(23); + matching_macro!(value); +} diff --git a/tests/ui/pattern_type_mismatch/syntax.stderr b/tests/ui/pattern_type_mismatch/syntax.stderr new file mode 100644 index 00000000000..110e8421a5e --- /dev/null +++ b/tests/ui/pattern_type_mismatch/syntax.stderr @@ -0,0 +1,78 @@ +error: type of pattern does not match the expression type + --> $DIR/syntax.rs:11:9 + | +LL | Some(_) => (), + | ^^^^^^^ + | + = note: `-D clippy::pattern-type-mismatch` implied by `-D warnings` + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/syntax.rs:30:12 + | +LL | if let Some(_) = ref_value {} + | ^^^^^^^ + | + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/syntax.rs:41:15 + | +LL | while let Some(_) = ref_value { + | ^^^^^^^ + | + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/syntax.rs:59:9 + | +LL | for (_a, _b) in slice.iter() {} + | ^^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/syntax.rs:69:9 + | +LL | let (_n, _m) = ref_value; + | ^^^^^^^^ + | + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/syntax.rs:78:12 + | +LL | fn foo((_a, _b): &(i32, i32)) {} + | ^^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/syntax.rs:92:10 + | +LL | foo(|(_a, _b)| ()); + | ^^^^^^^^ + | + = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/syntax.rs:108:9 + | +LL | Some(_) => (), + | ^^^^^^^ + | + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: type of pattern does not match the expression type + --> $DIR/syntax.rs:128:17 + | +LL | Some(_) => (), + | ^^^^^^^ +... +LL | matching_macro!(value); + | ----------------------- in this macro invocation + | + = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings + +error: aborting due to 9 previous errors + -- cgit 1.4.1-3-g733a5 From 346ee968bbe8925aa7961a3e2c99e7ef84c74623 Mon Sep 17 00:00:00 2001 From: Robert Sedlacek Date: Mon, 10 Feb 2020 22:19:19 +0100 Subject: Adjusted expected STDERR --- tests/ui/pattern_type_mismatch/syntax.stderr | 1 + 1 file changed, 1 insertion(+) (limited to 'tests') diff --git a/tests/ui/pattern_type_mismatch/syntax.stderr b/tests/ui/pattern_type_mismatch/syntax.stderr index 110e8421a5e..5a5186bd4fc 100644 --- a/tests/ui/pattern_type_mismatch/syntax.stderr +++ b/tests/ui/pattern_type_mismatch/syntax.stderr @@ -73,6 +73,7 @@ LL | matching_macro!(value); | ----------------------- 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 a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 9 previous errors -- cgit 1.4.1-3-g733a5 From bf48a2d50d82cccac58d7c4c73700eaf66926aee Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Thu, 5 Mar 2020 10:35:05 -0800 Subject: Lint for if let Some(x) = ... instead of Option::map_or --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 + clippy_lints/src/option_if_let_else.rs | 202 +++++++++++++++++++++++++++++++++ src/lintlist/mod.rs | 7 ++ tests/ui/option_if_let_else.fixed | 48 ++++++++ tests/ui/option_if_let_else.rs | 56 +++++++++ tests/ui/option_if_let_else.stderr | 62 ++++++++++ 7 files changed, 381 insertions(+) create mode 100644 clippy_lints/src/option_if_let_else.rs create mode 100644 tests/ui/option_if_let_else.fixed create mode 100644 tests/ui/option_if_let_else.rs create mode 100644 tests/ui/option_if_let_else.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index ed8f16e65bb..1a081bb85fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1577,6 +1577,7 @@ Released 2018-09-13 [`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref [`option_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref [`option_env_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_env_unwrap +[`option_if_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_if_let_else [`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none [`option_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn [`option_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_option diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 1d5be893ffb..cd91e7ceb32 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -264,6 +264,7 @@ mod non_copy_const; mod non_expressive_names; mod open_options; mod option_env_unwrap; +mod option_if_let_else; mod overflow_check_conditional; mod panic_unimplemented; mod partialeq_ne_impl; @@ -734,6 +735,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &non_expressive_names::SIMILAR_NAMES, &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_unimplemented::PANIC, &panic_unimplemented::PANIC_PARAMS, @@ -1052,6 +1054,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box redundant_pub_crate::RedundantPubCrate::default()); store.register_late_pass(|| box unnamed_address::UnnamedAddress); store.register_late_pass(|| box dereference::Dereferencing); + store.register_late_pass(|| box option_if_let_else::OptionIfLetElse); store.register_late_pass(|| box future_not_send::FutureNotSend); store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls); store.register_late_pass(|| box if_let_mutex::IfLetMutex); @@ -1369,6 +1372,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&non_expressive_names::MANY_SINGLE_CHAR_NAMES), LintId::of(&open_options::NONSENSICAL_OPEN_OPTIONS), LintId::of(&option_env_unwrap::OPTION_ENV_UNWRAP), + LintId::of(&option_if_let_else::OPTION_IF_LET_ELSE), LintId::of(&overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL), LintId::of(&panic_unimplemented::PANIC_PARAMS), LintId::of(&partialeq_ne_impl::PARTIALEQ_NE_IMPL), @@ -1517,6 +1521,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT), LintId::of(&non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), LintId::of(&non_expressive_names::MANY_SINGLE_CHAR_NAMES), + LintId::of(&option_if_let_else::OPTION_IF_LET_ELSE), LintId::of(&panic_unimplemented::PANIC_PARAMS), LintId::of(&ptr::CMP_NULL), LintId::of(&ptr::PTR_ARG), diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs new file mode 100644 index 00000000000..f092f1297c1 --- /dev/null +++ b/clippy_lints/src/option_if_let_else.rs @@ -0,0 +1,202 @@ +use crate::utils::sugg::Sugg; +use crate::utils::{match_type, paths, span_lint_and_sugg}; +use if_chain::if_chain; + +use rustc_errors::Applicability; +use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; +use rustc_hir::*; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +use std::marker::PhantomData; + +declare_clippy_lint! { + /// **What it does:** + /// Lints usage of `if let Some(v) = ... { y } else { x }` which is more + /// idiomatically done with `Option::map_or` (if the else bit is a simple + /// expression) or `Option::map_or_else` (if the else bit is a longer + /// block). + /// + /// **Why is this bad?** + /// Using the dedicated functions of the Option type is clearer and + /// more concise than an if let expression. + /// + /// **Known problems:** + /// This lint uses whether the block is just an expression or if it has + /// more statements to decide whether to use `Option::map_or` or + /// `Option::map_or_else`. If you have a single expression which calls + /// an expensive function, then it would be more efficient to use + /// `Option::map_or_else`, but this lint would suggest `Option::map_or`. + /// + /// Also, this lint uses a deliberately conservative metric for checking + /// if the inside of either body contains breaks or continues which will + /// cause it to not suggest a fix if either block contains a loop with + /// continues or breaks contained within the loop. + /// + /// **Example:** + /// + /// ```rust + /// # let optional: Option = Some(0); + /// let _ = if let Some(foo) = optional { + /// foo + /// } else { + /// 5 + /// }; + /// let _ = if let Some(foo) = optional { + /// foo + /// } else { + /// let y = do_complicated_function(); + /// y*y + /// }; + /// ``` + /// + /// should be + /// + /// ```rust + /// # let optional: Option = Some(0); + /// let _ = optional.map_or(5, |foo| foo); + /// let _ = optional.map_or_else(||{ + /// let y = do_complicated_function; + /// y*y + /// }, |foo| foo); + /// ``` + pub OPTION_IF_LET_ELSE, + style, + "reimplementation of Option::map_or" +} + +declare_lint_pass!(OptionIfLetElse => [OPTION_IF_LET_ELSE]); + +/// Returns true iff the given expression is the result of calling Result::ok +fn is_result_ok(cx: &LateContext<'_, '_>, expr: &'_ Expr<'_>) -> bool { + if_chain! { + if let ExprKind::MethodCall(ref path, _, &[ref receiver]) = &expr.kind; + if path.ident.name.to_ident_string() == "ok"; + if match_type(cx, &cx.tables.expr_ty(&receiver), &paths::RESULT); + then { + true + } else { + false + } + } +} + +/// A struct containing information about occurences of the +/// `if let Some(..) = .. else` construct that this lint detects. +struct OptionIfLetElseOccurence { + option: String, + method_sugg: String, + some_expr: String, + none_expr: String, +} + +struct ReturnBreakContinueVisitor<'tcx> { + seen_return_break_continue: bool, + phantom_data: PhantomData<&'tcx bool>, +} +impl<'tcx> ReturnBreakContinueVisitor<'tcx> { + fn new() -> ReturnBreakContinueVisitor<'tcx> { + ReturnBreakContinueVisitor { + seen_return_break_continue: false, + phantom_data: PhantomData, + } + } +} +impl<'tcx> Visitor<'tcx> for ReturnBreakContinueVisitor<'tcx> { + type Map = Map<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + + fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { + if self.seen_return_break_continue { + // No need to look farther if we've already seen one of them + return; + } + match &ex.kind { + ExprKind::Ret(..) | ExprKind::Break(..) | ExprKind::Continue(..) => { + self.seen_return_break_continue = true; + }, + // Something special could be done here to handle while or for loop + // desugaring, as this will detect a break if there's a while loop + // or a for loop inside the expression. + _ => { + rustc_hir::intravisit::walk_expr(self, ex); + }, + } + } +} + +fn contains_return_break_continue<'tcx>(expression: &'tcx Expr<'tcx>) -> bool { + let mut recursive_visitor: ReturnBreakContinueVisitor<'tcx> = ReturnBreakContinueVisitor::new(); + recursive_visitor.visit_expr(expression); + recursive_visitor.seen_return_break_continue +} + +/// If this expression is the option if let/else construct we're detecting, then +/// this function returns an OptionIfLetElseOccurence struct with details if +/// this construct is found, or None if this construct is not found. +fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) -> Option { + //(String, String, String, String)> { + if_chain! { + if let ExprKind::Match(let_body, arms, MatchSource::IfLetDesugar{contains_else_clause: true}) = &expr.kind; + if arms.len() == 2; + if match_type(cx, &cx.tables.expr_ty(let_body), &paths::OPTION); + if !is_result_ok(cx, let_body); // Don't lint on Result::ok because a different lint does it already + if let PatKind::TupleStruct(_, &[inner_pat], _) = &arms[0].pat.kind; + if let PatKind::Binding(_, _, id, _) = &inner_pat.kind; + if !contains_return_break_continue(arms[0].body); + if !contains_return_break_continue(arms[1].body); + then { + let some_body = if let ExprKind::Block(Block { stmts: statements, expr: Some(expr), .. }, _) + = &arms[0].body.kind { + if let &[] = &statements { + expr + } else { + &arms[0].body + } + } else { + return None; + }; + let (none_body, method_sugg) = if let ExprKind::Block(Block { stmts: statements, expr: Some(expr), .. }, _) + = &arms[1].body.kind { + if let &[] = &statements { + (expr, "map_or") + } else { + (&arms[1].body, "map_or_else") + } + } else { + return None; + }; + let capture_name = id.name.to_ident_string(); + Some(OptionIfLetElseOccurence { + option: format!("{}", Sugg::hir(cx, let_body, "..")), + method_sugg: format!("{}", method_sugg), + some_expr: format!("|{}| {}", capture_name, Sugg::hir(cx, some_body, "..")), + none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir(cx, none_body, "..")) + }) + } else { + None + } + } +} + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for OptionIfLetElse { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) { + if let Some(detection) = detect_option_if_let_else(cx, expr) { + span_lint_and_sugg( + cx, + OPTION_IF_LET_ELSE, + expr.span, + format!("use Option::{} instead of an if let/else", detection.method_sugg).as_str(), + "try", + format!( + "{}.{}({}, {})", + detection.option, detection.method_sugg, detection.none_expr, detection.some_expr + ), + Applicability::MachineApplicable, + ); + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 6402efc2521..b499d565fa7 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1620,6 +1620,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "option_env_unwrap", }, + Lint { + name: "option_if_let_else", + group: "style", + desc: "reimplementation of Option::map_or", + deprecation: None, + module: "option_if_let_else", + }, Lint { name: "option_map_or_none", group: "style", diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed new file mode 100644 index 00000000000..3aa895120d1 --- /dev/null +++ b/tests/ui/option_if_let_else.fixed @@ -0,0 +1,48 @@ +// run-rustfix +#![warn(clippy::option_if_let_else)] + +fn bad1(string: Option<&str>) -> (bool, &str) { + string.map_or((false, "hello"), |x| (true, x)) +} + +fn longer_body(arg: Option) -> u32 { + arg.map_or(13, |x| { + let y = x * x; + y * y + }) +} + +fn test_map_or_else(arg: Option) { + let _ = arg.map_or_else(|| { + let mut y = 1; + y = (y + 2 / y) / 2; + y = (y + 2 / y) / 2; + y + }, |x| x * x * x * x); +} + +fn negative_tests(arg: Option) -> u32 { + let _ = if let Some(13) = arg { "unlucky" } else { "lucky" }; + for _ in 0..10 { + let _ = if let Some(x) = arg { + x + } else { + continue; + }; + } + let _ = if let Some(x) = arg { + return x; + } else { + 5 + }; + 7 +} + +fn main() { + let optional = Some(5); + let _ = optional.map_or(5, |x| x + 2); + let _ = bad1(None); + let _ = longer_body(None); + test_map_or_else(None); + let _ = negative_tests(None); +} diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs new file mode 100644 index 00000000000..7d029b0bcf4 --- /dev/null +++ b/tests/ui/option_if_let_else.rs @@ -0,0 +1,56 @@ +// run-rustfix +#![warn(clippy::option_if_let_else)] + +fn bad1(string: Option<&str>) -> (bool, &str) { + if let Some(x) = string { + (true, x) + } else { + (false, "hello") + } +} + +fn longer_body(arg: Option) -> u32 { + if let Some(x) = arg { + let y = x * x; + y * y + } else { + 13 + } +} + +fn test_map_or_else(arg: Option) { + let _ = if let Some(x) = arg { + x * x * x * x + } else { + let mut y = 1; + y = (y + 2 / y) / 2; + y = (y + 2 / y) / 2; + y + }; +} + +fn negative_tests(arg: Option) -> u32 { + let _ = if let Some(13) = arg { "unlucky" } else { "lucky" }; + for _ in 0..10 { + let _ = if let Some(x) = arg { + x + } else { + continue; + }; + } + let _ = if let Some(x) = arg { + return x; + } else { + 5 + }; + 7 +} + +fn main() { + let optional = Some(5); + let _ = if let Some(x) = optional { x + 2 } else { 5 }; + let _ = bad1(None); + let _ = longer_body(None); + test_map_or_else(None); + let _ = negative_tests(None); +} diff --git a/tests/ui/option_if_let_else.stderr b/tests/ui/option_if_let_else.stderr new file mode 100644 index 00000000000..d6cf0836733 --- /dev/null +++ b/tests/ui/option_if_let_else.stderr @@ -0,0 +1,62 @@ +error: use Option::map_or instead of an if let/else + --> $DIR/option_if_let_else.rs:5:5 + | +LL | / if let Some(x) = string { +LL | | (true, x) +LL | | } else { +LL | | (false, "hello") +LL | | } + | |_____^ help: try: `string.map_or((false, "hello"), |x| (true, x))` + | + = 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:13:5 + | +LL | / if let Some(x) = arg { +LL | | let y = x * x; +LL | | y * y +LL | | } else { +LL | | 13 +LL | | } + | |_____^ + | +help: try + | +LL | arg.map_or(13, |x| { +LL | let y = x * x; +LL | y * y +LL | }) + | + +error: use Option::map_or_else instead of an if let/else + --> $DIR/option_if_let_else.rs:22:13 + | +LL | let _ = if let Some(x) = arg { + | _____________^ +LL | | x * x * x * x +LL | | } else { +LL | | let mut y = 1; +... | +LL | | y +LL | | }; + | |_____^ + | +help: try + | +LL | let _ = arg.map_or_else(|| { +LL | let mut y = 1; +LL | y = (y + 2 / y) / 2; +LL | y = (y + 2 / y) / 2; +LL | y +LL | }, |x| x * x * x * x); + | + +error: use Option::map_or instead of an if let/else + --> $DIR/option_if_let_else.rs:51:13 + | +LL | let _ = if let Some(x) = optional { x + 2 } else { 5 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)` + +error: aborting due to 4 previous errors + -- cgit 1.4.1-3-g733a5 From 82f8d4d6f1645dd08b107c3ead9155412637739b Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Sat, 25 Apr 2020 08:32:33 -0700 Subject: Stop linting on macros and correctly use braces for constructs --- clippy_lints/src/option_if_let_else.rs | 23 ++++++++++++++++++++--- tests/ui/option_if_let_else.fixed | 7 +++++++ tests/ui/option_if_let_else.rs | 11 +++++++++++ tests/ui/option_if_let_else.stderr | 19 +++++++++++++++---- 4 files changed, 53 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index f092f1297c1..1edec1cad6e 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -1,3 +1,4 @@ +use crate::utils; use crate::utils::sugg::Sugg; use crate::utils::{match_type, paths, span_lint_and_sugg}; use if_chain::if_chain; @@ -89,6 +90,7 @@ struct OptionIfLetElseOccurence { method_sugg: String, some_expr: String, none_expr: String, + wrap_braces: bool, } struct ReturnBreakContinueVisitor<'tcx> { @@ -140,6 +142,7 @@ fn contains_return_break_continue<'tcx>(expression: &'tcx Expr<'tcx>) -> bool { fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) -> Option { //(String, String, String, String)> { if_chain! { + // if !utils::in_macro(expr.span); // Don't lint macros, because it behaves weirdly if let ExprKind::Match(let_body, arms, MatchSource::IfLetDesugar{contains_else_clause: true}) = &expr.kind; if arms.len() == 2; if match_type(cx, &cx.tables.expr_ty(let_body), &paths::OPTION); @@ -170,11 +173,23 @@ fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) - return None; }; let capture_name = id.name.to_ident_string(); + let wrap_braces = utils::get_enclosing_block(cx, expr.hir_id).map_or(false, |parent| { + if_chain! { + if let Some(Expr { kind: ExprKind::Match(condition, arms, MatchSource::IfDesugar{contains_else_clause: true}|MatchSource::IfLetDesugar{contains_else_clause: true}), .. } ) = parent.expr; + if expr.hir_id == arms[1].body.hir_id; + then { + true + } else { + false + } + } + }); Some(OptionIfLetElseOccurence { option: format!("{}", Sugg::hir(cx, let_body, "..")), method_sugg: format!("{}", method_sugg), some_expr: format!("|{}| {}", capture_name, Sugg::hir(cx, some_body, "..")), - none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir(cx, none_body, "..")) + none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir(cx, none_body, "..")), + wrap_braces, }) } else { None @@ -192,8 +207,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for OptionIfLetElse { format!("use Option::{} instead of an if let/else", detection.method_sugg).as_str(), "try", format!( - "{}.{}({}, {})", - detection.option, detection.method_sugg, detection.none_expr, detection.some_expr + "{}{}.{}({}, {}){}", + if detection.wrap_braces { "{ " } else { "" }, + detection.option, detection.method_sugg, detection.none_expr, detection.some_expr, + if detection.wrap_braces { " }" } else { "" }, ), Applicability::MachineApplicable, ); diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed index 3aa895120d1..343e099b2b7 100644 --- a/tests/ui/option_if_let_else.fixed +++ b/tests/ui/option_if_let_else.fixed @@ -5,6 +5,12 @@ fn bad1(string: Option<&str>) -> (bool, &str) { string.map_or((false, "hello"), |x| (true, x)) } +fn bad2(string: Option<&str>) -> Option<(bool, &str)> { + if string.is_none() { + None + } else { string.map_or(Some((false, "")), |x| Some((true, x))) } +} + fn longer_body(arg: Option) -> u32 { arg.map_or(13, |x| { let y = x * x; @@ -42,6 +48,7 @@ fn main() { let optional = Some(5); let _ = optional.map_or(5, |x| x + 2); let _ = bad1(None); + let _ = bad2(None); let _ = longer_body(None); test_map_or_else(None); let _ = negative_tests(None); diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs index 7d029b0bcf4..b0c203f0637 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -9,6 +9,16 @@ fn bad1(string: Option<&str>) -> (bool, &str) { } } +fn bad2(string: Option<&str>) -> Option<(bool, &str)> { + if string.is_none() { + None + } else if let Some(x) = string { + Some((true, x)) + } else { + Some((false, "")) + } +} + fn longer_body(arg: Option) -> u32 { if let Some(x) = arg { let y = x * x; @@ -50,6 +60,7 @@ fn main() { let optional = Some(5); let _ = if let Some(x) = optional { x + 2 } else { 5 }; let _ = bad1(None); + let _ = bad2(None); let _ = longer_body(None); test_map_or_else(None); let _ = negative_tests(None); diff --git a/tests/ui/option_if_let_else.stderr b/tests/ui/option_if_let_else.stderr index d6cf0836733..656cfb2f62a 100644 --- a/tests/ui/option_if_let_else.stderr +++ b/tests/ui/option_if_let_else.stderr @@ -11,7 +11,18 @@ 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:13:5 + --> $DIR/option_if_let_else.rs:15:12 + | +LL | } else if let Some(x) = string { + | ____________^ +LL | | Some((true, x)) +LL | | } else { +LL | | Some((false, "")) +LL | | } + | |_____^ help: try: `{ string.map_or(Some((false, "")), |x| Some((true, x))) }` + +error: use Option::map_or instead of an if let/else + --> $DIR/option_if_let_else.rs:23:5 | LL | / if let Some(x) = arg { LL | | let y = x * x; @@ -30,7 +41,7 @@ LL | }) | error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:22:13 + --> $DIR/option_if_let_else.rs:32:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -53,10 +64,10 @@ LL | }, |x| x * x * x * x); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:51:13 + --> $DIR/option_if_let_else.rs:61:13 | LL | let _ = if let Some(x) = optional { x + 2 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)` -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors -- cgit 1.4.1-3-g733a5 From b85796fe3613e20a4af21933783a3d993bb8d7ad Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Sat, 25 Apr 2020 09:08:23 -0700 Subject: Properly parenthesize to avoid operator precedence errors --- clippy_lints/src/option_if_let_else.rs | 25 ++++++++++++++++++++++--- tests/ui/option_if_let_else.fixed | 9 +++++++-- tests/ui/option_if_let_else.rs | 13 +++++++++++-- tests/ui/option_if_let_else.stderr | 16 +++++++++++++--- 4 files changed, 53 insertions(+), 10 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 1edec1cad6e..66971ee0262 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -175,7 +175,12 @@ fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) - let capture_name = id.name.to_ident_string(); let wrap_braces = utils::get_enclosing_block(cx, expr.hir_id).map_or(false, |parent| { if_chain! { - if let Some(Expr { kind: ExprKind::Match(condition, arms, MatchSource::IfDesugar{contains_else_clause: true}|MatchSource::IfLetDesugar{contains_else_clause: true}), .. } ) = parent.expr; + if let Some(Expr { kind: ExprKind::Match( + _, + arms, + MatchSource::IfDesugar{contains_else_clause: true} + | MatchSource::IfLetDesugar{contains_else_clause: true}), + .. } ) = parent.expr; if expr.hir_id == arms[1].body.hir_id; then { true @@ -184,8 +189,19 @@ fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) - } } }); + let parens_around_option = match &let_body.kind { + ExprKind::Call(..) + | ExprKind::MethodCall(..) + | ExprKind::Loop(..) + | ExprKind::Match(..) + | ExprKind::Block(..) + | ExprKind::Field(..) + | ExprKind::Path(_) + => false, + _ => true, + }; Some(OptionIfLetElseOccurence { - option: format!("{}", Sugg::hir(cx, let_body, "..")), + option: format!("{}{}{}", if parens_around_option { "(" } else { "" }, Sugg::hir(cx, let_body, ".."), if parens_around_option { ")" } else { "" }), method_sugg: format!("{}", method_sugg), some_expr: format!("|{}| {}", capture_name, Sugg::hir(cx, some_body, "..")), none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir(cx, none_body, "..")), @@ -209,7 +225,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for OptionIfLetElse { format!( "{}{}.{}({}, {}){}", if detection.wrap_braces { "{ " } else { "" }, - detection.option, detection.method_sugg, detection.none_expr, detection.some_expr, + detection.option, + detection.method_sugg, + detection.none_expr, + detection.some_expr, if detection.wrap_braces { " }" } else { "" }, ), Applicability::MachineApplicable, diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed index 343e099b2b7..80b162714ac 100644 --- a/tests/ui/option_if_let_else.fixed +++ b/tests/ui/option_if_let_else.fixed @@ -5,12 +5,16 @@ fn bad1(string: Option<&str>) -> (bool, &str) { string.map_or((false, "hello"), |x| (true, x)) } -fn bad2(string: Option<&str>) -> Option<(bool, &str)> { +fn else_if_option(string: Option<&str>) -> Option<(bool, &str)> { if string.is_none() { None } else { string.map_or(Some((false, "")), |x| Some((true, x))) } } +fn unop_bad(string: &Option<&str>) -> usize { + (*string).map_or(0, |s| s.len()) +} + fn longer_body(arg: Option) -> u32 { arg.map_or(13, |x| { let y = x * x; @@ -48,7 +52,8 @@ fn main() { let optional = Some(5); let _ = optional.map_or(5, |x| x + 2); let _ = bad1(None); - let _ = bad2(None); + let _ = else_if_option(None); + let _ = unop_bad(&None); let _ = longer_body(None); test_map_or_else(None); let _ = negative_tests(None); diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs index b0c203f0637..7c43fbea373 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -9,7 +9,7 @@ fn bad1(string: Option<&str>) -> (bool, &str) { } } -fn bad2(string: Option<&str>) -> Option<(bool, &str)> { +fn else_if_option(string: Option<&str>) -> Option<(bool, &str)> { if string.is_none() { None } else if let Some(x) = string { @@ -19,6 +19,14 @@ fn bad2(string: Option<&str>) -> Option<(bool, &str)> { } } +fn unop_bad(string: &Option<&str>) -> usize { + if let Some(s) = *string { + s.len() + } else { + 0 + } +} + fn longer_body(arg: Option) -> u32 { if let Some(x) = arg { let y = x * x; @@ -60,7 +68,8 @@ fn main() { let optional = Some(5); let _ = if let Some(x) = optional { x + 2 } else { 5 }; let _ = bad1(None); - let _ = bad2(None); + let _ = else_if_option(None); + let _ = unop_bad(&None); let _ = longer_body(None); test_map_or_else(None); let _ = negative_tests(None); diff --git a/tests/ui/option_if_let_else.stderr b/tests/ui/option_if_let_else.stderr index 656cfb2f62a..b932fe59759 100644 --- a/tests/ui/option_if_let_else.stderr +++ b/tests/ui/option_if_let_else.stderr @@ -24,6 +24,16 @@ LL | | } error: use Option::map_or instead of an if let/else --> $DIR/option_if_let_else.rs:23:5 | +LL | / if let Some(s) = *string { +LL | | s.len() +LL | | } else { +LL | | 0 +LL | | } + | |_____^ 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:31:5 + | LL | / if let Some(x) = arg { LL | | let y = x * x; LL | | y * y @@ -41,7 +51,7 @@ LL | }) | error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:32:13 + --> $DIR/option_if_let_else.rs:40:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -64,10 +74,10 @@ LL | }, |x| x * x * x * x); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:61:13 + --> $DIR/option_if_let_else.rs:69:13 | LL | let _ = if let Some(x) = optional { x + 2 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)` -error: aborting due to 5 previous errors +error: aborting due to 6 previous errors -- cgit 1.4.1-3-g733a5 From 88c8afdddff07adeff4c87431cbe8bc630a36d68 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Sat, 9 May 2020 20:20:57 -0700 Subject: Handle ref, mut, &, and &mut on the option --- clippy_lints/src/option_if_let_else.rs | 28 ++++++---- tests/ui/option_if_let_else.fixed | 20 +++++-- tests/ui/option_if_let_else.rs | 36 +++++++++++-- tests/ui/option_if_let_else.stderr | 99 +++++++++++++++++++++++++++++++--- 4 files changed, 158 insertions(+), 25 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 66971ee0262..4e501f4ca02 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -140,18 +140,24 @@ fn contains_return_break_continue<'tcx>(expression: &'tcx Expr<'tcx>) -> bool { /// this function returns an OptionIfLetElseOccurence struct with details if /// this construct is found, or None if this construct is not found. fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) -> Option { - //(String, String, String, String)> { if_chain! { - // if !utils::in_macro(expr.span); // Don't lint macros, because it behaves weirdly + if !utils::in_macro(expr.span); // Don't lint macros, because it behaves weirdly if let ExprKind::Match(let_body, arms, MatchSource::IfLetDesugar{contains_else_clause: true}) = &expr.kind; if arms.len() == 2; - if match_type(cx, &cx.tables.expr_ty(let_body), &paths::OPTION); + // if type_is_option(cx, &cx.tables.expr_ty(let_body).kind); if !is_result_ok(cx, let_body); // Don't lint on Result::ok because a different lint does it already - if let PatKind::TupleStruct(_, &[inner_pat], _) = &arms[0].pat.kind; - if let PatKind::Binding(_, _, id, _) = &inner_pat.kind; + if let PatKind::TupleStruct(struct_qpath, &[inner_pat], _) = &arms[0].pat.kind; + if utils::match_qpath(struct_qpath, &paths::OPTION_SOME); + if let PatKind::Binding(bind_annotation, _, id, _) = &inner_pat.kind; if !contains_return_break_continue(arms[0].body); if !contains_return_break_continue(arms[1].body); then { + let (capture_mut, capture_ref, capture_ref_mut) = match bind_annotation { + BindingAnnotation::Unannotated => (false, false, false), + BindingAnnotation::Mutable => (true, false, false), + BindingAnnotation::Ref => (false, true, false), + BindingAnnotation::RefMut => (false, false, true), + }; let some_body = if let ExprKind::Block(Block { stmts: statements, expr: Some(expr), .. }, _) = &arms[0].body.kind { if let &[] = &statements { @@ -189,7 +195,7 @@ fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) - } } }); - let parens_around_option = match &let_body.kind { + let (parens_around_option, as_ref, as_mut, let_body) = match &let_body.kind { ExprKind::Call(..) | ExprKind::MethodCall(..) | ExprKind::Loop(..) @@ -197,13 +203,15 @@ fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) - | ExprKind::Block(..) | ExprKind::Field(..) | ExprKind::Path(_) - => false, - _ => true, + => (false, capture_ref, capture_ref_mut, let_body), + ExprKind::Unary(UnOp::UnDeref, expr) => (false, capture_ref, capture_ref_mut, expr), + ExprKind::AddrOf(_, mutability, expr) => (false, mutability == &Mutability::Not, mutability == &Mutability::Mut, expr), + _ => (true, capture_ref, capture_ref_mut, let_body), }; Some(OptionIfLetElseOccurence { - option: format!("{}{}{}", if parens_around_option { "(" } else { "" }, Sugg::hir(cx, let_body, ".."), if parens_around_option { ")" } else { "" }), + option: format!("{}{}{}{}", if parens_around_option { "(" } else { "" }, Sugg::hir(cx, let_body, ".."), if parens_around_option { ")" } else { "" }, if as_mut { ".as_mut()" } else if as_ref { ".as_ref()" } else { "" }), method_sugg: format!("{}", method_sugg), - some_expr: format!("|{}| {}", capture_name, Sugg::hir(cx, some_body, "..")), + some_expr: format!("|{}{}{}| {}", if false { "ref " } else { "" }, if capture_mut { "mut " } else { "" }, capture_name, Sugg::hir(cx, some_body, "..")), none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir(cx, none_body, "..")), wrap_braces, }) diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed index 80b162714ac..695a460cc4e 100644 --- a/tests/ui/option_if_let_else.fixed +++ b/tests/ui/option_if_let_else.fixed @@ -11,8 +11,22 @@ fn else_if_option(string: Option<&str>) -> Option<(bool, &str)> { } else { string.map_or(Some((false, "")), |x| Some((true, x))) } } -fn unop_bad(string: &Option<&str>) -> usize { - (*string).map_or(0, |s| s.len()) +fn unop_bad(string: &Option<&str>, mut num: Option) { + let _ = string.map_or(0, |s| s.len()); + let _ = num.as_ref().map_or(&0, |s| s); + let _ = num.as_mut().map_or(&mut 0, |s| { + *s += 1; + s + }); + let _ = num.as_ref().map_or(&0, |s| s); + let _ = num.map_or(0, |mut s| { + s += 1; + s + }); + let _ = num.as_mut().map_or(&mut 0, |s| { + *s += 1; + s + }); } fn longer_body(arg: Option) -> u32 { @@ -53,7 +67,7 @@ fn main() { let _ = optional.map_or(5, |x| x + 2); let _ = bad1(None); let _ = else_if_option(None); - let _ = unop_bad(&None); + unop_bad(&None, None); let _ = longer_body(None); test_map_or_else(None); let _ = negative_tests(None); diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs index 7c43fbea373..6f9d506d347 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -19,12 +19,40 @@ fn else_if_option(string: Option<&str>) -> Option<(bool, &str)> { } } -fn unop_bad(string: &Option<&str>) -> usize { - if let Some(s) = *string { +fn unop_bad(string: &Option<&str>, mut num: Option) { + let _ = if let Some(s) = *string { s.len() } else { 0 - } + }; + let _ = if let Some(s) = &num { + s + } else { + &0 + }; + let _ = if let Some(s) = &mut num { + *s += 1; + s + } else { + &mut 0 + }; + let _ = if let Some(ref s) = num { + s + } else { + &0 + }; + let _ = if let Some(mut s) = num { + s += 1; + s + } else { + 0 + }; + let _ = if let Some(ref mut s) = num { + *s += 1; + s + } else { + &mut 0 + }; } fn longer_body(arg: Option) -> u32 { @@ -69,7 +97,7 @@ fn main() { let _ = if let Some(x) = optional { x + 2 } else { 5 }; let _ = bad1(None); let _ = else_if_option(None); - let _ = unop_bad(&None); + unop_bad(&None, None); let _ = longer_body(None); test_map_or_else(None); let _ = negative_tests(None); diff --git a/tests/ui/option_if_let_else.stderr b/tests/ui/option_if_let_else.stderr index b932fe59759..9240d3edb27 100644 --- a/tests/ui/option_if_let_else.stderr +++ b/tests/ui/option_if_let_else.stderr @@ -22,17 +22,100 @@ LL | | } | |_____^ help: try: `{ string.map_or(Some((false, "")), |x| Some((true, x))) }` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:23:5 + --> $DIR/option_if_let_else.rs:23:13 | -LL | / if let Some(s) = *string { +LL | let _ = if let Some(s) = *string { + | _____________^ LL | | s.len() LL | | } else { LL | | 0 -LL | | } - | |_____^ help: try: `(*string).map_or(0, |s| s.len())` +LL | | }; + | |_____^ 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:28:13 + | +LL | let _ = if let Some(s) = &num { + | _____________^ +LL | | s +LL | | } else { +LL | | &0 +LL | | }; + | |_____^ help: try: `num.as_ref().map_or(&0, |s| s)` + +error: use Option::map_or instead of an if let/else + --> $DIR/option_if_let_else.rs:33:13 + | +LL | let _ = if let Some(s) = &mut num { + | _____________^ +LL | | *s += 1; +LL | | s +LL | | } else { +LL | | &mut 0 +LL | | }; + | |_____^ + | +help: try + | +LL | let _ = num.as_mut().map_or(&mut 0, |s| { +LL | *s += 1; +LL | s +LL | }); + | + +error: use Option::map_or instead of an if let/else + --> $DIR/option_if_let_else.rs:39:13 + | +LL | let _ = if let Some(ref s) = num { + | _____________^ +LL | | s +LL | | } else { +LL | | &0 +LL | | }; + | |_____^ 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:44:13 + | +LL | let _ = if let Some(mut s) = num { + | _____________^ +LL | | s += 1; +LL | | s +LL | | } else { +LL | | 0 +LL | | }; + | |_____^ + | +help: try + | +LL | let _ = num.map_or(0, |mut s| { +LL | s += 1; +LL | s +LL | }); + | + +error: use Option::map_or instead of an if let/else + --> $DIR/option_if_let_else.rs:50:13 + | +LL | let _ = if let Some(ref mut s) = num { + | _____________^ +LL | | *s += 1; +LL | | s +LL | | } else { +LL | | &mut 0 +LL | | }; + | |_____^ + | +help: try + | +LL | let _ = num.as_mut().map_or(&mut 0, |s| { +LL | *s += 1; +LL | s +LL | }); + | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:31:5 + --> $DIR/option_if_let_else.rs:59:5 | LL | / if let Some(x) = arg { LL | | let y = x * x; @@ -51,7 +134,7 @@ LL | }) | error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:40:13 + --> $DIR/option_if_let_else.rs:68:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -74,10 +157,10 @@ LL | }, |x| x * x * x * x); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:69:13 + --> $DIR/option_if_let_else.rs:97:13 | LL | let _ = if let Some(x) = optional { x + 2 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)` -error: aborting due to 6 previous errors +error: aborting due to 11 previous errors -- cgit 1.4.1-3-g733a5 From f73b455b99694fbc5ddec38317f705f546729db2 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Sat, 9 May 2020 20:44:56 -0700 Subject: Refactoring --- clippy_lints/src/option_if_let_else.rs | 170 ++++++++++++++++++++------------- tests/ui/option_if_let_else.rs | 18 +--- tests/ui/option_if_let_else.stderr | 43 +++------ 3 files changed, 123 insertions(+), 108 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 4e501f4ca02..208aa550765 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -5,7 +5,7 @@ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; -use rustc_hir::*; +use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, PatKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -69,17 +69,12 @@ declare_clippy_lint! { declare_lint_pass!(OptionIfLetElse => [OPTION_IF_LET_ELSE]); -/// Returns true iff the given expression is the result of calling Result::ok +/// Returns true iff the given expression is the result of calling `Result::ok` fn is_result_ok(cx: &LateContext<'_, '_>, expr: &'_ Expr<'_>) -> bool { - if_chain! { - if let ExprKind::MethodCall(ref path, _, &[ref receiver]) = &expr.kind; - if path.ident.name.to_ident_string() == "ok"; - if match_type(cx, &cx.tables.expr_ty(&receiver), &paths::RESULT); - then { - true - } else { - false - } + if let ExprKind::MethodCall(ref path, _, &[ref receiver]) = &expr.kind { + path.ident.name.to_ident_string() == "ok" && match_type(cx, &cx.tables.expr_ty(&receiver), &paths::RESULT) + } else { + false } } @@ -136,66 +131,108 @@ fn contains_return_break_continue<'tcx>(expression: &'tcx Expr<'tcx>) -> bool { recursive_visitor.seen_return_break_continue } +/// Extracts the body of a given arm. If the arm contains only an expression, +/// then it returns the expression. Otherwise, it returns the entire block +fn extract_body_from_arm<'a>(arm: &'a Arm<'a>) -> Option<&'a Expr<'a>> { + if let ExprKind::Block( + Block { + stmts: statements, + expr: Some(expr), + .. + }, + _, + ) = &arm.body.kind + { + if let [] = statements { + Some(&expr) + } else { + Some(&arm.body) + } + } else { + None + } +} + +/// If this is the else body of an if/else expression, then we need to wrap +/// it in curcly braces. Otherwise, we don't. +fn should_wrap_in_braces(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> bool { + utils::get_enclosing_block(cx, expr.hir_id).map_or(false, |parent| { + if let Some(Expr { + kind: + ExprKind::Match( + _, + arms, + MatchSource::IfDesugar { + contains_else_clause: true, + } + | MatchSource::IfLetDesugar { + contains_else_clause: true, + }, + ), + .. + }) = parent.expr + { + expr.hir_id == arms[1].body.hir_id + } else { + false + } + }) +} + +fn format_option_in_sugg( + cx: &LateContext<'_, '_>, + cond_expr: &Expr<'_>, + parens_around_option: bool, + as_ref: bool, + as_mut: bool, +) -> String { + format!( + "{}{}{}{}", + if parens_around_option { "(" } else { "" }, + Sugg::hir(cx, cond_expr, ".."), + if parens_around_option { ")" } else { "" }, + if as_mut { + ".as_mut()" + } else if as_ref { + ".as_ref()" + } else { + "" + } + ) +} + /// If this expression is the option if let/else construct we're detecting, then -/// this function returns an OptionIfLetElseOccurence struct with details if +/// this function returns an `OptionIfLetElseOccurence` struct with details if /// this construct is found, or None if this construct is not found. fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) -> Option { if_chain! { if !utils::in_macro(expr.span); // Don't lint macros, because it behaves weirdly - if let ExprKind::Match(let_body, arms, MatchSource::IfLetDesugar{contains_else_clause: true}) = &expr.kind; + if let ExprKind::Match(cond_expr, arms, MatchSource::IfLetDesugar{contains_else_clause: true}) = &expr.kind; if arms.len() == 2; - // if type_is_option(cx, &cx.tables.expr_ty(let_body).kind); - if !is_result_ok(cx, let_body); // Don't lint on Result::ok because a different lint does it already + if !is_result_ok(cx, cond_expr); // Don't lint on Result::ok because a different lint does it already if let PatKind::TupleStruct(struct_qpath, &[inner_pat], _) = &arms[0].pat.kind; if utils::match_qpath(struct_qpath, &paths::OPTION_SOME); if let PatKind::Binding(bind_annotation, _, id, _) = &inner_pat.kind; if !contains_return_break_continue(arms[0].body); if !contains_return_break_continue(arms[1].body); then { - let (capture_mut, capture_ref, capture_ref_mut) = match bind_annotation { - BindingAnnotation::Unannotated => (false, false, false), - BindingAnnotation::Mutable => (true, false, false), - BindingAnnotation::Ref => (false, true, false), - BindingAnnotation::RefMut => (false, false, true), - }; - let some_body = if let ExprKind::Block(Block { stmts: statements, expr: Some(expr), .. }, _) - = &arms[0].body.kind { - if let &[] = &statements { - expr - } else { - &arms[0].body - } - } else { - return None; - }; - let (none_body, method_sugg) = if let ExprKind::Block(Block { stmts: statements, expr: Some(expr), .. }, _) - = &arms[1].body.kind { - if let &[] = &statements { - (expr, "map_or") - } else { - (&arms[1].body, "map_or_else") - } - } else { - return None; + let capture_mut = if bind_annotation == &BindingAnnotation::Mutable { "mut " } else { "" }; + let some_body = extract_body_from_arm(&arms[0])?; + let none_body = extract_body_from_arm(&arms[1])?; + let method_sugg = match &none_body.kind { + ExprKind::Block(..) => "map_or_else", + _ => "map_or", }; let capture_name = id.name.to_ident_string(); - let wrap_braces = utils::get_enclosing_block(cx, expr.hir_id).map_or(false, |parent| { - if_chain! { - if let Some(Expr { kind: ExprKind::Match( - _, - arms, - MatchSource::IfDesugar{contains_else_clause: true} - | MatchSource::IfLetDesugar{contains_else_clause: true}), - .. } ) = parent.expr; - if expr.hir_id == arms[1].body.hir_id; - then { - true - } else { - false - } - } - }); - let (parens_around_option, as_ref, as_mut, let_body) = match &let_body.kind { + let wrap_braces = should_wrap_in_braces(cx, expr); + let (as_ref, as_mut) = match &cond_expr.kind { + ExprKind::AddrOf(_, Mutability::Not, _) => (true, false), + ExprKind::AddrOf(_, Mutability::Mut, _) => (false, true), + _ => (bind_annotation == &BindingAnnotation::Ref, bind_annotation == &BindingAnnotation::RefMut), + }; + let parens_around_option = match &cond_expr.kind { + // Put parens around the option expression if not doing so might + // mess up the order of operations. ExprKind::Call(..) | ExprKind::MethodCall(..) | ExprKind::Loop(..) @@ -203,15 +240,20 @@ fn detect_option_if_let_else<'a>(cx: &LateContext<'_, 'a>, expr: &'a Expr<'a>) - | ExprKind::Block(..) | ExprKind::Field(..) | ExprKind::Path(_) - => (false, capture_ref, capture_ref_mut, let_body), - ExprKind::Unary(UnOp::UnDeref, expr) => (false, capture_ref, capture_ref_mut, expr), - ExprKind::AddrOf(_, mutability, expr) => (false, mutability == &Mutability::Not, mutability == &Mutability::Mut, expr), - _ => (true, capture_ref, capture_ref_mut, let_body), + | ExprKind::Unary(UnOp::UnDeref, _) + | ExprKind::AddrOf(..) + => false, + _ => true, + }; + let cond_expr = match &cond_expr.kind { + // Pointer dereferencing happens automatically, so we can omit it in the suggestion + ExprKind::Unary(UnOp::UnDeref, expr)|ExprKind::AddrOf(_, _, expr) => expr, + _ => cond_expr, }; Some(OptionIfLetElseOccurence { - option: format!("{}{}{}{}", if parens_around_option { "(" } else { "" }, Sugg::hir(cx, let_body, ".."), if parens_around_option { ")" } else { "" }, if as_mut { ".as_mut()" } else if as_ref { ".as_ref()" } else { "" }), - method_sugg: format!("{}", method_sugg), - some_expr: format!("|{}{}{}| {}", if false { "ref " } else { "" }, if capture_mut { "mut " } else { "" }, capture_name, Sugg::hir(cx, some_body, "..")), + option: format_option_in_sugg(cx, cond_expr, parens_around_option, as_ref, as_mut), + method_sugg: method_sugg.to_string(), + some_expr: format!("|{}{}| {}", capture_mut, capture_name, Sugg::hir(cx, some_body, "..")), none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir(cx, none_body, "..")), wrap_braces, }) diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs index 6f9d506d347..dee80d26bd9 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -20,27 +20,15 @@ fn else_if_option(string: Option<&str>) -> Option<(bool, &str)> { } fn unop_bad(string: &Option<&str>, mut num: Option) { - let _ = if let Some(s) = *string { - s.len() - } else { - 0 - }; - let _ = if let Some(s) = &num { - s - } else { - &0 - }; + let _ = if let Some(s) = *string { s.len() } else { 0 }; + let _ = if let Some(s) = &num { s } else { &0 }; let _ = if let Some(s) = &mut num { *s += 1; s } else { &mut 0 }; - let _ = if let Some(ref s) = num { - s - } else { - &0 - }; + let _ = if let Some(ref s) = num { s } else { &0 }; let _ = if let Some(mut s) = num { s += 1; s diff --git a/tests/ui/option_if_let_else.stderr b/tests/ui/option_if_let_else.stderr index 9240d3edb27..7005850efaf 100644 --- a/tests/ui/option_if_let_else.stderr +++ b/tests/ui/option_if_let_else.stderr @@ -24,27 +24,17 @@ LL | | } error: use Option::map_or instead of an if let/else --> $DIR/option_if_let_else.rs:23:13 | -LL | let _ = if let Some(s) = *string { - | _____________^ -LL | | s.len() -LL | | } else { -LL | | 0 -LL | | }; - | |_____^ help: try: `string.map_or(0, |s| s.len())` +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:28:13 + --> $DIR/option_if_let_else.rs:24:13 | -LL | let _ = if let Some(s) = &num { - | _____________^ -LL | | s -LL | | } else { -LL | | &0 -LL | | }; - | |_____^ help: try: `num.as_ref().map_or(&0, |s| s)` +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:33:13 + --> $DIR/option_if_let_else.rs:25:13 | LL | let _ = if let Some(s) = &mut num { | _____________^ @@ -64,18 +54,13 @@ LL | }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:39:13 + --> $DIR/option_if_let_else.rs:31:13 | -LL | let _ = if let Some(ref s) = num { - | _____________^ -LL | | s -LL | | } else { -LL | | &0 -LL | | }; - | |_____^ help: try: `num.as_ref().map_or(&0, |s| s)` +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:44:13 + --> $DIR/option_if_let_else.rs:32:13 | LL | let _ = if let Some(mut s) = num { | _____________^ @@ -95,7 +80,7 @@ LL | }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:50:13 + --> $DIR/option_if_let_else.rs:38:13 | LL | let _ = if let Some(ref mut s) = num { | _____________^ @@ -115,7 +100,7 @@ LL | }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:59:5 + --> $DIR/option_if_let_else.rs:47:5 | LL | / if let Some(x) = arg { LL | | let y = x * x; @@ -134,7 +119,7 @@ LL | }) | error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:68:13 + --> $DIR/option_if_let_else.rs:56:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -157,7 +142,7 @@ LL | }, |x| x * x * x * x); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:97:13 + --> $DIR/option_if_let_else.rs:85:13 | LL | let _ = if let Some(x) = optional { x + 2 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)` -- cgit 1.4.1-3-g733a5 From 6e2d55c8db04a80f9c217f2089066b29302f62a9 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Sat, 27 Jun 2020 16:55:47 -0700 Subject: Update compile-test to follow new lint --- tests/compile-test.rs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) (limited to 'tests') diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 99505fc6b29..eb6d495acbe 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -12,19 +12,11 @@ use std::path::{Path, PathBuf}; mod cargo; fn host_lib() -> PathBuf { - if let Some(path) = option_env!("HOST_LIBS") { - PathBuf::from(path) - } else { - cargo::CARGO_TARGET_DIR.join(env!("PROFILE")) - } + option_env!("HOST_LIBS").map_or(cargo::CARGO_TARGET_DIR.join(env!("PROFILE")), PathBuf::from) } fn clippy_driver_path() -> PathBuf { - if let Some(path) = option_env!("CLIPPY_DRIVER_PATH") { - PathBuf::from(path) - } else { - cargo::TARGET_LIB.join("clippy-driver") - } + option_env!("CLIPPY_DRIVER_PATH").map_or(cargo::TARGET_LIB.join("clippy-driver"), PathBuf::from) } // When we'll want to use `extern crate ..` for a dependency that is used -- cgit 1.4.1-3-g733a5 From a6f1af75d71fcf8e029b78142370e7563798c503 Mon Sep 17 00:00:00 2001 From: Thiago Arrais Date: Fri, 3 Apr 2020 13:58:52 -0300 Subject: Lint for x.powi(2) => x * x --- clippy_lints/src/floating_point_arithmetic.rs | 27 ++++++++++++++++++++++++++- tests/ui/floating_point_log.fixed | 10 +++++----- tests/ui/floating_point_log.rs | 10 +++++----- tests/ui/floating_point_log.stderr | 20 ++++++++++---------- tests/ui/floating_point_powf.fixed | 4 ++-- tests/ui/floating_point_powf.rs | 4 ++-- tests/ui/floating_point_powf.stderr | 8 ++++---- tests/ui/floating_point_powi.fixed | 12 ++++++++++++ tests/ui/floating_point_powi.rs | 12 ++++++++++++ tests/ui/floating_point_powi.stderr | 16 ++++++++++++++++ 10 files changed, 94 insertions(+), 29 deletions(-) create mode 100644 tests/ui/floating_point_powi.fixed create mode 100644 tests/ui/floating_point_powi.rs create mode 100644 tests/ui/floating_point_powi.stderr (limited to 'tests') diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 4efd0689267..e3ee4296119 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -1,6 +1,6 @@ use crate::consts::{ constant, constant_simple, Constant, - Constant::{F32, F64}, + Constant::{Int, F32, F64}, }; use crate::utils::{higher, numeric_literal, span_lint_and_sugg, sugg, SpanlessEq}; use if_chain::if_chain; @@ -293,6 +293,30 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { } } +fn check_powi(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) { + // Check argument + if let Some((value, _)) = constant(cx, cx.tables, &args[1]) { + let (lint, help, suggestion) = match value { + Int(2) => ( + IMPRECISE_FLOPS, + "square can be computed more accurately", + format!("{} * {}", Sugg::hir(cx, &args[0], ".."), Sugg::hir(cx, &args[0], "..")), + ), + _ => return, + }; + + span_lint_and_sugg( + cx, + lint, + expr.span, + help, + "consider using", + suggestion, + Applicability::MachineApplicable, + ); + } +} + // TODO: Lint expressions of the form `x.exp() - y` where y > 1 // and suggest usage of `x.exp_m1() - (y - 1)` instead fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) { @@ -489,6 +513,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic { "ln" => check_ln1p(cx, expr, args), "log" => check_log_base(cx, expr, args), "powf" => check_powf(cx, expr, args), + "powi" => check_powi(cx, expr, args), _ => {}, } } diff --git a/tests/ui/floating_point_log.fixed b/tests/ui/floating_point_log.fixed index 42c5e5d2bae..7dc7ee94aff 100644 --- a/tests/ui/floating_point_log.fixed +++ b/tests/ui/floating_point_log.fixed @@ -25,11 +25,11 @@ fn check_ln1p() { let _ = 2.0f32.ln_1p(); let _ = x.ln_1p(); let _ = (x / 2.0).ln_1p(); - let _ = x.powi(2).ln_1p(); - let _ = (x.powi(2) / 2.0).ln_1p(); + let _ = x.powi(3).ln_1p(); + let _ = (x.powi(3) / 2.0).ln_1p(); let _ = ((std::f32::consts::E - 1.0)).ln_1p(); let _ = x.ln_1p(); - let _ = x.powi(2).ln_1p(); + let _ = x.powi(3).ln_1p(); let _ = (x + 2.0).ln_1p(); let _ = (x / 2.0).ln_1p(); // Cases where the lint shouldn't be applied @@ -43,9 +43,9 @@ fn check_ln1p() { let _ = 2.0f64.ln_1p(); let _ = x.ln_1p(); let _ = (x / 2.0).ln_1p(); - let _ = x.powi(2).ln_1p(); + let _ = x.powi(3).ln_1p(); let _ = x.ln_1p(); - let _ = x.powi(2).ln_1p(); + let _ = x.powi(3).ln_1p(); let _ = (x + 2.0).ln_1p(); let _ = (x / 2.0).ln_1p(); // Cases where the lint shouldn't be applied diff --git a/tests/ui/floating_point_log.rs b/tests/ui/floating_point_log.rs index 8be0d9ad56f..01181484e7d 100644 --- a/tests/ui/floating_point_log.rs +++ b/tests/ui/floating_point_log.rs @@ -25,11 +25,11 @@ fn check_ln1p() { let _ = (1f32 + 2.0).ln(); let _ = (1.0 + x).ln(); let _ = (1.0 + x / 2.0).ln(); - let _ = (1.0 + x.powi(2)).ln(); - let _ = (1.0 + x.powi(2) / 2.0).ln(); + let _ = (1.0 + x.powi(3)).ln(); + let _ = (1.0 + x.powi(3) / 2.0).ln(); let _ = (1.0 + (std::f32::consts::E - 1.0)).ln(); let _ = (x + 1.0).ln(); - let _ = (x.powi(2) + 1.0).ln(); + let _ = (x.powi(3) + 1.0).ln(); let _ = (x + 2.0 + 1.0).ln(); let _ = (x / 2.0 + 1.0).ln(); // Cases where the lint shouldn't be applied @@ -43,9 +43,9 @@ fn check_ln1p() { let _ = (1f64 + 2.0).ln(); let _ = (1.0 + x).ln(); let _ = (1.0 + x / 2.0).ln(); - let _ = (1.0 + x.powi(2)).ln(); + let _ = (1.0 + x.powi(3)).ln(); let _ = (x + 1.0).ln(); - let _ = (x.powi(2) + 1.0).ln(); + let _ = (x.powi(3) + 1.0).ln(); let _ = (x + 2.0 + 1.0).ln(); let _ = (x / 2.0 + 1.0).ln(); // Cases where the lint shouldn't be applied diff --git a/tests/ui/floating_point_log.stderr b/tests/ui/floating_point_log.stderr index 943fbdb0b83..900dc2b7933 100644 --- a/tests/ui/floating_point_log.stderr +++ b/tests/ui/floating_point_log.stderr @@ -77,14 +77,14 @@ LL | let _ = (1.0 + x / 2.0).ln(); error: ln(1 + x) can be computed more accurately --> $DIR/floating_point_log.rs:28:13 | -LL | let _ = (1.0 + x.powi(2)).ln(); - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()` +LL | let _ = (1.0 + x.powi(3)).ln(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()` error: ln(1 + x) can be computed more accurately --> $DIR/floating_point_log.rs:29:13 | -LL | let _ = (1.0 + x.powi(2) / 2.0).ln(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x.powi(2) / 2.0).ln_1p()` +LL | let _ = (1.0 + x.powi(3) / 2.0).ln(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x.powi(3) / 2.0).ln_1p()` error: ln(1 + x) can be computed more accurately --> $DIR/floating_point_log.rs:30:13 @@ -101,8 +101,8 @@ LL | let _ = (x + 1.0).ln(); error: ln(1 + x) can be computed more accurately --> $DIR/floating_point_log.rs:32:13 | -LL | let _ = (x.powi(2) + 1.0).ln(); - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()` +LL | let _ = (x.powi(3) + 1.0).ln(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()` error: ln(1 + x) can be computed more accurately --> $DIR/floating_point_log.rs:33:13 @@ -143,8 +143,8 @@ LL | let _ = (1.0 + x / 2.0).ln(); error: ln(1 + x) can be computed more accurately --> $DIR/floating_point_log.rs:46:13 | -LL | let _ = (1.0 + x.powi(2)).ln(); - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()` +LL | let _ = (1.0 + x.powi(3)).ln(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()` error: ln(1 + x) can be computed more accurately --> $DIR/floating_point_log.rs:47:13 @@ -155,8 +155,8 @@ LL | let _ = (x + 1.0).ln(); error: ln(1 + x) can be computed more accurately --> $DIR/floating_point_log.rs:48:13 | -LL | let _ = (x.powi(2) + 1.0).ln(); - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2).ln_1p()` +LL | let _ = (x.powi(3) + 1.0).ln(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()` error: ln(1 + x) can be computed more accurately --> $DIR/floating_point_log.rs:49:13 diff --git a/tests/ui/floating_point_powf.fixed b/tests/ui/floating_point_powf.fixed index 78a9d44829b..b0641a100cd 100644 --- a/tests/ui/floating_point_powf.fixed +++ b/tests/ui/floating_point_powf.fixed @@ -11,7 +11,7 @@ fn main() { let _ = (-3.1f32).exp(); let _ = x.sqrt(); let _ = x.cbrt(); - let _ = x.powi(2); + let _ = x.powi(3); let _ = x.powi(-2); let _ = x.powi(16_777_215); let _ = x.powi(-16_777_215); @@ -30,7 +30,7 @@ fn main() { let _ = (-3.1f64).exp(); let _ = x.sqrt(); let _ = x.cbrt(); - let _ = x.powi(2); + let _ = x.powi(3); let _ = x.powi(-2); let _ = x.powi(-2_147_483_648); let _ = x.powi(2_147_483_647); diff --git a/tests/ui/floating_point_powf.rs b/tests/ui/floating_point_powf.rs index dbc1cac5cb4..a0a2c973900 100644 --- a/tests/ui/floating_point_powf.rs +++ b/tests/ui/floating_point_powf.rs @@ -11,7 +11,7 @@ fn main() { let _ = std::f32::consts::E.powf(-3.1); let _ = x.powf(1.0 / 2.0); let _ = x.powf(1.0 / 3.0); - let _ = x.powf(2.0); + let _ = x.powf(3.0); let _ = x.powf(-2.0); let _ = x.powf(16_777_215.0); let _ = x.powf(-16_777_215.0); @@ -30,7 +30,7 @@ fn main() { let _ = std::f64::consts::E.powf(-3.1); let _ = x.powf(1.0 / 2.0); let _ = x.powf(1.0 / 3.0); - let _ = x.powf(2.0); + let _ = x.powf(3.0); let _ = x.powf(-2.0); let _ = x.powf(-2_147_483_648.0); let _ = x.powf(2_147_483_647.0); diff --git a/tests/ui/floating_point_powf.stderr b/tests/ui/floating_point_powf.stderr index ad5163f0079..2422eb911e9 100644 --- a/tests/ui/floating_point_powf.stderr +++ b/tests/ui/floating_point_powf.stderr @@ -53,8 +53,8 @@ LL | let _ = x.powf(1.0 / 3.0); error: exponentiation with integer powers can be computed more efficiently --> $DIR/floating_point_powf.rs:14:13 | -LL | let _ = x.powf(2.0); - | ^^^^^^^^^^^ help: consider using: `x.powi(2)` +LL | let _ = x.powf(3.0); + | ^^^^^^^^^^^ help: consider using: `x.powi(3)` error: exponentiation with integer powers can be computed more efficiently --> $DIR/floating_point_powf.rs:15:13 @@ -125,8 +125,8 @@ LL | let _ = x.powf(1.0 / 3.0); error: exponentiation with integer powers can be computed more efficiently --> $DIR/floating_point_powf.rs:33:13 | -LL | let _ = x.powf(2.0); - | ^^^^^^^^^^^ help: consider using: `x.powi(2)` +LL | let _ = x.powf(3.0); + | ^^^^^^^^^^^ help: consider using: `x.powi(3)` error: exponentiation with integer powers can be computed more efficiently --> $DIR/floating_point_powf.rs:34:13 diff --git a/tests/ui/floating_point_powi.fixed b/tests/ui/floating_point_powi.fixed new file mode 100644 index 00000000000..0ce6f72535d --- /dev/null +++ b/tests/ui/floating_point_powi.fixed @@ -0,0 +1,12 @@ +// run-rustfix +#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] + +fn main() { + let one = 1; + let x = 3f32; + let _ = x * x; + let _ = x * x; + // Cases where the lint shouldn't be applied + let _ = x.powi(3); + let _ = x.powi(one + 1); +} diff --git a/tests/ui/floating_point_powi.rs b/tests/ui/floating_point_powi.rs new file mode 100644 index 00000000000..c87e836bedd --- /dev/null +++ b/tests/ui/floating_point_powi.rs @@ -0,0 +1,12 @@ +// run-rustfix +#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] + +fn main() { + let one = 1; + let x = 3f32; + let _ = x.powi(2); + let _ = x.powi(1 + 1); + // Cases where the lint shouldn't be applied + let _ = x.powi(3); + let _ = x.powi(one + 1); +} diff --git a/tests/ui/floating_point_powi.stderr b/tests/ui/floating_point_powi.stderr new file mode 100644 index 00000000000..ae7bbaa4473 --- /dev/null +++ b/tests/ui/floating_point_powi.stderr @@ -0,0 +1,16 @@ +error: square can be computed more accurately + --> $DIR/floating_point_powi.rs:7:13 + | +LL | let _ = x.powi(2); + | ^^^^^^^^^ help: consider using: `x * x` + | + = note: `-D clippy::imprecise-flops` implied by `-D warnings` + +error: square can be computed more accurately + --> $DIR/floating_point_powi.rs:8:13 + | +LL | let _ = x.powi(1 + 1); + | ^^^^^^^^^^^^^ help: consider using: `x * x` + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From f62798454c8a7f9f2c3e87e0a913b3bd79b6d2ed Mon Sep 17 00:00:00 2001 From: Thiago Arrais Date: Mon, 25 May 2020 13:54:39 -0300 Subject: Lint (x * x + y * y).sqrt() => x.hypot(y) --- clippy_lints/src/floating_point_arithmetic.rs | 75 ++++++++++++++++++++++++++- tests/ui/floating_point_hypot.fixed | 13 +++++ tests/ui/floating_point_hypot.rs | 13 +++++ tests/ui/floating_point_hypot.stderr | 30 +++++++++++ tests/ui/floating_point_mul_add.fixed | 5 ++ tests/ui/floating_point_mul_add.rs | 5 ++ tests/ui/floating_point_mul_add.stderr | 8 ++- tests/ui/floating_point_powi.fixed | 7 ++- tests/ui/floating_point_powi.rs | 7 ++- tests/ui/floating_point_powi.stderr | 20 ++++++- 10 files changed, 177 insertions(+), 6 deletions(-) create mode 100644 tests/ui/floating_point_hypot.fixed create mode 100644 tests/ui/floating_point_hypot.rs create mode 100644 tests/ui/floating_point_hypot.stderr (limited to 'tests') diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index e3ee4296119..3b2e46a9a85 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -2,10 +2,10 @@ use crate::consts::{ constant, constant_simple, Constant, Constant::{Int, F32, F64}, }; -use crate::utils::{higher, numeric_literal, span_lint_and_sugg, sugg, SpanlessEq}; +use crate::utils::{get_parent_expr, higher, numeric_literal, span_lint_and_sugg, sugg, SpanlessEq}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; +use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -296,6 +296,17 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { fn check_powi(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) { // Check argument if let Some((value, _)) = constant(cx, cx.tables, &args[1]) { + // TODO: need more specific check. this is too wide. remember also to include tests + if let Some(parent) = get_parent_expr(cx, expr) { + if let Some(grandparent) = get_parent_expr(cx, parent) { + if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, _, args) = grandparent.kind { + if method_name.as_str() == "sqrt" && detect_hypot(cx, args).is_some() { + return; + } + } + } + } + let (lint, help, suggestion) = match value { Int(2) => ( IMPRECISE_FLOPS, @@ -317,6 +328,57 @@ fn check_powi(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) { } } +fn detect_hypot(cx: &LateContext<'_, '_>, args: &[Expr<'_>]) -> Option { + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Add, .. + }, + ref add_lhs, + ref add_rhs, + ) = args[0].kind + { + // check if expression of the form x * x + y * y + if_chain! { + if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, ref lmul_lhs, ref lmul_rhs) = add_lhs.kind; + if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, ref rmul_lhs, ref rmul_rhs) = add_rhs.kind; + if are_exprs_equal(cx, lmul_lhs, lmul_rhs); + if are_exprs_equal(cx, rmul_lhs, rmul_rhs); + then { + return Some(format!("{}.hypot({})", Sugg::hir(cx, &lmul_lhs, ".."), Sugg::hir(cx, &rmul_lhs, ".."))); + } + } + + // check if expression of the form x.powi(2) + y.powi(2) + if_chain! { + if let ExprKind::MethodCall(PathSegment { ident: lmethod_name, .. }, ref _lspan, ref largs) = add_lhs.kind; + if let ExprKind::MethodCall(PathSegment { ident: rmethod_name, .. }, ref _rspan, ref rargs) = add_rhs.kind; + if lmethod_name.as_str() == "powi" && rmethod_name.as_str() == "powi"; + if let Some((lvalue, _)) = constant(cx, cx.tables, &largs[1]); + if let Some((rvalue, _)) = constant(cx, cx.tables, &rargs[1]); + if Int(2) == lvalue && Int(2) == rvalue; + then { + return Some(format!("{}.hypot({})", Sugg::hir(cx, &largs[0], ".."), Sugg::hir(cx, &rargs[0], ".."))); + } + } + } + + None +} + +fn check_hypot(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) { + if let Some(message) = detect_hypot(cx, args) { + span_lint_and_sugg( + cx, + IMPRECISE_FLOPS, + expr.span, + "hypotenuse can be computed more accurately", + "consider using", + message, + Applicability::MachineApplicable, + ); + } +} + // TODO: Lint expressions of the form `x.exp() - y` where y > 1 // and suggest usage of `x.exp_m1() - (y - 1)` instead fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) { @@ -368,6 +430,14 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) { rhs, ) = &expr.kind { + if let Some(parent) = get_parent_expr(cx, expr) { + if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, _, args) = parent.kind { + if method_name.as_str() == "sqrt" && detect_hypot(cx, args).is_some() { + return; + } + } + } + let (recv, arg1, arg2) = if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, lhs) { (inner_lhs, inner_rhs, rhs) } else if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, rhs) { @@ -514,6 +584,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic { "log" => check_log_base(cx, expr, args), "powf" => check_powf(cx, expr, args), "powi" => check_powi(cx, expr, args), + "sqrt" => check_hypot(cx, expr, args), _ => {}, } } diff --git a/tests/ui/floating_point_hypot.fixed b/tests/ui/floating_point_hypot.fixed new file mode 100644 index 00000000000..f90695bc3fe --- /dev/null +++ b/tests/ui/floating_point_hypot.fixed @@ -0,0 +1,13 @@ +// run-rustfix +#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] + +fn main() { + let x = 3f32; + let y = 4f32; + let _ = x.hypot(y); + let _ = (x + 1f32).hypot(y); + let _ = x.hypot(y); + // Cases where the lint shouldn't be applied + let _ = x.mul_add(x, y * y).sqrt(); + let _ = x.mul_add(4f32, y * y).sqrt(); +} diff --git a/tests/ui/floating_point_hypot.rs b/tests/ui/floating_point_hypot.rs new file mode 100644 index 00000000000..e7b048e262f --- /dev/null +++ b/tests/ui/floating_point_hypot.rs @@ -0,0 +1,13 @@ +// run-rustfix +#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] + +fn main() { + let x = 3f32; + let y = 4f32; + let _ = (x * x + y * y).sqrt(); + let _ = ((x + 1f32) * (x + 1f32) + y * y).sqrt(); + let _ = (x.powi(2) + y.powi(2)).sqrt(); + // Cases where the lint shouldn't be applied + let _ = x.mul_add(x, y * y).sqrt(); + let _ = (x * 4f32 + y * y).sqrt(); +} diff --git a/tests/ui/floating_point_hypot.stderr b/tests/ui/floating_point_hypot.stderr new file mode 100644 index 00000000000..fe1dfc7a451 --- /dev/null +++ b/tests/ui/floating_point_hypot.stderr @@ -0,0 +1,30 @@ +error: hypotenuse can be computed more accurately + --> $DIR/floating_point_hypot.rs:7:13 + | +LL | let _ = (x * x + y * y).sqrt(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.hypot(y)` + | + = note: `-D clippy::imprecise-flops` implied by `-D warnings` + +error: hypotenuse can be computed more accurately + --> $DIR/floating_point_hypot.rs:8:13 + | +LL | let _ = ((x + 1f32) * (x + 1f32) + y * y).sqrt(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x + 1f32).hypot(y)` + +error: hypotenuse can be computed more accurately + --> $DIR/floating_point_hypot.rs:9:13 + | +LL | let _ = (x.powi(2) + y.powi(2)).sqrt(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.hypot(y)` + +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_hypot.rs:12:13 + | +LL | let _ = (x * 4f32 + y * y).sqrt(); + | ^^^^^^^^^^^^^^^^^^ help: consider using: `x.mul_add(4f32, y * y)` + | + = note: `-D clippy::suboptimal-flops` implied by `-D warnings` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/floating_point_mul_add.fixed b/tests/ui/floating_point_mul_add.fixed index e343c37740d..911700bab00 100644 --- a/tests/ui/floating_point_mul_add.fixed +++ b/tests/ui/floating_point_mul_add.fixed @@ -18,4 +18,9 @@ fn main() { let _ = a.mul_add(b, c).mul_add(a.mul_add(b, c), a.mul_add(b, c)) + c; let _ = 1234.567_f64.mul_add(45.67834_f64, 0.0004_f64); + + let _ = a.mul_add(a, b).sqrt(); + + // Cases where the lint shouldn't be applied + let _ = (a * a + b * b).sqrt(); } diff --git a/tests/ui/floating_point_mul_add.rs b/tests/ui/floating_point_mul_add.rs index 810f929c856..d202385fc8a 100644 --- a/tests/ui/floating_point_mul_add.rs +++ b/tests/ui/floating_point_mul_add.rs @@ -18,4 +18,9 @@ fn main() { let _ = a.mul_add(b, c) * a.mul_add(b, c) + a.mul_add(b, c) + c; let _ = 1234.567_f64 * 45.67834_f64 + 0.0004_f64; + + let _ = (a * a + b).sqrt(); + + // Cases where the lint shouldn't be applied + let _ = (a * a + b * b).sqrt(); } diff --git a/tests/ui/floating_point_mul_add.stderr b/tests/ui/floating_point_mul_add.stderr index 2dfbf562d15..ac8d0c0cae0 100644 --- a/tests/ui/floating_point_mul_add.stderr +++ b/tests/ui/floating_point_mul_add.stderr @@ -54,5 +54,11 @@ error: multiply and add expressions can be calculated more efficiently and accur LL | let _ = 1234.567_f64 * 45.67834_f64 + 0.0004_f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1234.567_f64.mul_add(45.67834_f64, 0.0004_f64)` -error: aborting due to 9 previous errors +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_mul_add.rs:22:13 + | +LL | let _ = (a * a + b).sqrt(); + | ^^^^^^^^^^^ help: consider using: `a.mul_add(a, b)` + +error: aborting due to 10 previous errors diff --git a/tests/ui/floating_point_powi.fixed b/tests/ui/floating_point_powi.fixed index 0ce6f72535d..98766e68aaf 100644 --- a/tests/ui/floating_point_powi.fixed +++ b/tests/ui/floating_point_powi.fixed @@ -1,12 +1,17 @@ // run-rustfix -#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] +#![warn(clippy::imprecise_flops)] fn main() { let one = 1; let x = 3f32; let _ = x * x; let _ = x * x; + + let y = 4f32; + let _ = (x * x + y).sqrt(); + let _ = (x + y * y).sqrt(); // Cases where the lint shouldn't be applied let _ = x.powi(3); let _ = x.powi(one + 1); + let _ = x.hypot(y); } diff --git a/tests/ui/floating_point_powi.rs b/tests/ui/floating_point_powi.rs index c87e836bedd..3c4b636a3d8 100644 --- a/tests/ui/floating_point_powi.rs +++ b/tests/ui/floating_point_powi.rs @@ -1,12 +1,17 @@ // run-rustfix -#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] +#![warn(clippy::imprecise_flops)] fn main() { let one = 1; let x = 3f32; let _ = x.powi(2); let _ = x.powi(1 + 1); + + let y = 4f32; + let _ = (x.powi(2) + y).sqrt(); + let _ = (x + y.powi(2)).sqrt(); // Cases where the lint shouldn't be applied let _ = x.powi(3); let _ = x.powi(one + 1); + let _ = (x.powi(2) + y.powi(2)).sqrt(); } diff --git a/tests/ui/floating_point_powi.stderr b/tests/ui/floating_point_powi.stderr index ae7bbaa4473..f370e24bf05 100644 --- a/tests/ui/floating_point_powi.stderr +++ b/tests/ui/floating_point_powi.stderr @@ -12,5 +12,23 @@ error: square can be computed more accurately LL | let _ = x.powi(1 + 1); | ^^^^^^^^^^^^^ help: consider using: `x * x` -error: aborting due to 2 previous errors +error: square can be computed more accurately + --> $DIR/floating_point_powi.rs:11:14 + | +LL | let _ = (x.powi(2) + y).sqrt(); + | ^^^^^^^^^ help: consider using: `x * x` + +error: square can be computed more accurately + --> $DIR/floating_point_powi.rs:12:18 + | +LL | let _ = (x + y.powi(2)).sqrt(); + | ^^^^^^^^^ help: consider using: `y * y` + +error: hypotenuse can be computed more accurately + --> $DIR/floating_point_powi.rs:16:13 + | +LL | let _ = (x.powi(2) + y.powi(2)).sqrt(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.hypot(y)` + +error: aborting due to 5 previous errors -- cgit 1.4.1-3-g733a5 From 2e8a1be444afc6a9b5137d3e7e4fdcfcb1f89e0d Mon Sep 17 00:00:00 2001 From: robojumper Date: Sun, 5 Jul 2020 22:10:59 +0200 Subject: new lint: match_like_matches_macro --- CHANGELOG.md | 1 + clippy_lints/src/matches.rs | 395 ++++++++++++++++++++- clippy_lints/src/redundant_pattern_matching.rs | 260 -------------- src/lintlist/mod.rs | 9 +- tests/ui/find_map.rs | 1 + tests/ui/find_map.stderr | 2 +- tests/ui/match_expr_like_matches_macro.fixed | 32 ++ tests/ui/match_expr_like_matches_macro.rs | 41 +++ tests/ui/match_expr_like_matches_macro.stderr | 42 +++ tests/ui/neg_cmp_op_on_partial_ord.rs | 2 +- tests/ui/question_mark.fixed | 5 +- tests/ui/question_mark.rs | 5 +- tests/ui/question_mark.stderr | 20 +- tests/ui/redundant_pattern_matching.fixed | 8 +- tests/ui/redundant_pattern_matching.rs | 8 +- tests/ui/redundant_pattern_matching.stderr | 56 +-- .../redundant_pattern_matching_const_result.fixed | 2 +- .../ui/redundant_pattern_matching_const_result.rs | 2 +- 18 files changed, 563 insertions(+), 328 deletions(-) delete mode 100644 clippy_lints/src/redundant_pattern_matching.rs create mode 100644 tests/ui/match_expr_like_matches_macro.fixed create mode 100644 tests/ui/match_expr_like_matches_macro.rs create mode 100644 tests/ui/match_expr_like_matches_macro.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a081bb85fe..6261ca4879a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1513,6 +1513,7 @@ Released 2018-09-13 [`map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or [`match_as_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_as_ref [`match_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_bool +[`match_like_matches_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro [`match_on_vec_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_on_vec_items [`match_overlapping_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_overlapping_arm [`match_ref_pats`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_ref_pats diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index b754a45aa40..34aa2981535 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -13,14 +13,14 @@ use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::CtorKind; use rustc_hir::{ - Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Local, MatchSource, Mutability, Node, Pat, PatKind, - QPath, RangeEnd, + Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Guard, Local, MatchSource, Mutability, Node, Pat, + PatKind, QPath, RangeEnd, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::source_map::Span; +use rustc_span::source_map::{Span, Spanned}; use std::cmp::Ordering; use std::collections::Bound; @@ -409,6 +409,67 @@ declare_clippy_lint! { "a match on a struct that binds all fields but still uses the wildcard pattern" } +declare_clippy_lint! { + /// **What it does:** Lint for redundant pattern matching over `Result` or + /// `Option` + /// + /// **Why is this bad?** It's more concise and clear to just use the proper + /// utility function + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// if let Ok(_) = Ok::(42) {} + /// if let Err(_) = Err::(42) {} + /// if let None = None::<()> {} + /// if let Some(_) = Some(42) {} + /// match Ok::(42) { + /// Ok(_) => true, + /// Err(_) => false, + /// }; + /// ``` + /// + /// The more idiomatic use would be: + /// + /// ```rust + /// if Ok::(42).is_ok() {} + /// if Err::(42).is_err() {} + /// if None::<()>.is_none() {} + /// if Some(42).is_some() {} + /// Ok::(42).is_ok(); + /// ``` + pub REDUNDANT_PATTERN_MATCHING, + style, + "use the proper utility function avoiding an `if let`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for `match` expressions producing a `bool` that could be written using `matches!` + /// + /// **Why is this bad?** Readability and needless complexity. + /// + /// **Known problems:** This can turn an intentionally exhaustive match into a non-exhaustive one. + /// + /// **Example:** + /// ```rust + /// let x = Some(5); + /// + /// // Bad + /// let a = match x { + /// Some(0) => true, + /// _ => false, + /// }; + /// + /// // Good + /// let a = matches!(x, Some(5)); + /// ``` + pub MATCH_LIKE_MATCHES_MACRO, + style, + "a match that could be written with the matches! macro" +} + #[derive(Default)] pub struct Matches { infallible_destructuring_match_linted: bool, @@ -427,7 +488,9 @@ impl_lint_pass!(Matches => [ WILDCARD_IN_OR_PATTERNS, MATCH_SINGLE_BINDING, INFALLIBLE_DESTRUCTURING_MATCH, - REST_PAT_IN_FULLY_BOUND_STRUCTS + REST_PAT_IN_FULLY_BOUND_STRUCTS, + REDUNDANT_PATTERN_MATCHING, + MATCH_LIKE_MATCHES_MACRO ]); impl<'tcx> LateLintPass<'tcx> for Matches { @@ -435,6 +498,11 @@ impl<'tcx> LateLintPass<'tcx> for Matches { if in_external_macro(cx.sess(), expr.span) { return; } + + if !redundant_pattern_match::check(cx, expr) { + check_match_like_matches(cx, expr); + } + if let ExprKind::Match(ref ex, ref arms, MatchSource::Normal) = expr.kind { check_single_match(cx, ex, arms, expr); check_match_bool(cx, ex, arms, expr); @@ -802,13 +870,8 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) // Some simple checks for exhaustive patterns. // There is a room for improvements to detect more cases, // but it can be more expensive to do so. - let is_pattern_exhaustive = |pat: &&Pat<'_>| { - if let PatKind::Wild | PatKind::Binding(.., None) = pat.kind { - true - } else { - false - } - }; + let is_pattern_exhaustive = + |pat: &&Pat<'_>| matches!(pat.kind, PatKind::Wild | PatKind::Binding(.., None)); if patterns.iter().all(is_pattern_exhaustive) { missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id())); } @@ -989,6 +1052,78 @@ fn check_wild_in_or_pats(cx: &LateContext<'_>, arms: &[Arm<'_>]) { } } +/// Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!` +fn check_match_like_matches<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::Match(ex, arms, ref match_source) = &expr.kind { + match match_source { + MatchSource::Normal => find_matches_sugg(cx, ex, arms, expr, false), + MatchSource::IfLetDesugar { .. } => find_matches_sugg(cx, ex, arms, expr, true), + _ => return, + } + } +} + +/// Lint a `match` or desugared `if let` for replacement by `matches!` +fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>, desugared: bool) { + if_chain! { + if arms.len() == 2; + if cx.tables().expr_ty(expr).is_bool(); + if let Some(first) = find_bool_lit(&arms[0].body.kind, desugared); + if let Some(second) = find_bool_lit(&arms[1].body.kind, desugared); + if first != second; + then { + let mut applicability = Applicability::MachineApplicable; + + let pat_and_guard = if let Some(Guard::If(g)) = arms[0].guard { + format!("{} if {}", snippet_with_applicability(cx, arms[0].pat.span, "..", &mut applicability), snippet_with_applicability(cx, g.span, "..", &mut applicability)) + } else { + format!("{}", snippet_with_applicability(cx, arms[0].pat.span, "..", &mut applicability)) + }; + span_lint_and_sugg( + cx, + MATCH_LIKE_MATCHES_MACRO, + expr.span, + &format!("{} expression looks like `matches!` macro", if desugared { "if let .. else" } else { "match" }), + "try this", + format!( + "{}matches!({}, {})", + if first { "" } else { "!" }, + snippet_with_applicability(cx, ex.span, "..", &mut applicability), + pat_and_guard, + ), + applicability, + ) + } + } +} + +/// Extract a `bool` or `{ bool }` +fn find_bool_lit(ex: &ExprKind<'_>, desugared: bool) -> Option { + match ex { + ExprKind::Lit(Spanned { + node: LitKind::Bool(b), .. + }) => Some(*b), + ExprKind::Block( + rustc_hir::Block { + stmts: &[], + expr: Some(exp), + .. + }, + _, + ) if desugared => { + if let ExprKind::Lit(Spanned { + node: LitKind::Bool(b), .. + }) = exp.kind + { + Some(b) + } else { + None + } + }, + _ => None, + } +} + fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], expr: &Expr<'_>) { if in_macro(expr.span) || arms.len() != 1 || is_refutable(cx, arms[0].pat) { return; @@ -1179,10 +1314,7 @@ fn is_unit_expr(expr: &Expr<'_>) -> bool { // Checks if arm has the form `None => None` fn is_none_arm(arm: &Arm<'_>) -> bool { - match arm.pat.kind { - PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE) => true, - _ => false, - } + matches!(arm.pat.kind, PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE)) } // Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`) @@ -1293,6 +1425,239 @@ where None } +mod redundant_pattern_match { + use super::REDUNDANT_PATTERN_MATCHING; + use crate::utils::{in_constant, match_qpath, match_trait_method, paths, snippet, span_lint_and_then}; + use if_chain::if_chain; + use rustc_ast::ast::LitKind; + use rustc_errors::Applicability; + use rustc_hir::{Arm, Expr, ExprKind, HirId, MatchSource, PatKind, QPath}; + use rustc_lint::LateContext; + use rustc_middle::ty; + use rustc_mir::const_eval::is_const_fn; + use rustc_span::source_map::Symbol; + + pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { + if let ExprKind::Match(op, arms, ref match_source) = &expr.kind { + match match_source { + MatchSource::Normal => find_sugg_for_match(cx, expr, op, arms), + MatchSource::IfLetDesugar { .. } => find_sugg_for_if_let(cx, expr, op, arms, "if"), + MatchSource::WhileLetDesugar => find_sugg_for_if_let(cx, expr, op, arms, "while"), + _ => false, + } + } else { + false + } + } + + fn find_sugg_for_if_let<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + op: &Expr<'_>, + arms: &[Arm<'_>], + keyword: &'static str, + ) -> bool { + fn find_suggestion(cx: &LateContext<'_>, hir_id: HirId, path: &QPath<'_>) -> Option<&'static str> { + if match_qpath(path, &paths::RESULT_OK) && can_suggest(cx, hir_id, sym!(result_type), "is_ok") { + return Some("is_ok()"); + } + if match_qpath(path, &paths::RESULT_ERR) && can_suggest(cx, hir_id, sym!(result_type), "is_err") { + return Some("is_err()"); + } + if match_qpath(path, &paths::OPTION_SOME) && can_suggest(cx, hir_id, sym!(option_type), "is_some") { + return Some("is_some()"); + } + if match_qpath(path, &paths::OPTION_NONE) && can_suggest(cx, hir_id, sym!(option_type), "is_none") { + return Some("is_none()"); + } + None + } + + let hir_id = expr.hir_id; + let good_method = match arms[0].pat.kind { + PatKind::TupleStruct(ref path, ref patterns, _) if patterns.len() == 1 => { + if let PatKind::Wild = patterns[0].kind { + find_suggestion(cx, hir_id, path) + } else { + None + } + }, + PatKind::Path(ref path) => find_suggestion(cx, hir_id, path), + _ => None, + }; + let good_method = match good_method { + Some(method) => method, + None => return false, + }; + + // check that `while_let_on_iterator` lint does not trigger + if_chain! { + if keyword == "while"; + if let ExprKind::MethodCall(method_path, _, _, _) = op.kind; + if method_path.ident.name == sym!(next); + if match_trait_method(cx, op, &paths::ITERATOR); + then { + return false; + } + } + + span_lint_and_then( + cx, + REDUNDANT_PATTERN_MATCHING, + arms[0].pat.span, + &format!("redundant pattern matching, consider using `{}`", good_method), + |diag| { + // while let ... = ... { ... } + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + let expr_span = expr.span; + + // while let ... = ... { ... } + // ^^^ + let op_span = op.span.source_callsite(); + + // while let ... = ... { ... } + // ^^^^^^^^^^^^^^^^^^^ + let span = expr_span.until(op_span.shrink_to_hi()); + diag.span_suggestion( + span, + "try this", + format!("{} {}.{}", keyword, snippet(cx, op_span, "_"), good_method), + Applicability::MachineApplicable, // snippet + ); + }, + ); + true + } + + fn find_sugg_for_match<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + op: &Expr<'_>, + arms: &[Arm<'_>], + ) -> bool { + if arms.len() == 2 { + let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind); + + let hir_id = expr.hir_id; + let found_good_method = match node_pair { + ( + PatKind::TupleStruct(ref path_left, ref patterns_left, _), + PatKind::TupleStruct(ref path_right, ref patterns_right, _), + ) if patterns_left.len() == 1 && patterns_right.len() == 1 => { + if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].kind, &patterns_right[0].kind) { + find_good_method_for_match( + arms, + path_left, + path_right, + &paths::RESULT_OK, + &paths::RESULT_ERR, + "is_ok()", + "is_err()", + || can_suggest(cx, hir_id, sym!(result_type), "is_ok"), + || can_suggest(cx, hir_id, sym!(result_type), "is_err"), + ) + } else { + None + } + }, + (PatKind::TupleStruct(ref path_left, ref patterns, _), PatKind::Path(ref path_right)) + | (PatKind::Path(ref path_left), PatKind::TupleStruct(ref path_right, ref patterns, _)) + if patterns.len() == 1 => + { + if let PatKind::Wild = patterns[0].kind { + find_good_method_for_match( + arms, + path_left, + path_right, + &paths::OPTION_SOME, + &paths::OPTION_NONE, + "is_some()", + "is_none()", + || can_suggest(cx, hir_id, sym!(option_type), "is_some"), + || can_suggest(cx, hir_id, sym!(option_type), "is_none"), + ) + } else { + None + } + }, + _ => None, + }; + + if let Some(good_method) = found_good_method { + span_lint_and_then( + cx, + REDUNDANT_PATTERN_MATCHING, + expr.span, + &format!("redundant pattern matching, consider using `{}`", good_method), + |diag| { + let span = expr.span.to(op.span); + diag.span_suggestion( + span, + "try this", + format!("{}.{}", snippet(cx, op.span, "_"), good_method), + Applicability::MaybeIncorrect, // snippet + ); + }, + ); + return true; + } + } + false + } + + #[allow(clippy::too_many_arguments)] + fn find_good_method_for_match<'a>( + arms: &[Arm<'_>], + path_left: &QPath<'_>, + path_right: &QPath<'_>, + expected_left: &[&str], + expected_right: &[&str], + should_be_left: &'a str, + should_be_right: &'a str, + can_suggest_left: impl Fn() -> bool, + can_suggest_right: impl Fn() -> bool, + ) -> Option<&'a str> { + let body_node_pair = if match_qpath(path_left, expected_left) && match_qpath(path_right, expected_right) { + (&(*arms[0].body).kind, &(*arms[1].body).kind) + } else if match_qpath(path_right, expected_left) && match_qpath(path_left, expected_right) { + (&(*arms[1].body).kind, &(*arms[0].body).kind) + } else { + return None; + }; + + match body_node_pair { + (ExprKind::Lit(ref lit_left), ExprKind::Lit(ref lit_right)) => match (&lit_left.node, &lit_right.node) { + (LitKind::Bool(true), LitKind::Bool(false)) if can_suggest_left() => Some(should_be_left), + (LitKind::Bool(false), LitKind::Bool(true)) if can_suggest_right() => Some(should_be_right), + _ => None, + }, + _ => None, + } + } + + fn can_suggest(cx: &LateContext<'_>, hir_id: HirId, diag_item: Symbol, name: &str) -> bool { + if !in_constant(cx, hir_id) { + return true; + } + + // Avoid suggesting calls to non-`const fn`s in const contexts, see #5697. + cx.tcx + .get_diagnostic_item(diag_item) + .and_then(|def_id| { + cx.tcx.inherent_impls(def_id).iter().find_map(|imp| { + cx.tcx + .associated_items(*imp) + .in_definition_order() + .find_map(|item| match item.kind { + ty::AssocKind::Fn if item.ident.name.as_str() == name => Some(item.def_id), + _ => None, + }) + }) + }) + .map_or(false, |def_id| is_const_fn(cx.tcx, def_id)) + } +} + #[test] fn test_overlapping() { use rustc_span::source_map::DUMMY_SP; diff --git a/clippy_lints/src/redundant_pattern_matching.rs b/clippy_lints/src/redundant_pattern_matching.rs deleted file mode 100644 index d8d16efb978..00000000000 --- a/clippy_lints/src/redundant_pattern_matching.rs +++ /dev/null @@ -1,260 +0,0 @@ -use crate::utils::{in_constant, match_qpath, match_trait_method, paths, snippet, span_lint_and_then}; -use if_chain::if_chain; -use rustc_ast::ast::LitKind; -use rustc_errors::Applicability; -use rustc_hir::{Arm, Expr, ExprKind, HirId, MatchSource, PatKind, QPath}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; -use rustc_mir::const_eval::is_const_fn; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Symbol; - -declare_clippy_lint! { - /// **What it does:** Lint for redundant pattern matching over `Result` or - /// `Option` - /// - /// **Why is this bad?** It's more concise and clear to just use the proper - /// utility function - /// - /// **Known problems:** None. - /// - /// **Example:** - /// - /// ```rust - /// if let Ok(_) = Ok::(42) {} - /// if let Err(_) = Err::(42) {} - /// if let None = None::<()> {} - /// if let Some(_) = Some(42) {} - /// match Ok::(42) { - /// Ok(_) => true, - /// Err(_) => false, - /// }; - /// ``` - /// - /// The more idiomatic use would be: - /// - /// ```rust - /// if Ok::(42).is_ok() {} - /// if Err::(42).is_err() {} - /// if None::<()>.is_none() {} - /// if Some(42).is_some() {} - /// Ok::(42).is_ok(); - /// ``` - pub REDUNDANT_PATTERN_MATCHING, - style, - "use the proper utility function avoiding an `if let`" -} - -declare_lint_pass!(RedundantPatternMatching => [REDUNDANT_PATTERN_MATCHING]); - -impl<'tcx> LateLintPass<'tcx> for RedundantPatternMatching { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Match(op, arms, ref match_source) = &expr.kind { - match match_source { - MatchSource::Normal => find_sugg_for_match(cx, expr, op, arms), - MatchSource::IfLetDesugar { .. } => find_sugg_for_if_let(cx, expr, op, arms, "if"), - MatchSource::WhileLetDesugar => find_sugg_for_if_let(cx, expr, op, arms, "while"), - _ => return, - } - } - } -} - -fn find_sugg_for_if_let<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'_>, - op: &Expr<'_>, - arms: &[Arm<'_>], - keyword: &'static str, -) { - fn find_suggestion(cx: &LateContext<'_>, hir_id: HirId, path: &QPath<'_>) -> Option<&'static str> { - if match_qpath(path, &paths::RESULT_OK) && can_suggest(cx, hir_id, sym!(result_type), "is_ok") { - return Some("is_ok()"); - } - if match_qpath(path, &paths::RESULT_ERR) && can_suggest(cx, hir_id, sym!(result_type), "is_err") { - return Some("is_err()"); - } - if match_qpath(path, &paths::OPTION_SOME) && can_suggest(cx, hir_id, sym!(option_type), "is_some") { - return Some("is_some()"); - } - if match_qpath(path, &paths::OPTION_NONE) && can_suggest(cx, hir_id, sym!(option_type), "is_none") { - return Some("is_none()"); - } - None - } - - let hir_id = expr.hir_id; - let good_method = match arms[0].pat.kind { - PatKind::TupleStruct(ref path, ref patterns, _) if patterns.len() == 1 => { - if let PatKind::Wild = patterns[0].kind { - find_suggestion(cx, hir_id, path) - } else { - None - } - }, - PatKind::Path(ref path) => find_suggestion(cx, hir_id, path), - _ => None, - }; - let good_method = match good_method { - Some(method) => method, - None => return, - }; - - // check that `while_let_on_iterator` lint does not trigger - if_chain! { - if keyword == "while"; - if let ExprKind::MethodCall(method_path, _, _, _) = op.kind; - if method_path.ident.name == sym!(next); - if match_trait_method(cx, op, &paths::ITERATOR); - then { - return; - } - } - - span_lint_and_then( - cx, - REDUNDANT_PATTERN_MATCHING, - arms[0].pat.span, - &format!("redundant pattern matching, consider using `{}`", good_method), - |diag| { - // while let ... = ... { ... } - // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - let expr_span = expr.span; - - // while let ... = ... { ... } - // ^^^ - let op_span = op.span.source_callsite(); - - // while let ... = ... { ... } - // ^^^^^^^^^^^^^^^^^^^ - let span = expr_span.until(op_span.shrink_to_hi()); - diag.span_suggestion( - span, - "try this", - format!("{} {}.{}", keyword, snippet(cx, op_span, "_"), good_method), - Applicability::MachineApplicable, // snippet - ); - }, - ); -} - -fn find_sugg_for_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op: &Expr<'_>, arms: &[Arm<'_>]) { - if arms.len() == 2 { - let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind); - - let hir_id = expr.hir_id; - let found_good_method = match node_pair { - ( - PatKind::TupleStruct(ref path_left, ref patterns_left, _), - PatKind::TupleStruct(ref path_right, ref patterns_right, _), - ) if patterns_left.len() == 1 && patterns_right.len() == 1 => { - if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].kind, &patterns_right[0].kind) { - find_good_method_for_match( - arms, - path_left, - path_right, - &paths::RESULT_OK, - &paths::RESULT_ERR, - "is_ok()", - "is_err()", - || can_suggest(cx, hir_id, sym!(result_type), "is_ok"), - || can_suggest(cx, hir_id, sym!(result_type), "is_err"), - ) - } else { - None - } - }, - (PatKind::TupleStruct(ref path_left, ref patterns, _), PatKind::Path(ref path_right)) - | (PatKind::Path(ref path_left), PatKind::TupleStruct(ref path_right, ref patterns, _)) - if patterns.len() == 1 => - { - if let PatKind::Wild = patterns[0].kind { - find_good_method_for_match( - arms, - path_left, - path_right, - &paths::OPTION_SOME, - &paths::OPTION_NONE, - "is_some()", - "is_none()", - || can_suggest(cx, hir_id, sym!(option_type), "is_some"), - || can_suggest(cx, hir_id, sym!(option_type), "is_none"), - ) - } else { - None - } - }, - _ => None, - }; - - if let Some(good_method) = found_good_method { - span_lint_and_then( - cx, - REDUNDANT_PATTERN_MATCHING, - expr.span, - &format!("redundant pattern matching, consider using `{}`", good_method), - |diag| { - let span = expr.span.to(op.span); - diag.span_suggestion( - span, - "try this", - format!("{}.{}", snippet(cx, op.span, "_"), good_method), - Applicability::MaybeIncorrect, // snippet - ); - }, - ); - } - } -} - -#[allow(clippy::too_many_arguments)] -fn find_good_method_for_match<'a>( - arms: &[Arm<'_>], - path_left: &QPath<'_>, - path_right: &QPath<'_>, - expected_left: &[&str], - expected_right: &[&str], - should_be_left: &'a str, - should_be_right: &'a str, - can_suggest_left: impl Fn() -> bool, - can_suggest_right: impl Fn() -> bool, -) -> Option<&'a str> { - let body_node_pair = if match_qpath(path_left, expected_left) && match_qpath(path_right, expected_right) { - (&(*arms[0].body).kind, &(*arms[1].body).kind) - } else if match_qpath(path_right, expected_left) && match_qpath(path_left, expected_right) { - (&(*arms[1].body).kind, &(*arms[0].body).kind) - } else { - return None; - }; - - match body_node_pair { - (ExprKind::Lit(ref lit_left), ExprKind::Lit(ref lit_right)) => match (&lit_left.node, &lit_right.node) { - (LitKind::Bool(true), LitKind::Bool(false)) if can_suggest_left() => Some(should_be_left), - (LitKind::Bool(false), LitKind::Bool(true)) if can_suggest_right() => Some(should_be_right), - _ => None, - }, - _ => None, - } -} - -fn can_suggest(cx: &LateContext<'_>, hir_id: HirId, diag_item: Symbol, name: &str) -> bool { - if !in_constant(cx, hir_id) { - return true; - } - - // Avoid suggesting calls to non-`const fn`s in const contexts, see #5697. - cx.tcx - .get_diagnostic_item(diag_item) - .and_then(|def_id| { - cx.tcx.inherent_impls(def_id).iter().find_map(|imp| { - cx.tcx - .associated_items(*imp) - .in_definition_order() - .find_map(|item| match item.kind { - ty::AssocKind::Fn if item.ident.name.as_str() == name => Some(item.def_id), - _ => None, - }) - }) - }) - .map_or(false, |def_id| is_const_fn(cx.tcx, def_id)) -} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index e681f47f949..888b4755484 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1179,6 +1179,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "matches", }, + Lint { + name: "match_like_matches_macro", + group: "style", + desc: "a match that could be written with the matches! macro", + deprecation: None, + module: "matches", + }, Lint { name: "match_on_vec_items", group: "pedantic", @@ -1856,7 +1863,7 @@ pub static ref ALL_LINTS: Vec = vec![ group: "style", desc: "use the proper utility function avoiding an `if let`", deprecation: None, - module: "redundant_pattern_matching", + module: "matches", }, Lint { name: "redundant_pub_crate", diff --git a/tests/ui/find_map.rs b/tests/ui/find_map.rs index c28cca144ca..88d3b0e7490 100644 --- a/tests/ui/find_map.rs +++ b/tests/ui/find_map.rs @@ -19,6 +19,7 @@ fn main() { let _: Option = a.iter().find(|s| s.parse::().is_ok()).map(|s| s.parse().unwrap()); + #[allow(clippy::match_like_matches_macro)] let _: Option = desserts_of_the_week .iter() .find(|dessert| match *dessert { diff --git a/tests/ui/find_map.stderr b/tests/ui/find_map.stderr index 92f40fe6f1f..f279850fef8 100644 --- a/tests/ui/find_map.stderr +++ b/tests/ui/find_map.stderr @@ -8,7 +8,7 @@ LL | let _: Option = a.iter().find(|s| s.parse::().is_ok()).map(|s = help: this is more succinctly expressed by calling `.find_map(..)` instead error: called `find(p).map(q)` on an `Iterator` - --> $DIR/find_map.rs:22:29 + --> $DIR/find_map.rs:23:29 | LL | let _: Option = desserts_of_the_week | _____________________________^ diff --git a/tests/ui/match_expr_like_matches_macro.fixed b/tests/ui/match_expr_like_matches_macro.fixed new file mode 100644 index 00000000000..2d1ac8836d6 --- /dev/null +++ b/tests/ui/match_expr_like_matches_macro.fixed @@ -0,0 +1,32 @@ +// run-rustfix + +#![warn(clippy::match_like_matches_macro)] + +fn main() { + let x = Some(5); + + // Lint + let _y = matches!(x, Some(0)); + + // Turn into is_none + let _z = x.is_none(); + + // Lint + let _z = !matches!(x, Some(r) if r == 0); + + // Lint + let _zz = matches!(x, Some(5)); + + // No lint + let _a = match x { + Some(_) => false, + None => false, + }; + + // No lint + let _a = match x { + Some(0) => false, + Some(_) => true, + None => false, + }; +} diff --git a/tests/ui/match_expr_like_matches_macro.rs b/tests/ui/match_expr_like_matches_macro.rs new file mode 100644 index 00000000000..376abf9244e --- /dev/null +++ b/tests/ui/match_expr_like_matches_macro.rs @@ -0,0 +1,41 @@ +// run-rustfix + +#![warn(clippy::match_like_matches_macro)] + +fn main() { + let x = Some(5); + + // Lint + let _y = match x { + Some(0) => true, + _ => false, + }; + + // Turn into is_none + let _z = match x { + Some(_) => false, + None => true, + }; + + // Lint + let _z = match x { + Some(r) if r == 0 => false, + _ => true, + }; + + // Lint + let _zz = if let Some(5) = x { true } else { false }; + + // No lint + let _a = match x { + Some(_) => false, + None => false, + }; + + // No lint + let _a = match x { + Some(0) => false, + Some(_) => true, + None => false, + }; +} diff --git a/tests/ui/match_expr_like_matches_macro.stderr b/tests/ui/match_expr_like_matches_macro.stderr new file mode 100644 index 00000000000..0b32af039a8 --- /dev/null +++ b/tests/ui/match_expr_like_matches_macro.stderr @@ -0,0 +1,42 @@ +error: match expression looks like `matches!` macro + --> $DIR/match_expr_like_matches_macro.rs:9:14 + | +LL | let _y = match x { + | ______________^ +LL | | Some(0) => true, +LL | | _ => false, +LL | | }; + | |_____^ help: try this: `matches!(x, Some(0))` + | + = note: `-D clippy::match-like-matches-macro` implied by `-D warnings` + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/match_expr_like_matches_macro.rs:15:14 + | +LL | let _z = match x { + | ______________^ +LL | | Some(_) => false, +LL | | None => true, +LL | | }; + | |_____^ help: try this: `x.is_none()` + | + = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` + +error: match expression looks like `matches!` macro + --> $DIR/match_expr_like_matches_macro.rs:21:14 + | +LL | let _z = match x { + | ______________^ +LL | | Some(r) if r == 0 => false, +LL | | _ => true, +LL | | }; + | |_____^ help: try this: `!matches!(x, Some(r) if r == 0)` + +error: if let .. else expression looks like `matches!` macro + --> $DIR/match_expr_like_matches_macro.rs:27:15 + | +LL | let _zz = if let Some(5) = x { true } else { false }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `matches!(x, Some(5))` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/neg_cmp_op_on_partial_ord.rs b/tests/ui/neg_cmp_op_on_partial_ord.rs index ca70e3b7148..0cee0a28fc7 100644 --- a/tests/ui/neg_cmp_op_on_partial_ord.rs +++ b/tests/ui/neg_cmp_op_on_partial_ord.rs @@ -4,7 +4,7 @@ use std::cmp::Ordering; -#[allow(clippy::unnested_or_patterns)] +#[allow(clippy::unnested_or_patterns, clippy::match_like_matches_macro)] #[warn(clippy::neg_cmp_op_on_partial_ord)] fn main() { let a_value = 1.0; diff --git a/tests/ui/question_mark.fixed b/tests/ui/question_mark.fixed index 11dff94a288..bd13cf1bdfa 100644 --- a/tests/ui/question_mark.fixed +++ b/tests/ui/question_mark.fixed @@ -23,10 +23,7 @@ pub enum SeemsOption { impl SeemsOption { pub fn is_none(&self) -> bool { - match *self { - SeemsOption::None => true, - SeemsOption::Some(_) => false, - } + matches!(*self, SeemsOption::None) } } diff --git a/tests/ui/question_mark.rs b/tests/ui/question_mark.rs index 1d0ee82b4f7..94479e68555 100644 --- a/tests/ui/question_mark.rs +++ b/tests/ui/question_mark.rs @@ -25,10 +25,7 @@ pub enum SeemsOption { impl SeemsOption { pub fn is_none(&self) -> bool { - match *self { - SeemsOption::None => true, - SeemsOption::Some(_) => false, - } + matches!(*self, SeemsOption::None) } } diff --git a/tests/ui/question_mark.stderr b/tests/ui/question_mark.stderr index 502615fb175..be323035d6c 100644 --- a/tests/ui/question_mark.stderr +++ b/tests/ui/question_mark.stderr @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::question-mark` implied by `-D warnings` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:50:9 + --> $DIR/question_mark.rs:47:9 | LL | / if (self.opt).is_none() { LL | | return None; @@ -17,7 +17,7 @@ LL | | } | |_________^ help: replace it with: `(self.opt)?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:54:9 + --> $DIR/question_mark.rs:51:9 | LL | / if self.opt.is_none() { LL | | return None @@ -25,7 +25,7 @@ LL | | } | |_________^ help: replace it with: `self.opt?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:58:17 + --> $DIR/question_mark.rs:55:17 | LL | let _ = if self.opt.is_none() { | _________________^ @@ -36,7 +36,7 @@ LL | | }; | |_________^ help: replace it with: `Some(self.opt?)` error: this if-let-else may be rewritten with the `?` operator - --> $DIR/question_mark.rs:64:17 + --> $DIR/question_mark.rs:61:17 | LL | let _ = if let Some(x) = self.opt { | _________________^ @@ -47,7 +47,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt?` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:81:9 + --> $DIR/question_mark.rs:78:9 | LL | / if self.opt.is_none() { LL | | return None; @@ -55,7 +55,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:89:9 + --> $DIR/question_mark.rs:86:9 | LL | / if self.opt.is_none() { LL | | return None; @@ -63,7 +63,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:97:9 + --> $DIR/question_mark.rs:94:9 | LL | / if self.opt.is_none() { LL | | return None; @@ -71,7 +71,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this if-let-else may be rewritten with the `?` operator - --> $DIR/question_mark.rs:104:26 + --> $DIR/question_mark.rs:101:26 | LL | let v: &Vec<_> = if let Some(ref v) = self.opt { | __________________________^ @@ -82,7 +82,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt.as_ref()?` error: this if-let-else may be rewritten with the `?` operator - --> $DIR/question_mark.rs:114:17 + --> $DIR/question_mark.rs:111:17 | LL | let v = if let Some(v) = self.opt { | _________________^ @@ -93,7 +93,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt?` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:129:5 + --> $DIR/question_mark.rs:126:5 | LL | / if f().is_none() { LL | | return None; diff --git a/tests/ui/redundant_pattern_matching.fixed b/tests/ui/redundant_pattern_matching.fixed index 8b4e2d21331..ce8582d2b22 100644 --- a/tests/ui/redundant_pattern_matching.fixed +++ b/tests/ui/redundant_pattern_matching.fixed @@ -2,7 +2,13 @@ #![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] -#![allow(clippy::unit_arg, unused_must_use, clippy::needless_bool, deprecated)] +#![allow( + clippy::unit_arg, + unused_must_use, + clippy::needless_bool, + clippy::match_like_matches_macro, + deprecated +)] fn main() { if Ok::(42).is_ok() {} diff --git a/tests/ui/redundant_pattern_matching.rs b/tests/ui/redundant_pattern_matching.rs index b0904e41b6f..a3a9aa40e3b 100644 --- a/tests/ui/redundant_pattern_matching.rs +++ b/tests/ui/redundant_pattern_matching.rs @@ -2,7 +2,13 @@ #![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] -#![allow(clippy::unit_arg, unused_must_use, clippy::needless_bool, deprecated)] +#![allow( + clippy::unit_arg, + unused_must_use, + clippy::needless_bool, + clippy::match_like_matches_macro, + deprecated +)] fn main() { if let Ok(_) = Ok::(42) {} diff --git a/tests/ui/redundant_pattern_matching.stderr b/tests/ui/redundant_pattern_matching.stderr index 51a6f4350d3..25d1476062e 100644 --- a/tests/ui/redundant_pattern_matching.stderr +++ b/tests/ui/redundant_pattern_matching.stderr @@ -1,5 +1,5 @@ error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:8:12 + --> $DIR/redundant_pattern_matching.rs:14:12 | LL | if let Ok(_) = Ok::(42) {} | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` @@ -7,67 +7,67 @@ LL | if let Ok(_) = Ok::(42) {} = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:10:12 + --> $DIR/redundant_pattern_matching.rs:16:12 | LL | if let Err(_) = Err::(42) {} | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:12:12 + --> $DIR/redundant_pattern_matching.rs:18: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.rs:14:12 + --> $DIR/redundant_pattern_matching.rs:20: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.rs:16:12 + --> $DIR/redundant_pattern_matching.rs:22: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.rs:22:15 + --> $DIR/redundant_pattern_matching.rs:28: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.rs:24:15 + --> $DIR/redundant_pattern_matching.rs:30: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.rs:26:15 + --> $DIR/redundant_pattern_matching.rs:32:15 | LL | while let None = None::<()> {} | ----------^^^^------------- help: try this: `while None::<()>.is_none()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:28:15 + --> $DIR/redundant_pattern_matching.rs:34:15 | LL | while let Ok(_) = Ok::(10) {} | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:30:15 + --> $DIR/redundant_pattern_matching.rs:36:15 | LL | while let Err(_) = Ok::(10) {} | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:33:15 + --> $DIR/redundant_pattern_matching.rs:39:15 | LL | while let Some(_) = v.pop() { | ----------^^^^^^^---------- help: try this: `while v.pop().is_some()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:49:5 + --> $DIR/redundant_pattern_matching.rs:55:5 | LL | / match Ok::(42) { LL | | Ok(_) => true, @@ -76,7 +76,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:54:5 + --> $DIR/redundant_pattern_matching.rs:60:5 | LL | / match Ok::(42) { LL | | Ok(_) => false, @@ -85,7 +85,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_err()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:59:5 + --> $DIR/redundant_pattern_matching.rs:65:5 | LL | / match Err::(42) { LL | | Ok(_) => false, @@ -94,7 +94,7 @@ LL | | }; | |_____^ help: try this: `Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:64:5 + --> $DIR/redundant_pattern_matching.rs:70:5 | LL | / match Err::(42) { LL | | Ok(_) => true, @@ -103,7 +103,7 @@ LL | | }; | |_____^ help: try this: `Err::(42).is_ok()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:69:5 + --> $DIR/redundant_pattern_matching.rs:75:5 | LL | / match Some(42) { LL | | Some(_) => true, @@ -112,7 +112,7 @@ LL | | }; | |_____^ help: try this: `Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:74:5 + --> $DIR/redundant_pattern_matching.rs:80:5 | LL | / match None::<()> { LL | | Some(_) => false, @@ -121,7 +121,7 @@ LL | | }; | |_____^ help: try this: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:79:13 + --> $DIR/redundant_pattern_matching.rs:85:13 | LL | let _ = match None::<()> { | _____________^ @@ -131,61 +131,61 @@ LL | | }; | |_____^ help: try this: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:84:20 + --> $DIR/redundant_pattern_matching.rs:90:20 | LL | let _ = if let Ok(_) = Ok::(4) { true } else { false }; | -------^^^^^--------------------- help: try this: `if Ok::(4).is_ok()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:87:20 + --> $DIR/redundant_pattern_matching.rs:93:20 | LL | let x = 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.rs:93:20 + --> $DIR/redundant_pattern_matching.rs:99: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.rs:95:19 + --> $DIR/redundant_pattern_matching.rs:101:19 | LL | } else if let None = gen_opt() { | -------^^^^------------ help: try this: `if gen_opt().is_none()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:97:19 + --> $DIR/redundant_pattern_matching.rs:103:19 | LL | } else if let Ok(_) = gen_res() { | -------^^^^^------------ help: try this: `if gen_res().is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:99:19 + --> $DIR/redundant_pattern_matching.rs:105:19 | LL | } else if let Err(_) = gen_res() { | -------^^^^^^------------ help: try this: `if gen_res().is_err()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:132:19 + --> $DIR/redundant_pattern_matching.rs:138:19 | LL | while let Some(_) = r#try!(result_opt()) {} | ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:133:16 + --> $DIR/redundant_pattern_matching.rs:139:16 | LL | if let Some(_) = r#try!(result_opt()) {} | -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:139:12 + --> $DIR/redundant_pattern_matching.rs:145:12 | LL | if let Some(_) = m!() {} | -------^^^^^^^------- help: try this: `if m!().is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:140:15 + --> $DIR/redundant_pattern_matching.rs:146:15 | LL | while let Some(_) = m!() {} | ----------^^^^^^^------- help: try this: `while m!().is_some()` diff --git a/tests/ui/redundant_pattern_matching_const_result.fixed b/tests/ui/redundant_pattern_matching_const_result.fixed index 8a81e92f04a..de3fe00d5fa 100644 --- a/tests/ui/redundant_pattern_matching_const_result.fixed +++ b/tests/ui/redundant_pattern_matching_const_result.fixed @@ -2,7 +2,7 @@ #![feature(const_result)] #![warn(clippy::redundant_pattern_matching)] -#![allow(unused)] +#![allow(clippy::match_like_matches_macro, unused)] // Test that results are linted with the feature enabled. diff --git a/tests/ui/redundant_pattern_matching_const_result.rs b/tests/ui/redundant_pattern_matching_const_result.rs index 1cd515441d1..b77969d53d9 100644 --- a/tests/ui/redundant_pattern_matching_const_result.rs +++ b/tests/ui/redundant_pattern_matching_const_result.rs @@ -2,7 +2,7 @@ #![feature(const_result)] #![warn(clippy::redundant_pattern_matching)] -#![allow(unused)] +#![allow(clippy::match_like_matches_macro, unused)] // Test that results are linted with the feature enabled. -- cgit 1.4.1-3-g733a5 From 0c8afa39ce8b2e7f0f9be7fc33fe3993d9bc3c53 Mon Sep 17 00:00:00 2001 From: Thiago Arrais Date: Mon, 1 Jun 2020 18:32:52 -0300 Subject: Lint x.log(b) / y.log(b) => x.log(y) --- clippy_lints/src/floating_point_arithmetic.rs | 65 ++++++++++++++++++++++----- tests/ui/floating_point_logbase.fixed | 16 +++++++ tests/ui/floating_point_logbase.rs | 16 +++++++ tests/ui/floating_point_logbase.stderr | 28 ++++++++++++ 4 files changed, 115 insertions(+), 10 deletions(-) create mode 100644 tests/ui/floating_point_logbase.fixed create mode 100644 tests/ui/floating_point_logbase.rs create mode 100644 tests/ui/floating_point_logbase.stderr (limited to 'tests') diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 3b2e46a9a85..9f241c2c3a2 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -293,13 +293,13 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { } } -fn check_powi(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) { +fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { // Check argument - if let Some((value, _)) = constant(cx, cx.tables, &args[1]) { + if let Some((value, _)) = constant(cx, cx.tables(), &args[1]) { // TODO: need more specific check. this is too wide. remember also to include tests if let Some(parent) = get_parent_expr(cx, expr) { if let Some(grandparent) = get_parent_expr(cx, parent) { - if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, _, args) = grandparent.kind { + if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, _, args, _) = grandparent.kind { if method_name.as_str() == "sqrt" && detect_hypot(cx, args).is_some() { return; } @@ -328,7 +328,7 @@ fn check_powi(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) { } } -fn detect_hypot(cx: &LateContext<'_, '_>, args: &[Expr<'_>]) -> Option { +fn detect_hypot(cx: &LateContext<'_>, args: &[Expr<'_>]) -> Option { if let ExprKind::Binary( Spanned { node: BinOpKind::Add, .. @@ -350,11 +350,11 @@ fn detect_hypot(cx: &LateContext<'_, '_>, args: &[Expr<'_>]) -> Option { // check if expression of the form x.powi(2) + y.powi(2) if_chain! { - if let ExprKind::MethodCall(PathSegment { ident: lmethod_name, .. }, ref _lspan, ref largs) = add_lhs.kind; - if let ExprKind::MethodCall(PathSegment { ident: rmethod_name, .. }, ref _rspan, ref rargs) = add_rhs.kind; + if let ExprKind::MethodCall(PathSegment { ident: lmethod_name, .. }, ref _lspan, ref largs, _) = add_lhs.kind; + if let ExprKind::MethodCall(PathSegment { ident: rmethod_name, .. }, ref _rspan, ref rargs, _) = add_rhs.kind; if lmethod_name.as_str() == "powi" && rmethod_name.as_str() == "powi"; - if let Some((lvalue, _)) = constant(cx, cx.tables, &largs[1]); - if let Some((rvalue, _)) = constant(cx, cx.tables, &rargs[1]); + if let Some((lvalue, _)) = constant(cx, cx.tables(), &largs[1]); + if let Some((rvalue, _)) = constant(cx, cx.tables(), &rargs[1]); if Int(2) == lvalue && Int(2) == rvalue; then { return Some(format!("{}.hypot({})", Sugg::hir(cx, &largs[0], ".."), Sugg::hir(cx, &rargs[0], ".."))); @@ -365,7 +365,7 @@ fn detect_hypot(cx: &LateContext<'_, '_>, args: &[Expr<'_>]) -> Option { None } -fn check_hypot(cx: &LateContext<'_, '_>, expr: &Expr<'_>, args: &[Expr<'_>]) { +fn check_hypot(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { if let Some(message) = detect_hypot(cx, args) { span_lint_and_sugg( cx, @@ -431,7 +431,7 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) { ) = &expr.kind { if let Some(parent) = get_parent_expr(cx, expr) { - if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, _, args) = parent.kind { + if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, _, args, _) = parent.kind { if method_name.as_str() == "sqrt" && detect_hypot(cx, args).is_some() { return; } @@ -573,6 +573,50 @@ fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) { } } +fn are_same_base_logs(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>) -> bool { + if_chain! { + if let ExprKind::MethodCall(PathSegment { ident: method_name_a, .. }, _, ref args_a, _) = expr_a.kind; + if let ExprKind::MethodCall(PathSegment { ident: method_name_b, .. }, _, ref args_b, _) = expr_b.kind; + then { + return method_name_a.as_str() == method_name_b.as_str() && + args_a.len() == args_b.len() && + ( + ["ln", "log2", "log10"].contains(&&*method_name_a.as_str()) || + method_name_a.as_str() == "log" && args_a.len() == 2 && are_exprs_equal(cx, &args_a[1], &args_b[1]) + ); + } + } + + false +} + +fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) { + // check if expression of the form x.logN() / y.logN() + if_chain! { + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Div, .. + }, + lhs, + rhs, + ) = &expr.kind; + if are_same_base_logs(cx, lhs, rhs); + if let ExprKind::MethodCall(_, _, ref largs, _) = lhs.kind; + if let ExprKind::MethodCall(_, _, ref rargs, _) = rhs.kind; + then { + span_lint_and_sugg( + cx, + SUBOPTIMAL_FLOPS, + expr.span, + "division of logarithms can be calculated more efficiently and accurately", + "consider using", + format!("{}.log({})", Sugg::hir(cx, &largs[0], ".."), Sugg::hir(cx, &rargs[0], ".."),), + Applicability::MachineApplicable, + ); + } + } +} + impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::MethodCall(ref path, _, args, _) = &expr.kind { @@ -592,6 +636,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic { check_expm1(cx, expr); check_mul_add(cx, expr); check_custom_abs(cx, expr); + check_log_division(cx, expr); } } } diff --git a/tests/ui/floating_point_logbase.fixed b/tests/ui/floating_point_logbase.fixed new file mode 100644 index 00000000000..13962a272d4 --- /dev/null +++ b/tests/ui/floating_point_logbase.fixed @@ -0,0 +1,16 @@ +// run-rustfix +#![warn(clippy::suboptimal_flops)] + +fn main() { + let x = 3f32; + let y = 5f32; + let _ = x.log(y); + let _ = x.log(y); + let _ = x.log(y); + let _ = x.log(y); + // Cases where the lint shouldn't be applied + let _ = x.ln() / y.powf(3.2); + let _ = x.powf(3.2) / y.powf(3.2); + let _ = x.powf(3.2) / y.ln(); + let _ = x.log(5f32) / y.log(7f32); +} diff --git a/tests/ui/floating_point_logbase.rs b/tests/ui/floating_point_logbase.rs new file mode 100644 index 00000000000..26bc20d5370 --- /dev/null +++ b/tests/ui/floating_point_logbase.rs @@ -0,0 +1,16 @@ +// run-rustfix +#![warn(clippy::suboptimal_flops)] + +fn main() { + let x = 3f32; + let y = 5f32; + let _ = x.ln() / y.ln(); + let _ = x.log2() / y.log2(); + let _ = x.log10() / y.log10(); + let _ = x.log(5f32) / y.log(5f32); + // Cases where the lint shouldn't be applied + let _ = x.ln() / y.powf(3.2); + let _ = x.powf(3.2) / y.powf(3.2); + let _ = x.powf(3.2) / y.ln(); + let _ = x.log(5f32) / y.log(7f32); +} diff --git a/tests/ui/floating_point_logbase.stderr b/tests/ui/floating_point_logbase.stderr new file mode 100644 index 00000000000..fa956b9139e --- /dev/null +++ b/tests/ui/floating_point_logbase.stderr @@ -0,0 +1,28 @@ +error: division of logarithms can be calculated more efficiently and accurately + --> $DIR/floating_point_logbase.rs:7:13 + | +LL | let _ = x.ln() / y.ln(); + | ^^^^^^^^^^^^^^^ help: consider using: `x.log(y)` + | + = note: `-D clippy::suboptimal-flops` implied by `-D warnings` + +error: division of logarithms can be calculated more efficiently and accurately + --> $DIR/floating_point_logbase.rs:8:13 + | +LL | let _ = x.log2() / y.log2(); + | ^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)` + +error: division of logarithms can be calculated more efficiently and accurately + --> $DIR/floating_point_logbase.rs:9:13 + | +LL | let _ = x.log10() / y.log10(); + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)` + +error: division of logarithms can be calculated more efficiently and accurately + --> $DIR/floating_point_logbase.rs:10:13 + | +LL | let _ = x.log(5f32) / y.log(5f32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)` + +error: aborting due to 4 previous errors + -- cgit 1.4.1-3-g733a5 From 076ec872ce122403f3c75f20c773c64b194a5891 Mon Sep 17 00:00:00 2001 From: Thiago Arrais Date: Wed, 10 Jun 2020 13:52:00 -0300 Subject: Lint for to_radians and to_degrees --- clippy_lints/src/floating_point_arithmetic.rs | 64 ++++++++++++++++++++++++++- tests/ui/floating_point_rad.fixed | 13 ++++++ tests/ui/floating_point_rad.rs | 13 ++++++ tests/ui/floating_point_rad.stderr | 16 +++++++ 4 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 tests/ui/floating_point_rad.fixed create mode 100644 tests/ui/floating_point_rad.rs create mode 100644 tests/ui/floating_point_rad.stderr (limited to 'tests') diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 9f241c2c3a2..d88e47f396c 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -350,8 +350,18 @@ fn detect_hypot(cx: &LateContext<'_>, args: &[Expr<'_>]) -> Option { // check if expression of the form x.powi(2) + y.powi(2) if_chain! { - if let ExprKind::MethodCall(PathSegment { ident: lmethod_name, .. }, ref _lspan, ref largs, _) = add_lhs.kind; - if let ExprKind::MethodCall(PathSegment { ident: rmethod_name, .. }, ref _rspan, ref rargs, _) = add_rhs.kind; + if let ExprKind::MethodCall( + PathSegment { ident: lmethod_name, .. }, + ref _lspan, + ref largs, + _ + ) = add_lhs.kind; + if let ExprKind::MethodCall( + PathSegment { ident: rmethod_name, .. }, + ref _rspan, + ref rargs, + _ + ) = add_rhs.kind; if lmethod_name.as_str() == "powi" && rmethod_name.as_str() == "powi"; if let Some((lvalue, _)) = constant(cx, cx.tables(), &largs[1]); if let Some((rvalue, _)) = constant(cx, cx.tables(), &rargs[1]); @@ -617,6 +627,55 @@ fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) { } } +fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) { + if_chain! { + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Div, .. + }, + div_lhs, + div_rhs, + ) = &expr.kind; + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Mul, .. + }, + mul_lhs, + mul_rhs, + ) = &div_lhs.kind; + if let Some((rvalue, _)) = constant(cx, cx.tables(), div_rhs); + if let Some((lvalue, _)) = constant(cx, cx.tables(), mul_rhs); + then { + if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue) && + (F32(180_f32) == lvalue || F64(180_f64) == lvalue) + { + span_lint_and_sugg( + cx, + IMPRECISE_FLOPS, + expr.span, + "conversion to degrees can be done more accurately", + "consider using", + format!("{}.to_degrees()", Sugg::hir(cx, &mul_lhs, "..")), + Applicability::MachineApplicable, + ); + } else if + (F32(180_f32) == rvalue || F64(180_f64) == rvalue) && + (F32(f32_consts::PI) == lvalue || F64(f64_consts::PI) == lvalue) + { + span_lint_and_sugg( + cx, + IMPRECISE_FLOPS, + expr.span, + "conversion to radians can be done more accurately", + "consider using", + format!("{}.to_radians()", Sugg::hir(cx, &mul_lhs, "..")), + Applicability::MachineApplicable, + ); + } + } + } +} + impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::MethodCall(ref path, _, args, _) = &expr.kind { @@ -637,6 +696,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic { check_mul_add(cx, expr); check_custom_abs(cx, expr); check_log_division(cx, expr); + check_radians(cx, expr); } } } diff --git a/tests/ui/floating_point_rad.fixed b/tests/ui/floating_point_rad.fixed new file mode 100644 index 00000000000..64461417a6a --- /dev/null +++ b/tests/ui/floating_point_rad.fixed @@ -0,0 +1,13 @@ +// run-rustfix +#![warn(clippy::imprecise_flops)] + +fn main() { + let x = 3f32; + let _ = x.to_degrees(); + let _ = x.to_radians(); + // Cases where the lint shouldn't be applied + let _ = x * 90f32 / std::f32::consts::PI; + let _ = x * std::f32::consts::PI / 90f32; + let _ = x * 180f32 / std::f32::consts::E; + let _ = x * std::f32::consts::E / 180f32; +} diff --git a/tests/ui/floating_point_rad.rs b/tests/ui/floating_point_rad.rs new file mode 100644 index 00000000000..9046f184b3e --- /dev/null +++ b/tests/ui/floating_point_rad.rs @@ -0,0 +1,13 @@ +// run-rustfix +#![warn(clippy::imprecise_flops)] + +fn main() { + let x = 3f32; + let _ = x * 180f32 / std::f32::consts::PI; + let _ = x * std::f32::consts::PI / 180f32; + // Cases where the lint shouldn't be applied + let _ = x * 90f32 / std::f32::consts::PI; + let _ = x * std::f32::consts::PI / 90f32; + let _ = x * 180f32 / std::f32::consts::E; + let _ = x * std::f32::consts::E / 180f32; +} diff --git a/tests/ui/floating_point_rad.stderr b/tests/ui/floating_point_rad.stderr new file mode 100644 index 00000000000..81e81821513 --- /dev/null +++ b/tests/ui/floating_point_rad.stderr @@ -0,0 +1,16 @@ +error: conversion to degrees can be done more accurately + --> $DIR/floating_point_rad.rs:6:13 + | +LL | let _ = x * 180f32 / std::f32::consts::PI; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.to_degrees()` + | + = note: `-D clippy::imprecise-flops` implied by `-D warnings` + +error: conversion to radians can be done more accurately + --> $DIR/floating_point_rad.rs:7:13 + | +LL | let _ = x * std::f32::consts::PI / 180f32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.to_radians()` + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From f5596826fa59035e6c57d8968df0d61f69c2537b Mon Sep 17 00:00:00 2001 From: Thiago Arrais Date: Mon, 15 Jun 2020 13:55:12 -0300 Subject: Better copy for lint message Since x.log(y) is actually implemented as x.ln() / y.ln() --- clippy_lints/src/floating_point_arithmetic.rs | 2 +- tests/ui/floating_point_logbase.stderr | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index d88e47f396c..b1e258f4b16 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -618,7 +618,7 @@ fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) { cx, SUBOPTIMAL_FLOPS, expr.span, - "division of logarithms can be calculated more efficiently and accurately", + "log base can be expressed more clearly", "consider using", format!("{}.log({})", Sugg::hir(cx, &largs[0], ".."), Sugg::hir(cx, &rargs[0], ".."),), Applicability::MachineApplicable, diff --git a/tests/ui/floating_point_logbase.stderr b/tests/ui/floating_point_logbase.stderr index fa956b9139e..78354c2f62d 100644 --- a/tests/ui/floating_point_logbase.stderr +++ b/tests/ui/floating_point_logbase.stderr @@ -1,4 +1,4 @@ -error: division of logarithms can be calculated more efficiently and accurately +error: log base can be expressed more clearly --> $DIR/floating_point_logbase.rs:7:13 | LL | let _ = x.ln() / y.ln(); @@ -6,19 +6,19 @@ LL | let _ = x.ln() / y.ln(); | = note: `-D clippy::suboptimal-flops` implied by `-D warnings` -error: division of logarithms can be calculated more efficiently and accurately +error: log base can be expressed more clearly --> $DIR/floating_point_logbase.rs:8:13 | LL | let _ = x.log2() / y.log2(); | ^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)` -error: division of logarithms can be calculated more efficiently and accurately +error: log base can be expressed more clearly --> $DIR/floating_point_logbase.rs:9:13 | LL | let _ = x.log10() / y.log10(); | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)` -error: division of logarithms can be calculated more efficiently and accurately +error: log base can be expressed more clearly --> $DIR/floating_point_logbase.rs:10:13 | LL | let _ = x.log(5f32) / y.log(5f32); -- cgit 1.4.1-3-g733a5 From db7bc6b3bd0e58c8fbf7507713a4214299f39c36 Mon Sep 17 00:00:00 2001 From: Thiago Arrais Date: Mon, 15 Jun 2020 13:59:44 -0300 Subject: Place radian lints under suboptimal_flops --- clippy_lints/src/floating_point_arithmetic.rs | 4 ++-- tests/ui/floating_point_rad.fixed | 2 +- tests/ui/floating_point_rad.rs | 2 +- tests/ui/floating_point_rad.stderr | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index b1e258f4b16..beb0b234408 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -651,7 +651,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) { { span_lint_and_sugg( cx, - IMPRECISE_FLOPS, + SUBOPTIMAL_FLOPS, expr.span, "conversion to degrees can be done more accurately", "consider using", @@ -664,7 +664,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) { { span_lint_and_sugg( cx, - IMPRECISE_FLOPS, + SUBOPTIMAL_FLOPS, expr.span, "conversion to radians can be done more accurately", "consider using", diff --git a/tests/ui/floating_point_rad.fixed b/tests/ui/floating_point_rad.fixed index 64461417a6a..92480c5db8b 100644 --- a/tests/ui/floating_point_rad.fixed +++ b/tests/ui/floating_point_rad.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![warn(clippy::imprecise_flops)] +#![warn(clippy::suboptimal_flops)] fn main() { let x = 3f32; diff --git a/tests/ui/floating_point_rad.rs b/tests/ui/floating_point_rad.rs index 9046f184b3e..062e7c3fdc1 100644 --- a/tests/ui/floating_point_rad.rs +++ b/tests/ui/floating_point_rad.rs @@ -1,5 +1,5 @@ // run-rustfix -#![warn(clippy::imprecise_flops)] +#![warn(clippy::suboptimal_flops)] fn main() { let x = 3f32; diff --git a/tests/ui/floating_point_rad.stderr b/tests/ui/floating_point_rad.stderr index 81e81821513..a6ffdca64ee 100644 --- a/tests/ui/floating_point_rad.stderr +++ b/tests/ui/floating_point_rad.stderr @@ -4,7 +4,7 @@ error: conversion to degrees can be done more accurately LL | let _ = x * 180f32 / std::f32::consts::PI; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.to_degrees()` | - = note: `-D clippy::imprecise-flops` implied by `-D warnings` + = note: `-D clippy::suboptimal-flops` implied by `-D warnings` error: conversion to radians can be done more accurately --> $DIR/floating_point_rad.rs:7:13 -- cgit 1.4.1-3-g733a5 From 6dc066fdb9103122cd918b1b38e26fa6edbc7e2e Mon Sep 17 00:00:00 2001 From: Thiago Arrais Date: Wed, 17 Jun 2020 13:43:11 -0300 Subject: Includes TODO comment for hypot lint --- tests/ui/floating_point_hypot.fixed | 5 +++-- tests/ui/floating_point_hypot.rs | 3 ++- tests/ui/floating_point_hypot.stderr | 10 +--------- 3 files changed, 6 insertions(+), 12 deletions(-) (limited to 'tests') diff --git a/tests/ui/floating_point_hypot.fixed b/tests/ui/floating_point_hypot.fixed index f90695bc3fe..bbe411b3f48 100644 --- a/tests/ui/floating_point_hypot.fixed +++ b/tests/ui/floating_point_hypot.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] +#![warn(clippy::imprecise_flops)] fn main() { let x = 3f32; @@ -8,6 +8,7 @@ fn main() { let _ = (x + 1f32).hypot(y); let _ = x.hypot(y); // Cases where the lint shouldn't be applied + // TODO: linting this adds some complexity, but could be done let _ = x.mul_add(x, y * y).sqrt(); - let _ = x.mul_add(4f32, y * y).sqrt(); + let _ = (x * 4f32 + y * y).sqrt(); } diff --git a/tests/ui/floating_point_hypot.rs b/tests/ui/floating_point_hypot.rs index e7b048e262f..586fd170ea1 100644 --- a/tests/ui/floating_point_hypot.rs +++ b/tests/ui/floating_point_hypot.rs @@ -1,5 +1,5 @@ // run-rustfix -#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)] +#![warn(clippy::imprecise_flops)] fn main() { let x = 3f32; @@ -8,6 +8,7 @@ fn main() { let _ = ((x + 1f32) * (x + 1f32) + y * y).sqrt(); let _ = (x.powi(2) + y.powi(2)).sqrt(); // Cases where the lint shouldn't be applied + // TODO: linting this adds some complexity, but could be done let _ = x.mul_add(x, y * y).sqrt(); let _ = (x * 4f32 + y * y).sqrt(); } diff --git a/tests/ui/floating_point_hypot.stderr b/tests/ui/floating_point_hypot.stderr index fe1dfc7a451..42069d9ee9e 100644 --- a/tests/ui/floating_point_hypot.stderr +++ b/tests/ui/floating_point_hypot.stderr @@ -18,13 +18,5 @@ error: hypotenuse can be computed more accurately LL | let _ = (x.powi(2) + y.powi(2)).sqrt(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.hypot(y)` -error: multiply and add expressions can be calculated more efficiently and accurately - --> $DIR/floating_point_hypot.rs:12:13 - | -LL | let _ = (x * 4f32 + y * y).sqrt(); - | ^^^^^^^^^^^^^^^^^^ help: consider using: `x.mul_add(4f32, y * y)` - | - = note: `-D clippy::suboptimal-flops` implied by `-D warnings` - -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors -- cgit 1.4.1-3-g733a5 From 6be9491eace540d341f3b8dbf775a10e25f6431a Mon Sep 17 00:00:00 2001 From: Thiago Arrais Date: Mon, 22 Jun 2020 14:16:27 -0300 Subject: Reclassify powi(2) lint under suboptimal_flops --- clippy_lints/src/floating_point_arithmetic.rs | 69 +++++++++++++++++---------- tests/ui/floating_point_powi.fixed | 10 ++-- tests/ui/floating_point_powi.rs | 4 +- tests/ui/floating_point_powi.stderr | 38 ++++++++------- 4 files changed, 75 insertions(+), 46 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index beb0b234408..11bd0ae23aa 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -294,37 +294,56 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { } fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { - // Check argument if let Some((value, _)) = constant(cx, cx.tables(), &args[1]) { - // TODO: need more specific check. this is too wide. remember also to include tests - if let Some(parent) = get_parent_expr(cx, expr) { - if let Some(grandparent) = get_parent_expr(cx, parent) { - if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, _, args, _) = grandparent.kind { - if method_name.as_str() == "sqrt" && detect_hypot(cx, args).is_some() { - return; + if value == Int(2) { + if let Some(parent) = get_parent_expr(cx, expr) { + if let Some(grandparent) = get_parent_expr(cx, parent) { + if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, _, args, _) = grandparent.kind { + if method_name.as_str() == "sqrt" && detect_hypot(cx, args).is_some() { + return; + } } } + + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Add, .. + }, + ref lhs, + ref rhs, + ) = parent.kind + { + let other_addend = if lhs.hir_id == expr.hir_id { rhs } else { lhs }; + + span_lint_and_sugg( + cx, + SUBOPTIMAL_FLOPS, + parent.span, + "square can be computed more efficiently", + "consider using", + format!( + "{}.mul_add({}, {})", + Sugg::hir(cx, &args[0], ".."), + Sugg::hir(cx, &args[0], ".."), + Sugg::hir(cx, &other_addend, ".."), + ), + Applicability::MachineApplicable, + ); + + return; + } } - } - let (lint, help, suggestion) = match value { - Int(2) => ( - IMPRECISE_FLOPS, - "square can be computed more accurately", + span_lint_and_sugg( + cx, + SUBOPTIMAL_FLOPS, + expr.span, + "square can be computed more efficiently", + "consider using", format!("{} * {}", Sugg::hir(cx, &args[0], ".."), Sugg::hir(cx, &args[0], "..")), - ), - _ => return, - }; - - span_lint_and_sugg( - cx, - lint, - expr.span, - help, - "consider using", - suggestion, - Applicability::MachineApplicable, - ); + Applicability::MachineApplicable, + ); + } } } diff --git a/tests/ui/floating_point_powi.fixed b/tests/ui/floating_point_powi.fixed index 98766e68aaf..56762400593 100644 --- a/tests/ui/floating_point_powi.fixed +++ b/tests/ui/floating_point_powi.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![warn(clippy::imprecise_flops)] +#![warn(clippy::suboptimal_flops)] fn main() { let one = 1; @@ -8,10 +8,12 @@ fn main() { let _ = x * x; let y = 4f32; - let _ = (x * x + y).sqrt(); - let _ = (x + y * y).sqrt(); + let _ = x.mul_add(x, y); + let _ = y.mul_add(y, x); + let _ = x.mul_add(x, y).sqrt(); + let _ = y.mul_add(y, x).sqrt(); // Cases where the lint shouldn't be applied let _ = x.powi(3); let _ = x.powi(one + 1); - let _ = x.hypot(y); + let _ = (x.powi(2) + y.powi(2)).sqrt(); } diff --git a/tests/ui/floating_point_powi.rs b/tests/ui/floating_point_powi.rs index 3c4b636a3d8..1f800e4628d 100644 --- a/tests/ui/floating_point_powi.rs +++ b/tests/ui/floating_point_powi.rs @@ -1,5 +1,5 @@ // run-rustfix -#![warn(clippy::imprecise_flops)] +#![warn(clippy::suboptimal_flops)] fn main() { let one = 1; @@ -8,6 +8,8 @@ fn main() { let _ = x.powi(1 + 1); let y = 4f32; + let _ = x.powi(2) + y; + let _ = x + y.powi(2); let _ = (x.powi(2) + y).sqrt(); let _ = (x + y.powi(2)).sqrt(); // Cases where the lint shouldn't be applied diff --git a/tests/ui/floating_point_powi.stderr b/tests/ui/floating_point_powi.stderr index f370e24bf05..d5a5f1bcca1 100644 --- a/tests/ui/floating_point_powi.stderr +++ b/tests/ui/floating_point_powi.stderr @@ -1,34 +1,40 @@ -error: square can be computed more accurately +error: square can be computed more efficiently --> $DIR/floating_point_powi.rs:7:13 | LL | let _ = x.powi(2); | ^^^^^^^^^ help: consider using: `x * x` | - = note: `-D clippy::imprecise-flops` implied by `-D warnings` + = note: `-D clippy::suboptimal-flops` implied by `-D warnings` -error: square can be computed more accurately +error: square can be computed more efficiently --> $DIR/floating_point_powi.rs:8:13 | LL | let _ = x.powi(1 + 1); | ^^^^^^^^^^^^^ help: consider using: `x * x` -error: square can be computed more accurately - --> $DIR/floating_point_powi.rs:11:14 +error: square can be computed more efficiently + --> $DIR/floating_point_powi.rs:11:13 | -LL | let _ = (x.powi(2) + y).sqrt(); - | ^^^^^^^^^ help: consider using: `x * x` +LL | let _ = x.powi(2) + y; + | ^^^^^^^^^^^^^ help: consider using: `x.mul_add(x, y)` -error: square can be computed more accurately - --> $DIR/floating_point_powi.rs:12:18 +error: square can be computed more efficiently + --> $DIR/floating_point_powi.rs:12:13 | -LL | let _ = (x + y.powi(2)).sqrt(); - | ^^^^^^^^^ help: consider using: `y * y` +LL | let _ = x + y.powi(2); + | ^^^^^^^^^^^^^ help: consider using: `y.mul_add(y, x)` + +error: square can be computed more efficiently + --> $DIR/floating_point_powi.rs:13:13 + | +LL | let _ = (x.powi(2) + y).sqrt(); + | ^^^^^^^^^^^^^^^ help: consider using: `x.mul_add(x, y)` -error: hypotenuse can be computed more accurately - --> $DIR/floating_point_powi.rs:16:13 +error: square can be computed more efficiently + --> $DIR/floating_point_powi.rs:14:13 | -LL | let _ = (x.powi(2) + y.powi(2)).sqrt(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.hypot(y)` +LL | let _ = (x + y.powi(2)).sqrt(); + | ^^^^^^^^^^^^^^^ help: consider using: `y.mul_add(y, x)` -error: aborting due to 5 previous errors +error: aborting due to 6 previous errors -- cgit 1.4.1-3-g733a5 From 5307cb5614e7498f069bb634ab293e176e63c67f Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Sat, 4 Jul 2020 22:50:03 +0900 Subject: Add a lint for `.repeat(1)` fix #3028. --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 ++ clippy_lints/src/repeat_once.rs | 126 ++++++++++++++++++++++++++++++++++++++++ src/lintlist/mod.rs | 7 +++ tests/ui/repeat_once.fixed | 16 +++++ tests/ui/repeat_once.rs | 16 +++++ tests/ui/repeat_once.stderr | 40 +++++++++++++ 7 files changed, 211 insertions(+) create mode 100644 clippy_lints/src/repeat_once.rs create mode 100644 tests/ui/repeat_once.fixed create mode 100644 tests/ui/repeat_once.rs create mode 100644 tests/ui/repeat_once.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a081bb85fe..c5bbaac0df6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1616,6 +1616,7 @@ Released 2018-09-13 [`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes [`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref [`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro +[`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once [`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts [`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs [`result_map_or_into_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_or_into_option diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index fe34e4390d6..0f362dbf86b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -282,6 +282,7 @@ mod redundant_pub_crate; mod redundant_static_lifetimes; mod reference; mod regex; +mod repeat_once; mod returns; mod serde_api; mod shadow; @@ -764,6 +765,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &reference::REF_IN_DEREF, ®ex::INVALID_REGEX, ®ex::TRIVIAL_REGEX, + &repeat_once::REPEAT_ONCE, &returns::NEEDLESS_RETURN, &returns::UNUSED_UNIT, &serde_api::SERDE_API_MISUSE, @@ -1071,6 +1073,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box macro_use::MacroUseImports::default()); store.register_late_pass(|| box map_identity::MapIdentity); store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch); + store.register_late_pass(|| box repeat_once::RepeatOnce); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1393,6 +1396,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&reference::REF_IN_DEREF), LintId::of(®ex::INVALID_REGEX), LintId::of(®ex::TRIVIAL_REGEX), + LintId::of(&repeat_once::REPEAT_ONCE), LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&returns::UNUSED_UNIT), LintId::of(&serde_api::SERDE_API_MISUSE), @@ -1602,6 +1606,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ranges::RANGE_ZIP_WITH_LEN), LintId::of(&reference::DEREF_ADDROF), LintId::of(&reference::REF_IN_DEREF), + LintId::of(&repeat_once::REPEAT_ONCE), LintId::of(&swap::MANUAL_SWAP), LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT), LintId::of(&transmute::CROSSPOINTER_TRANSMUTE), diff --git a/clippy_lints/src/repeat_once.rs b/clippy_lints/src/repeat_once.rs new file mode 100644 index 00000000000..af3c948ec82 --- /dev/null +++ b/clippy_lints/src/repeat_once.rs @@ -0,0 +1,126 @@ +use crate::consts::{miri_to_const, Constant}; +use crate::utils::{in_macro, is_type_diagnostic_item, snippet, span_lint_and_sugg, walk_ptrs_ty}; +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, Ty}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `.repeat(1)` and suggest the following method for each types. + /// - `.to_string()` for `str` + /// - `.clone()` for `String` + /// - `.to_vec()` for `slice` + /// + /// **Why is this bad?** For example, `String.repeat(1)` is equivalent to `.clone()`. If cloning the string is the intention behind thi, `clone()` should be used. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// fn main() { + /// let x = String::from("hello world").repeat(1); + /// } + /// ``` + /// Use instead: + /// ```rust + /// fn main() { + /// let x = String::from("hello world").clone(); + /// } + /// ``` + pub REPEAT_ONCE, + complexity, + "using `.repeat(1)` instead of `String.clone()`, `str.to_string()` or `slice.to_vec()` " +} + +declare_lint_pass!(RepeatOnce => [REPEAT_ONCE]); + +impl<'tcx> LateLintPass<'tcx> for RepeatOnce { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) { + if_chain! { + if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind; + if path.ident.name == sym!(repeat); + if is_once(cx, &args[1]) && !in_macro(args[0].span); + then { + let ty = walk_ptrs_ty(cx.tables().expr_ty(&args[0])); + if is_str(ty){ + span_lint_and_sugg( + cx, + REPEAT_ONCE, + expr.span, + "calling `repeat(1)` on str", + "consider using `.to_string()` instead", + format!("{}.to_string()", snippet(cx, args[0].span, r#""...""#)), + Applicability::MachineApplicable, + ); + } else if is_slice(ty) { + span_lint_and_sugg( + cx, + REPEAT_ONCE, + expr.span, + "calling `repeat(1)` on slice", + "consider using `.to_vec()` instead", + format!("{}.to_vec()", snippet(cx, args[0].span, r#""...""#)), + Applicability::MachineApplicable, + ); + } else if is_type_diagnostic_item(cx, ty, sym!(string_type)) { + span_lint_and_sugg( + cx, + REPEAT_ONCE, + expr.span, + "calling `repeat(1)` on a string literal", + "consider using `.clone()` instead", + format!("{}.clone()", snippet(cx, args[0].span, r#""...""#)), + Applicability::MachineApplicable, + ); + } + } + } + } +} + +fn is_once<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> bool { + match expr.kind { + ExprKind::Lit(ref lit) => { + if let LitKind::Int(ref lit_content, _) = lit.node { + *lit_content == 1 + } else { + false + } + }, + ExprKind::Path(rustc_hir::QPath::Resolved(None, path)) => { + if let Res::Def(DefKind::Const, def_id) = path.res { + let ty = cx.tcx.type_of(def_id); + let con = cx + .tcx + .const_eval_poly(def_id) + .ok() + .map(|val| rustc_middle::ty::Const::from_value(cx.tcx, val, ty)) + .unwrap(); + let con = miri_to_const(con); + con == Some(Constant::Int(1)) + } else { + false + } + }, + _ => false, + } +} + +fn is_str(ty: Ty<'_>) -> bool { + match ty.kind { + ty::Str => true, + _ => false, + } +} + +fn is_slice(ty: Ty<'_>) -> bool { + match ty.kind { + ty::Slice(..) | ty::Array(..) => true, + _ => false, + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index e681f47f949..078924d3f9b 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1879,6 +1879,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "reference", }, + Lint { + name: "repeat_once", + group: "complexity", + desc: "using `.repeat(1)` instead of `String.clone()`, `str.to_string()` or `slice.to_vec()` ", + deprecation: None, + module: "repeat_once", + }, Lint { name: "rest_pat_in_fully_bound_structs", group: "restriction", diff --git a/tests/ui/repeat_once.fixed b/tests/ui/repeat_once.fixed new file mode 100644 index 00000000000..a637c22fbcd --- /dev/null +++ b/tests/ui/repeat_once.fixed @@ -0,0 +1,16 @@ +// run-rustfix +#![warn(clippy::repeat_once)] +#[allow(unused, clippy::many_single_char_names, clippy::redundant_clone)] +fn main() { + const N: usize = 1; + let s = "str"; + let string = "String".to_string(); + let slice = [1; 5]; + + let a = [1; 5].to_vec(); + let b = slice.to_vec(); + let c = "hello".to_string(); + let d = "hi".to_string(); + let e = s.to_string(); + let f = string.clone(); +} diff --git a/tests/ui/repeat_once.rs b/tests/ui/repeat_once.rs new file mode 100644 index 00000000000..d99ca1b5b55 --- /dev/null +++ b/tests/ui/repeat_once.rs @@ -0,0 +1,16 @@ +// run-rustfix +#![warn(clippy::repeat_once)] +#[allow(unused, clippy::many_single_char_names, clippy::redundant_clone)] +fn main() { + const N: usize = 1; + let s = "str"; + let string = "String".to_string(); + let slice = [1; 5]; + + let a = [1; 5].repeat(1); + let b = slice.repeat(1); + let c = "hello".repeat(N); + let d = "hi".repeat(1); + let e = s.repeat(1); + let f = string.repeat(1); +} diff --git a/tests/ui/repeat_once.stderr b/tests/ui/repeat_once.stderr new file mode 100644 index 00000000000..915eea3bfc6 --- /dev/null +++ b/tests/ui/repeat_once.stderr @@ -0,0 +1,40 @@ +error: calling `repeat(1)` on slice + --> $DIR/repeat_once.rs:10:13 + | +LL | let a = [1; 5].repeat(1); + | ^^^^^^^^^^^^^^^^ help: consider using `.to_vec()` instead: `[1; 5].to_vec()` + | + = note: `-D clippy::repeat-once` implied by `-D warnings` + +error: calling `repeat(1)` on slice + --> $DIR/repeat_once.rs:11:13 + | +LL | let b = slice.repeat(1); + | ^^^^^^^^^^^^^^^ help: consider using `.to_vec()` instead: `slice.to_vec()` + +error: calling `repeat(1)` on str + --> $DIR/repeat_once.rs:12:13 + | +LL | let c = "hello".repeat(N); + | ^^^^^^^^^^^^^^^^^ help: consider using `.to_string()` instead: `"hello".to_string()` + +error: calling `repeat(1)` on str + --> $DIR/repeat_once.rs:13:13 + | +LL | let d = "hi".repeat(1); + | ^^^^^^^^^^^^^^ help: consider using `.to_string()` instead: `"hi".to_string()` + +error: calling `repeat(1)` on str + --> $DIR/repeat_once.rs:14:13 + | +LL | let e = s.repeat(1); + | ^^^^^^^^^^^ help: consider using `.to_string()` instead: `s.to_string()` + +error: calling `repeat(1)` on a string literal + --> $DIR/repeat_once.rs:15:13 + | +LL | let f = string.repeat(1); + | ^^^^^^^^^^^^^^^^ help: consider using `.clone()` instead: `string.clone()` + +error: aborting due to 6 previous errors + -- cgit 1.4.1-3-g733a5 From 37d75da266443dd4253ceedebd692ba77dd72e03 Mon Sep 17 00:00:00 2001 From: robojumper Date: Wed, 8 Jul 2020 18:04:51 +0200 Subject: make match_like_matches_macro only apply to matches with a wildcard --- clippy_lints/src/assign_ops.rs | 1 - clippy_lints/src/matches.rs | 41 +++++++++++++-------------- tests/ui/match_expr_like_matches_macro.fixed | 14 +++++---- tests/ui/match_expr_like_matches_macro.rs | 17 +++++++---- tests/ui/match_expr_like_matches_macro.stderr | 28 ++++++++++++------ tests/ui/question_mark.fixed | 5 +++- tests/ui/question_mark.rs | 5 +++- tests/ui/question_mark.stderr | 20 ++++++------- 8 files changed, 77 insertions(+), 54 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/assign_ops.rs b/clippy_lints/src/assign_ops.rs index 3d48bf739eb..bc6e868823f 100644 --- a/clippy_lints/src/assign_ops.rs +++ b/clippy_lints/src/assign_ops.rs @@ -237,7 +237,6 @@ fn is_commutative(op: hir::BinOpKind) -> bool { use rustc_hir::BinOpKind::{ Add, And, BitAnd, BitOr, BitXor, Div, Eq, Ge, Gt, Le, Lt, Mul, Ne, Or, Rem, Shl, Shr, Sub, }; - #[allow(clippy::match_like_matches_macro)] match op { Add | Mul | And | Or | BitXor | BitAnd | BitOr | Eq | Ne => true, Sub | Div | Rem | Shl | Shr | Lt | Le | Ge | Gt => false, diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 34aa2981535..aeabb99a30d 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -446,11 +446,12 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for `match` expressions producing a `bool` that could be written using `matches!` + /// **What it does:** Checks for `match` or `if let` expressions producing a + /// `bool` that could be written using `matches!` /// /// **Why is this bad?** Readability and needless complexity. /// - /// **Known problems:** This can turn an intentionally exhaustive match into a non-exhaustive one. + /// **Known problems:** None /// /// **Example:** /// ```rust @@ -462,8 +463,14 @@ declare_clippy_lint! { /// _ => false, /// }; /// + /// let a = if let Some(0) = x { + /// true + /// } else { + /// false + /// }; + /// /// // Good - /// let a = matches!(x, Some(5)); + /// let a = matches!(x, Some(0)); /// ``` pub MATCH_LIKE_MATCHES_MACRO, style, @@ -499,9 +506,8 @@ impl<'tcx> LateLintPass<'tcx> for Matches { return; } - if !redundant_pattern_match::check(cx, expr) { - check_match_like_matches(cx, expr); - } + redundant_pattern_match::check(cx, expr); + check_match_like_matches(cx, expr); if let ExprKind::Match(ref ex, ref arms, MatchSource::Normal) = expr.kind { check_single_match(cx, ex, arms, expr); @@ -1068,6 +1074,7 @@ fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr if_chain! { if arms.len() == 2; if cx.tables().expr_ty(expr).is_bool(); + if is_wild(&arms[1].pat); if let Some(first) = find_bool_lit(&arms[0].body.kind, desugared); if let Some(second) = find_bool_lit(&arms[1].body.kind, desugared); if first != second; @@ -1437,16 +1444,14 @@ mod redundant_pattern_match { use rustc_mir::const_eval::is_const_fn; use rustc_span::source_map::Symbol; - pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { + pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Match(op, arms, ref match_source) = &expr.kind { match match_source { MatchSource::Normal => find_sugg_for_match(cx, expr, op, arms), MatchSource::IfLetDesugar { .. } => find_sugg_for_if_let(cx, expr, op, arms, "if"), MatchSource::WhileLetDesugar => find_sugg_for_if_let(cx, expr, op, arms, "while"), - _ => false, + _ => {}, } - } else { - false } } @@ -1456,7 +1461,7 @@ mod redundant_pattern_match { op: &Expr<'_>, arms: &[Arm<'_>], keyword: &'static str, - ) -> bool { + ) { fn find_suggestion(cx: &LateContext<'_>, hir_id: HirId, path: &QPath<'_>) -> Option<&'static str> { if match_qpath(path, &paths::RESULT_OK) && can_suggest(cx, hir_id, sym!(result_type), "is_ok") { return Some("is_ok()"); @@ -1487,7 +1492,7 @@ mod redundant_pattern_match { }; let good_method = match good_method { Some(method) => method, - None => return false, + None => return, }; // check that `while_let_on_iterator` lint does not trigger @@ -1497,7 +1502,7 @@ mod redundant_pattern_match { if method_path.ident.name == sym!(next); if match_trait_method(cx, op, &paths::ITERATOR); then { - return false; + return; } } @@ -1526,15 +1531,9 @@ mod redundant_pattern_match { ); }, ); - true } - fn find_sugg_for_match<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'_>, - op: &Expr<'_>, - arms: &[Arm<'_>], - ) -> bool { + fn find_sugg_for_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op: &Expr<'_>, arms: &[Arm<'_>]) { if arms.len() == 2 { let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind); @@ -1599,10 +1598,8 @@ mod redundant_pattern_match { ); }, ); - return true; } } - false } #[allow(clippy::too_many_arguments)] diff --git a/tests/ui/match_expr_like_matches_macro.fixed b/tests/ui/match_expr_like_matches_macro.fixed index 2d1ac8836d6..f3e19092480 100644 --- a/tests/ui/match_expr_like_matches_macro.fixed +++ b/tests/ui/match_expr_like_matches_macro.fixed @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::match_like_matches_macro)] +#![allow(unreachable_patterns)] fn main() { let x = Some(5); @@ -8,25 +9,28 @@ fn main() { // Lint let _y = matches!(x, Some(0)); + // Lint + let _w = matches!(x, Some(_)); + // Turn into is_none let _z = x.is_none(); // Lint - let _z = !matches!(x, Some(r) if r == 0); + let _zz = !matches!(x, Some(r) if r == 0); // Lint - let _zz = matches!(x, Some(5)); + let _zzz = matches!(x, Some(5)); // No lint let _a = match x { Some(_) => false, - None => false, + _ => false, }; // No lint - let _a = match x { + let _ab = match x { Some(0) => false, - Some(_) => true, + _ => true, None => false, }; } diff --git a/tests/ui/match_expr_like_matches_macro.rs b/tests/ui/match_expr_like_matches_macro.rs index 376abf9244e..fbae7c18b92 100644 --- a/tests/ui/match_expr_like_matches_macro.rs +++ b/tests/ui/match_expr_like_matches_macro.rs @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::match_like_matches_macro)] +#![allow(unreachable_patterns)] fn main() { let x = Some(5); @@ -11,6 +12,12 @@ fn main() { _ => false, }; + // Lint + let _w = match x { + Some(_) => true, + _ => false, + }; + // Turn into is_none let _z = match x { Some(_) => false, @@ -18,24 +25,24 @@ fn main() { }; // Lint - let _z = match x { + let _zz = match x { Some(r) if r == 0 => false, _ => true, }; // Lint - let _zz = if let Some(5) = x { true } else { false }; + let _zzz = if let Some(5) = x { true } else { false }; // No lint let _a = match x { Some(_) => false, - None => false, + _ => false, }; // No lint - let _a = match x { + let _ab = match x { Some(0) => false, - Some(_) => true, + _ => true, None => false, }; } diff --git a/tests/ui/match_expr_like_matches_macro.stderr b/tests/ui/match_expr_like_matches_macro.stderr index 0b32af039a8..4668f8565a6 100644 --- a/tests/ui/match_expr_like_matches_macro.stderr +++ b/tests/ui/match_expr_like_matches_macro.stderr @@ -1,5 +1,5 @@ error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:9:14 + --> $DIR/match_expr_like_matches_macro.rs:10:14 | LL | let _y = match x { | ______________^ @@ -10,8 +10,18 @@ LL | | }; | = note: `-D clippy::match-like-matches-macro` implied by `-D warnings` +error: match expression looks like `matches!` macro + --> $DIR/match_expr_like_matches_macro.rs:16:14 + | +LL | let _w = match x { + | ______________^ +LL | | Some(_) => true, +LL | | _ => false, +LL | | }; + | |_____^ help: try this: `matches!(x, Some(_))` + error: redundant pattern matching, consider using `is_none()` - --> $DIR/match_expr_like_matches_macro.rs:15:14 + --> $DIR/match_expr_like_matches_macro.rs:22:14 | LL | let _z = match x { | ______________^ @@ -23,20 +33,20 @@ LL | | }; = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: match expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:21:14 + --> $DIR/match_expr_like_matches_macro.rs:28:15 | -LL | let _z = match x { - | ______________^ +LL | let _zz = match x { + | _______________^ LL | | Some(r) if r == 0 => false, LL | | _ => true, LL | | }; | |_____^ help: try this: `!matches!(x, Some(r) if r == 0)` error: if let .. else expression looks like `matches!` macro - --> $DIR/match_expr_like_matches_macro.rs:27:15 + --> $DIR/match_expr_like_matches_macro.rs:34:16 | -LL | let _zz = if let Some(5) = x { true } else { false }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `matches!(x, Some(5))` +LL | let _zzz = if let Some(5) = x { true } else { false }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `matches!(x, Some(5))` -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors diff --git a/tests/ui/question_mark.fixed b/tests/ui/question_mark.fixed index bd13cf1bdfa..11dff94a288 100644 --- a/tests/ui/question_mark.fixed +++ b/tests/ui/question_mark.fixed @@ -23,7 +23,10 @@ pub enum SeemsOption { impl SeemsOption { pub fn is_none(&self) -> bool { - matches!(*self, SeemsOption::None) + match *self { + SeemsOption::None => true, + SeemsOption::Some(_) => false, + } } } diff --git a/tests/ui/question_mark.rs b/tests/ui/question_mark.rs index 94479e68555..1d0ee82b4f7 100644 --- a/tests/ui/question_mark.rs +++ b/tests/ui/question_mark.rs @@ -25,7 +25,10 @@ pub enum SeemsOption { impl SeemsOption { pub fn is_none(&self) -> bool { - matches!(*self, SeemsOption::None) + match *self { + SeemsOption::None => true, + SeemsOption::Some(_) => false, + } } } diff --git a/tests/ui/question_mark.stderr b/tests/ui/question_mark.stderr index be323035d6c..502615fb175 100644 --- a/tests/ui/question_mark.stderr +++ b/tests/ui/question_mark.stderr @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::question-mark` implied by `-D warnings` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:47:9 + --> $DIR/question_mark.rs:50:9 | LL | / if (self.opt).is_none() { LL | | return None; @@ -17,7 +17,7 @@ LL | | } | |_________^ help: replace it with: `(self.opt)?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:51:9 + --> $DIR/question_mark.rs:54:9 | LL | / if self.opt.is_none() { LL | | return None @@ -25,7 +25,7 @@ LL | | } | |_________^ help: replace it with: `self.opt?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:55:17 + --> $DIR/question_mark.rs:58:17 | LL | let _ = if self.opt.is_none() { | _________________^ @@ -36,7 +36,7 @@ LL | | }; | |_________^ help: replace it with: `Some(self.opt?)` error: this if-let-else may be rewritten with the `?` operator - --> $DIR/question_mark.rs:61:17 + --> $DIR/question_mark.rs:64:17 | LL | let _ = if let Some(x) = self.opt { | _________________^ @@ -47,7 +47,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt?` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:78:9 + --> $DIR/question_mark.rs:81:9 | LL | / if self.opt.is_none() { LL | | return None; @@ -55,7 +55,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:86:9 + --> $DIR/question_mark.rs:89:9 | LL | / if self.opt.is_none() { LL | | return None; @@ -63,7 +63,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:94:9 + --> $DIR/question_mark.rs:97:9 | LL | / if self.opt.is_none() { LL | | return None; @@ -71,7 +71,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this if-let-else may be rewritten with the `?` operator - --> $DIR/question_mark.rs:101:26 + --> $DIR/question_mark.rs:104:26 | LL | let v: &Vec<_> = if let Some(ref v) = self.opt { | __________________________^ @@ -82,7 +82,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt.as_ref()?` error: this if-let-else may be rewritten with the `?` operator - --> $DIR/question_mark.rs:111:17 + --> $DIR/question_mark.rs:114:17 | LL | let v = if let Some(v) = self.opt { | _________________^ @@ -93,7 +93,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt?` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:126:5 + --> $DIR/question_mark.rs:129:5 | LL | / if f().is_none() { LL | | return None; -- cgit 1.4.1-3-g733a5 From db1c946aaa02e1192d271dbcfe4598d726806108 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Tue, 30 Jun 2020 21:48:34 +0200 Subject: unnecessary_sort_by: avoid linting if key borrows --- clippy_lints/src/let_and_return.rs | 21 ++------------- clippy_lints/src/unnecessary_sort_by.rs | 48 ++++++++++++++++++++------------- clippy_lints/src/utils/mod.rs | 15 +++++++++++ tests/ui/unnecessary_sort_by.fixed | 46 ++++++++++++++++++++++++++++--- tests/ui/unnecessary_sort_by.rs | 46 ++++++++++++++++++++++++++++--- 5 files changed, 131 insertions(+), 45 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/let_and_return.rs b/clippy_lints/src/let_and_return.rs index ddc41f89f8d..fa560ffb980 100644 --- a/clippy_lints/src/let_and_return.rs +++ b/clippy_lints/src/let_and_return.rs @@ -1,6 +1,5 @@ use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::{Block, Expr, ExprKind, PatKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -9,7 +8,7 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::utils::{in_macro, match_qpath, snippet_opt, span_lint_and_then}; +use crate::utils::{fn_def_id, in_macro, match_qpath, snippet_opt, span_lint_and_then}; declare_clippy_lint! { /// **What it does:** Checks for `let`-bindings, which are subsequently @@ -97,22 +96,6 @@ struct BorrowVisitor<'a, 'tcx> { borrows: bool, } -impl BorrowVisitor<'_, '_> { - fn fn_def_id(&self, expr: &Expr<'_>) -> Option { - match &expr.kind { - ExprKind::MethodCall(..) => self.cx.tables().type_dependent_def_id(expr.hir_id), - ExprKind::Call( - Expr { - kind: ExprKind::Path(qpath), - .. - }, - .., - ) => self.cx.qpath_res(qpath, expr.hir_id).opt_def_id(), - _ => None, - } - } -} - impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> { type Map = Map<'tcx>; @@ -121,7 +104,7 @@ impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> { return; } - if let Some(def_id) = self.fn_def_id(expr) { + if let Some(def_id) = fn_def_id(self.cx, expr) { self.borrows = self .cx .tcx diff --git a/clippy_lints/src/unnecessary_sort_by.rs b/clippy_lints/src/unnecessary_sort_by.rs index d940776817c..91c1789a2ff 100644 --- a/clippy_lints/src/unnecessary_sort_by.rs +++ b/clippy_lints/src/unnecessary_sort_by.rs @@ -5,24 +5,23 @@ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, subst::GenericArgKind}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::Ident; declare_clippy_lint! { /// **What it does:** - /// Detects when people use `Vec::sort_by` and pass in a function + /// Detects uses of `Vec::sort_by` passing in a closure /// which compares the two arguments, either directly or indirectly. /// /// **Why is this bad?** /// It is more clear to use `Vec::sort_by_key` (or `Vec::sort` if - /// possible) than to use `Vec::sort_by` and and a more complicated + /// possible) than to use `Vec::sort_by` and a more complicated /// closure. /// /// **Known problems:** - /// If the suggested `Vec::sort_by_key` uses Reverse and it isn't - /// imported by a use statement in the current frame, then a `use` - /// statement that imports it will need to be added (which this lint - /// can't do). + /// If the suggested `Vec::sort_by_key` uses Reverse and it isn't already + /// imported by a use statement, then it will need to be added manually. /// /// **Example:** /// @@ -201,28 +200,41 @@ 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; then { - Some(LintTrigger::Sort(SortDetection { vec_name, unstable })) - } - else { - Some(LintTrigger::SortByKey(SortByKeyDetection { - vec_name, - unstable, - closure_arg, - closure_body, - reverse - })) + return Some(LintTrigger::Sort(SortDetection { vec_name, unstable })) + } else { + if !key_returns_borrow(cx, left_expr) { + return Some(LintTrigger::SortByKey(SortByKeyDetection { + vec_name, + unstable, + closure_arg, + closure_body, + reverse + })) + } } } - } else { - None } } + + None +} + +fn key_returns_borrow(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + if let Some(def_id) = utils::fn_def_id(cx, expr) { + let output = cx.tcx.fn_sig(def_id).output(); + let ty = output.skip_binder(); + return matches!(ty.kind, ty::Ref(..)) + || ty.walk().any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))); + } + + false } impl LateLintPass<'_> for UnnecessarySortBy { diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 3a3b79925ff..93075b9f0b5 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -1363,6 +1363,21 @@ pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool { ) } +/// Returns the `DefId` of the callee if the given expression is a function or method call. +pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { + match &expr.kind { + ExprKind::MethodCall(..) => cx.tables().type_dependent_def_id(expr.hir_id), + ExprKind::Call( + Expr { + kind: ExprKind::Path(qpath), + .. + }, + .., + ) => cx.tables().qpath_res(qpath, expr.hir_id).opt_def_id(), + _ => None, + } +} + pub fn run_lints(cx: &LateContext<'_>, lints: &[&'static Lint], id: HirId) -> bool { lints.iter().any(|lint| { matches!( diff --git a/tests/ui/unnecessary_sort_by.fixed b/tests/ui/unnecessary_sort_by.fixed index 779fd57707a..c017d1cf9a4 100644 --- a/tests/ui/unnecessary_sort_by.fixed +++ b/tests/ui/unnecessary_sort_by.fixed @@ -2,11 +2,11 @@ use std::cmp::Reverse; -fn id(x: isize) -> isize { - x -} +fn unnecessary_sort_by() { + fn id(x: isize) -> isize { + x + } -fn main() { let mut vec: Vec = vec![3, 6, 1, 2, 5]; // Forward examples vec.sort(); @@ -24,3 +24,41 @@ fn main() { vec.sort_by(|_, b| b.cmp(c)); vec.sort_unstable_by(|a, _| a.cmp(c)); } + +// Should not be linted to avoid hitting https://github.com/rust-lang/rust/issues/34162 +mod issue_5754 { + struct Test(String); + + #[derive(PartialOrd, Ord, PartialEq, Eq)] + struct Wrapper<'a>(&'a str); + + impl Test { + fn name(&self) -> &str { + &self.0 + } + + fn wrapped(&self) -> Wrapper<'_> { + Wrapper(&self.0) + } + } + + pub fn test() { + let mut args: Vec = vec![]; + + // Forward + args.sort_by(|a, b| a.name().cmp(b.name())); + args.sort_by(|a, b| a.wrapped().cmp(&b.wrapped())); + args.sort_unstable_by(|a, b| a.name().cmp(b.name())); + args.sort_unstable_by(|a, b| a.wrapped().cmp(&b.wrapped())); + // Reverse + args.sort_by(|a, b| b.name().cmp(a.name())); + args.sort_by(|a, b| b.wrapped().cmp(&a.wrapped())); + args.sort_unstable_by(|a, b| b.name().cmp(a.name())); + args.sort_unstable_by(|a, b| b.wrapped().cmp(&a.wrapped())); + } +} + +fn main() { + unnecessary_sort_by(); + issue_5754::test(); +} diff --git a/tests/ui/unnecessary_sort_by.rs b/tests/ui/unnecessary_sort_by.rs index 0485a5630af..1929c72b2f2 100644 --- a/tests/ui/unnecessary_sort_by.rs +++ b/tests/ui/unnecessary_sort_by.rs @@ -2,11 +2,11 @@ use std::cmp::Reverse; -fn id(x: isize) -> isize { - x -} +fn unnecessary_sort_by() { + fn id(x: isize) -> isize { + x + } -fn main() { let mut vec: Vec = vec![3, 6, 1, 2, 5]; // Forward examples vec.sort_by(|a, b| a.cmp(b)); @@ -24,3 +24,41 @@ fn main() { vec.sort_by(|_, b| b.cmp(c)); vec.sort_unstable_by(|a, _| a.cmp(c)); } + +// Should not be linted to avoid hitting https://github.com/rust-lang/rust/issues/34162 +mod issue_5754 { + struct Test(String); + + #[derive(PartialOrd, Ord, PartialEq, Eq)] + struct Wrapper<'a>(&'a str); + + impl Test { + fn name(&self) -> &str { + &self.0 + } + + fn wrapped(&self) -> Wrapper<'_> { + Wrapper(&self.0) + } + } + + pub fn test() { + let mut args: Vec = vec![]; + + // Forward + args.sort_by(|a, b| a.name().cmp(b.name())); + args.sort_by(|a, b| a.wrapped().cmp(&b.wrapped())); + args.sort_unstable_by(|a, b| a.name().cmp(b.name())); + args.sort_unstable_by(|a, b| a.wrapped().cmp(&b.wrapped())); + // Reverse + args.sort_by(|a, b| b.name().cmp(a.name())); + args.sort_by(|a, b| b.wrapped().cmp(&a.wrapped())); + args.sort_unstable_by(|a, b| b.name().cmp(a.name())); + args.sort_unstable_by(|a, b| b.wrapped().cmp(&a.wrapped())); + } +} + +fn main() { + unnecessary_sort_by(); + issue_5754::test(); +} -- cgit 1.4.1-3-g733a5 From dac19e3afccc63ff976bcf0a5ee385bdd0e075d5 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Tue, 7 Jul 2020 00:35:51 +0200 Subject: single_match_else - single expr/stmt else block corner case --- clippy_lints/src/matches.rs | 20 +++++++++------ tests/ui/single_match_else.rs | 51 +++++++++++++++++++++++++++++++++++++++ tests/ui/single_match_else.stderr | 44 +++++++++++++++++++++++++++++++-- 3 files changed, 106 insertions(+), 9 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index b754a45aa40..a6cc1097441 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -530,16 +530,22 @@ fn check_single_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], exp // the lint noisy in unnecessary situations return; } - let els = remove_blocks(&arms[1].body); - let els = if is_unit_expr(els) { + let els = arms[1].body; + let els = if is_unit_expr(remove_blocks(els)) { None - } else if let ExprKind::Block(_, _) = els.kind { - // matches with blocks that contain statements are prettier as `if let + else` - Some(els) + } else if let ExprKind::Block(Block { stmts, expr: block_expr, .. }, _) = els.kind { + if stmts.len() == 1 && block_expr.is_none() || stmts.is_empty() && block_expr.is_some() { + // single statement/expr "else" block, don't lint + return; + } else { + // block with 2+ statements or 1 expr and 1+ statement + Some(els) + } } else { - // allow match arms with just expressions - return; + // not a block, don't lint + return; }; + let ty = cx.tables().expr_ty(ex); if ty.kind != ty::Bool || is_allowed(cx, MATCH_BOOL, ex.hir_id) { check_single_match_single_pattern(cx, ex, arms, expr, els); diff --git a/tests/ui/single_match_else.rs b/tests/ui/single_match_else.rs index 34193be0b75..b624a41a29b 100644 --- a/tests/ui/single_match_else.rs +++ b/tests/ui/single_match_else.rs @@ -1,4 +1,6 @@ #![warn(clippy::single_match_else)] +#![allow(clippy::needless_return)] +#![allow(clippy::no_effect)] enum ExprNode { ExprAddrOf, @@ -30,6 +32,55 @@ macro_rules! unwrap_addr { }; } +#[rustfmt::skip] fn main() { unwrap_addr!(ExprNode::Unicorns); + + // + // don't lint single exprs/statements + // + + // don't lint here + match Some(1) { + Some(a) => println!("${:?}", a), + None => return, + } + + // don't lint here + match Some(1) { + Some(a) => println!("${:?}", a), + None => { + return + }, + } + + // don't lint here + match Some(1) { + Some(a) => println!("${:?}", a), + None => { + return; + }, + } + + // + // lint multiple exprs/statements "else" blocks + // + + // lint here + match Some(1) { + Some(a) => println!("${:?}", a), + None => { + println!("else block"); + return + }, + } + + // lint here + match Some(1) { + Some(a) => println!("${:?}", a), + None => { + println!("else block"); + return; + }, + } } diff --git a/tests/ui/single_match_else.stderr b/tests/ui/single_match_else.stderr index 59861d46eb3..3a07c2ec542 100644 --- a/tests/ui/single_match_else.stderr +++ b/tests/ui/single_match_else.stderr @@ -1,5 +1,5 @@ error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match_else.rs:12:5 + --> $DIR/single_match_else.rs:14:5 | LL | / match ExprNode::Butterflies { LL | | ExprNode::ExprAddrOf => Some(&NODE), @@ -19,5 +19,45 @@ LL | None LL | } | -error: aborting due to previous error +error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match_else.rs:70:5 + | +LL | / match Some(1) { +LL | | Some(a) => println!("${:?}", a), +LL | | None => { +LL | | println!("else block"); +LL | | return +LL | | }, +LL | | } + | |_____^ + | +help: try this + | +LL | if let Some(a) = Some(1) { println!("${:?}", a) } else { +LL | println!("else block"); +LL | return +LL | } + | + +error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match_else.rs:79:5 + | +LL | / match Some(1) { +LL | | Some(a) => println!("${:?}", a), +LL | | None => { +LL | | println!("else block"); +LL | | return; +LL | | }, +LL | | } + | |_____^ + | +help: try this + | +LL | if let Some(a) = Some(1) { println!("${:?}", a) } else { +LL | println!("else block"); +LL | return; +LL | } + | + +error: aborting due to 3 previous errors -- cgit 1.4.1-3-g733a5 From b3c719608d2c969323714517837f0c68aca23d81 Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Fri, 10 Jul 2020 17:23:03 +0200 Subject: Fix test failures --- tests/ui/range_plus_minus_one.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'tests') diff --git a/tests/ui/range_plus_minus_one.rs b/tests/ui/range_plus_minus_one.rs index 3cfed4125b3..7d034117547 100644 --- a/tests/ui/range_plus_minus_one.rs +++ b/tests/ui/range_plus_minus_one.rs @@ -7,6 +7,7 @@ fn f() -> usize { } #[warn(clippy::range_plus_one)] +#[warn(clippy::range_minus_one)] fn main() { for _ in 0..2 {} for _ in 0..=2 {} -- cgit 1.4.1-3-g733a5 From afa4148cc6dee0f9e0ca5b33f2511b9305d84fcb Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Fri, 10 Jul 2020 17:53:01 +0200 Subject: Fix tests a bit more --- tests/ui/range_plus_minus_one.fixed | 1 + tests/ui/range_plus_minus_one.stderr | 18 +++++++++--------- 2 files changed, 10 insertions(+), 9 deletions(-) (limited to 'tests') diff --git a/tests/ui/range_plus_minus_one.fixed b/tests/ui/range_plus_minus_one.fixed index 6b402114099..19b253b0fe2 100644 --- a/tests/ui/range_plus_minus_one.fixed +++ b/tests/ui/range_plus_minus_one.fixed @@ -7,6 +7,7 @@ fn f() -> usize { } #[warn(clippy::range_plus_one)] +#[warn(clippy::range_minus_one)] fn main() { for _ in 0..2 {} for _ in 0..=2 {} diff --git a/tests/ui/range_plus_minus_one.stderr b/tests/ui/range_plus_minus_one.stderr index f72943a04f2..fb4f1658597 100644 --- a/tests/ui/range_plus_minus_one.stderr +++ b/tests/ui/range_plus_minus_one.stderr @@ -1,5 +1,5 @@ error: an inclusive range would be more readable - --> $DIR/range_plus_minus_one.rs:14:14 + --> $DIR/range_plus_minus_one.rs:15:14 | LL | for _ in 0..3 + 1 {} | ^^^^^^^^ help: use: `0..=3` @@ -7,25 +7,25 @@ LL | for _ in 0..3 + 1 {} = note: `-D clippy::range-plus-one` implied by `-D warnings` error: an inclusive range would be more readable - --> $DIR/range_plus_minus_one.rs:17:14 + --> $DIR/range_plus_minus_one.rs:18:14 | LL | for _ in 0..1 + 5 {} | ^^^^^^^^ help: use: `0..=5` error: an inclusive range would be more readable - --> $DIR/range_plus_minus_one.rs:20:14 + --> $DIR/range_plus_minus_one.rs:21:14 | LL | for _ in 1..1 + 1 {} | ^^^^^^^^ help: use: `1..=1` error: an inclusive range would be more readable - --> $DIR/range_plus_minus_one.rs:26:14 + --> $DIR/range_plus_minus_one.rs:27:14 | LL | for _ in 0..(1 + f()) {} | ^^^^^^^^^^^^ help: use: `0..=f()` error: an exclusive range would be more readable - --> $DIR/range_plus_minus_one.rs:30:13 + --> $DIR/range_plus_minus_one.rs:31:13 | LL | let _ = ..=11 - 1; | ^^^^^^^^^ help: use: `..11` @@ -33,25 +33,25 @@ LL | let _ = ..=11 - 1; = note: `-D clippy::range-minus-one` implied by `-D warnings` error: an exclusive range would be more readable - --> $DIR/range_plus_minus_one.rs:31:13 + --> $DIR/range_plus_minus_one.rs:32:13 | LL | let _ = ..=(11 - 1); | ^^^^^^^^^^^ help: use: `..11` error: an inclusive range would be more readable - --> $DIR/range_plus_minus_one.rs:32:13 + --> $DIR/range_plus_minus_one.rs:33:13 | LL | let _ = (1..11 + 1); | ^^^^^^^^^^^ help: use: `(1..=11)` error: an inclusive range would be more readable - --> $DIR/range_plus_minus_one.rs:33:13 + --> $DIR/range_plus_minus_one.rs:34:13 | LL | let _ = (f() + 1)..(f() + 1); | ^^^^^^^^^^^^^^^^^^^^ help: use: `((f() + 1)..=f())` error: an inclusive range would be more readable - --> $DIR/range_plus_minus_one.rs:37:14 + --> $DIR/range_plus_minus_one.rs:38:14 | LL | for _ in 1..ONE + ONE {} | ^^^^^^^^^^^^ help: use: `1..=ONE` -- cgit 1.4.1-3-g733a5 From ff796b6d7082d5b5c073797aba17f454eebe3359 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Fri, 19 Jun 2020 11:44:03 +0200 Subject: Rename collapsable_if fix suggestion to "collapse nested if block" The name "try" is confusing when shown as quick fix by rust-analyzer --- clippy_lints/src/collapsible_if.rs | 4 ++-- tests/ui/collapsible_else_if.stderr | 14 +++++++------- tests/ui/collapsible_if.stderr | 14 +++++++------- 3 files changed, 16 insertions(+), 16 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index 8090f4673aa..42bff564de0 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -115,7 +115,7 @@ fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, else_: &ast::Expr) { COLLAPSIBLE_IF, block.span, "this `else { if .. }` block can be collapsed", - "try", + "collapse nested if block", snippet_block_with_applicability(cx, else_.span, "..", Some(block.span), &mut applicability).into_owned(), applicability, ); @@ -142,7 +142,7 @@ fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: & let rhs = Sugg::ast(cx, check_inner, ".."); diag.span_suggestion( expr.span, - "try", + "collapse nested if block", format!( "if {} {}", lhs.and(&rhs), diff --git a/tests/ui/collapsible_else_if.stderr b/tests/ui/collapsible_else_if.stderr index 28048999e8e..3d1c458879e 100644 --- a/tests/ui/collapsible_else_if.stderr +++ b/tests/ui/collapsible_else_if.stderr @@ -10,7 +10,7 @@ LL | | } | |_____^ | = note: `-D clippy::collapsible-if` implied by `-D warnings` -help: try +help: collapse nested if block | LL | } else if y == "world" { LL | println!("world!") @@ -28,7 +28,7 @@ LL | | } LL | | } | |_____^ | -help: try +help: collapse nested if block | LL | } else if let Some(42) = Some(42) { LL | println!("world!") @@ -48,7 +48,7 @@ LL | | } LL | | } | |_____^ | -help: try +help: collapse nested if block | LL | } else if y == "world" { LL | println!("world") @@ -71,7 +71,7 @@ LL | | } LL | | } | |_____^ | -help: try +help: collapse nested if block | LL | } else if let Some(42) = Some(42) { LL | println!("world") @@ -94,7 +94,7 @@ LL | | } LL | | } | |_____^ | -help: try +help: collapse nested if block | LL | } else if let Some(42) = Some(42) { LL | println!("world") @@ -117,7 +117,7 @@ LL | | } LL | | } | |_____^ | -help: try +help: collapse nested if block | LL | } else if x == "hello" { LL | println!("world") @@ -140,7 +140,7 @@ LL | | } LL | | } | |_____^ | -help: try +help: collapse nested if block | LL | } else if let Some(42) = Some(42) { LL | println!("world") diff --git a/tests/ui/collapsible_if.stderr b/tests/ui/collapsible_if.stderr index 6440ff41be8..f56dd65b9dd 100644 --- a/tests/ui/collapsible_if.stderr +++ b/tests/ui/collapsible_if.stderr @@ -9,7 +9,7 @@ LL | | } | |_____^ | = note: `-D clippy::collapsible-if` implied by `-D warnings` -help: try +help: collapse nested if block | LL | if x == "hello" && y == "world" { LL | println!("Hello world!"); @@ -26,7 +26,7 @@ LL | | } LL | | } | |_____^ | -help: try +help: collapse nested if block | LL | if (x == "hello" || x == "world") && (y == "world" || y == "hello") { LL | println!("Hello world!"); @@ -43,7 +43,7 @@ LL | | } LL | | } | |_____^ | -help: try +help: collapse nested if block | LL | if x == "hello" && x == "world" && (y == "world" || y == "hello") { LL | println!("Hello world!"); @@ -60,7 +60,7 @@ LL | | } LL | | } | |_____^ | -help: try +help: collapse nested if block | LL | if (x == "hello" || x == "world") && y == "world" && y == "hello" { LL | println!("Hello world!"); @@ -77,7 +77,7 @@ LL | | } LL | | } | |_____^ | -help: try +help: collapse nested if block | LL | if x == "hello" && x == "world" && y == "world" && y == "hello" { LL | println!("Hello world!"); @@ -94,7 +94,7 @@ LL | | } LL | | } | |_____^ | -help: try +help: collapse nested if block | LL | if 42 == 1337 && 'a' != 'A' { LL | println!("world!") @@ -111,7 +111,7 @@ LL | | } LL | | } | |_____^ | -help: try +help: collapse nested if block | LL | if x == "hello" && y == "world" { // Collapsible LL | println!("Hello world!"); -- cgit 1.4.1-3-g733a5 From 201999ccfd18a9debe1f186f30f40659ebc6b933 Mon Sep 17 00:00:00 2001 From: Brian Warner Date: Mon, 13 Jul 2020 10:33:25 -0700 Subject: improve advice in iter_nth_zero The "use .next()" replacement advice is on the last line of the code snippet, where it is vulnerable to truncation. Display that advice at the beginning instead. closes #5783 --- clippy_lints/src/methods/mod.rs | 4 ++-- tests/ui/iter_nth_zero.stderr | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 4c595029ff7..565a08f1292 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2348,8 +2348,8 @@ fn lint_iter_nth_zero<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, nth_ar cx, ITER_NTH_ZERO, expr.span, - "called `.nth(0)` on a `std::iter::Iterator`", - "try calling", + "called `.nth(0)` on a `std::iter::Iterator`, when `.next()` is equivalent", + "try calling .next() instead of .nth(0)", format!("{}.next()", snippet_with_applicability(cx, nth_args[0].span, "..", &mut applicability)), applicability, ); diff --git a/tests/ui/iter_nth_zero.stderr b/tests/ui/iter_nth_zero.stderr index 2b20a4ceb4a..6c4200a7905 100644 --- a/tests/ui/iter_nth_zero.stderr +++ b/tests/ui/iter_nth_zero.stderr @@ -1,22 +1,22 @@ -error: called `.nth(0)` on a `std::iter::Iterator` +error: called `.nth(0)` on a `std::iter::Iterator`, when `.next()` is equivalent --> $DIR/iter_nth_zero.rs:20:14 | LL | let _x = s.iter().nth(0); - | ^^^^^^^^^^^^^^^ help: try calling: `s.iter().next()` + | ^^^^^^^^^^^^^^^ help: try calling .next() instead of .nth(0): `s.iter().next()` | = note: `-D clippy::iter-nth-zero` implied by `-D warnings` -error: called `.nth(0)` on a `std::iter::Iterator` +error: called `.nth(0)` on a `std::iter::Iterator`, when `.next()` is equivalent --> $DIR/iter_nth_zero.rs:25:14 | LL | let _y = iter.nth(0); - | ^^^^^^^^^^^ help: try calling: `iter.next()` + | ^^^^^^^^^^^ help: try calling .next() instead of .nth(0): `iter.next()` -error: called `.nth(0)` on a `std::iter::Iterator` +error: called `.nth(0)` on a `std::iter::Iterator`, when `.next()` is equivalent --> $DIR/iter_nth_zero.rs:30:22 | LL | let _unwrapped = iter2.nth(0).unwrap(); - | ^^^^^^^^^^^^ help: try calling: `iter2.next()` + | ^^^^^^^^^^^^ help: try calling .next() instead of .nth(0): `iter2.next()` error: aborting due to 3 previous errors -- cgit 1.4.1-3-g733a5 From d067d0352bfc5a6979f477bc96c969b040437618 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Tue, 14 Jul 2020 08:18:15 +0200 Subject: Add test for `needless_range_loop` issue Closes #2277 This was fixed when we fixed #2542. --- tests/ui/needless_range_loop2.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'tests') diff --git a/tests/ui/needless_range_loop2.rs b/tests/ui/needless_range_loop2.rs index 2ed1b09bece..a82b1159161 100644 --- a/tests/ui/needless_range_loop2.rs +++ b/tests/ui/needless_range_loop2.rs @@ -83,3 +83,13 @@ fn main() { println!("{}", arr[i]); } } + +mod issue2277 { + pub fn example(list: &[[f64; 3]]) { + let mut x: [f64; 3] = [10.; 3]; + + for i in 0..3 { + x[i] = list.iter().map(|item| item[i]).sum::(); + } + } +} -- cgit 1.4.1-3-g733a5 From 126790999a128a880ca276c49afd2927a66ffbbe Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Mon, 30 Mar 2020 11:02:14 +0200 Subject: new lint: Returning unit from closures expecting Ord This lint catches cases where the last statement of a closure expecting an instance of Ord has a trailing semi-colon. It compiles since the closure ends up return () which also implements Ord but causes unexpected results in cases such as sort_by_key. Fixes #5080 reprise: rebase, update and address all concerns --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 + clippy_lints/src/unit_return_expecting_ord.rs | 177 ++++++++++++++++++++++++++ src/lintlist/mod.rs | 7 + tests/ui/unit_return_expecting_ord.rs | 36 ++++++ tests/ui/unit_return_expecting_ord.stderr | 39 ++++++ 6 files changed, 265 insertions(+) create mode 100644 clippy_lints/src/unit_return_expecting_ord.rs create mode 100644 tests/ui/unit_return_expecting_ord.rs create mode 100644 tests/ui/unit_return_expecting_ord.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d08b44ba40..1c927b5f83a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1679,6 +1679,7 @@ Released 2018-09-13 [`uninit_assumed_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_assumed_init [`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 [`unknown_clippy_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#unknown_clippy_lints [`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast [`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 32e79317f82..7a4ca3902b3 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -300,6 +300,7 @@ mod trivially_copy_pass_by_ref; mod try_err; mod types; mod unicode; +mod unit_return_expecting_ord; mod unnamed_address; mod unnecessary_sort_by; mod unnested_or_patterns; @@ -826,6 +827,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &unicode::NON_ASCII_LITERAL, &unicode::UNICODE_NOT_NFC, &unicode::ZERO_WIDTH_SPACE, + &unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD, &unnamed_address::FN_ADDRESS_COMPARISONS, &unnamed_address::VTABLE_ADDRESS_COMPARISONS, &unnecessary_sort_by::UNNECESSARY_SORT_BY, @@ -891,6 +893,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box attrs::Attributes); store.register_late_pass(|| box blocks_in_if_conditions::BlocksInIfConditions); store.register_late_pass(|| box unicode::Unicode); + store.register_late_pass(|| box unit_return_expecting_ord::UnitReturnExpectingOrd); store.register_late_pass(|| box strings::StringAdd); store.register_late_pass(|| box implicit_return::ImplicitReturn); store.register_late_pass(|| box implicit_saturating_sub::ImplicitSaturatingSub); @@ -1436,6 +1439,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::UNNECESSARY_CAST), LintId::of(&types::VEC_BOX), LintId::of(&unicode::ZERO_WIDTH_SPACE), + LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), @@ -1692,6 +1696,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::CAST_REF_TO_MUT), LintId::of(&types::UNIT_CMP), LintId::of(&unicode::ZERO_WIDTH_SPACE), + LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), diff --git a/clippy_lints/src/unit_return_expecting_ord.rs b/clippy_lints/src/unit_return_expecting_ord.rs new file mode 100644 index 00000000000..fceb885516b --- /dev/null +++ b/clippy_lints/src/unit_return_expecting_ord.rs @@ -0,0 +1,177 @@ +use crate::utils::{get_trait_def_id, paths, span_lint, span_lint_and_help}; +use if_chain::if_chain; +use rustc_hir::def_id::DefId; +use rustc_hir::{Expr, ExprKind, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_middle::ty::{GenericPredicates, PredicateKind, ProjectionPredicate, TraitPredicate}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{BytePos, Span}; + +declare_clippy_lint! { + /// **What it does:** Checks for functions that expect closures of type + /// Fn(...) -> Ord where the implemented closure returns the unit type. + /// The lint also suggests to remove the semi-colon at the end of the statement if present. + /// + /// **Why is this bad?** Likely, returning the unit type is unintentional, and + /// could simply be caused by an extra semi-colon. Since () implements Ord + /// it doesn't cause a compilation error. + /// This is the same reasoning behind the unit_cmp lint. + /// + /// **Known problems:** If returning unit is intentional, then there is no + /// way of specifying this without triggering needless_return lint + /// + /// **Example:** + /// + /// ```rust + /// let mut twins = vec!((1,1), (2,2)); + /// twins.sort_by_key(|x| { x.1; }); + /// ``` + pub UNIT_RETURN_EXPECTING_ORD, + correctness, + "fn arguments of type Fn(...) -> Ord returning the unit type ()." +} + +declare_lint_pass!(UnitReturnExpectingOrd => [UNIT_RETURN_EXPECTING_ORD]); + +fn get_trait_predicates_for_trait_id<'tcx>( + cx: &LateContext<'tcx>, + generics: GenericPredicates<'tcx>, + trait_id: Option, +) -> Vec> { + let mut preds = Vec::new(); + for (pred, _) in generics.predicates { + if_chain! { + if let PredicateKind::Trait(poly_trait_pred, _) = pred.kind(); + let trait_pred = cx.tcx.erase_late_bound_regions(&poly_trait_pred); + if let Some(trait_def_id) = trait_id; + if trait_def_id == trait_pred.trait_ref.def_id; + then { + preds.push(trait_pred); + } + } + } + preds +} + +fn get_projection_pred<'tcx>( + cx: &LateContext<'tcx>, + generics: GenericPredicates<'tcx>, + pred: TraitPredicate<'tcx>, +) -> Option> { + generics.predicates.iter().find_map(|(proj_pred, _)| { + if let PredicateKind::Projection(proj_pred) = proj_pred.kind() { + let projection_pred = cx.tcx.erase_late_bound_regions(proj_pred); + if projection_pred.projection_ty.substs == pred.trait_ref.substs { + return Some(projection_pred); + } + } + None + }) +} + +fn get_args_to_check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Vec<(usize, String)> { + let mut args_to_check = Vec::new(); + if let Some(def_id) = cx.tables().type_dependent_def_id(expr.hir_id) { + let fn_sig = cx.tcx.fn_sig(def_id); + let generics = cx.tcx.predicates_of(def_id); + let fn_mut_preds = get_trait_predicates_for_trait_id(cx, generics, cx.tcx.lang_items().fn_mut_trait()); + let ord_preds = get_trait_predicates_for_trait_id(cx, generics, get_trait_def_id(cx, &paths::ORD)); + let partial_ord_preds = + get_trait_predicates_for_trait_id(cx, generics, cx.tcx.lang_items().partial_ord_trait()); + // Trying to call erase_late_bound_regions on fn_sig.inputs() gives the following error + // The trait `rustc::ty::TypeFoldable<'_>` is not implemented for `&[&rustc::ty::TyS<'_>]` + let inputs_output = cx.tcx.erase_late_bound_regions(&fn_sig.inputs_and_output()); + inputs_output + .iter() + .rev() + .skip(1) + .rev() + .enumerate() + .for_each(|(i, inp)| { + for trait_pred in &fn_mut_preds { + if_chain! { + if trait_pred.self_ty() == inp; + if let Some(return_ty_pred) = get_projection_pred(cx, generics, *trait_pred); + then { + if ord_preds.iter().any(|ord| ord.self_ty() == return_ty_pred.ty) { + args_to_check.push((i, "Ord".to_string())); + } else if partial_ord_preds.iter().any(|pord| pord.self_ty() == return_ty_pred.ty) { + args_to_check.push((i, "PartialOrd".to_string())); + } + } + } + } + }); + } + args_to_check +} + +fn check_arg<'tcx>(cx: &LateContext<'tcx>, arg: &'tcx Expr<'tcx>) -> Option<(Span, Option)> { + if_chain! { + if let ExprKind::Closure(_, _fn_decl, body_id, span, _) = arg.kind; + if let ty::Closure(_def_id, substs) = &cx.tables().node_type(arg.hir_id).kind; + let ret_ty = substs.as_closure().sig().output(); + let ty = cx.tcx.erase_late_bound_regions(&ret_ty); + if ty.is_unit(); + then { + if_chain! { + let body = cx.tcx.hir().body(body_id); + if let ExprKind::Block(block, _) = body.value.kind; + if block.expr.is_none(); + if let Some(stmt) = block.stmts.last(); + if let StmtKind::Semi(_) = stmt.kind; + then { + let data = stmt.span.data(); + // Make a span out of the semicolon for the help message + Some((span, Some(Span::new(data.hi-BytePos(1), data.hi, data.ctxt)))) + } else { + Some((span, None)) + } + } + } else { + None + } + } +} + +impl<'tcx> LateLintPass<'tcx> for UnitReturnExpectingOrd { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if let ExprKind::MethodCall(_, _, ref args, _) = expr.kind { + let arg_indices = get_args_to_check(cx, expr); + for (i, trait_name) in arg_indices { + if i < args.len() { + match check_arg(cx, &args[i]) { + Some((span, None)) => { + span_lint( + cx, + UNIT_RETURN_EXPECTING_ORD, + span, + &format!( + "this closure returns \ + the unit type which also implements {}", + trait_name + ), + ); + }, + Some((span, Some(last_semi))) => { + span_lint_and_help( + cx, + UNIT_RETURN_EXPECTING_ORD, + span, + &format!( + "this closure returns \ + the unit type which also implements {}", + trait_name + ), + Some(last_semi), + &"probably caused by this trailing semicolon".to_string(), + ); + }, + None => {}, + } + } + } + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index b89a8712862..96b004904aa 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2292,6 +2292,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "types", }, + Lint { + name: "unit_return_expecting_ord", + group: "correctness", + desc: "fn arguments of type Fn(...) -> Ord returning the unit type ().", + deprecation: None, + module: "unit_return_expecting_ord", + }, Lint { name: "unknown_clippy_lints", group: "style", diff --git a/tests/ui/unit_return_expecting_ord.rs b/tests/ui/unit_return_expecting_ord.rs new file mode 100644 index 00000000000..bdb4710cc69 --- /dev/null +++ b/tests/ui/unit_return_expecting_ord.rs @@ -0,0 +1,36 @@ +#![warn(clippy::unit_return_expecting_ord)] +#![allow(clippy::needless_return)] +#![allow(clippy::unused_unit)] +#![feature(is_sorted)] + +struct Struct { + field: isize, +} + +fn double(i: isize) -> isize { + i * 2 +} + +fn unit(_i: isize) {} + +fn main() { + let mut structs = vec![Struct { field: 2 }, Struct { field: 1 }]; + structs.sort_by_key(|s| { + double(s.field); + }); + structs.sort_by_key(|s| double(s.field)); + structs.is_sorted_by_key(|s| { + double(s.field); + }); + structs.is_sorted_by_key(|s| { + if s.field > 0 { + () + } else { + return (); + } + }); + structs.sort_by_key(|s| { + return double(s.field); + }); + structs.sort_by_key(|s| unit(s.field)); +} diff --git a/tests/ui/unit_return_expecting_ord.stderr b/tests/ui/unit_return_expecting_ord.stderr new file mode 100644 index 00000000000..e63d5874609 --- /dev/null +++ b/tests/ui/unit_return_expecting_ord.stderr @@ -0,0 +1,39 @@ +error: this closure returns the unit type which also implements Ord + --> $DIR/unit_return_expecting_ord.rs:18:25 + | +LL | structs.sort_by_key(|s| { + | ^^^ + | + = note: `-D clippy::unit-return-expecting-ord` implied by `-D warnings` +help: probably caused by this trailing semicolon + --> $DIR/unit_return_expecting_ord.rs:19:24 + | +LL | double(s.field); + | ^ + +error: this closure returns the unit type which also implements PartialOrd + --> $DIR/unit_return_expecting_ord.rs:22:30 + | +LL | structs.is_sorted_by_key(|s| { + | ^^^ + | +help: probably caused by this trailing semicolon + --> $DIR/unit_return_expecting_ord.rs:23:24 + | +LL | double(s.field); + | ^ + +error: this closure returns the unit type which also implements PartialOrd + --> $DIR/unit_return_expecting_ord.rs:25:30 + | +LL | structs.is_sorted_by_key(|s| { + | ^^^ + +error: this closure returns the unit type which also implements Ord + --> $DIR/unit_return_expecting_ord.rs:35:25 + | +LL | structs.sort_by_key(|s| unit(s.field)); + | ^^^ + +error: aborting due to 4 previous errors + -- cgit 1.4.1-3-g733a5 From e83b3eb9930ab527451edaaa6f524acd26c1bf1c Mon Sep 17 00:00:00 2001 From: Brian Warner Date: Tue, 14 Jul 2020 09:20:19 -0700 Subject: formatting nits --- clippy_lints/src/methods/mod.rs | 2 +- tests/ui/iter_nth_zero.stderr | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 565a08f1292..69e985cc0a4 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2349,7 +2349,7 @@ fn lint_iter_nth_zero<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, nth_ar ITER_NTH_ZERO, expr.span, "called `.nth(0)` on a `std::iter::Iterator`, when `.next()` is equivalent", - "try calling .next() instead of .nth(0)", + "try calling `.next()` instead of `.nth(0)`", format!("{}.next()", snippet_with_applicability(cx, nth_args[0].span, "..", &mut applicability)), applicability, ); diff --git a/tests/ui/iter_nth_zero.stderr b/tests/ui/iter_nth_zero.stderr index 6c4200a7905..29c56f3a94f 100644 --- a/tests/ui/iter_nth_zero.stderr +++ b/tests/ui/iter_nth_zero.stderr @@ -2,7 +2,7 @@ error: called `.nth(0)` on a `std::iter::Iterator`, when `.next()` is equivalent --> $DIR/iter_nth_zero.rs:20:14 | LL | let _x = s.iter().nth(0); - | ^^^^^^^^^^^^^^^ help: try calling .next() instead of .nth(0): `s.iter().next()` + | ^^^^^^^^^^^^^^^ help: try calling `.next()` instead of `.nth(0)`: `s.iter().next()` | = note: `-D clippy::iter-nth-zero` implied by `-D warnings` @@ -10,13 +10,13 @@ error: called `.nth(0)` on a `std::iter::Iterator`, when `.next()` is equivalent --> $DIR/iter_nth_zero.rs:25:14 | LL | let _y = iter.nth(0); - | ^^^^^^^^^^^ help: try calling .next() instead of .nth(0): `iter.next()` + | ^^^^^^^^^^^ help: try calling `.next()` instead of `.nth(0)`: `iter.next()` error: called `.nth(0)` on a `std::iter::Iterator`, when `.next()` is equivalent --> $DIR/iter_nth_zero.rs:30:22 | LL | let _unwrapped = iter2.nth(0).unwrap(); - | ^^^^^^^^^^^^ help: try calling .next() instead of .nth(0): `iter2.next()` + | ^^^^^^^^^^^^ help: try calling `.next()` instead of `.nth(0)`: `iter2.next()` error: aborting due to 3 previous errors -- cgit 1.4.1-3-g733a5 From a0640457a912720a5473d7feff40576a2f97df1e Mon Sep 17 00:00:00 2001 From: Leo Meira Vital Date: Mon, 13 Jul 2020 01:57:19 -0300 Subject: Removing snippet from SHADOW_UNRELATED message. --- clippy_lints/src/shadow.rs | 6 +----- tests/ui/shadow.stderr | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index 194786c5c41..fab13c8c124 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -295,11 +295,7 @@ fn lint_shadow<'tcx>( cx, SHADOW_UNRELATED, pattern_span, - &format!( - "`{}` is shadowed by `{}`", - snippet(cx, pattern_span, "_"), - snippet(cx, expr.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"); diff --git a/tests/ui/shadow.stderr b/tests/ui/shadow.stderr index 7fa58cf7649..8a831375b41 100644 --- a/tests/ui/shadow.stderr +++ b/tests/ui/shadow.stderr @@ -104,7 +104,7 @@ note: previous binding is here LL | let x = (1, x); | ^ -error: `x` is shadowed by `y` +error: `x` is being shadowed --> $DIR/shadow.rs:34:9 | LL | let x = y; -- cgit 1.4.1-3-g733a5 From 70a41a92815a79c88dd9a2e8aa02503a3b95eae8 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Thu, 16 Jul 2020 16:51:12 -0700 Subject: Enable detecting multiple-argument panics --- clippy_lints/src/panic_unimplemented.rs | 9 ++--- tests/ui/panicking_macros.rs | 8 +++++ tests/ui/panicking_macros.stderr | 62 ++++++++++++++++++++++++++++++--- 3 files changed, 69 insertions(+), 10 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/panic_unimplemented.rs b/clippy_lints/src/panic_unimplemented.rs index 10f4694640e..9944b4096ba 100644 --- a/clippy_lints/src/panic_unimplemented.rs +++ b/clippy_lints/src/panic_unimplemented.rs @@ -96,23 +96,20 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { if_chain! { if let ExprKind::Block(ref block, _) = expr.kind; if let Some(ref ex) = block.expr; - if let Some(params) = match_function_call(cx, ex, &paths::BEGIN_PANIC); - if params.len() == 1; + if let Some(params) = match_function_call(cx, ex, &paths::BEGIN_PANIC) + .or(match_function_call(cx, ex, &paths::BEGIN_PANIC_FMT)); then { + let span = get_outer_span(expr); if is_expn_of(expr.span, "unimplemented").is_some() { - let span = get_outer_span(expr); span_lint(cx, UNIMPLEMENTED, span, "`unimplemented` should not be present in production code"); } else if is_expn_of(expr.span, "todo").is_some() { - let span = get_outer_span(expr); span_lint(cx, TODO, span, "`todo` should not be present in production code"); } else if is_expn_of(expr.span, "unreachable").is_some() { - let span = get_outer_span(expr); span_lint(cx, UNREACHABLE, span, "`unreachable` should not be present in production code"); } else if is_expn_of(expr.span, "panic").is_some() { - let span = get_outer_span(expr); span_lint(cx, PANIC, span, "`panic` should not be present in production code"); match_panic(params, expr, cx); diff --git a/tests/ui/panicking_macros.rs b/tests/ui/panicking_macros.rs index dabb695368d..f91ccfaed74 100644 --- a/tests/ui/panicking_macros.rs +++ b/tests/ui/panicking_macros.rs @@ -4,24 +4,32 @@ fn panic() { let a = 2; panic!(); + panic!("message"); + panic!("{} {}", "panic with", "multiple arguments"); let b = a + 2; } fn todo() { let a = 2; todo!(); + todo!("message"); + todo!("{} {}", "panic with", "multiple arguments"); let b = a + 2; } fn unimplemented() { let a = 2; unimplemented!(); + unimplemented!("message"); + unimplemented!("{} {}", "panic with", "multiple arguments"); let b = a + 2; } fn unreachable() { let a = 2; unreachable!(); + unreachable!("message"); + unreachable!("{} {}", "panic with", "multiple arguments"); let b = a + 2; } diff --git a/tests/ui/panicking_macros.stderr b/tests/ui/panicking_macros.stderr index 72319bc7e45..37c11d72a57 100644 --- a/tests/ui/panicking_macros.stderr +++ b/tests/ui/panicking_macros.stderr @@ -6,29 +6,83 @@ LL | panic!(); | = note: `-D clippy::panic` implied by `-D warnings` +error: `panic` should not be present in production code + --> $DIR/panicking_macros.rs:7:5 + | +LL | panic!("message"); + | ^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `panic` should not be present in production code + --> $DIR/panicking_macros.rs:8:5 + | +LL | panic!("{} {}", "panic with", "multiple arguments"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + error: `todo` should not be present in production code - --> $DIR/panicking_macros.rs:12:5 + --> $DIR/panicking_macros.rs:14:5 | LL | todo!(); | ^^^^^^^^ | = note: `-D clippy::todo` implied by `-D warnings` +error: `todo` should not be present in production code + --> $DIR/panicking_macros.rs:15:5 + | +LL | todo!("message"); + | ^^^^^^^^^^^^^^^^^ + +error: `todo` should not be present in production code + --> $DIR/panicking_macros.rs:16:5 + | +LL | todo!("{} {}", "panic with", "multiple arguments"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: `unimplemented` should not be present in production code - --> $DIR/panicking_macros.rs:18:5 + --> $DIR/panicking_macros.rs:22:5 | LL | unimplemented!(); | ^^^^^^^^^^^^^^^^^ | = note: `-D clippy::unimplemented` implied by `-D warnings` -error: `unreachable` should not be present in production code +error: `unimplemented` should not be present in production code + --> $DIR/panicking_macros.rs:23:5 + | +LL | unimplemented!("message"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `unimplemented` should not be present in production code --> $DIR/panicking_macros.rs:24:5 | +LL | unimplemented!("{} {}", "panic with", "multiple arguments"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `unreachable` should not be present in production code + --> $DIR/panicking_macros.rs:30:5 + | LL | unreachable!(); | ^^^^^^^^^^^^^^^ | = note: `-D clippy::unreachable` implied by `-D warnings` -error: aborting due to 4 previous errors +error: `unreachable` should not be present in production code + --> $DIR/panicking_macros.rs:31:5 + | +LL | unreachable!("message"); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `unreachable` should not be present in production code + --> $DIR/panicking_macros.rs:32:5 + | +LL | unreachable!("{} {}", "panic with", "multiple arguments"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 12 previous errors -- cgit 1.4.1-3-g733a5 From 3618b97f59d9696c3e5ef83948269d0d7abfdc5b Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Fri, 17 Jul 2020 01:58:41 +0200 Subject: fix typos (found by codespell) --- clippy_lints/src/deprecated_lints.rs | 2 +- clippy_lints/src/dereference.rs | 2 +- clippy_lints/src/inherent_impl.rs | 2 +- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/missing_const_for_fn.rs | 2 +- clippy_lints/src/modulo_arithmetic.rs | 2 +- clippy_lints/src/option_if_let_else.rs | 2 +- clippy_lints/src/utils/numeric_literal.rs | 2 +- tests/ui/manual_async_fn.fixed | 2 +- tests/ui/manual_async_fn.rs | 2 +- tests/ui/manual_async_fn.stderr | 2 +- tests/ui/never_loop.rs | 2 +- tests/ui/precedence.fixed | 2 +- tests/ui/precedence.rs | 2 +- tests/ui/vec_resize_to_zero.rs | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index 818d8188a78..c17a0e83330 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -153,7 +153,7 @@ declare_deprecated_lint! { /// /// **Deprecation reason:** Associated-constants are now preferred. pub REPLACE_CONSTS, - "associated-constants `MIN`/`MAX` of integers are prefered to `{min,max}_value()` and module constants" + "associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants" } declare_deprecated_lint! { diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 323cad7fa1a..7a3f35aca0a 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -10,7 +10,7 @@ use rustc_span::source_map::Span; declare_clippy_lint! { /// **What it does:** Checks for explicit `deref()` or `deref_mut()` method calls. /// - /// **Why is this bad?** Derefencing by `&*x` or `&mut *x` is clearer and more concise, + /// **Why is this bad?** Dereferencing by `&*x` or `&mut *x` is clearer and more concise, /// when not part of a method chain. /// /// **Example:** diff --git a/clippy_lints/src/inherent_impl.rs b/clippy_lints/src/inherent_impl.rs index bd7ca038839..9fb10c7f627 100644 --- a/clippy_lints/src/inherent_impl.rs +++ b/clippy_lints/src/inherent_impl.rs @@ -55,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl { .. } = item.kind { - // Remember for each inherent implementation encoutered its span and generics + // Remember for each inherent implementation encountered its span and generics // but filter out implementations that have generic params (type or lifetime) // or are derived from a macro if !in_macro(item.span) && generics.params.is_empty() { diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 7a4ca3902b3..823afdfd289 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -463,7 +463,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ); store.register_removed( "clippy::replace_consts", - "associated-constants `MIN`/`MAX` of integers are prefered to `{min,max}_value()` and module constants", + "associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants", ); store.register_removed( "clippy::regex_macro", diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index bdce1bf1521..1ad184dfc46 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -131,7 +131,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { /// Returns true if any of the method parameters is a type that implements `Drop`. The method /// can't be made const then, because `drop` can't be const-evaluated. fn method_accepts_dropable(cx: &LateContext<'_>, param_tys: &[hir::Ty<'_>]) -> bool { - // If any of the params are dropable, return true + // If any of the params are droppable, return true param_tys.iter().any(|hir_ty| { let ty_ty = hir_ty_to_ty(cx.tcx, hir_ty); has_drop(cx, ty_ty) diff --git a/clippy_lints/src/modulo_arithmetic.rs b/clippy_lints/src/modulo_arithmetic.rs index 59ccc6333fd..5041af750ce 100644 --- a/clippy_lints/src/modulo_arithmetic.rs +++ b/clippy_lints/src/modulo_arithmetic.rs @@ -8,7 +8,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use std::fmt::Display; declare_clippy_lint! { - /// **What it does:** Checks for modulo arithemtic. + /// **What it does:** Checks for modulo arithmetic. /// /// **Why is this bad?** The results of modulo (%) operation might differ /// depending on the language, when negative numbers are involved. diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 8dbe58763bf..9922d906118 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -78,7 +78,7 @@ fn is_result_ok(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool { } } -/// A struct containing information about occurences of the +/// A struct containing information about occurrences of the /// `if let Some(..) = .. else` construct that this lint detects. struct OptionIfLetElseOccurence { option: String, diff --git a/clippy_lints/src/utils/numeric_literal.rs b/clippy_lints/src/utils/numeric_literal.rs index 87cb454f654..5e8800d38eb 100644 --- a/clippy_lints/src/utils/numeric_literal.rs +++ b/clippy_lints/src/utils/numeric_literal.rs @@ -36,7 +36,7 @@ pub struct NumericLiteral<'a> { pub integer: &'a str, /// The fraction part of the number. pub fraction: Option<&'a str>, - /// The character used as exponent seperator (b'e' or b'E') and the exponent part. + /// The character used as exponent separator (b'e' or b'E') and the exponent part. pub exponent: Option<(char, &'a str)>, /// The type suffix, including preceding underscore if present. diff --git a/tests/ui/manual_async_fn.fixed b/tests/ui/manual_async_fn.fixed index 6bb1032a172..27222cc0869 100644 --- a/tests/ui/manual_async_fn.fixed +++ b/tests/ui/manual_async_fn.fixed @@ -30,7 +30,7 @@ async fn already_async() -> impl Future { struct S {} impl S { async fn inh_fut() -> i32 { - // NOTE: this code is here just to check that the identation is correct in the suggested fix + // NOTE: this code is here just to check that the indentation is correct in the suggested fix let a = 42; let b = 21; if a < b { diff --git a/tests/ui/manual_async_fn.rs b/tests/ui/manual_async_fn.rs index d50c919188b..6a0f1b26c88 100644 --- a/tests/ui/manual_async_fn.rs +++ b/tests/ui/manual_async_fn.rs @@ -37,7 +37,7 @@ struct S {} impl S { fn inh_fut() -> impl Future { async { - // NOTE: this code is here just to check that the identation is correct in the suggested fix + // NOTE: this code is here just to check that the indentation is correct in the suggested fix let a = 42; let b = 21; if a < b { diff --git a/tests/ui/manual_async_fn.stderr b/tests/ui/manual_async_fn.stderr index f278ee41aa3..a1904c904d0 100644 --- a/tests/ui/manual_async_fn.stderr +++ b/tests/ui/manual_async_fn.stderr @@ -57,7 +57,7 @@ LL | async fn inh_fut() -> i32 { help: move the body of the async block to the enclosing function | LL | fn inh_fut() -> impl Future { -LL | // NOTE: this code is here just to check that the identation is correct in the suggested fix +LL | // NOTE: this code is here just to check that the indentation is correct in the suggested fix LL | let a = 42; LL | let b = 21; LL | if a < b { diff --git a/tests/ui/never_loop.rs b/tests/ui/never_loop.rs index cbc4ca39161..2770eb2b2ab 100644 --- a/tests/ui/never_loop.rs +++ b/tests/ui/never_loop.rs @@ -166,7 +166,7 @@ pub fn test14() { } } -// Issue #1991: the outter loop should not warn. +// Issue #1991: the outer loop should not warn. pub fn test15() { 'label: loop { while false { diff --git a/tests/ui/precedence.fixed b/tests/ui/precedence.fixed index 17b1f1bd0bf..4d284ae1319 100644 --- a/tests/ui/precedence.fixed +++ b/tests/ui/precedence.fixed @@ -32,7 +32,7 @@ fn main() { let _ = -(1i32.abs()); let _ = -(1f32.abs()); - // Odd functions shoud not trigger an error + // Odd functions should not trigger an error let _ = -1f64.asin(); let _ = -1f64.asinh(); let _ = -1f64.atan(); diff --git a/tests/ui/precedence.rs b/tests/ui/precedence.rs index 2d0891fd3c2..2d08e82f349 100644 --- a/tests/ui/precedence.rs +++ b/tests/ui/precedence.rs @@ -32,7 +32,7 @@ fn main() { let _ = -(1i32.abs()); let _ = -(1f32.abs()); - // Odd functions shoud not trigger an error + // Odd functions should not trigger an error let _ = -1f64.asin(); let _ = -1f64.asinh(); let _ = -1f64.atan(); diff --git a/tests/ui/vec_resize_to_zero.rs b/tests/ui/vec_resize_to_zero.rs index 0263e2f5f20..7ed27439ec6 100644 --- a/tests/ui/vec_resize_to_zero.rs +++ b/tests/ui/vec_resize_to_zero.rs @@ -7,7 +7,7 @@ fn main() { // not applicable vec![1, 2, 3, 4, 5].resize(2, 5); - // applicable here, but only implemented for integer litterals for now + // applicable here, but only implemented for integer literals for now vec!["foo", "bar", "baz"].resize(0, "bar"); // not applicable -- cgit 1.4.1-3-g733a5 From 7c5d4a41459abeb40eea734efaf08657602815cb Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Fri, 17 Jul 2020 09:27:43 -0700 Subject: Add test for correct behavior --- tests/ui/redundant_pattern_matching.fixed | 3 ++ tests/ui/redundant_pattern_matching.rs | 3 ++ tests/ui/redundant_pattern_matching.stderr | 68 ++++++++++++++++-------------- 3 files changed, 43 insertions(+), 31 deletions(-) (limited to 'tests') diff --git a/tests/ui/redundant_pattern_matching.fixed b/tests/ui/redundant_pattern_matching.fixed index ce8582d2b22..adbff8af8d9 100644 --- a/tests/ui/redundant_pattern_matching.fixed +++ b/tests/ui/redundant_pattern_matching.fixed @@ -11,6 +11,9 @@ )] fn main() { + let result: Result = Err(5); + if result.is_ok() {} + if Ok::(42).is_ok() {} if Err::(42).is_err() {} diff --git a/tests/ui/redundant_pattern_matching.rs b/tests/ui/redundant_pattern_matching.rs index a3a9aa40e3b..4c2870e7803 100644 --- a/tests/ui/redundant_pattern_matching.rs +++ b/tests/ui/redundant_pattern_matching.rs @@ -11,6 +11,9 @@ )] fn main() { + let result: Result = Err(5); + if let Ok(_) = &result {} + if let Ok(_) = Ok::(42) {} if let Err(_) = Err::(42) {} diff --git a/tests/ui/redundant_pattern_matching.stderr b/tests/ui/redundant_pattern_matching.stderr index 25d1476062e..d3c9ceaa3d7 100644 --- a/tests/ui/redundant_pattern_matching.stderr +++ b/tests/ui/redundant_pattern_matching.stderr @@ -1,73 +1,79 @@ error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:14:12 + --> $DIR/redundant_pattern_matching.rs:15:12 | -LL | if let Ok(_) = Ok::(42) {} - | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` +LL | if let Ok(_) = &result {} + | -------^^^^^---------- help: try this: `if result.is_ok()` | = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching.rs:17:12 + | +LL | if let Ok(_) = Ok::(42) {} + | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` + error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:16:12 + --> $DIR/redundant_pattern_matching.rs:19:12 | LL | if let Err(_) = Err::(42) {} | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:18:12 + --> $DIR/redundant_pattern_matching.rs:21: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.rs:20:12 + --> $DIR/redundant_pattern_matching.rs:23: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.rs:22:12 + --> $DIR/redundant_pattern_matching.rs:25: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.rs:28:15 + --> $DIR/redundant_pattern_matching.rs:31: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.rs:30:15 + --> $DIR/redundant_pattern_matching.rs:33: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.rs:32:15 + --> $DIR/redundant_pattern_matching.rs:35:15 | LL | while let None = None::<()> {} | ----------^^^^------------- help: try this: `while None::<()>.is_none()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:34:15 + --> $DIR/redundant_pattern_matching.rs:37:15 | LL | while let Ok(_) = Ok::(10) {} | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:36:15 + --> $DIR/redundant_pattern_matching.rs:39:15 | LL | while let Err(_) = Ok::(10) {} | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:39:15 + --> $DIR/redundant_pattern_matching.rs:42:15 | LL | while let Some(_) = v.pop() { | ----------^^^^^^^---------- help: try this: `while v.pop().is_some()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:55:5 + --> $DIR/redundant_pattern_matching.rs:58:5 | LL | / match Ok::(42) { LL | | Ok(_) => true, @@ -76,7 +82,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:60:5 + --> $DIR/redundant_pattern_matching.rs:63:5 | LL | / match Ok::(42) { LL | | Ok(_) => false, @@ -85,7 +91,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_err()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:65:5 + --> $DIR/redundant_pattern_matching.rs:68:5 | LL | / match Err::(42) { LL | | Ok(_) => false, @@ -94,7 +100,7 @@ LL | | }; | |_____^ help: try this: `Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:70:5 + --> $DIR/redundant_pattern_matching.rs:73:5 | LL | / match Err::(42) { LL | | Ok(_) => true, @@ -103,7 +109,7 @@ LL | | }; | |_____^ help: try this: `Err::(42).is_ok()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:75:5 + --> $DIR/redundant_pattern_matching.rs:78:5 | LL | / match Some(42) { LL | | Some(_) => true, @@ -112,7 +118,7 @@ LL | | }; | |_____^ help: try this: `Some(42).is_some()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:80:5 + --> $DIR/redundant_pattern_matching.rs:83:5 | LL | / match None::<()> { LL | | Some(_) => false, @@ -121,7 +127,7 @@ LL | | }; | |_____^ help: try this: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:85:13 + --> $DIR/redundant_pattern_matching.rs:88:13 | LL | let _ = match None::<()> { | _____________^ @@ -131,64 +137,64 @@ LL | | }; | |_____^ help: try this: `None::<()>.is_none()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:90:20 + --> $DIR/redundant_pattern_matching.rs:93:20 | LL | let _ = if let Ok(_) = Ok::(4) { true } else { false }; | -------^^^^^--------------------- help: try this: `if Ok::(4).is_ok()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:93:20 + --> $DIR/redundant_pattern_matching.rs:96:20 | LL | let x = 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.rs:99:20 + --> $DIR/redundant_pattern_matching.rs:102: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.rs:101:19 + --> $DIR/redundant_pattern_matching.rs:104:19 | LL | } else if let None = gen_opt() { | -------^^^^------------ help: try this: `if gen_opt().is_none()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:103:19 + --> $DIR/redundant_pattern_matching.rs:106:19 | LL | } else if let Ok(_) = gen_res() { | -------^^^^^------------ help: try this: `if gen_res().is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:105:19 + --> $DIR/redundant_pattern_matching.rs:108:19 | LL | } else if let Err(_) = gen_res() { | -------^^^^^^------------ help: try this: `if gen_res().is_err()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:138:19 + --> $DIR/redundant_pattern_matching.rs:141:19 | LL | while let Some(_) = r#try!(result_opt()) {} | ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:139:16 + --> $DIR/redundant_pattern_matching.rs:142:16 | LL | if let Some(_) = r#try!(result_opt()) {} | -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:145:12 + --> $DIR/redundant_pattern_matching.rs:148:12 | LL | if let Some(_) = m!() {} | -------^^^^^^^------- help: try this: `if m!().is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:146:15 + --> $DIR/redundant_pattern_matching.rs:149:15 | LL | while let Some(_) = m!() {} | ----------^^^^^^^------- help: try this: `while m!().is_some()` -error: aborting due to 28 previous errors +error: aborting due to 29 previous errors -- cgit 1.4.1-3-g733a5 From 442c8ae23b90874485468b3becfc011f8c9d40bb Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sat, 18 Jul 2020 23:59:34 +0200 Subject: Fix FP for `suspicious_arithmetic_impl` from `suspicious_trait_impl` lint --- clippy_lints/src/suspicious_trait_impl.rs | 37 ++++++++++++++----------------- tests/ui/suspicious_arithmetic_impl.rs | 30 +++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 20 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/suspicious_trait_impl.rs b/clippy_lints/src/suspicious_trait_impl.rs index 6d1d083fa8d..502fffc5e6c 100644 --- a/clippy_lints/src/suspicious_trait_impl.rs +++ b/clippy_lints/src/suspicious_trait_impl.rs @@ -64,26 +64,22 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl { | hir::BinOpKind::Gt => return, _ => {}, } - // Check if the binary expression is part of another bi/unary expression - // or operator assignment as a child node - let mut parent_expr = cx.tcx.hir().get_parent_node(expr.hir_id); - while parent_expr != hir::CRATE_HIR_ID { - if let hir::Node::Expr(e) = cx.tcx.hir().get(parent_expr) { - match e.kind { - hir::ExprKind::Binary(..) - | hir::ExprKind::Unary(hir::UnOp::UnNot | hir::UnOp::UnNeg, _) - | hir::ExprKind::AssignOp(..) => return, - _ => {}, + + // Check for more than one binary operation in the implemented function + // Linting when multiple operations are involved can result in false positives + if_chain! { + let parent_fn = cx.tcx.hir().get_parent_item(expr.hir_id); + if let hir::Node::ImplItem(impl_item) = cx.tcx.hir().get(parent_fn); + if let hir::ImplItemKind::Fn(_, body_id) = impl_item.kind; + let body = cx.tcx.hir().body(body_id); + let mut visitor = BinaryExprVisitor { nb_binops: 0 }; + + then { + walk_expr(&mut visitor, &body.value); + if visitor.nb_binops > 1 { + return; } } - parent_expr = cx.tcx.hir().get_parent_node(parent_expr); - } - // as a parent node - let mut visitor = BinaryExprVisitor { in_binary_expr: false }; - walk_expr(&mut visitor, expr); - - if visitor.in_binary_expr { - return; } if let Some(impl_trait) = check_binop( @@ -181,7 +177,7 @@ fn check_binop( } struct BinaryExprVisitor { - in_binary_expr: bool, + nb_binops: u32, } impl<'tcx> Visitor<'tcx> for BinaryExprVisitor { @@ -191,12 +187,13 @@ impl<'tcx> Visitor<'tcx> for BinaryExprVisitor { match expr.kind { hir::ExprKind::Binary(..) | hir::ExprKind::Unary(hir::UnOp::UnNot | hir::UnOp::UnNeg, _) - | hir::ExprKind::AssignOp(..) => self.in_binary_expr = true, + | hir::ExprKind::AssignOp(..) => self.nb_binops += 1, _ => {}, } walk_expr(self, expr); } + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None } diff --git a/tests/ui/suspicious_arithmetic_impl.rs b/tests/ui/suspicious_arithmetic_impl.rs index 1f5b9811887..60c2f3ec9b6 100644 --- a/tests/ui/suspicious_arithmetic_impl.rs +++ b/tests/ui/suspicious_arithmetic_impl.rs @@ -88,3 +88,33 @@ fn main() {} fn do_nothing(x: u32) -> u32 { x } + +struct MultipleBinops(u32); + +impl Add for MultipleBinops { + type Output = MultipleBinops; + + // OK: multiple Binops in `add` impl + fn add(self, other: Self) -> Self::Output { + let mut result = self.0 + other.0; + if result >= u32::max_value() { + result -= u32::max_value(); + } + MultipleBinops(result) + } +} + +impl Mul for MultipleBinops { + type Output = MultipleBinops; + + // OK: multiple Binops in `mul` impl + fn mul(self, other: Self) -> Self::Output { + let mut result: u32 = 0; + let size = std::cmp::max(self.0, other.0) as usize; + let mut v = vec![0; size + 1]; + for i in 0..size + 1 { + result *= i as u32; + } + MultipleBinops(result) + } +} -- cgit 1.4.1-3-g733a5 From c720d823e1e633cccfd24e1df76cc04a628e83b0 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Tue, 14 Jul 2020 20:27:25 +0200 Subject: redundant_closure_call - don't lint when used more than once --- clippy_lints/src/misc_early.rs | 40 +++++++++++++++++++++++++++------- tests/ui/redundant_closure_call.rs | 15 +++++++++---- tests/ui/redundant_closure_call.stderr | 14 ++++-------- 3 files changed, 47 insertions(+), 22 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/misc_early.rs b/clippy_lints/src/misc_early.rs index b84a1a3fe24..125df226ceb 100644 --- a/clippy_lints/src/misc_early.rs +++ b/clippy_lints/src/misc_early.rs @@ -14,6 +14,7 @@ use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; +use rustc_span::symbol::Ident; declare_clippy_lint! { /// **What it does:** Checks for structure field patterns bound to wildcards. @@ -493,6 +494,29 @@ impl EarlyLintPass for MiscEarlyLints { } fn check_block(&mut self, cx: &EarlyContext<'_>, block: &Block) { + fn count_closure_usage(block: &Block, ident: &Ident) -> usize { + struct ClosureUsageCount<'ast> { + ident: &'ast Ident, + count: usize, + }; + impl<'ast> Visitor<'ast> for ClosureUsageCount<'ast> { + fn visit_expr(&mut self, expr: &'ast Expr) { + if_chain! { + if let ExprKind::Call(ref closure, _) = expr.kind; + if let ExprKind::Path(_, ref path) = closure.kind; + if self.ident == &path.segments[0].ident; + then { + self.count += 1; + } + } + walk_expr(self, expr); + } + } + let mut closure_usage_count = ClosureUsageCount { ident, count: 0 }; + closure_usage_count.visit_block(block); + closure_usage_count.count + } + for w in block.stmts.windows(2) { if_chain! { if let StmtKind::Local(ref local) = w[0].kind; @@ -503,15 +527,15 @@ impl EarlyLintPass for MiscEarlyLints { if let ExprKind::Assign(_, ref call, _) = second.kind; if let ExprKind::Call(ref closure, _) = call.kind; if let ExprKind::Path(_, ref path) = closure.kind; + if ident == path.segments[0].ident; + if count_closure_usage(block, &ident) == 1; then { - if ident == path.segments[0].ident { - span_lint( - cx, - REDUNDANT_CLOSURE_CALL, - second.span, - "Closure called just once immediately after it was declared", - ); - } + span_lint( + cx, + REDUNDANT_CLOSURE_CALL, + second.span, + "Closure called just once immediately after it was declared", + ); } } } diff --git a/tests/ui/redundant_closure_call.rs b/tests/ui/redundant_closure_call.rs index bacd67db7c3..0f2ba4a075d 100644 --- a/tests/ui/redundant_closure_call.rs +++ b/tests/ui/redundant_closure_call.rs @@ -8,14 +8,21 @@ fn main() { k = (|a, b| a * b)(1, 5); - let closure = || 32; - i = closure(); - + // don't lint here, the closure is used more than once let closure = |i| i + 1; i = closure(3); - i = closure(4); + // lint here + let redun_closure = || 1; + i = redun_closure(); + + // the lint is applicable here but the lint doesn't support redefinition + let redefined_closure = || 1; + i = redefined_closure(); + let redefined_closure = || 2; + i = redefined_closure(); + #[allow(clippy::needless_return)] (|| return 2)(); (|| -> Option { None? })(); diff --git a/tests/ui/redundant_closure_call.stderr b/tests/ui/redundant_closure_call.stderr index 68c1416bb6b..d5e0664319b 100644 --- a/tests/ui/redundant_closure_call.stderr +++ b/tests/ui/redundant_closure_call.stderr @@ -1,17 +1,11 @@ error: Closure called just once immediately after it was declared - --> $DIR/redundant_closure_call.rs:12:5 + --> $DIR/redundant_closure_call.rs:18:5 | -LL | i = closure(); - | ^^^^^^^^^^^^^ +LL | i = redun_closure(); + | ^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::redundant-closure-call` implied by `-D warnings` -error: Closure called just once immediately after it was declared - --> $DIR/redundant_closure_call.rs:15:5 - | -LL | i = closure(3); - | ^^^^^^^^^^^^^^ - error: Try not to call a closure in the expression where it is declared. --> $DIR/redundant_closure_call.rs:7:17 | @@ -24,5 +18,5 @@ error: Try not to call a closure in the expression where it is declared. LL | k = (|a, b| a * b)(1, 5); | ^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors -- cgit 1.4.1-3-g733a5 From 0fecaf1abcbe8d4ba1a9c532b69aab6fab3097c8 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Sun, 19 Jul 2020 00:33:54 +0200 Subject: redundant_closure_call - extract lint from misc_early.rs, adapt to LatePass --- clippy_lints/src/lib.rs | 9 +- clippy_lints/src/misc_early.rs | 132 +---------------------- clippy_lints/src/redundant_closure_call.rs | 151 +++++++++++++++++++++++++++ src/lintlist/mod.rs | 2 +- tests/ui/redundant_closure_call.rs | 30 ------ tests/ui/redundant_closure_call.stderr | 22 ---- tests/ui/redundant_closure_call_early.rs | 19 ++++ tests/ui/redundant_closure_call_early.stderr | 16 +++ tests/ui/redundant_closure_call_late.rs | 22 ++++ tests/ui/redundant_closure_call_late.stderr | 10 ++ 10 files changed, 229 insertions(+), 184 deletions(-) create mode 100644 clippy_lints/src/redundant_closure_call.rs delete mode 100644 tests/ui/redundant_closure_call.rs delete mode 100644 tests/ui/redundant_closure_call.stderr create mode 100644 tests/ui/redundant_closure_call_early.rs create mode 100644 tests/ui/redundant_closure_call_early.stderr create mode 100644 tests/ui/redundant_closure_call_late.rs create mode 100644 tests/ui/redundant_closure_call_late.stderr (limited to 'tests') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 823afdfd289..f371942dbee 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -276,6 +276,7 @@ mod ptr_offset_with_cast; mod question_mark; mod ranges; mod redundant_clone; +mod redundant_closure_call; mod redundant_field_names; mod redundant_pub_crate; mod redundant_static_lifetimes; @@ -702,7 +703,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &misc_early::DOUBLE_NEG, &misc_early::DUPLICATE_UNDERSCORE_ARGUMENT, &misc_early::MIXED_CASE_HEX_LITERALS, - &misc_early::REDUNDANT_CLOSURE_CALL, &misc_early::REDUNDANT_PATTERN, &misc_early::UNNEEDED_FIELD_PATTERN, &misc_early::UNNEEDED_WILDCARD_PATTERN, @@ -759,6 +759,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &ranges::RANGE_ZIP_WITH_LEN, &ranges::REVERSED_EMPTY_RANGES, &redundant_clone::REDUNDANT_CLONE, + &redundant_closure_call::REDUNDANT_CLOSURE_CALL, &redundant_field_names::REDUNDANT_FIELD_NAMES, &redundant_pub_crate::REDUNDANT_PUB_CRATE, &redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES, @@ -1018,6 +1019,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box int_plus_one::IntPlusOne); store.register_early_pass(|| box formatting::Formatting); store.register_early_pass(|| box misc_early::MiscEarlyLints); + store.register_early_pass(|| box redundant_closure_call::RedundantClosureCall); + store.register_late_pass(|| box redundant_closure_call::RedundantClosureCall); store.register_early_pass(|| box returns::Return); store.register_late_pass(|| box let_and_return::LetReturn); store.register_early_pass(|| box collapsible_if::CollapsibleIf); @@ -1359,7 +1362,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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_CLOSURE_CALL), LintId::of(&misc_early::REDUNDANT_PATTERN), LintId::of(&misc_early::UNNEEDED_WILDCARD_PATTERN), LintId::of(&misc_early::ZERO_PREFIXED_LITERAL), @@ -1393,6 +1395,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), LintId::of(&reference::DEREF_ADDROF), @@ -1593,7 +1596,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::UNNECESSARY_FILTER_MAP), LintId::of(&methods::USELESS_ASREF), LintId::of(&misc::SHORT_CIRCUIT_STATEMENT), - LintId::of(&misc_early::REDUNDANT_CLOSURE_CALL), LintId::of(&misc_early::UNNEEDED_WILDCARD_PATTERN), LintId::of(&misc_early::ZERO_PREFIXED_LITERAL), LintId::of(&needless_bool::BOOL_COMPARISON), @@ -1608,6 +1610,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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(&reference::DEREF_ADDROF), LintId::of(&reference::REF_IN_DEREF), LintId::of(&repeat_once::REPEAT_ONCE), diff --git a/clippy_lints/src/misc_early.rs b/clippy_lints/src/misc_early.rs index 125df226ceb..29aba7c1218 100644 --- a/clippy_lints/src/misc_early.rs +++ b/clippy_lints/src/misc_early.rs @@ -1,20 +1,15 @@ -use crate::utils::{ - constants, snippet_opt, snippet_with_applicability, span_lint, span_lint_and_help, span_lint_and_sugg, - span_lint_and_then, -}; -use if_chain::if_chain; +use crate::utils::{constants, snippet_opt, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use rustc_ast::ast::{ - BindingMode, Block, Expr, ExprKind, GenericParamKind, Generics, Lit, LitFloatType, LitIntType, LitKind, Mutability, - NodeId, Pat, PatKind, StmtKind, UnOp, + BindingMode, Expr, ExprKind, GenericParamKind, Generics, Lit, LitFloatType, LitIntType, LitKind, Mutability, + NodeId, Pat, PatKind, UnOp, }; -use rustc_ast::visit::{walk_expr, FnKind, Visitor}; +use rustc_ast::visit::FnKind; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use rustc_span::symbol::Ident; declare_clippy_lint! { /// **What it does:** Checks for structure field patterns bound to wildcards. @@ -71,28 +66,6 @@ declare_clippy_lint! { "function arguments having names which only differ by an underscore" } -declare_clippy_lint! { - /// **What it does:** Detects closures called in the same expression where they - /// are defined. - /// - /// **Why is this bad?** It is unnecessarily adding to the expression's - /// complexity. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust,ignore - /// // Bad - /// let a = (|| 42)() - /// - /// // Good - /// let a = 42 - /// ``` - pub REDUNDANT_CLOSURE_CALL, - complexity, - "throwaway closures called in the expression they are defined" -} - declare_clippy_lint! { /// **What it does:** Detects expressions of the form `--x`. /// @@ -279,7 +252,6 @@ declare_clippy_lint! { declare_lint_pass!(MiscEarlyLints => [ UNNEEDED_FIELD_PATTERN, DUPLICATE_UNDERSCORE_ARGUMENT, - REDUNDANT_CLOSURE_CALL, DOUBLE_NEG, MIXED_CASE_HEX_LITERALS, UNSEPARATED_LITERAL_SUFFIX, @@ -289,30 +261,6 @@ declare_lint_pass!(MiscEarlyLints => [ UNNEEDED_WILDCARD_PATTERN, ]); -// Used to find `return` statements or equivalents e.g., `?` -struct ReturnVisitor { - found_return: bool, -} - -impl ReturnVisitor { - #[must_use] - fn new() -> Self { - Self { found_return: false } - } -} - -impl<'ast> Visitor<'ast> for ReturnVisitor { - fn visit_expr(&mut self, ex: &'ast Expr) { - if let ExprKind::Ret(_) = ex.kind { - self.found_return = true; - } else if let ExprKind::Try(_) = ex.kind { - self.found_return = true; - } - - walk_expr(self, ex) - } -} - impl EarlyLintPass for MiscEarlyLints { fn check_generics(&mut self, cx: &EarlyContext<'_>, gen: &Generics) { for param in &gen.params { @@ -454,30 +402,6 @@ impl EarlyLintPass for MiscEarlyLints { return; } match expr.kind { - ExprKind::Call(ref paren, _) => { - if let ExprKind::Paren(ref closure) = paren.kind { - if let ExprKind::Closure(_, _, _, ref decl, ref block, _) = closure.kind { - let mut visitor = ReturnVisitor::new(); - visitor.visit_expr(block); - if !visitor.found_return { - span_lint_and_then( - cx, - REDUNDANT_CLOSURE_CALL, - expr.span, - "Try not to call a closure in the expression where it is declared.", - |diag| { - if decl.inputs.is_empty() { - let mut app = Applicability::MachineApplicable; - let hint = - snippet_with_applicability(cx, block.span, "..", &mut app).into_owned(); - diag.span_suggestion(expr.span, "Try doing something like: ", hint, app); - } - }, - ); - } - } - } - }, ExprKind::Unary(UnOp::Neg, ref inner) => { if let ExprKind::Unary(UnOp::Neg, _) = inner.kind { span_lint( @@ -492,54 +416,6 @@ impl EarlyLintPass for MiscEarlyLints { _ => (), } } - - fn check_block(&mut self, cx: &EarlyContext<'_>, block: &Block) { - fn count_closure_usage(block: &Block, ident: &Ident) -> usize { - struct ClosureUsageCount<'ast> { - ident: &'ast Ident, - count: usize, - }; - impl<'ast> Visitor<'ast> for ClosureUsageCount<'ast> { - fn visit_expr(&mut self, expr: &'ast Expr) { - if_chain! { - if let ExprKind::Call(ref closure, _) = expr.kind; - if let ExprKind::Path(_, ref path) = closure.kind; - if self.ident == &path.segments[0].ident; - then { - self.count += 1; - } - } - walk_expr(self, expr); - } - } - let mut closure_usage_count = ClosureUsageCount { ident, count: 0 }; - closure_usage_count.visit_block(block); - closure_usage_count.count - } - - for w in block.stmts.windows(2) { - if_chain! { - if let StmtKind::Local(ref local) = w[0].kind; - if let Option::Some(ref t) = local.init; - if let ExprKind::Closure(..) = t.kind; - if let PatKind::Ident(_, ident, _) = local.pat.kind; - if let StmtKind::Semi(ref second) = w[1].kind; - if let ExprKind::Assign(_, ref call, _) = second.kind; - if let ExprKind::Call(ref closure, _) = call.kind; - if let ExprKind::Path(_, ref path) = closure.kind; - if ident == path.segments[0].ident; - if count_closure_usage(block, &ident) == 1; - then { - span_lint( - cx, - REDUNDANT_CLOSURE_CALL, - second.span, - "Closure called just once immediately after it was declared", - ); - } - } - } - } } impl MiscEarlyLints { diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs new file mode 100644 index 00000000000..391d70af159 --- /dev/null +++ b/clippy_lints/src/redundant_closure_call.rs @@ -0,0 +1,151 @@ +use crate::utils::{snippet_with_applicability, span_lint, span_lint_and_then}; +use if_chain::if_chain; +use rustc_ast::ast; +use rustc_ast::visit as ast_visit; +use rustc_ast::visit::Visitor as AstVisitor; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::intravisit as hir_visit; +use rustc_hir::intravisit::Visitor as HirVisitor; +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; +use rustc_middle::hir::map::Map; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::Ident; + +declare_clippy_lint! { + /// **What it does:** Detects closures called in the same expression where they + /// are defined. + /// + /// **Why is this bad?** It is unnecessarily adding to the expression's + /// complexity. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// // Bad + /// let a = (|| 42)() + /// + /// // Good + /// let a = 42 + /// ``` + pub REDUNDANT_CLOSURE_CALL, + complexity, + "throwaway closures called in the expression they are defined" +} + +declare_lint_pass!(RedundantClosureCall => [REDUNDANT_CLOSURE_CALL]); + +// Used to find `return` statements or equivalents e.g., `?` +struct ReturnVisitor { + found_return: bool, +} + +impl ReturnVisitor { + #[must_use] + fn new() -> Self { + Self { found_return: false } + } +} + +impl<'ast> ast_visit::Visitor<'ast> for ReturnVisitor { + fn visit_expr(&mut self, ex: &'ast ast::Expr) { + if let ast::ExprKind::Ret(_) = ex.kind { + self.found_return = true; + } else if let ast::ExprKind::Try(_) = ex.kind { + self.found_return = true; + } + + ast_visit::walk_expr(self, ex) + } +} + +impl EarlyLintPass for RedundantClosureCall { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { + if in_external_macro(cx.sess(), expr.span) { + return; + } + if_chain! { + if let ast::ExprKind::Call(ref paren, _) = expr.kind; + if let ast::ExprKind::Paren(ref closure) = paren.kind; + if let ast::ExprKind::Closure(_, _, _, ref decl, ref block, _) = closure.kind; + then { + let mut visitor = ReturnVisitor::new(); + visitor.visit_expr(block); + if !visitor.found_return { + span_lint_and_then( + cx, + REDUNDANT_CLOSURE_CALL, + expr.span, + "Try not to call a closure in the expression where it is declared.", + |diag| { + if decl.inputs.is_empty() { + let mut app = Applicability::MachineApplicable; + let hint = + snippet_with_applicability(cx, block.span, "..", &mut app).into_owned(); + diag.span_suggestion(expr.span, "Try doing something like: ", hint, app); + } + }, + ); + } + } + } + } +} + +impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { + fn count_closure_usage<'tcx>(block: &'tcx hir::Block<'_>, ident: &'tcx Ident) -> usize { + struct ClosureUsageCount<'tcx> { + ident: &'tcx Ident, + count: usize, + }; + impl<'tcx> hir_visit::Visitor<'tcx> for ClosureUsageCount<'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { + if_chain! { + if let hir::ExprKind::Call(ref closure, _) = expr.kind; + if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = closure.kind; + if self.ident == &path.segments[0].ident; + then { + self.count += 1; + } + } + hir_visit::walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> hir_visit::NestedVisitorMap { + hir_visit::NestedVisitorMap::None + } + }; + let mut closure_usage_count = ClosureUsageCount { ident, count: 0 }; + closure_usage_count.visit_block(block); + closure_usage_count.count + } + + for w in block.stmts.windows(2) { + if_chain! { + if let hir::StmtKind::Local(ref local) = w[0].kind; + if let Option::Some(ref t) = local.init; + if let hir::ExprKind::Closure(..) = t.kind; + if let hir::PatKind::Binding(_, _, ident, _) = local.pat.kind; + if let hir::StmtKind::Semi(ref second) = w[1].kind; + if let hir::ExprKind::Assign(_, ref call, _) = second.kind; + if let hir::ExprKind::Call(ref closure, _) = call.kind; + if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = closure.kind; + if ident == path.segments[0].ident; + if count_closure_usage(block, &ident) == 1; + then { + span_lint( + cx, + REDUNDANT_CLOSURE_CALL, + second.span, + "Closure called just once immediately after it was declared", + ); + } + } + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 96b004904aa..1879aae77fb 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1835,7 +1835,7 @@ pub static ref ALL_LINTS: Vec = vec![ group: "complexity", desc: "throwaway closures called in the expression they are defined", deprecation: None, - module: "misc_early", + module: "redundant_closure_call", }, Lint { name: "redundant_closure_for_method_calls", diff --git a/tests/ui/redundant_closure_call.rs b/tests/ui/redundant_closure_call.rs deleted file mode 100644 index 0f2ba4a075d..00000000000 --- a/tests/ui/redundant_closure_call.rs +++ /dev/null @@ -1,30 +0,0 @@ -// non rustfixable, see redundant_closure_call_fixable.rs - -#![warn(clippy::redundant_closure_call)] - -fn main() { - let mut i = 1; - let mut k = (|m| m + 1)(i); - - k = (|a, b| a * b)(1, 5); - - // don't lint here, the closure is used more than once - let closure = |i| i + 1; - i = closure(3); - i = closure(4); - - // lint here - let redun_closure = || 1; - i = redun_closure(); - - // the lint is applicable here but the lint doesn't support redefinition - let redefined_closure = || 1; - i = redefined_closure(); - let redefined_closure = || 2; - i = redefined_closure(); - - #[allow(clippy::needless_return)] - (|| return 2)(); - (|| -> Option { None? })(); - (|| -> Result { Err(2)? })(); -} diff --git a/tests/ui/redundant_closure_call.stderr b/tests/ui/redundant_closure_call.stderr deleted file mode 100644 index d5e0664319b..00000000000 --- a/tests/ui/redundant_closure_call.stderr +++ /dev/null @@ -1,22 +0,0 @@ -error: Closure called just once immediately after it was declared - --> $DIR/redundant_closure_call.rs:18:5 - | -LL | i = redun_closure(); - | ^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::redundant-closure-call` implied by `-D warnings` - -error: Try not to call a closure in the expression where it is declared. - --> $DIR/redundant_closure_call.rs:7:17 - | -LL | let mut k = (|m| m + 1)(i); - | ^^^^^^^^^^^^^^ - -error: Try not to call a closure in the expression where it is declared. - --> $DIR/redundant_closure_call.rs:9:9 - | -LL | k = (|a, b| a * b)(1, 5); - | ^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 3 previous errors - diff --git a/tests/ui/redundant_closure_call_early.rs b/tests/ui/redundant_closure_call_early.rs new file mode 100644 index 00000000000..3dd365620cc --- /dev/null +++ b/tests/ui/redundant_closure_call_early.rs @@ -0,0 +1,19 @@ +// non rustfixable, see redundant_closure_call_fixable.rs + +#![warn(clippy::redundant_closure_call)] + +fn main() { + let mut i = 1; + + // lint here + let mut k = (|m| m + 1)(i); + + // lint here + k = (|a, b| a * b)(1, 5); + + // don't lint these + #[allow(clippy::needless_return)] + (|| return 2)(); + (|| -> Option { None? })(); + (|| -> Result { Err(2)? })(); +} diff --git a/tests/ui/redundant_closure_call_early.stderr b/tests/ui/redundant_closure_call_early.stderr new file mode 100644 index 00000000000..0ac97ae25d0 --- /dev/null +++ b/tests/ui/redundant_closure_call_early.stderr @@ -0,0 +1,16 @@ +error: Try not to call a closure in the expression where it is declared. + --> $DIR/redundant_closure_call_early.rs:9:17 + | +LL | let mut k = (|m| m + 1)(i); + | ^^^^^^^^^^^^^^ + | + = note: `-D clippy::redundant-closure-call` implied by `-D warnings` + +error: Try not to call a closure in the expression where it is declared. + --> $DIR/redundant_closure_call_early.rs:12:9 + | +LL | k = (|a, b| a * b)(1, 5); + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/redundant_closure_call_late.rs b/tests/ui/redundant_closure_call_late.rs new file mode 100644 index 00000000000..21ee58e5b44 --- /dev/null +++ b/tests/ui/redundant_closure_call_late.rs @@ -0,0 +1,22 @@ +// non rustfixable, see redundant_closure_call_fixable.rs + +#![warn(clippy::redundant_closure_call)] + +fn main() { + let mut i = 1; + + // don't lint here, the closure is used more than once + let closure = |i| i + 1; + i = closure(3); + i = closure(4); + + // lint here + let redun_closure = || 1; + i = redun_closure(); + + // the lint is applicable here but the lint doesn't support redefinition + let redefined_closure = || 1; + i = redefined_closure(); + let redefined_closure = || 2; + i = redefined_closure(); +} diff --git a/tests/ui/redundant_closure_call_late.stderr b/tests/ui/redundant_closure_call_late.stderr new file mode 100644 index 00000000000..0aebc3c419e --- /dev/null +++ b/tests/ui/redundant_closure_call_late.stderr @@ -0,0 +1,10 @@ +error: Closure called just once immediately after it was declared + --> $DIR/redundant_closure_call_late.rs:15:5 + | +LL | i = redun_closure(); + | ^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::redundant-closure-call` implied by `-D warnings` + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From 9603d9652b9d172842c77f566121437768bc962f Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Sun, 19 Jul 2020 11:47:32 +0200 Subject: redundant_closure_call - add support for shadowed closures --- clippy_lints/src/redundant_closure_call.rs | 12 ++++++------ tests/ui/redundant_closure_call_late.rs | 15 ++++++++++----- tests/ui/redundant_closure_call_late.stderr | 14 +++++++++++++- 3 files changed, 29 insertions(+), 12 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index 391d70af159..9477e79f567 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -11,7 +11,6 @@ use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintCon use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::symbol::Ident; declare_clippy_lint! { /// **What it does:** Detects closures called in the same expression where they @@ -96,9 +95,9 @@ impl EarlyLintPass for RedundantClosureCall { impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { - fn count_closure_usage<'tcx>(block: &'tcx hir::Block<'_>, ident: &'tcx Ident) -> usize { + fn count_closure_usage<'tcx>(block: &'tcx hir::Block<'_>, path: &'tcx hir::Path<'tcx>) -> usize { struct ClosureUsageCount<'tcx> { - ident: &'tcx Ident, + path: &'tcx hir::Path<'tcx>, count: usize, }; impl<'tcx> hir_visit::Visitor<'tcx> for ClosureUsageCount<'tcx> { @@ -108,7 +107,8 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { if_chain! { if let hir::ExprKind::Call(ref closure, _) = expr.kind; if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = closure.kind; - if self.ident == &path.segments[0].ident; + if self.path.segments[0].ident == path.segments[0].ident + && self.path.res == path.res; then { self.count += 1; } @@ -120,7 +120,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { hir_visit::NestedVisitorMap::None } }; - let mut closure_usage_count = ClosureUsageCount { ident, count: 0 }; + let mut closure_usage_count = ClosureUsageCount { path, count: 0 }; closure_usage_count.visit_block(block); closure_usage_count.count } @@ -136,7 +136,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { if let hir::ExprKind::Call(ref closure, _) = call.kind; if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = closure.kind; if ident == path.segments[0].ident; - if count_closure_usage(block, &ident) == 1; + if count_closure_usage(block, path) == 1; then { span_lint( cx, diff --git a/tests/ui/redundant_closure_call_late.rs b/tests/ui/redundant_closure_call_late.rs index 21ee58e5b44..e29a1dce0c7 100644 --- a/tests/ui/redundant_closure_call_late.rs +++ b/tests/ui/redundant_closure_call_late.rs @@ -14,9 +14,14 @@ fn main() { let redun_closure = || 1; i = redun_closure(); - // the lint is applicable here but the lint doesn't support redefinition - let redefined_closure = || 1; - i = redefined_closure(); - let redefined_closure = || 2; - i = redefined_closure(); + // shadowed closures are supported, lint here + let shadowed_closure = || 1; + i = shadowed_closure(); + let shadowed_closure = || 2; + i = shadowed_closure(); + + // don't lint here + let shadowed_closure = || 2; + i = shadowed_closure(); + i = shadowed_closure(); } diff --git a/tests/ui/redundant_closure_call_late.stderr b/tests/ui/redundant_closure_call_late.stderr index 0aebc3c419e..0e000fee85a 100644 --- a/tests/ui/redundant_closure_call_late.stderr +++ b/tests/ui/redundant_closure_call_late.stderr @@ -6,5 +6,17 @@ LL | i = redun_closure(); | = note: `-D clippy::redundant-closure-call` implied by `-D warnings` -error: aborting due to previous error +error: Closure called just once immediately after it was declared + --> $DIR/redundant_closure_call_late.rs:19:5 + | +LL | i = shadowed_closure(); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: Closure called just once immediately after it was declared + --> $DIR/redundant_closure_call_late.rs:21:5 + | +LL | i = shadowed_closure(); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors -- cgit 1.4.1-3-g733a5 From 1ac8b85c9fd38ff0a14061cae0b4d31e9bfafd56 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Mon, 20 Jul 2020 00:36:31 +0200 Subject: redundant_closure_call - pr review --- clippy_lints/src/redundant_closure_call.rs | 8 ++++---- tests/ui/redundant_closure_call_early.stderr | 4 ++-- tests/ui/redundant_closure_call_fixable.stderr | 4 ++-- tests/ui/redundant_closure_call_late.stderr | 6 +++--- 4 files changed, 11 insertions(+), 11 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index 9477e79f567..8aa478ea2d6 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -77,13 +77,13 @@ impl EarlyLintPass for RedundantClosureCall { cx, REDUNDANT_CLOSURE_CALL, expr.span, - "Try not to call a closure in the expression where it is declared.", + "try not to call a closure in the expression where it is declared.", |diag| { if decl.inputs.is_empty() { let mut app = Applicability::MachineApplicable; let hint = snippet_with_applicability(cx, block.span, "..", &mut app).into_owned(); - diag.span_suggestion(expr.span, "Try doing something like: ", hint, app); + diag.span_suggestion(expr.span, "try doing something like", hint, app); } }, ); @@ -136,13 +136,13 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { if let hir::ExprKind::Call(ref closure, _) = call.kind; if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = closure.kind; if ident == path.segments[0].ident; - if count_closure_usage(block, path) == 1; + if count_closure_usage(block, path) == 1; then { span_lint( cx, REDUNDANT_CLOSURE_CALL, second.span, - "Closure called just once immediately after it was declared", + "closure called just once immediately after it was declared", ); } } diff --git a/tests/ui/redundant_closure_call_early.stderr b/tests/ui/redundant_closure_call_early.stderr index 0ac97ae25d0..79f27663461 100644 --- a/tests/ui/redundant_closure_call_early.stderr +++ b/tests/ui/redundant_closure_call_early.stderr @@ -1,4 +1,4 @@ -error: Try not to call a closure in the expression where it is declared. +error: try not to call a closure in the expression where it is declared. --> $DIR/redundant_closure_call_early.rs:9:17 | LL | let mut k = (|m| m + 1)(i); @@ -6,7 +6,7 @@ LL | let mut k = (|m| m + 1)(i); | = note: `-D clippy::redundant-closure-call` implied by `-D warnings` -error: Try not to call a closure in the expression where it is declared. +error: try not to call a closure in the expression where it is declared. --> $DIR/redundant_closure_call_early.rs:12:9 | LL | k = (|a, b| a * b)(1, 5); diff --git a/tests/ui/redundant_closure_call_fixable.stderr b/tests/ui/redundant_closure_call_fixable.stderr index e7737f9dd85..644161d9f5d 100644 --- a/tests/ui/redundant_closure_call_fixable.stderr +++ b/tests/ui/redundant_closure_call_fixable.stderr @@ -1,8 +1,8 @@ -error: Try not to call a closure in the expression where it is declared. +error: try not to call a closure in the expression where it is declared. --> $DIR/redundant_closure_call_fixable.rs:7:13 | LL | let a = (|| 42)(); - | ^^^^^^^^^ help: Try doing something like: : `42` + | ^^^^^^^^^ help: try doing something like: `42` | = note: `-D clippy::redundant-closure-call` implied by `-D warnings` diff --git a/tests/ui/redundant_closure_call_late.stderr b/tests/ui/redundant_closure_call_late.stderr index 0e000fee85a..7c8865f1bd3 100644 --- a/tests/ui/redundant_closure_call_late.stderr +++ b/tests/ui/redundant_closure_call_late.stderr @@ -1,4 +1,4 @@ -error: Closure called just once immediately after it was declared +error: closure called just once immediately after it was declared --> $DIR/redundant_closure_call_late.rs:15:5 | LL | i = redun_closure(); @@ -6,13 +6,13 @@ LL | i = redun_closure(); | = note: `-D clippy::redundant-closure-call` implied by `-D warnings` -error: Closure called just once immediately after it was declared +error: closure called just once immediately after it was declared --> $DIR/redundant_closure_call_late.rs:19:5 | LL | i = shadowed_closure(); | ^^^^^^^^^^^^^^^^^^^^^^ -error: Closure called just once immediately after it was declared +error: closure called just once immediately after it was declared --> $DIR/redundant_closure_call_late.rs:21:5 | LL | i = shadowed_closure(); -- cgit 1.4.1-3-g733a5 From a5cdd4aeb11fad6b0bf73d342398700a27c4484b Mon Sep 17 00:00:00 2001 From: Tomasz Miąsko Date: Mon, 20 Jul 2020 00:00:00 +0000 Subject: Ignore not really redundant clones of ManuallyDrop "Redundant" clones of `ManuallyDrop` are sometimes used for the side effect of invoking the clone, without running the drop implementation of the inner type. In other words, they aren't really redundant. For example, futures-rs crate: ```rust #[allow(clippy::redundant_clone)] // The clone here isn't actually redundant. unsafe fn increase_refcount(data: *const ()) { // Retain Arc, but don't touch refcount by wrapping in ManuallyDrop let arc = mem::ManuallyDrop::new(Arc::::from_raw(data as *const T)); // Now increase refcount, but don't drop new refcount either let _arc_clone: mem::ManuallyDrop<_> = arc.clone(); } ``` Ignore redundant clone lint for ManuallyDrop. --- clippy_lints/src/redundant_clone.rs | 6 ++++++ clippy_lints/src/utils/paths.rs | 1 + tests/ui/redundant_clone.fixed | 15 +++++++++++++++ tests/ui/redundant_clone.rs | 15 +++++++++++++++ tests/ui/redundant_clone.stderr | 20 ++++++++++---------- 5 files changed, 47 insertions(+), 10 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index fda7480194d..7932be0d4b1 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -124,6 +124,12 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { continue; } + if let ty::Adt(ref def, _) = arg_ty.kind { + if match_def_path(cx, def.did, &paths::MEM_MANUALLY_DROP) { + continue; + } + } + // `{ cloned = &arg; clone(move cloned); }` or `{ cloned = &arg; to_path_buf(cloned); }` let (cloned, cannot_move_out) = unwrap_or_continue!(find_stmt_assigns_to(cx, mir, arg, from_borrow, bb)); diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 4c3462802e9..a515ee29c82 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -59,6 +59,7 @@ pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "Link pub const LINT: [&str; 3] = ["rustc_session", "lint", "Lint"]; pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"]; pub const MEM_FORGET: [&str; 3] = ["core", "mem", "forget"]; +pub const MEM_MANUALLY_DROP: [&str; 4] = ["core", "mem", "manually_drop", "ManuallyDrop"]; pub const MEM_MAYBEUNINIT: [&str; 4] = ["core", "mem", "maybe_uninit", "MaybeUninit"]; pub const MEM_MAYBEUNINIT_UNINIT: [&str; 5] = ["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"]; pub const MEM_REPLACE: [&str; 3] = ["core", "mem", "replace"]; diff --git a/tests/ui/redundant_clone.fixed b/tests/ui/redundant_clone.fixed index 764c10a6d39..cdeefda4c23 100644 --- a/tests/ui/redundant_clone.fixed +++ b/tests/ui/redundant_clone.fixed @@ -52,6 +52,7 @@ fn main() { borrower_propagation(); not_consumed(); issue_5405(); + manually_drop(); } #[derive(Clone)] @@ -170,3 +171,17 @@ fn issue_5405() { let c: [usize; 2] = [2, 3]; let _d: usize = c[1].clone(); } + +fn manually_drop() { + use std::mem::ManuallyDrop; + use std::sync::Arc; + + let a = ManuallyDrop::new(Arc::new("Hello!".to_owned())); + let _ = a.clone(); // OK + + let p: *const String = Arc::into_raw(ManuallyDrop::into_inner(a)); + unsafe { + Arc::from_raw(p); + Arc::from_raw(p); + } +} diff --git a/tests/ui/redundant_clone.rs b/tests/ui/redundant_clone.rs index 839747b131d..acb7ffb305f 100644 --- a/tests/ui/redundant_clone.rs +++ b/tests/ui/redundant_clone.rs @@ -52,6 +52,7 @@ fn main() { borrower_propagation(); not_consumed(); issue_5405(); + manually_drop(); } #[derive(Clone)] @@ -170,3 +171,17 @@ fn issue_5405() { let c: [usize; 2] = [2, 3]; let _d: usize = c[1].clone(); } + +fn manually_drop() { + use std::mem::ManuallyDrop; + use std::sync::Arc; + + let a = ManuallyDrop::new(Arc::new("Hello!".to_owned())); + let _ = a.clone(); // OK + + let p: *const String = Arc::into_raw(ManuallyDrop::into_inner(a)); + unsafe { + Arc::from_raw(p); + Arc::from_raw(p); + } +} diff --git a/tests/ui/redundant_clone.stderr b/tests/ui/redundant_clone.stderr index eced198283c..89b39254299 100644 --- a/tests/ui/redundant_clone.stderr +++ b/tests/ui/redundant_clone.stderr @@ -108,61 +108,61 @@ LL | let _t = tup.0.clone(); | ^^^^^ error: redundant clone - --> $DIR/redundant_clone.rs:61:22 + --> $DIR/redundant_clone.rs:62:22 | LL | (a.clone(), a.clone()) | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:61:21 + --> $DIR/redundant_clone.rs:62:21 | LL | (a.clone(), a.clone()) | ^ error: redundant clone - --> $DIR/redundant_clone.rs:121:15 + --> $DIR/redundant_clone.rs:122:15 | LL | let _s = s.clone(); | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:121:14 + --> $DIR/redundant_clone.rs:122:14 | LL | let _s = s.clone(); | ^ error: redundant clone - --> $DIR/redundant_clone.rs:122:15 + --> $DIR/redundant_clone.rs:123:15 | LL | let _t = t.clone(); | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:122:14 + --> $DIR/redundant_clone.rs:123:14 | LL | let _t = t.clone(); | ^ error: redundant clone - --> $DIR/redundant_clone.rs:132:19 + --> $DIR/redundant_clone.rs:133:19 | LL | let _f = f.clone(); | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:132:18 + --> $DIR/redundant_clone.rs:133:18 | LL | let _f = f.clone(); | ^ error: redundant clone - --> $DIR/redundant_clone.rs:144:14 + --> $DIR/redundant_clone.rs:145:14 | LL | let y = x.clone().join("matthias"); | ^^^^^^^^ help: remove this | note: cloned value is neither consumed nor mutated - --> $DIR/redundant_clone.rs:144:13 + --> $DIR/redundant_clone.rs:145:13 | LL | let y = x.clone().join("matthias"); | ^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From b375f1dd20bf07175ec06d13e1e9dc8b20287cd3 Mon Sep 17 00:00:00 2001 From: Dmitry Murzin Date: Sat, 25 Jul 2020 17:09:44 +0300 Subject: Add suggestion for `iter_skip_next` lint --- clippy_lints/src/methods/mod.rs | 24 ++++++++++++++---------- tests/ui/iter_skip_next.fixed | 22 ++++++++++++++++++++++ tests/ui/iter_skip_next.rs | 8 ++++---- tests/ui/iter_skip_next.stderr | 23 ++++++++--------------- 4 files changed, 48 insertions(+), 29 deletions(-) create mode 100644 tests/ui/iter_skip_next.fixed (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 97cc58023f5..56686f6d24f 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1408,7 +1408,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["nth", "iter_mut"] => lint_iter_nth(cx, expr, &arg_lists, true), ["nth", ..] => lint_iter_nth_zero(cx, expr, arg_lists[0]), ["step_by", ..] => lint_step_by(cx, expr, arg_lists[0]), - ["next", "skip"] => lint_iter_skip_next(cx, expr), + ["next", "skip"] => lint_iter_skip_next(cx, expr, arg_lists[1]), ["collect", "cloned"] => lint_iter_cloned_collect(cx, expr, arg_lists[1]), ["as_ref"] => lint_asref(cx, expr, "as_ref", arg_lists[0]), ["as_mut"] => lint_asref(cx, expr, "as_mut", arg_lists[0]), @@ -2436,17 +2436,21 @@ fn lint_get_unwrap<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, get_args: ); } -fn lint_iter_skip_next(cx: &LateContext<'_>, expr: &hir::Expr<'_>) { +fn lint_iter_skip_next(cx: &LateContext<'_>, expr: &hir::Expr<'_>, skip_args: &[hir::Expr<'_>]) { // lint if caller of skip is an Iterator if match_trait_method(cx, expr, &paths::ITERATOR) { - span_lint_and_help( - cx, - ITER_SKIP_NEXT, - expr.span, - "called `skip(x).next()` on an iterator", - None, - "this is more succinctly expressed by calling `nth(x)`", - ); + if let [caller, n] = skip_args { + let hint = format!(".nth({})", snippet(cx, n.span, "..")); + span_lint_and_sugg( + cx, + ITER_SKIP_NEXT, + expr.span.trim_start(caller.span).unwrap(), + "called `skip(x).next()` on an iterator", + "use `nth` instead", + hint, + Applicability::MachineApplicable, + ); + } } } diff --git a/tests/ui/iter_skip_next.fixed b/tests/ui/iter_skip_next.fixed new file mode 100644 index 00000000000..928b6acb951 --- /dev/null +++ b/tests/ui/iter_skip_next.fixed @@ -0,0 +1,22 @@ +// run-rustfix +// aux-build:option_helpers.rs + +#![warn(clippy::iter_skip_next)] +#![allow(clippy::blacklisted_name)] +#![allow(clippy::iter_nth)] + +extern crate option_helpers; + +use option_helpers::IteratorFalsePositives; + +/// Checks implementation of `ITER_SKIP_NEXT` lint +fn main() { + let some_vec = vec![0, 1, 2, 3]; + let _ = some_vec.iter().nth(42); + let _ = some_vec.iter().cycle().nth(42); + let _ = (1..10).nth(10); + let _ = &some_vec[..].iter().nth(3); + let foo = IteratorFalsePositives { foo: 0 }; + let _ = foo.skip(42).next(); + let _ = foo.filter().skip(42).next(); +} diff --git a/tests/ui/iter_skip_next.rs b/tests/ui/iter_skip_next.rs index a65ca3bbb13..7075e2598eb 100644 --- a/tests/ui/iter_skip_next.rs +++ b/tests/ui/iter_skip_next.rs @@ -1,15 +1,17 @@ +// run-rustfix // aux-build:option_helpers.rs #![warn(clippy::iter_skip_next)] #![allow(clippy::blacklisted_name)] +#![allow(clippy::iter_nth)] extern crate option_helpers; use option_helpers::IteratorFalsePositives; /// Checks implementation of `ITER_SKIP_NEXT` lint -fn iter_skip_next() { - let mut some_vec = vec![0, 1, 2, 3]; +fn main() { + let some_vec = vec![0, 1, 2, 3]; let _ = some_vec.iter().skip(42).next(); let _ = some_vec.iter().cycle().skip(42).next(); let _ = (1..10).skip(10).next(); @@ -18,5 +20,3 @@ fn iter_skip_next() { let _ = foo.skip(42).next(); let _ = foo.filter().skip(42).next(); } - -fn main() {} diff --git a/tests/ui/iter_skip_next.stderr b/tests/ui/iter_skip_next.stderr index 5709f335529..feedc2f288a 100644 --- a/tests/ui/iter_skip_next.stderr +++ b/tests/ui/iter_skip_next.stderr @@ -1,35 +1,28 @@ error: called `skip(x).next()` on an iterator - --> $DIR/iter_skip_next.rs:13:13 + --> $DIR/iter_skip_next.rs:15:28 | LL | let _ = some_vec.iter().skip(42).next(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(42)` | = note: `-D clippy::iter-skip-next` implied by `-D warnings` - = help: this is more succinctly expressed by calling `nth(x)` error: called `skip(x).next()` on an iterator - --> $DIR/iter_skip_next.rs:14:13 + --> $DIR/iter_skip_next.rs:16:36 | LL | let _ = some_vec.iter().cycle().skip(42).next(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: this is more succinctly expressed by calling `nth(x)` + | ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(42)` error: called `skip(x).next()` on an iterator - --> $DIR/iter_skip_next.rs:15:13 + --> $DIR/iter_skip_next.rs:17:20 | LL | let _ = (1..10).skip(10).next(); - | ^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: this is more succinctly expressed by calling `nth(x)` + | ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(10)` error: called `skip(x).next()` on an iterator - --> $DIR/iter_skip_next.rs:16:14 + --> $DIR/iter_skip_next.rs:18:33 | LL | let _ = &some_vec[..].iter().skip(3).next(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: this is more succinctly expressed by calling `nth(x)` + | ^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(3)` error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From c81bbd05b95ae03da9af4e7e25e3784edd039465 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Sat, 25 Jul 2020 23:58:22 +0900 Subject: Fix FP `useless_conversion` Fix #5833. --- clippy_lints/src/useless_conversion.rs | 11 +++++++++-- tests/ui/useless_conversion.fixed | 9 +++++++++ tests/ui/useless_conversion.rs | 9 +++++++++ tests/ui/useless_conversion.stderr | 14 +++++++------- 4 files changed, 34 insertions(+), 9 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index a48ad3185e9..1bf37632e32 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,6 +1,6 @@ use crate::utils::{ - is_type_diagnostic_item, match_def_path, match_trait_method, paths, snippet, snippet_with_macro_callsite, - span_lint_and_help, span_lint_and_sugg, + get_parent_expr, is_type_diagnostic_item, match_def_path, match_trait_method, paths, snippet, + snippet_with_macro_callsite, span_lint_and_help, span_lint_and_sugg, }; use if_chain::if_chain; use rustc_errors::Applicability; @@ -79,6 +79,13 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { } } if match_trait_method(cx, e, &paths::INTO_ITERATOR) && &*name.ident.as_str() == "into_iter" { + if let Some(parent_expr) = get_parent_expr(cx, e) { + if let ExprKind::MethodCall(ref parent_name, ..) = parent_expr.kind { + if &*parent_name.ident.as_str() != "into_iter" { + return; + } + } + } let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(&args[0]); if TyS::same_type(a, b) { diff --git a/tests/ui/useless_conversion.fixed b/tests/ui/useless_conversion.fixed index fdd4bc581f3..813cdaecaa9 100644 --- a/tests/ui/useless_conversion.fixed +++ b/tests/ui/useless_conversion.fixed @@ -32,11 +32,20 @@ fn test_issue_3913() -> Result<(), std::io::Error> { Ok(()) } +fn test_issue_5833() -> Result<(), ()> { + let text = "foo\r\nbar\n\nbaz\n"; + let lines = text.lines(); + if Some("ok") == lines.into_iter().next() {} + + Ok(()) +} + fn main() { test_generic(10i32); test_generic2::(10i32); test_questionmark().unwrap(); test_issue_3913().unwrap(); + test_issue_5833().unwrap(); let _: String = "foo".into(); let _: String = From::from("foo"); diff --git a/tests/ui/useless_conversion.rs b/tests/ui/useless_conversion.rs index 4cae745e7c0..540fea23b36 100644 --- a/tests/ui/useless_conversion.rs +++ b/tests/ui/useless_conversion.rs @@ -32,11 +32,20 @@ fn test_issue_3913() -> Result<(), std::io::Error> { Ok(()) } +fn test_issue_5833() -> Result<(), ()> { + let text = "foo\r\nbar\n\nbaz\n"; + let lines = text.lines(); + if Some("ok") == lines.into_iter().next() {} + + Ok(()) +} + fn main() { test_generic(10i32); test_generic2::(10i32); test_questionmark().unwrap(); test_issue_3913().unwrap(); + test_issue_5833().unwrap(); let _: String = "foo".into(); let _: String = From::from("foo"); diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index 84ec5370278..b958b035452 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -23,43 +23,43 @@ LL | let _: i32 = 0i32.into(); | ^^^^^^^^^^^ help: consider removing `.into()`: `0i32` error: useless conversion to the same type - --> $DIR/useless_conversion.rs:51:21 + --> $DIR/useless_conversion.rs:60:21 | LL | let _: String = "foo".to_string().into(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `"foo".to_string()` error: useless conversion to the same type - --> $DIR/useless_conversion.rs:52:21 + --> $DIR/useless_conversion.rs:61:21 | LL | let _: String = From::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `From::from()`: `"foo".to_string()` error: useless conversion to the same type - --> $DIR/useless_conversion.rs:53:13 + --> $DIR/useless_conversion.rs:62:13 | LL | let _ = String::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `"foo".to_string()` error: useless conversion to the same type - --> $DIR/useless_conversion.rs:54:13 + --> $DIR/useless_conversion.rs:63:13 | LL | let _ = String::from(format!("A: {:04}", 123)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `format!("A: {:04}", 123)` error: useless conversion to the same type - --> $DIR/useless_conversion.rs:55:13 + --> $DIR/useless_conversion.rs:64:13 | LL | let _ = "".lines().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()` error: useless conversion to the same type - --> $DIR/useless_conversion.rs:56:13 + --> $DIR/useless_conversion.rs:65:13 | LL | let _ = vec![1, 2, 3].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![1, 2, 3].into_iter()` error: useless conversion to the same type - --> $DIR/useless_conversion.rs:57:21 + --> $DIR/useless_conversion.rs:66:21 | LL | let _: String = format!("Hello {}", "world").into(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `format!("Hello {}", "world")` -- cgit 1.4.1-3-g733a5 From 5a644964fc05752a1283dab238b81de7583f7d03 Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Sun, 26 Jul 2020 20:40:57 -0600 Subject: run cargo dev new_lint specifically: cargo dev new_lint --name derive_ord_xor_partial_ord --category correctness --pass late --- CHANGELOG.md | 1 + clippy_lints/src/derive_ord_xor_partial_ord.rs | 28 ++++++++++++++++++++++++++ clippy_lints/src/lib.rs | 4 ++++ src/lintlist/mod.rs | 7 +++++++ tests/ui/derive_ord_xor_partial_ord.rs | 5 +++++ 5 files changed, 45 insertions(+) create mode 100644 clippy_lints/src/derive_ord_xor_partial_ord.rs create mode 100644 tests/ui/derive_ord_xor_partial_ord.rs (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 776b04295f9..a1780725044 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1454,6 +1454,7 @@ Released 2018-09-13 [`deprecated_semver`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_semver [`deref_addrof`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_addrof [`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq +[`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord [`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression [`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown [`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons diff --git a/clippy_lints/src/derive_ord_xor_partial_ord.rs b/clippy_lints/src/derive_ord_xor_partial_ord.rs new file mode 100644 index 00000000000..7913aab6f24 --- /dev/null +++ b/clippy_lints/src/derive_ord_xor_partial_ord.rs @@ -0,0 +1,28 @@ +use rustc_lint::{LateLintPass, LateContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_hir::*; + +declare_clippy_lint! { + /// **What it does:** + /// + /// **Why is this bad?** + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // example code where clippy issues a warning + /// ``` + /// Use instead: + /// ```rust + /// // example code which does not raise clippy warning + /// ``` + pub DERIVE_ORD_XOR_PARTIAL_ORD, + correctness, + "default lint description" +} + +declare_lint_pass!(DeriveOrdXorPartialOrd => [DERIVE_ORD_XOR_PARTIAL_ORD]); + +impl LateLintPass<'_, '_> for DeriveOrdXorPartialOrd {} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f371942dbee..6d6dd06cc21 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -173,6 +173,7 @@ mod dbg_macro; mod default_trait_access; mod dereference; mod derive; +mod derive_ord_xor_partial_ord; mod doc; mod double_comparison; mod double_parens; @@ -515,6 +516,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &derive::DERIVE_HASH_XOR_EQ, &derive::EXPL_IMPL_CLONE_ON_COPY, &derive::UNSAFE_DERIVE_DESERIALIZE, + &derive_ord_xor_partial_ord::DERIVE_ORD_XOR_PARTIAL_ORD, &doc::DOC_MARKDOWN, &doc::MISSING_ERRORS_DOC, &doc::MISSING_SAFETY_DOC, @@ -1230,6 +1232,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&copies::IFS_SAME_COND), LintId::of(&copies::IF_SAME_THEN_ELSE), LintId::of(&derive::DERIVE_HASH_XOR_EQ), + LintId::of(&derive_ord_xor_partial_ord::DERIVE_ORD_XOR_PARTIAL_ORD), LintId::of(&doc::MISSING_SAFETY_DOC), LintId::of(&doc::NEEDLESS_DOCTEST_MAIN), LintId::of(&double_comparison::DOUBLE_COMPARISONS), @@ -1648,6 +1651,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&copies::IFS_SAME_COND), LintId::of(&copies::IF_SAME_THEN_ELSE), LintId::of(&derive::DERIVE_HASH_XOR_EQ), + LintId::of(&derive_ord_xor_partial_ord::DERIVE_ORD_XOR_PARTIAL_ORD), LintId::of(&drop_bounds::DROP_BOUNDS), LintId::of(&drop_forget_ref::DROP_COPY), LintId::of(&drop_forget_ref::DROP_REF), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 1879aae77fb..00d3df8f94f 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -360,6 +360,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "derive", }, + Lint { + name: "derive_ord_xor_partial_ord", + group: "correctness", + desc: "default lint description", + deprecation: None, + module: "derive_ord_xor_partial_ord", + }, Lint { name: "diverging_sub_expression", group: "complexity", diff --git a/tests/ui/derive_ord_xor_partial_ord.rs b/tests/ui/derive_ord_xor_partial_ord.rs new file mode 100644 index 00000000000..63687e7b3db --- /dev/null +++ b/tests/ui/derive_ord_xor_partial_ord.rs @@ -0,0 +1,5 @@ +#![warn(clippy::derive_ord_xor_partial_ord)] + +fn main() { + // test code goes here +} -- cgit 1.4.1-3-g733a5 From 0722991b62fd6e4d7d7a51425274f3288bcc96bc Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Sun, 26 Jul 2020 21:36:50 -0600 Subject: add test for derive_ord_xor_partial_ord based on test for derive_hash_xor_partial_eq --- tests/ui/derive_ord_xor_partial_ord.rs | 67 +++++++++++++++++++++++++++++- tests/ui/derive_ord_xor_partial_ord.stderr | 1 + 2 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 tests/ui/derive_ord_xor_partial_ord.stderr (limited to 'tests') diff --git a/tests/ui/derive_ord_xor_partial_ord.rs b/tests/ui/derive_ord_xor_partial_ord.rs index 63687e7b3db..15f66b7a9c5 100644 --- a/tests/ui/derive_ord_xor_partial_ord.rs +++ b/tests/ui/derive_ord_xor_partial_ord.rs @@ -1,5 +1,68 @@ #![warn(clippy::derive_ord_xor_partial_ord)] -fn main() { - // test code goes here +use std::cmp::Ordering; + +#[derive(PartialOrd, Ord, PartialEq, Eq)] +struct DeriveBoth; + +impl PartialEq for DeriveBoth { + fn eq(&self, _: &u64) -> bool { + true + } +} + +impl PartialOrd for DeriveBoth { + fn partial_cmp(&self, _: &u64) -> Option { + Some(Ordering::Equal) + } } + +#[derive(Ord, PartialEq, Eq)] +struct DeriveOrd; + +impl PartialOrd for DeriveOrd { + fn partial_cmp(&self, other: &Self) -> Option { + Some(other.cmp(self)) + } +} + +#[derive(Ord, PartialEq, Eq)] +struct DeriveOrdWithExplicitTypeVariable; + +impl PartialOrd for DeriveOrdWithExplicitTypeVariable { + fn partial_cmp(&self, other: &Self) -> Option { + Some(other.cmp(self)) + } +} + +#[derive(PartialOrd, PartialEq, Eq)] +struct DerivePartialOrd; + +impl std::cmp::Ord for DerivePartialOrd { + fn cmp(&self, other: &Self) -> Ordering { + Ordering::Less + } +} + +#[derive(PartialOrd, PartialEq, Eq)] +struct ImplUserOrd; + +trait Ord {} + +// We don't want to lint on user-defined traits called `Ord` +impl Ord for ImplUserOrd {} + +mod use_ord { + use std::cmp::{Ord, Ordering}; + + #[derive(PartialOrd, PartialEq, Eq)] + struct DerivePartialOrdInUseOrd; + + impl Ord for DerivePartialOrdInUseOrd { + fn cmp(&self, other: &Self) -> Ordering { + Ordering::Less + } + } +} + +fn main() {} \ No newline at end of file diff --git a/tests/ui/derive_ord_xor_partial_ord.stderr b/tests/ui/derive_ord_xor_partial_ord.stderr new file mode 100644 index 00000000000..30404ce4c54 --- /dev/null +++ b/tests/ui/derive_ord_xor_partial_ord.stderr @@ -0,0 +1 @@ +TODO \ No newline at end of file -- cgit 1.4.1-3-g733a5 From 6c3e4591b87e6c690b31166867484675dcb1e48c Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Sun, 26 Jul 2020 23:04:25 -0600 Subject: update reference since we see the expected four errors --- tests/ui/derive_ord_xor_partial_ord.stderr | 72 +++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/ui/derive_ord_xor_partial_ord.stderr b/tests/ui/derive_ord_xor_partial_ord.stderr index 30404ce4c54..66bc4d42ce8 100644 --- a/tests/ui/derive_ord_xor_partial_ord.stderr +++ b/tests/ui/derive_ord_xor_partial_ord.stderr @@ -1 +1,71 @@ -TODO \ No newline at end of file +error: you are deriving `Ord` but have implemented `PartialOrd` explicitly + --> $DIR/derive_ord_xor_partial_ord.rs:20:10 + | +LL | #[derive(Ord, PartialEq, Eq)] + | ^^^ + | + = note: `-D clippy::derive-ord-xor-partial-ord` implied by `-D warnings` +note: `PartialOrd` implemented here + --> $DIR/derive_ord_xor_partial_ord.rs:23:1 + | +LL | / impl PartialOrd for DeriveOrd { +LL | | fn partial_cmp(&self, other: &Self) -> Option { +LL | | Some(other.cmp(self)) +LL | | } +LL | | } + | |_^ + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: you are deriving `Ord` but have implemented `PartialOrd` explicitly + --> $DIR/derive_ord_xor_partial_ord.rs:29:10 + | +LL | #[derive(Ord, PartialEq, Eq)] + | ^^^ + | +note: `PartialOrd` implemented here + --> $DIR/derive_ord_xor_partial_ord.rs:32:1 + | +LL | / impl PartialOrd for DeriveOrdWithExplicitTypeVariable { +LL | | fn partial_cmp(&self, other: &Self) -> Option { +LL | | Some(other.cmp(self)) +LL | | } +LL | | } + | |_^ + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: you are implementing `Ord` explicitly but have derived `PartialOrd` + --> $DIR/derive_ord_xor_partial_ord.rs:41:1 + | +LL | / impl std::cmp::Ord for DerivePartialOrd { +LL | | fn cmp(&self, other: &Self) -> Ordering { +LL | | Ordering::Less +LL | | } +LL | | } + | |_^ + | +note: `PartialOrd` implemented here + --> $DIR/derive_ord_xor_partial_ord.rs:38:10 + | +LL | #[derive(PartialOrd, PartialEq, Eq)] + | ^^^^^^^^^^ + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: you are implementing `Ord` explicitly but have derived `PartialOrd` + --> $DIR/derive_ord_xor_partial_ord.rs:61:5 + | +LL | / impl Ord for DerivePartialOrdInUseOrd { +LL | | fn cmp(&self, other: &Self) -> Ordering { +LL | | Ordering::Less +LL | | } +LL | | } + | |_____^ + | +note: `PartialOrd` implemented here + --> $DIR/derive_ord_xor_partial_ord.rs:58:14 + | +LL | #[derive(PartialOrd, PartialEq, Eq)] + | ^^^^^^^^^^ + = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 4 previous errors + -- cgit 1.4.1-3-g733a5 From 668b7474b47791c8c9af10130356b681b3bf3a84 Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Sun, 26 Jul 2020 23:30:00 -0600 Subject: run cargo dev fmt and fix overly long line --- clippy_lints/src/derive.rs | 10 ++++++++-- tests/ui/derive_ord_xor_partial_ord.rs | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 84566252abd..16a6f0c20e1 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -1,6 +1,7 @@ use crate::utils::paths; use crate::utils::{ - get_trait_def_id, is_automatically_derived, is_copy, match_path, span_lint_and_help, span_lint_and_note, span_lint_and_then, + get_trait_def_id, is_automatically_derived, is_copy, match_path, span_lint_and_help, span_lint_and_note, + span_lint_and_then, }; use if_chain::if_chain; use rustc_hir::def_id::DefId; @@ -154,7 +155,12 @@ declare_clippy_lint! { "deriving `serde::Deserialize` on a type that has methods using `unsafe`" } -declare_lint_pass!(Derive => [EXPL_IMPL_CLONE_ON_COPY, DERIVE_HASH_XOR_EQ, DERIVE_ORD_XOR_PARTIAL_ORD, UNSAFE_DERIVE_DESERIALIZE]); +declare_lint_pass!(Derive => [ + EXPL_IMPL_CLONE_ON_COPY, + DERIVE_HASH_XOR_EQ, + DERIVE_ORD_XOR_PARTIAL_ORD, + UNSAFE_DERIVE_DESERIALIZE +]); impl<'tcx> LateLintPass<'tcx> for Derive { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { diff --git a/tests/ui/derive_ord_xor_partial_ord.rs b/tests/ui/derive_ord_xor_partial_ord.rs index 15f66b7a9c5..b82dc518a3b 100644 --- a/tests/ui/derive_ord_xor_partial_ord.rs +++ b/tests/ui/derive_ord_xor_partial_ord.rs @@ -65,4 +65,4 @@ mod use_ord { } } -fn main() {} \ No newline at end of file +fn main() {} -- cgit 1.4.1-3-g733a5 From 94c50bc8c913ef58eba0f4f10b682dcf6d6e0991 Mon Sep 17 00:00:00 2001 From: Christoph Walcher Date: Tue, 28 Jul 2020 16:23:47 +0200 Subject: Lint duplicate methods of trait bounds Fixes #5777 --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/trait_bounds.rs | 94 ++++++++++++++++++++++++++++- src/lintlist/mod.rs | 7 +++ tests/ui/trait_duplication_in_bounds.rs | 31 ++++++++++ tests/ui/trait_duplication_in_bounds.stderr | 23 +++++++ 6 files changed, 156 insertions(+), 2 deletions(-) create mode 100644 tests/ui/trait_duplication_in_bounds.rs create mode 100644 tests/ui/trait_duplication_in_bounds.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 776b04295f9..0ca4d88ed38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1723,6 +1723,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 +[`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 [`transmute_int_to_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_bool diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f371942dbee..07ef087c2b0 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -786,6 +786,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &tabs_in_doc_comments::TABS_IN_DOC_COMMENTS, &temporary_assignment::TEMPORARY_ASSIGNMENT, &to_digit_is_some::TO_DIGIT_IS_SOME, + &trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS, &trait_bounds::TYPE_REPETITION_IN_BOUNDS, &transmute::CROSSPOINTER_TRANSMUTE, &transmute::TRANSMUTE_BYTES_TO_STR, @@ -1174,6 +1175,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ranges::RANGE_PLUS_ONE), 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(&trivially_copy_pass_by_ref::TRIVIALLY_COPY_PASS_BY_REF), LintId::of(&types::CAST_LOSSLESS), diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 0ef70311fb1..6bfdac37180 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -2,9 +2,10 @@ use crate::utils::{in_macro, snippet, snippet_with_applicability, span_lint_and_ use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; -use rustc_hir::{GenericBound, Generics, WherePredicate}; +use rustc_hir::{def::Res, GenericBound, Generics, ParamName, Path, QPath, TyKind, WherePredicate}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::Span; declare_clippy_lint! { /// **What it does:** This lint warns about unnecessary type repetitions in trait bounds @@ -29,6 +30,35 @@ declare_clippy_lint! { "Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`" } +declare_clippy_lint! { + /// **What it does:** Checks for cases where generics are being used and multiple + /// syntax specifications for trait bounds are used simultaneously. + /// + /// **Why is this bad?** Duplicate bounds makes the code + /// less readable than specifing them only once. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// fn func(arg: T) where T: Clone + Default {} + /// ``` + /// + /// Could be written as: + /// + /// ```rust + /// fn func(arg: T) {} + /// ``` + /// or + /// /// + /// ```rust + /// fn func(arg: T) where T: Clone + Default {} + /// ``` + pub TRAIT_DUPLICATION_IN_BOUNDS, + pedantic, + "Check if the same trait bounds are specifed twice during a function declaration" +} + #[derive(Copy, Clone)] pub struct TraitBounds { max_trait_bounds: u64, @@ -41,10 +71,25 @@ impl TraitBounds { } } -impl_lint_pass!(TraitBounds => [TYPE_REPETITION_IN_BOUNDS]); +impl_lint_pass!(TraitBounds => [TYPE_REPETITION_IN_BOUNDS, TRAIT_DUPLICATION_IN_BOUNDS]); impl<'tcx> LateLintPass<'tcx> for TraitBounds { fn check_generics(&mut self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) { + self.check_type_repetition(cx, gen); + check_trait_bound_duplication(cx, gen); + } +} + +fn get_trait_res_span_from_bound(bound: &GenericBound<'_>) -> Option<(Res, Span)> { + if let GenericBound::Trait(t, _) = bound { + Some((t.trait_ref.path.res, t.span)) + } else { + None + } +} + +impl TraitBounds { + fn check_type_repetition(self, cx: &LateContext<'_>, gen: &'_ Generics<'_>) { if in_macro(gen.span) { return; } @@ -101,3 +146,48 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { } } } + +fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { + if in_macro(gen.span) { + return; + } + + let mut map = FxHashMap::default(); + for param in gen.params { + if let ParamName::Plain(ref ident) = param.name { + let res = param + .bounds + .iter() + .filter_map(get_trait_res_span_from_bound) + .collect::>(); + map.insert(*ident, res); + } + } + + for predicate in gen.where_clause.predicates { + if_chain! { + if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate; + if !in_macro(bound_predicate.span); + if let TyKind::Path(ref path) = bound_predicate.bounded_ty.kind; + if let QPath::Resolved(_, Path { ref segments, .. }) = path; + if let Some(segment) = segments.first(); + if let Some(trait_resolutions_direct) = map.get(&segment.ident); + then { + for (res_where, _) in bound_predicate.bounds.iter().filter_map(get_trait_res_span_from_bound) { + if let Some((_, span_direct)) = trait_resolutions_direct + .iter() + .find(|(res_direct, _)| *res_direct == res_where) { + span_lint_and_help( + cx, + TRAIT_DUPLICATION_IN_BOUNDS, + *span_direct, + "this trait bound is already specified in the where clause", + None, + "consider removing this trait bound", + ); + } + } + } + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 1879aae77fb..9fb3dfc96ec 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2166,6 +2166,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "misc", }, + Lint { + name: "trait_duplication_in_bounds", + group: "pedantic", + desc: "Check if the same trait bounds are specifed twice during a function declaration", + deprecation: None, + module: "trait_bounds", + }, Lint { name: "transmute_bytes_to_str", group: "complexity", diff --git a/tests/ui/trait_duplication_in_bounds.rs b/tests/ui/trait_duplication_in_bounds.rs new file mode 100644 index 00000000000..cb2b0054e35 --- /dev/null +++ b/tests/ui/trait_duplication_in_bounds.rs @@ -0,0 +1,31 @@ +#![deny(clippy::trait_duplication_in_bounds)] + +use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; + +fn bad_foo(arg0: T, arg1: Z) +where + T: Clone, + T: Default, +{ + unimplemented!(); +} + +fn good_bar(arg: T) { + unimplemented!(); +} + +fn good_foo(arg: T) +where + T: Clone + Default, +{ + unimplemented!(); +} + +fn good_foobar(arg: T) +where + T: Clone, +{ + unimplemented!(); +} + +fn main() {} diff --git a/tests/ui/trait_duplication_in_bounds.stderr b/tests/ui/trait_duplication_in_bounds.stderr new file mode 100644 index 00000000000..027e1c75204 --- /dev/null +++ b/tests/ui/trait_duplication_in_bounds.stderr @@ -0,0 +1,23 @@ +error: this trait bound is already specified in the where clause + --> $DIR/trait_duplication_in_bounds.rs:5:15 + | +LL | fn bad_foo(arg0: T, arg1: Z) + | ^^^^^ + | +note: the lint level is defined here + --> $DIR/trait_duplication_in_bounds.rs:1:9 + | +LL | #![deny(clippy::trait_duplication_in_bounds)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: consider removing this trait bound + +error: this trait bound is already specified in the where clause + --> $DIR/trait_duplication_in_bounds.rs:5:23 + | +LL | fn bad_foo(arg0: T, arg1: Z) + | ^^^^^^^ + | + = help: consider removing this trait bound + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From a427c99f3d2a0b2c55d19af73bcad81f1dc761ab Mon Sep 17 00:00:00 2001 From: Dmitry Murzin Date: Sat, 25 Jul 2020 20:04:59 +0300 Subject: Handle mapping to Option in `map_flatten` lint --- clippy_lints/src/methods/mod.rs | 26 ++++++++++++++++++++++---- tests/ui/map_flatten.fixed | 1 + tests/ui/map_flatten.rs | 1 + tests/ui/map_flatten.stderr | 16 +++++++++++----- 4 files changed, 35 insertions(+), 9 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 9edcdd979ff..3f62a3cab1c 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2569,17 +2569,35 @@ fn lint_ok_expect(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ok_args: &[hir::Ex fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map_args: &'tcx [hir::Expr<'_>]) { // lint if caller of `.map().flatten()` is an Iterator if match_trait_method(cx, expr, &paths::ITERATOR) { - let msg = "called `map(..).flatten()` on an `Iterator`. \ - This is more succinctly expressed by calling `.flat_map(..)`"; + let map_closure_ty = cx.typeck_results().expr_ty(&map_args[1]); + let is_map_to_option = if let ty::Closure(_def_id, substs) = map_closure_ty.kind { + let map_closure_return_ty = cx.tcx.erase_late_bound_regions(&substs.as_closure().sig().output()); + is_type_diagnostic_item(cx, map_closure_return_ty, sym!(option_type)) + } else { + false + }; + + let method_to_use = if is_map_to_option { + // `(...).map(...)` has type `impl Iterator> + "filter_map" + } else { + // `(...).map(...)` has type `impl Iterator> + "flat_map" + }; + let msg = &format!( + "called `map(..).flatten()` on an `Iterator`. \ + This is more succinctly expressed by calling `.{}(..)`", + method_to_use + ); let self_snippet = snippet(cx, map_args[0].span, ".."); let func_snippet = snippet(cx, map_args[1].span, ".."); - let hint = format!("{0}.flat_map({1})", self_snippet, func_snippet); + let hint = format!("{0}.{1}({2})", self_snippet, method_to_use, func_snippet); span_lint_and_sugg( cx, MAP_FLATTEN, expr.span, msg, - "try using `flat_map` instead", + &format!("try using `{}` instead", method_to_use), hint, Applicability::MachineApplicable, ); diff --git a/tests/ui/map_flatten.fixed b/tests/ui/map_flatten.fixed index 4171d80f48a..684a28aebcb 100644 --- a/tests/ui/map_flatten.fixed +++ b/tests/ui/map_flatten.fixed @@ -5,6 +5,7 @@ #![allow(clippy::map_identity)] fn main() { + let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(|x| x.checked_add(1)).collect(); let _: Vec<_> = vec![5_i8; 6].into_iter().flat_map(|x| 0..x).collect(); let _: Option<_> = (Some(Some(1))).and_then(|x| x); } diff --git a/tests/ui/map_flatten.rs b/tests/ui/map_flatten.rs index 16a0fd090ad..05789ee5232 100644 --- a/tests/ui/map_flatten.rs +++ b/tests/ui/map_flatten.rs @@ -5,6 +5,7 @@ #![allow(clippy::map_identity)] fn main() { + let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); let _: Option<_> = (Some(Some(1))).map(|x| x).flatten(); } diff --git a/tests/ui/map_flatten.stderr b/tests/ui/map_flatten.stderr index 00bc41c15e9..d2d15362a6c 100644 --- a/tests/ui/map_flatten.stderr +++ b/tests/ui/map_flatten.stderr @@ -1,16 +1,22 @@ -error: called `map(..).flatten()` on an `Iterator`. This is more succinctly expressed by calling `.flat_map(..)` +error: called `map(..).flatten()` on an `Iterator`. This is more succinctly expressed by calling `.filter_map(..)` --> $DIR/map_flatten.rs:8:21 | -LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `vec![5_i8; 6].into_iter().flat_map(|x| 0..x)` +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `vec![5_i8; 6].into_iter().filter_map(|x| x.checked_add(1))` | = note: `-D clippy::map-flatten` implied by `-D warnings` +error: called `map(..).flatten()` on an `Iterator`. This is more succinctly expressed by calling `.flat_map(..)` + --> $DIR/map_flatten.rs:9:21 + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `vec![5_i8; 6].into_iter().flat_map(|x| 0..x)` + error: called `map(..).flatten()` on an `Option`. This is more succinctly expressed by calling `.and_then(..)` - --> $DIR/map_flatten.rs:9:24 + --> $DIR/map_flatten.rs:10:24 | LL | let _: Option<_> = (Some(Some(1))).map(|x| x).flatten(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `(Some(Some(1))).and_then(|x| x)` -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors -- cgit 1.4.1-3-g733a5 From d4ba561aafb501972f581c1f8e6d1885959f9306 Mon Sep 17 00:00:00 2001 From: Dmitry Murzin Date: Thu, 30 Jul 2020 22:20:31 +0300 Subject: Review fixes --- clippy_lints/src/methods/mod.rs | 36 ++++++++++++++++-------------------- tests/ui/map_flatten.fixed | 13 +++++++++++++ tests/ui/map_flatten.rs | 13 +++++++++++++ tests/ui/map_flatten.stderr | 40 +++++++++++++++++++++++++++++----------- 4 files changed, 71 insertions(+), 31 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 3f62a3cab1c..9217324b18c 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2570,11 +2570,16 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map // lint if caller of `.map().flatten()` is an Iterator if match_trait_method(cx, expr, &paths::ITERATOR) { let map_closure_ty = cx.typeck_results().expr_ty(&map_args[1]); - let is_map_to_option = if let ty::Closure(_def_id, substs) = map_closure_ty.kind { - let map_closure_return_ty = cx.tcx.erase_late_bound_regions(&substs.as_closure().sig().output()); - is_type_diagnostic_item(cx, map_closure_return_ty, sym!(option_type)) - } else { - false + let is_map_to_option = match map_closure_ty.kind { + ty::Closure(_, _) | ty::FnDef(_, _) | ty::FnPtr(_) => { + let map_closure_sig = match map_closure_ty.kind { + ty::Closure(_, substs) => substs.as_closure().sig(), + _ => map_closure_ty.fn_sig(cx.tcx), + }; + let map_closure_return_ty = cx.tcx.erase_late_bound_regions(&map_closure_sig.output()); + is_type_diagnostic_item(cx, map_closure_return_ty, sym!(option_type)) + }, + _ => false, }; let method_to_use = if is_map_to_option { @@ -2584,19 +2589,13 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map // `(...).map(...)` has type `impl Iterator> "flat_map" }; - let msg = &format!( - "called `map(..).flatten()` on an `Iterator`. \ - This is more succinctly expressed by calling `.{}(..)`", - method_to_use - ); - let self_snippet = snippet(cx, map_args[0].span, ".."); let func_snippet = snippet(cx, map_args[1].span, ".."); - let hint = format!("{0}.{1}({2})", self_snippet, method_to_use, func_snippet); + let hint = format!(".{0}({1})", method_to_use, func_snippet); span_lint_and_sugg( cx, MAP_FLATTEN, - expr.span, - msg, + expr.span.with_lo(map_args[0].span.hi()), + "called `map(..).flatten()` on an `Iterator`", &format!("try using `{}` instead", method_to_use), hint, Applicability::MachineApplicable, @@ -2605,16 +2604,13 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map // lint if caller of `.map().flatten()` is an Option if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym!(option_type)) { - let msg = "called `map(..).flatten()` on an `Option`. \ - This is more succinctly expressed by calling `.and_then(..)`"; - let self_snippet = snippet(cx, map_args[0].span, ".."); let func_snippet = snippet(cx, map_args[1].span, ".."); - let hint = format!("{0}.and_then({1})", self_snippet, func_snippet); + let hint = format!(".and_then({})", func_snippet); span_lint_and_sugg( cx, MAP_FLATTEN, - expr.span, - msg, + expr.span.with_lo(map_args[0].span.hi()), + "called `map(..).flatten()` on an `Option`", "try using `and_then` instead", hint, Applicability::MachineApplicable, diff --git a/tests/ui/map_flatten.fixed b/tests/ui/map_flatten.fixed index 684a28aebcb..a5fdf7df613 100644 --- a/tests/ui/map_flatten.fixed +++ b/tests/ui/map_flatten.fixed @@ -5,7 +5,20 @@ #![allow(clippy::map_identity)] fn main() { + // mapping to Option on Iterator + fn option_id(x: i8) -> Option { + Some(x) + } + let option_id_ref: fn(i8) -> Option = option_id; + let option_id_closure = |x| Some(x); + let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id).collect(); + let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id_ref).collect(); + let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id_closure).collect(); let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(|x| x.checked_add(1)).collect(); + + // mapping to Iterator on Iterator let _: Vec<_> = vec![5_i8; 6].into_iter().flat_map(|x| 0..x).collect(); + + // mapping to Option on Option let _: Option<_> = (Some(Some(1))).and_then(|x| x); } diff --git a/tests/ui/map_flatten.rs b/tests/ui/map_flatten.rs index 05789ee5232..abbc4e16e56 100644 --- a/tests/ui/map_flatten.rs +++ b/tests/ui/map_flatten.rs @@ -5,7 +5,20 @@ #![allow(clippy::map_identity)] fn main() { + // mapping to Option on Iterator + fn option_id(x: i8) -> Option { + Some(x) + } + let option_id_ref: fn(i8) -> Option = option_id; + let option_id_closure = |x| Some(x); + let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect(); + let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect(); + let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect(); let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); + + // mapping to Iterator on Iterator let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); + + // mapping to Option on Option let _: Option<_> = (Some(Some(1))).map(|x| x).flatten(); } diff --git a/tests/ui/map_flatten.stderr b/tests/ui/map_flatten.stderr index d2d15362a6c..b6479cd69ea 100644 --- a/tests/ui/map_flatten.stderr +++ b/tests/ui/map_flatten.stderr @@ -1,22 +1,40 @@ -error: called `map(..).flatten()` on an `Iterator`. This is more succinctly expressed by calling `.filter_map(..)` - --> $DIR/map_flatten.rs:8:21 +error: called `map(..).flatten()` on an `Iterator` + --> $DIR/map_flatten.rs:14:46 | -LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `vec![5_i8; 6].into_iter().filter_map(|x| x.checked_add(1))` +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id)` | = note: `-D clippy::map-flatten` implied by `-D warnings` -error: called `map(..).flatten()` on an `Iterator`. This is more succinctly expressed by calling `.flat_map(..)` - --> $DIR/map_flatten.rs:9:21 +error: called `map(..).flatten()` on an `Iterator` + --> $DIR/map_flatten.rs:15:46 + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_ref)` + +error: called `map(..).flatten()` on an `Iterator` + --> $DIR/map_flatten.rs:16:46 + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_closure)` + +error: called `map(..).flatten()` on an `Iterator` + --> $DIR/map_flatten.rs:17:46 + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(|x| x.checked_add(1))` + +error: called `map(..).flatten()` on an `Iterator` + --> $DIR/map_flatten.rs:20:46 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `vec![5_i8; 6].into_iter().flat_map(|x| 0..x)` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `.flat_map(|x| 0..x)` -error: called `map(..).flatten()` on an `Option`. This is more succinctly expressed by calling `.and_then(..)` - --> $DIR/map_flatten.rs:10:24 +error: called `map(..).flatten()` on an `Option` + --> $DIR/map_flatten.rs:23:39 | LL | let _: Option<_> = (Some(Some(1))).map(|x| x).flatten(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `(Some(Some(1))).and_then(|x| x)` + | ^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `.and_then(|x| x)` -error: aborting due to 3 previous errors +error: aborting due to 6 previous errors -- cgit 1.4.1-3-g733a5 From e336fe80d2f991a170b98190683039035b53c6ba Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Mon, 3 Aug 2020 00:36:28 +0200 Subject: manual_async_fn: take input lifetimes into account The anonymous future returned from an `async fn` captures all input lifetimes. This was not being taken into account. See https://github.com/rust-lang/rfcs/blob/master/text/2394-async_await.md#lifetime-capture-in-the-anonymous-future --- clippy_lints/src/manual_async_fn.rs | 63 ++++++++++++++++++++++++++++++------- tests/ui/await_holding_lock.rs | 1 + tests/ui/await_holding_lock.stderr | 4 +-- tests/ui/manual_async_fn.fixed | 40 ++++++++++++++++++++--- tests/ui/manual_async_fn.rs | 48 +++++++++++++++++++++++----- tests/ui/manual_async_fn.stderr | 30 +++++++++--------- 6 files changed, 146 insertions(+), 40 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index c19fb148cda..864d1ea87f5 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -4,8 +4,8 @@ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{ - AsyncGeneratorKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericBound, HirId, IsAsync, - ItemKind, TraitRef, Ty, TyKind, TypeBindingKind, + AsyncGeneratorKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericArg, GenericBound, HirId, + IsAsync, ItemKind, LifetimeName, TraitRef, Ty, TyKind, TypeBindingKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -27,8 +27,6 @@ declare_clippy_lint! { /// ``` /// Use instead: /// ```rust - /// use std::future::Future; - /// /// async fn foo() -> i32 { 42 } /// ``` pub MANUAL_ASYNC_FN, @@ -53,8 +51,9 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { if let IsAsync::NotAsync = header.asyncness; // Check that this function returns `impl Future` if let FnRetTy::Return(ret_ty) = decl.output; - if let Some(trait_ref) = future_trait_ref(cx, ret_ty); + if let Some((trait_ref, output_lifetimes)) = future_trait_ref(cx, ret_ty); if let Some(output) = future_output_ty(trait_ref); + if captures_all_lifetimes(decl.inputs, &output_lifetimes); // Check that the body of the function consists of one async block if let ExprKind::Block(block, _) = body.value.kind; if block.stmts.is_empty(); @@ -97,16 +96,35 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { } } -fn future_trait_ref<'tcx>(cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) -> Option<&'tcx TraitRef<'tcx>> { +fn future_trait_ref<'tcx>( + cx: &LateContext<'tcx>, + ty: &'tcx Ty<'tcx>, +) -> Option<(&'tcx TraitRef<'tcx>, Vec)> { if_chain! { - if let TyKind::OpaqueDef(item_id, _) = ty.kind; + if let TyKind::OpaqueDef(item_id, bounds) = ty.kind; let item = cx.tcx.hir().item(item_id.id); if let ItemKind::OpaqueTy(opaque) = &item.kind; - if opaque.bounds.len() == 1; - if let GenericBound::Trait(poly, _) = &opaque.bounds[0]; - if poly.trait_ref.trait_def_id() == cx.tcx.lang_items().future_trait(); + if let Some(trait_ref) = opaque.bounds.iter().find_map(|bound| { + if let GenericBound::Trait(poly, _) = bound { + Some(&poly.trait_ref) + } else { + None + } + }); + if trait_ref.trait_def_id() == cx.tcx.lang_items().future_trait(); then { - return Some(&poly.trait_ref); + let output_lifetimes = bounds + .iter() + .filter_map(|bound| { + if let GenericArg::Lifetime(lt) = bound { + Some(lt.name) + } else { + None + } + }) + .collect(); + + return Some((trait_ref, output_lifetimes)); } } @@ -129,6 +147,29 @@ fn future_output_ty<'tcx>(trait_ref: &'tcx TraitRef<'tcx>) -> Option<&'tcx Ty<'t None } +fn captures_all_lifetimes(inputs: &[Ty<'_>], output_lifetimes: &[LifetimeName]) -> bool { + let input_lifetimes: Vec = inputs + .iter() + .filter_map(|ty| { + if let TyKind::Rptr(lt, _) = ty.kind { + Some(lt.name) + } else { + None + } + }) + .collect(); + + // The lint should trigger in one of these cases: + // - There are no input lifetimes + // - There's only one output lifetime bound using `+ '_` + // - All input lifetimes are explicitly bound to the output + input_lifetimes.is_empty() + || (output_lifetimes.len() == 1 && matches!(output_lifetimes[0], LifetimeName::Underscore)) + || input_lifetimes + .iter() + .all(|in_lt| output_lifetimes.iter().any(|out_lt| in_lt == out_lt)) +} + fn desugared_async_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) -> Option<&'tcx Body<'tcx>> { if_chain! { if let Some(block_expr) = block.expr; diff --git a/tests/ui/await_holding_lock.rs b/tests/ui/await_holding_lock.rs index 5c1fdd83efb..0458950edee 100644 --- a/tests/ui/await_holding_lock.rs +++ b/tests/ui/await_holding_lock.rs @@ -47,6 +47,7 @@ async fn not_good(x: &Mutex) -> u32 { first + second + third } +#[allow(clippy::manual_async_fn)] fn block_bad(x: &Mutex) -> impl std::future::Future + '_ { async move { let guard = x.lock().unwrap(); diff --git a/tests/ui/await_holding_lock.stderr b/tests/ui/await_holding_lock.stderr index 8c47cb37d8c..21bf49d16f0 100644 --- a/tests/ui/await_holding_lock.stderr +++ b/tests/ui/await_holding_lock.stderr @@ -46,13 +46,13 @@ LL | | }; | |_____^ error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_lock.rs:52:13 + --> $DIR/await_holding_lock.rs:53:13 | LL | let guard = x.lock().unwrap(); | ^^^^^ | note: these are all the await points this lock is held through - --> $DIR/await_holding_lock.rs:52:9 + --> $DIR/await_holding_lock.rs:53:9 | LL | / let guard = x.lock().unwrap(); LL | | baz().await diff --git a/tests/ui/manual_async_fn.fixed b/tests/ui/manual_async_fn.fixed index 27222cc0869..4f551690c43 100644 --- a/tests/ui/manual_async_fn.fixed +++ b/tests/ui/manual_async_fn.fixed @@ -43,10 +43,6 @@ impl S { 42 } - async fn meth_fut(&self) -> i32 { 42 } - - async fn empty_fut(&self) {} - // should be ignored fn not_fut(&self) -> i32 { 42 @@ -64,4 +60,40 @@ impl S { } } +// Tests related to lifetime capture + +async fn elided(_: &i32) -> i32 { 42 } + +// should be ignored +fn elided_not_bound(_: &i32) -> impl Future { + async { 42 } +} + +async fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> i32 { 42 } + +// should be ignored +#[allow(clippy::needless_lifetimes)] +fn explicit_not_bound<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future { + async { 42 } +} + +// should be ignored +mod issue_5765 { + use std::future::Future; + + struct A; + impl A { + fn f(&self) -> impl Future { + async {} + } + } + + fn test() { + let _future = { + let a = A; + a.f() + }; + } +} + fn main() {} diff --git a/tests/ui/manual_async_fn.rs b/tests/ui/manual_async_fn.rs index 6a0f1b26c88..6ed60309947 100644 --- a/tests/ui/manual_async_fn.rs +++ b/tests/ui/manual_async_fn.rs @@ -51,14 +51,6 @@ impl S { } } - fn meth_fut(&self) -> impl Future { - async { 42 } - } - - fn empty_fut(&self) -> impl Future { - async {} - } - // should be ignored fn not_fut(&self) -> i32 { 42 @@ -76,4 +68,44 @@ impl S { } } +// Tests related to lifetime capture + +fn elided(_: &i32) -> impl Future + '_ { + async { 42 } +} + +// should be ignored +fn elided_not_bound(_: &i32) -> impl Future { + async { 42 } +} + +fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future + 'a + 'b { + async { 42 } +} + +// should be ignored +#[allow(clippy::needless_lifetimes)] +fn explicit_not_bound<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future { + async { 42 } +} + +// should be ignored +mod issue_5765 { + use std::future::Future; + + struct A; + impl A { + fn f(&self) -> impl Future { + async {} + } + } + + fn test() { + let _future = { + let a = A; + a.f() + }; + } +} + fn main() {} diff --git a/tests/ui/manual_async_fn.stderr b/tests/ui/manual_async_fn.stderr index a1904c904d0..ccd82867427 100644 --- a/tests/ui/manual_async_fn.stderr +++ b/tests/ui/manual_async_fn.stderr @@ -65,34 +65,34 @@ LL | let c = 21; ... error: this function can be simplified using the `async fn` syntax - --> $DIR/manual_async_fn.rs:54:5 + --> $DIR/manual_async_fn.rs:73:1 | -LL | fn meth_fut(&self) -> impl Future { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn elided(_: &i32) -> impl Future + '_ { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: make the function `async` and return the output of the future directly | -LL | async fn meth_fut(&self) -> i32 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | async fn elided(_: &i32) -> i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: move the body of the async block to the enclosing function | -LL | fn meth_fut(&self) -> impl Future { 42 } - | ^^^^^^ +LL | fn elided(_: &i32) -> impl Future + '_ { 42 } + | ^^^^^^ error: this function can be simplified using the `async fn` syntax - --> $DIR/manual_async_fn.rs:58:5 + --> $DIR/manual_async_fn.rs:82:1 | -LL | fn empty_fut(&self) -> impl Future { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future + 'a + 'b { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: make the function `async` and remove the return type +help: make the function `async` and return the output of the future directly | -LL | async fn empty_fut(&self) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | async fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: move the body of the async block to the enclosing function | -LL | fn empty_fut(&self) -> impl Future {} - | ^^ +LL | fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future + 'a + 'b { 42 } + | ^^^^^^ error: aborting due to 6 previous errors -- cgit 1.4.1-3-g733a5 From 05bb6e6bdb1894de5803f729339a631a9222499f Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Mon, 20 Jul 2020 08:58:55 -0700 Subject: Create test for wanted behavior --- tests/ui/needless_collect.rs | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'tests') diff --git a/tests/ui/needless_collect.rs b/tests/ui/needless_collect.rs index 7ee603afeb0..1577e7a46ed 100644 --- a/tests/ui/needless_collect.rs +++ b/tests/ui/needless_collect.rs @@ -8,6 +8,9 @@ use std::collections::{BTreeSet, HashMap, HashSet}; #[allow(unused_variables, clippy::iter_cloned_collect)] fn main() { let sample = [1; 5]; + let indirect_with_into_iter = sample.iter().collect::>(); + let indirect_with_iter = sample.iter().collect::>();; + let indirect_negative = sample.iter().collect::>();; let len = sample.iter().collect::>().len(); if sample.iter().collect::>().is_empty() { // Empty @@ -18,4 +21,8 @@ fn main() { sample.iter().collect::>().len(); // Neither should this sample.iter().collect::>().len(); + indirect_with_into_iter.into_iter().map(|x| (x, x+1)).collect::>(); + indirect_with_iter.iter().map(|x| (x, x+1)).collect::>(); + indirect_negative.iter().map(|x| (x, x+1)).collect::>(); + indirect_negative.iter().map(|x| (x, x+1)).collect::>(); } -- cgit 1.4.1-3-g733a5 From 3ee61373fe056efb46b6b1b243b31cec0d7e6099 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Wed, 22 Jul 2020 22:46:23 -0700 Subject: Write the lint and write tests --- clippy_lints/src/loops.rs | 107 ++++++++++++++++++++++++++++++++++++--- tests/ui/needless_collect.fixed | 11 ++++ tests/ui/needless_collect.rs | 18 ++++--- tests/ui/needless_collect.stderr | 17 +++++-- 4 files changed, 137 insertions(+), 16 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 7e3876ff49b..231c440463d 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1,14 +1,15 @@ use crate::consts::constant; use crate::reexport::Name; use crate::utils::paths; +use crate::utils::sugg::Sugg; use crate::utils::usage::{is_unused, mutated_variables}; use crate::utils::{ get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, - is_integer_const, is_no_std_crate, is_refutable, last_path_segment, match_trait_method, match_type, match_var, - multispan_sugg, snippet, snippet_opt, snippet_with_applicability, span_lint, span_lint_and_help, - span_lint_and_sugg, span_lint_and_then, SpanlessEq, + is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, match_path, + match_trait_method, match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_opt, + snippet_with_applicability, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, + SpanlessEq, }; -use crate::utils::{is_type_diagnostic_item, qpath_res, sugg}; use if_chain::if_chain; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -17,7 +18,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{walk_block, walk_expr, walk_pat, walk_stmt, NestedVisitorMap, Visitor}; use rustc_hir::{ def_id, BinOpKind, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, GenericArg, HirId, InlineAsmOperand, - LoopSource, MatchSource, Mutability, Node, Pat, PatKind, QPath, Stmt, StmtKind, + Local, LoopSource, MatchSource, Mutability, Node, Pat, PatKind, QPath, Stmt, StmtKind, }; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -27,7 +28,7 @@ use rustc_middle::middle::region; use rustc_middle::ty::{self, Ty, TyS}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use rustc_span::symbol::Symbol; +use rustc_span::symbol::{Ident, Symbol}; use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use std::iter::{once, Iterator}; use std::mem; @@ -2358,6 +2359,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VarCollectorVisitor<'a, 'tcx> { const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed"; fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { + // Check for direct, immediate usage if_chain! { if let ExprKind::MethodCall(ref method, _, ref args, _) = expr.kind; if let ExprKind::MethodCall(ref chain_method, _, _, _) = args[0].kind; @@ -2423,6 +2425,99 @@ fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { } } } + // Check for collecting it and then turning it back into an iterator later + if let ExprKind::Block(ref block, _) = expr.kind { + for ref stmt in block.stmts { + if_chain! { + // TODO also work for assignments to an existing variable + if let StmtKind::Local( + Local { pat: Pat { kind: PatKind::Binding(_, _, ident, .. ), .. }, + init: Some(ref init_expr), .. } + ) = stmt.kind; + if let ExprKind::MethodCall(ref method_name, _, &[ref iter_source], ..) = init_expr.kind; + if method_name.ident.name == sym!(collect) && match_trait_method(cx, &init_expr, &paths::ITERATOR); + if let Some(ref generic_args) = method_name.args; + if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0); + if let ty = cx.typeck_results().node_type(ty.hir_id); + if is_type_diagnostic_item(cx, ty, sym!(vec_type)) || + is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) || + match_type(cx, ty, &paths::LINKED_LIST); + if let Some(iter_calls) = detect_iter_and_into_iters(block, *ident); + if iter_calls.len() == 1; + then { + // Suggest replacing iter_call with iter_replacement, and removing stmt + span_lint_and_then( + cx, + NEEDLESS_COLLECT, + stmt.span, + NEEDLESS_COLLECT_MSG, + |diag| { + let iter_replacement = Sugg::hir(cx, iter_source, "..").to_string(); + diag.multipart_suggestion( + "Use the original Iterator instead of collecting it and then producing a new one", + vec![ + (stmt.span, String::new()), + (iter_calls[0].span, iter_replacement) + ], + Applicability::MaybeIncorrect, + ); + }, + ); + } + } + } + } +} + +struct IntoIterVisitor<'tcx> { + iters: Vec<&'tcx Expr<'tcx>>, + seen_other: bool, + target: String, +} +impl<'tcx> Visitor<'tcx> for IntoIterVisitor<'tcx> { + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { + match &expr.kind { + ExprKind::MethodCall( + method_name, + _, + &[Expr { + kind: ExprKind::Path(QPath::Resolved(_, ref path)), + .. + }], + _, + ) if match_path(path, &[&self.target]) => { + // TODO Check what method is being called, if it's called on target, and act + // accordingly + if method_name.ident.name == sym!(into_iter) { + self.iters.push(expr); + } else { + self.seen_other = true; + } + }, + _ => walk_expr(self, expr), + } + } + + type Map = Map<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +/// Detect the occurences of calls to `iter` or `into_iter` for the +/// given identifier +fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, identifier: Ident) -> Option>> { + let mut visitor = IntoIterVisitor { + iters: Vec::new(), + target: identifier.name.to_ident_string(), + seen_other: false, + }; + visitor.visit_block(block); + if visitor.seen_other { + None + } else { + Some(visitor.iters) + } } fn shorten_span(expr: &Expr<'_>, target_fn_name: Symbol) -> Span { diff --git a/tests/ui/needless_collect.fixed b/tests/ui/needless_collect.fixed index be37dc16b9a..60a3e206283 100644 --- a/tests/ui/needless_collect.fixed +++ b/tests/ui/needless_collect.fixed @@ -18,4 +18,15 @@ fn main() { sample.iter().collect::>().len(); // Neither should this sample.iter().collect::>().len(); + let indirect_positive = sample.iter().collect::>(); + indirect_positive + .into_iter() + .map(|x| (x, x + 1)) + .collect::>(); + let indirect_negative = sample.iter().collect::>(); + indirect_negative.len(); + indirect_negative + .iter() + .map(|x| (*x, *x + 1)) + .collect::>(); } diff --git a/tests/ui/needless_collect.rs b/tests/ui/needless_collect.rs index 1577e7a46ed..33a1ea36095 100644 --- a/tests/ui/needless_collect.rs +++ b/tests/ui/needless_collect.rs @@ -8,9 +8,6 @@ use std::collections::{BTreeSet, HashMap, HashSet}; #[allow(unused_variables, clippy::iter_cloned_collect)] fn main() { let sample = [1; 5]; - let indirect_with_into_iter = sample.iter().collect::>(); - let indirect_with_iter = sample.iter().collect::>();; - let indirect_negative = sample.iter().collect::>();; let len = sample.iter().collect::>().len(); if sample.iter().collect::>().is_empty() { // Empty @@ -21,8 +18,15 @@ fn main() { sample.iter().collect::>().len(); // Neither should this sample.iter().collect::>().len(); - indirect_with_into_iter.into_iter().map(|x| (x, x+1)).collect::>(); - indirect_with_iter.iter().map(|x| (x, x+1)).collect::>(); - indirect_negative.iter().map(|x| (x, x+1)).collect::>(); - indirect_negative.iter().map(|x| (x, x+1)).collect::>(); + let indirect_positive = sample.iter().collect::>(); + indirect_positive + .into_iter() + .map(|x| (x, x + 1)) + .collect::>(); + let indirect_negative = sample.iter().collect::>(); + indirect_negative.len(); + indirect_negative + .iter() + .map(|x| (*x, *x + 1)) + .collect::>(); } diff --git a/tests/ui/needless_collect.stderr b/tests/ui/needless_collect.stderr index 9113aad90dd..bb67bfa83e9 100644 --- a/tests/ui/needless_collect.stderr +++ b/tests/ui/needless_collect.stderr @@ -1,10 +1,21 @@ +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:21:5 + | +LL | let indirect_positive = sample.iter().collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::needless-collect` implied by `-D warnings` +help: Use the original Iterator instead of collecting it and then producing a new one + | +LL | +LL | sample.iter() + | + error: avoid using `collect()` when not needed --> $DIR/needless_collect.rs:11:29 | LL | let len = sample.iter().collect::>().len(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `count()` - | - = note: `-D clippy::needless-collect` implied by `-D warnings` error: avoid using `collect()` when not needed --> $DIR/needless_collect.rs:12:15 @@ -24,5 +35,5 @@ error: avoid using `collect()` when not needed LL | sample.iter().map(|x| (x, x)).collect::>().len(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `count()` -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors -- cgit 1.4.1-3-g733a5 From c86f4109fdd83fef1ea69c0f3c878ace0aa7c56f Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Thu, 23 Jul 2020 09:15:16 -0700 Subject: Split indirect collects into their own test case --- tests/ui/needless_collect.fixed | 11 ------- tests/ui/needless_collect.rs | 11 ------- tests/ui/needless_collect.stderr | 17 ++-------- tests/ui/needless_collect_indirect.fixed | 26 +++++++++++++++ tests/ui/needless_collect_indirect.rs | 26 +++++++++++++++ tests/ui/needless_collect_indirect.stderr | 55 +++++++++++++++++++++++++++++++ 6 files changed, 110 insertions(+), 36 deletions(-) create mode 100644 tests/ui/needless_collect_indirect.fixed create mode 100644 tests/ui/needless_collect_indirect.rs create mode 100644 tests/ui/needless_collect_indirect.stderr (limited to 'tests') diff --git a/tests/ui/needless_collect.fixed b/tests/ui/needless_collect.fixed index 60a3e206283..be37dc16b9a 100644 --- a/tests/ui/needless_collect.fixed +++ b/tests/ui/needless_collect.fixed @@ -18,15 +18,4 @@ fn main() { sample.iter().collect::>().len(); // Neither should this sample.iter().collect::>().len(); - let indirect_positive = sample.iter().collect::>(); - indirect_positive - .into_iter() - .map(|x| (x, x + 1)) - .collect::>(); - let indirect_negative = sample.iter().collect::>(); - indirect_negative.len(); - indirect_negative - .iter() - .map(|x| (*x, *x + 1)) - .collect::>(); } diff --git a/tests/ui/needless_collect.rs b/tests/ui/needless_collect.rs index 33a1ea36095..7ee603afeb0 100644 --- a/tests/ui/needless_collect.rs +++ b/tests/ui/needless_collect.rs @@ -18,15 +18,4 @@ fn main() { sample.iter().collect::>().len(); // Neither should this sample.iter().collect::>().len(); - let indirect_positive = sample.iter().collect::>(); - indirect_positive - .into_iter() - .map(|x| (x, x + 1)) - .collect::>(); - let indirect_negative = sample.iter().collect::>(); - indirect_negative.len(); - indirect_negative - .iter() - .map(|x| (*x, *x + 1)) - .collect::>(); } diff --git a/tests/ui/needless_collect.stderr b/tests/ui/needless_collect.stderr index bb67bfa83e9..9113aad90dd 100644 --- a/tests/ui/needless_collect.stderr +++ b/tests/ui/needless_collect.stderr @@ -1,21 +1,10 @@ -error: avoid using `collect()` when not needed - --> $DIR/needless_collect.rs:21:5 - | -LL | let indirect_positive = sample.iter().collect::>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::needless-collect` implied by `-D warnings` -help: Use the original Iterator instead of collecting it and then producing a new one - | -LL | -LL | sample.iter() - | - error: avoid using `collect()` when not needed --> $DIR/needless_collect.rs:11:29 | LL | let len = sample.iter().collect::>().len(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `count()` + | + = note: `-D clippy::needless-collect` implied by `-D warnings` error: avoid using `collect()` when not needed --> $DIR/needless_collect.rs:12:15 @@ -35,5 +24,5 @@ error: avoid using `collect()` when not needed LL | sample.iter().map(|x| (x, x)).collect::>().len(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `count()` -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/needless_collect_indirect.fixed b/tests/ui/needless_collect_indirect.fixed new file mode 100644 index 00000000000..136af42a9fe --- /dev/null +++ b/tests/ui/needless_collect_indirect.fixed @@ -0,0 +1,26 @@ +// run-rustfix + +#[allow(unused)] + +use std::collections::{HashMap, VecDeque}; + +fn main() { + let sample = [1; 5]; + let indirect_iter = sample.iter().collect::>(); + indirect_iter + .into_iter() + .map(|x| (x, x + 1)) + .collect::>(); + let indirect_len = sample.iter().collect::>(); + indirect_len.len(); + let indirect_empty = sample.iter().collect::>(); + indirect_empty.is_empty(); + let indirect_contains = sample.iter().collect::>(); + indirect_contains.contains(&&5); + let indirect_negative = sample.iter().collect::>(); + indirect_negative.len(); + indirect_negative + .into_iter() + .map(|x| (*x, *x + 1)) + .collect::>(); +} diff --git a/tests/ui/needless_collect_indirect.rs b/tests/ui/needless_collect_indirect.rs new file mode 100644 index 00000000000..136af42a9fe --- /dev/null +++ b/tests/ui/needless_collect_indirect.rs @@ -0,0 +1,26 @@ +// run-rustfix + +#[allow(unused)] + +use std::collections::{HashMap, VecDeque}; + +fn main() { + let sample = [1; 5]; + let indirect_iter = sample.iter().collect::>(); + indirect_iter + .into_iter() + .map(|x| (x, x + 1)) + .collect::>(); + let indirect_len = sample.iter().collect::>(); + indirect_len.len(); + let indirect_empty = sample.iter().collect::>(); + indirect_empty.is_empty(); + let indirect_contains = sample.iter().collect::>(); + indirect_contains.contains(&&5); + let indirect_negative = sample.iter().collect::>(); + indirect_negative.len(); + indirect_negative + .into_iter() + .map(|x| (*x, *x + 1)) + .collect::>(); +} diff --git a/tests/ui/needless_collect_indirect.stderr b/tests/ui/needless_collect_indirect.stderr new file mode 100644 index 00000000000..5058c171ac2 --- /dev/null +++ b/tests/ui/needless_collect_indirect.stderr @@ -0,0 +1,55 @@ +error: avoid using `collect()` when not needed + --> $DIR/needless_collect_indirect.rs:9:5 + | +LL | / let indirect_iter = sample.iter().collect::>(); +LL | | indirect_iter + | |____^ + | + = note: `-D clippy::needless-collect` implied by `-D warnings` +help: Use the original Iterator instead of collecting it and then producing a new one + | +LL | +LL | sample.iter() + | + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect_indirect.rs:14:5 + | +LL | / let indirect_len = sample.iter().collect::>(); +LL | | indirect_len.len(); + | |____^ + | +help: Take the original Iterator's count instead of collecting it and finding the length + | +LL | +LL | sample.iter().count(); + | + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect_indirect.rs:16:5 + | +LL | / let indirect_empty = sample.iter().collect::>(); +LL | | indirect_empty.is_empty(); + | |____^ + | +help: Check if the original Iterator has anything instead of collecting it and seeing if it's empty + | +LL | +LL | sample.iter().next().is_none(); + | + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect_indirect.rs:18:5 + | +LL | / let indirect_contains = sample.iter().collect::>(); +LL | | indirect_contains.contains(&&5); + | |____^ + | +help: Check if the original Iterator contains an element instead of collecting then checking + | +LL | +LL | sample.iter().any(|x| x == &&5); + | + +error: aborting due to 4 previous errors + -- cgit 1.4.1-3-g733a5 From a84948329459e650af4eb2f8cff8f55282316ea5 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Thu, 23 Jul 2020 09:44:44 -0700 Subject: Fix formatting and dogfood fallout --- clippy_lints/src/loops.rs | 43 ++++++++++++++++++++++++------- tests/ui/needless_collect_indirect.fixed | 6 +---- tests/ui/needless_collect_indirect.rs | 6 +---- tests/ui/needless_collect_indirect.stderr | 12 ++++----- 4 files changed, 41 insertions(+), 26 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 11a9b1e531c..2181304f006 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2359,7 +2359,10 @@ impl<'a, 'tcx> Visitor<'tcx> for VarCollectorVisitor<'a, 'tcx> { const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed"; fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { - // Check for direct, immediate usage + check_needless_collect_direct_usage(expr, cx); + check_needless_collect_indirect_usage(expr, cx); +} +fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { if_chain! { if let ExprKind::MethodCall(ref method, _, ref args, _) = expr.kind; if let ExprKind::MethodCall(ref chain_method, _, _, _) = args[0].kind; @@ -2425,7 +2428,9 @@ fn check_needless_collect<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { } } } - // Check for collecting it and then turning it back into an iterator later +} + +fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { if let ExprKind::Block(ref block, _) = expr.kind { for ref stmt in block.stmts { if_chain! { @@ -2484,10 +2489,18 @@ impl IterFunction { } fn get_suggestion_text(&self) -> &'static str { match &self.func { - IterFunctionKind::IntoIter => "Use the original Iterator instead of collecting it and then producing a new one", - IterFunctionKind::Len => "Take the original Iterator's count instead of collecting it and finding the length", - IterFunctionKind::IsEmpty => "Check if the original Iterator has anything instead of collecting it and seeing if it's empty", - IterFunctionKind::Contains(_) => "Check if the original Iterator contains an element instead of collecting then checking", + IterFunctionKind::IntoIter => { + "Use the original Iterator instead of collecting it and then producing a new one" + }, + IterFunctionKind::Len => { + "Take the original Iterator's count instead of collecting it and finding the length" + }, + IterFunctionKind::IsEmpty => { + "Check if the original Iterator has anything instead of collecting it and seeing if it's empty" + }, + IterFunctionKind::Contains(_) => { + "Check if the original Iterator contains an element instead of collecting then checking" + }, } } } @@ -2505,6 +2518,8 @@ struct IterFunctionVisitor { } impl<'tcx> Visitor<'tcx> for IterFunctionVisitor { fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { + // TODO Check if the target identifier is being used in something other + // than a function call if_chain! { if let ExprKind::MethodCall(method_name, _, ref args, _) = &expr.kind; if let Some(Expr { kind: ExprKind::Path(QPath::Resolved(_, ref path)), .. }) = args.get(0); @@ -2515,10 +2530,18 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor { let is_empty = sym!(is_empty); let contains = sym!(contains); match method_name.ident.name { - name if name == into_iter => self.uses.push(IterFunction { func: IterFunctionKind::IntoIter, span: expr.span }), - name if name == len => self.uses.push(IterFunction { func: IterFunctionKind::Len, span: expr.span }), - name if name == is_empty => self.uses.push(IterFunction { func: IterFunctionKind::IsEmpty, span: expr.span }), - name if name == contains => self.uses.push(IterFunction { func: IterFunctionKind::Contains(args[1].span), span: expr.span }), + name if name == into_iter => self.uses.push( + IterFunction { func: IterFunctionKind::IntoIter, span: expr.span } + ), + name if name == len => self.uses.push( + IterFunction { func: IterFunctionKind::Len, span: expr.span } + ), + name if name == is_empty => self.uses.push( + IterFunction { func: IterFunctionKind::IsEmpty, span: expr.span } + ), + name if name == contains => self.uses.push( + IterFunction { func: IterFunctionKind::Contains(args[1].span), span: expr.span } + ), _ => self.seen_other = true, } } diff --git a/tests/ui/needless_collect_indirect.fixed b/tests/ui/needless_collect_indirect.fixed index 136af42a9fe..163eaf965dd 100644 --- a/tests/ui/needless_collect_indirect.fixed +++ b/tests/ui/needless_collect_indirect.fixed @@ -1,16 +1,12 @@ // run-rustfix #[allow(unused)] - use std::collections::{HashMap, VecDeque}; fn main() { let sample = [1; 5]; let indirect_iter = sample.iter().collect::>(); - indirect_iter - .into_iter() - .map(|x| (x, x + 1)) - .collect::>(); + indirect_iter.into_iter().map(|x| (x, x + 1)).collect::>(); let indirect_len = sample.iter().collect::>(); indirect_len.len(); let indirect_empty = sample.iter().collect::>(); diff --git a/tests/ui/needless_collect_indirect.rs b/tests/ui/needless_collect_indirect.rs index 136af42a9fe..163eaf965dd 100644 --- a/tests/ui/needless_collect_indirect.rs +++ b/tests/ui/needless_collect_indirect.rs @@ -1,16 +1,12 @@ // run-rustfix #[allow(unused)] - use std::collections::{HashMap, VecDeque}; fn main() { let sample = [1; 5]; let indirect_iter = sample.iter().collect::>(); - indirect_iter - .into_iter() - .map(|x| (x, x + 1)) - .collect::>(); + indirect_iter.into_iter().map(|x| (x, x + 1)).collect::>(); let indirect_len = sample.iter().collect::>(); indirect_len.len(); let indirect_empty = sample.iter().collect::>(); diff --git a/tests/ui/needless_collect_indirect.stderr b/tests/ui/needless_collect_indirect.stderr index 5058c171ac2..700c73b0b22 100644 --- a/tests/ui/needless_collect_indirect.stderr +++ b/tests/ui/needless_collect_indirect.stderr @@ -1,19 +1,19 @@ error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:9:5 + --> $DIR/needless_collect_indirect.rs:8:5 | LL | / let indirect_iter = sample.iter().collect::>(); -LL | | indirect_iter +LL | | indirect_iter.into_iter().map(|x| (x, x + 1)).collect::>(); | |____^ | = note: `-D clippy::needless-collect` implied by `-D warnings` help: Use the original Iterator instead of collecting it and then producing a new one | LL | -LL | sample.iter() +LL | sample.iter().map(|x| (x, x + 1)).collect::>(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:14:5 + --> $DIR/needless_collect_indirect.rs:10:5 | LL | / let indirect_len = sample.iter().collect::>(); LL | | indirect_len.len(); @@ -26,7 +26,7 @@ LL | sample.iter().count(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:16:5 + --> $DIR/needless_collect_indirect.rs:12:5 | LL | / let indirect_empty = sample.iter().collect::>(); LL | | indirect_empty.is_empty(); @@ -39,7 +39,7 @@ LL | sample.iter().next().is_none(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:18:5 + --> $DIR/needless_collect_indirect.rs:14:5 | LL | / let indirect_contains = sample.iter().collect::>(); LL | | indirect_contains.contains(&&5); -- cgit 1.4.1-3-g733a5 From 5e10b039a3f215b48a54ce7dd38f95115f92e55a Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Sun, 2 Aug 2020 21:46:18 -0700 Subject: Implement review suggestions --- clippy_lints/src/loops.rs | 7 +++---- tests/ui/needless_collect_indirect.fixed | 22 ---------------------- tests/ui/needless_collect_indirect.rs | 3 --- tests/ui/needless_collect_indirect.stderr | 8 ++++---- 4 files changed, 7 insertions(+), 33 deletions(-) delete mode 100644 tests/ui/needless_collect_indirect.fixed (limited to 'tests') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index c931c212735..e9ce804320f 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -27,7 +27,7 @@ use rustc_middle::middle::region; use rustc_middle::ty::{self, Ty, TyS}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use std::iter::{once, Iterator}; use std::mem; @@ -2442,7 +2442,7 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo if let Some(ref generic_args) = method_name.args; if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0); if let ty = cx.typeck_results().node_type(ty.hir_id); - if is_type_diagnostic_item(cx, ty, sym!(vec_type)) || + if is_type_diagnostic_item(cx, ty, sym::vec_type) || is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) || match_type(cx, ty, &paths::LINKED_LIST); if let Some(iter_calls) = detect_iter_and_into_iters(block, *ident); @@ -2524,12 +2524,11 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor { if let &[name] = &path.segments; if name.ident == self.target; then { - let into_iter = sym!(into_iter); let len = sym!(len); let is_empty = sym!(is_empty); let contains = sym!(contains); match method_name.ident.name { - name if name == into_iter => self.uses.push( + sym::into_iter => self.uses.push( IterFunction { func: IterFunctionKind::IntoIter, span: expr.span } ), name if name == len => self.uses.push( diff --git a/tests/ui/needless_collect_indirect.fixed b/tests/ui/needless_collect_indirect.fixed deleted file mode 100644 index 163eaf965dd..00000000000 --- a/tests/ui/needless_collect_indirect.fixed +++ /dev/null @@ -1,22 +0,0 @@ -// run-rustfix - -#[allow(unused)] -use std::collections::{HashMap, VecDeque}; - -fn main() { - let sample = [1; 5]; - let indirect_iter = sample.iter().collect::>(); - indirect_iter.into_iter().map(|x| (x, x + 1)).collect::>(); - let indirect_len = sample.iter().collect::>(); - indirect_len.len(); - let indirect_empty = sample.iter().collect::>(); - indirect_empty.is_empty(); - let indirect_contains = sample.iter().collect::>(); - indirect_contains.contains(&&5); - let indirect_negative = sample.iter().collect::>(); - indirect_negative.len(); - indirect_negative - .into_iter() - .map(|x| (*x, *x + 1)) - .collect::>(); -} diff --git a/tests/ui/needless_collect_indirect.rs b/tests/ui/needless_collect_indirect.rs index 163eaf965dd..4cf03e82035 100644 --- a/tests/ui/needless_collect_indirect.rs +++ b/tests/ui/needless_collect_indirect.rs @@ -1,6 +1,3 @@ -// run-rustfix - -#[allow(unused)] use std::collections::{HashMap, VecDeque}; fn main() { diff --git a/tests/ui/needless_collect_indirect.stderr b/tests/ui/needless_collect_indirect.stderr index 700c73b0b22..0c1e61d7496 100644 --- a/tests/ui/needless_collect_indirect.stderr +++ b/tests/ui/needless_collect_indirect.stderr @@ -1,5 +1,5 @@ error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:8:5 + --> $DIR/needless_collect_indirect.rs:5:5 | LL | / let indirect_iter = sample.iter().collect::>(); LL | | indirect_iter.into_iter().map(|x| (x, x + 1)).collect::>(); @@ -13,7 +13,7 @@ LL | sample.iter().map(|x| (x, x + 1)).collect::>(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:10:5 + --> $DIR/needless_collect_indirect.rs:7:5 | LL | / let indirect_len = sample.iter().collect::>(); LL | | indirect_len.len(); @@ -26,7 +26,7 @@ LL | sample.iter().count(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:12:5 + --> $DIR/needless_collect_indirect.rs:9:5 | LL | / let indirect_empty = sample.iter().collect::>(); LL | | indirect_empty.is_empty(); @@ -39,7 +39,7 @@ LL | sample.iter().next().is_none(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:14:5 + --> $DIR/needless_collect_indirect.rs:11:5 | LL | / let indirect_contains = sample.iter().collect::>(); LL | | indirect_contains.contains(&&5); -- cgit 1.4.1-3-g733a5 From 0e44ed5ca9b1436c245a4660b04e76fd99be7420 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Mon, 3 Aug 2020 17:22:47 +0200 Subject: Fix ui-cargo tests in CI --- tests/compile-test.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/compile-test.rs b/tests/compile-test.rs index eb6d495acbe..911da40d27b 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -3,7 +3,7 @@ use compiletest_rs as compiletest; use compiletest_rs::common::Mode as TestMode; -use std::env::{self, set_var}; +use std::env::{self, set_var, var}; use std::ffi::OsStr; use std::fs; use std::io; @@ -136,7 +136,9 @@ fn run_ui_toml(config: &mut compiletest::Config) { let tests = compiletest::make_tests(&config); + let manifest_dir = var("CARGO_MANIFEST_DIR").unwrap_or_default(); let res = run_tests(&config, tests); + set_var("CARGO_MANIFEST_DIR", &manifest_dir); match res { Ok(true) => {}, Ok(false) => panic!("Some tests failed"), -- cgit 1.4.1-3-g733a5 From 25abd7ae76e2a708dda5487119c20af3be64edb7 Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Wed, 8 Jul 2020 20:29:56 -0700 Subject: Create stable_sort_primitive lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 ++ clippy_lints/src/stable_sort_primitive.rs | 130 ++++++++++++++++++++++++++++++ clippy_lints/src/utils/mod.rs | 30 +++++++ src/lintlist/mod.rs | 7 ++ tests/ui/stable_sort_primitive.fixed | 32 ++++++++ tests/ui/stable_sort_primitive.rs | 32 ++++++++ tests/ui/stable_sort_primitive.stderr | 46 +++++++++++ tests/ui/unnecessary_sort_by.fixed | 2 + tests/ui/unnecessary_sort_by.rs | 2 + tests/ui/unnecessary_sort_by.stderr | 14 ++-- 11 files changed, 294 insertions(+), 7 deletions(-) create mode 100644 clippy_lints/src/stable_sort_primitive.rs create mode 100644 tests/ui/stable_sort_primitive.fixed create mode 100644 tests/ui/stable_sort_primitive.rs create mode 100644 tests/ui/stable_sort_primitive.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 776b04295f9..43a32e828d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1701,6 +1701,7 @@ Released 2018-09-13 [`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else [`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next [`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization +[`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive [`str_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_to_string [`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add [`string_add_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add_assign diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f371942dbee..9fc07e07fd3 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -288,6 +288,7 @@ mod serde_api; mod shadow; mod single_component_path_imports; mod slow_vector_initialization; +mod stable_sort_primitive; mod strings; mod suspicious_trait_impl; mod swap; @@ -776,6 +777,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &shadow::SHADOW_UNRELATED, &single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, &slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, + &stable_sort_primitive::STABLE_SORT_PRIMITIVE, &strings::STRING_ADD, &strings::STRING_ADD_ASSIGN, &strings::STRING_LIT_AS_BYTES, @@ -1078,6 +1080,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box macro_use::MacroUseImports::default()); store.register_late_pass(|| box map_identity::MapIdentity); store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch); + store.register_late_pass(|| box stable_sort_primitive::StableSortPrimitive); store.register_late_pass(|| box repeat_once::RepeatOnce); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ @@ -1408,6 +1411,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&serde_api::SERDE_API_MISUSE), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), + LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE), LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), @@ -1723,6 +1727,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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_VEC), LintId::of(&types::REDUNDANT_ALLOCATION), LintId::of(&vec::USELESS_VEC), diff --git a/clippy_lints/src/stable_sort_primitive.rs b/clippy_lints/src/stable_sort_primitive.rs new file mode 100644 index 00000000000..c48da004a60 --- /dev/null +++ b/clippy_lints/src/stable_sort_primitive.rs @@ -0,0 +1,130 @@ +use crate::utils::{is_slice_of_primitives, span_lint_and_sugg, sugg::Sugg}; + +use if_chain::if_chain; + +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** + /// When sorting primitive values (integers, bools, chars, as well + /// as arrays, slices, and tuples of such items), it is better to + /// use an unstable sort than a stable sort. + /// + /// **Why is this bad?** + /// Using a stable sort consumes more memory and cpu cycles. Because + /// values which compare equal are identical, preserving their + /// relative order (the guarantee that a stable sort provides) means + /// nothing, while the extra costs still apply. + /// + /// **Known problems:** + /// None + /// + /// **Example:** + /// + /// ```rust + /// let mut vec = vec![2, 1, 3]; + /// vec.sort(); + /// ``` + /// Use instead: + /// ```rust + /// let mut vec = vec![2, 1, 3]; + /// vec.sort_unstable(); + /// ``` + pub STABLE_SORT_PRIMITIVE, + perf, + "use of sort() when sort_unstable() is equivalent" +} + +declare_lint_pass!(StableSortPrimitive => [STABLE_SORT_PRIMITIVE]); + +/// The three "kinds" of sorts +enum SortingKind { + Vanilla, + // The other kinds of lint are currently commented out because they + // can map distinct values to equal ones. If the key function is + // provably one-to-one, or if the Cmp function conserves equality, + // then they could be linted on, but I don't know if we can check + // for that. + + // ByKey, + // ByCmp, +} +impl SortingKind { + /// The name of the stable version of this kind of sort + fn stable_name(&self) -> &str { + match self { + SortingKind::Vanilla => "sort", + // SortingKind::ByKey => "sort_by_key", + // SortingKind::ByCmp => "sort_by", + } + } + /// The name of the unstable version of this kind of sort + fn unstable_name(&self) -> &str { + match self { + SortingKind::Vanilla => "sort_unstable", + // SortingKind::ByKey => "sort_unstable_by_key", + // SortingKind::ByCmp => "sort_unstable_by", + } + } + /// Takes the name of a function call and returns the kind of sort + /// that corresponds to that function name (or None if it isn't) + fn from_stable_name(name: &str) -> Option { + match name { + "sort" => Some(SortingKind::Vanilla), + // "sort_by" => Some(SortingKind::ByCmp), + // "sort_by_key" => Some(SortingKind::ByKey), + _ => None, + } + } +} + +/// A detected instance of this lint +struct LintDetection { + slice_name: String, + method: SortingKind, + method_args: String, +} + +fn detect_stable_sort_primitive(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { + if_chain! { + if let ExprKind::MethodCall(method_name, _, args, _) = &expr.kind; + if let Some(slice) = &args.get(0); + if let Some(method) = SortingKind::from_stable_name(&method_name.ident.name.as_str()); + if is_slice_of_primitives(cx, slice); + then { + let args_str = args.iter().skip(1).map(|arg| Sugg::hir(cx, arg, "..").to_string()).collect::>().join(", "); + Some(LintDetection { slice_name: Sugg::hir(cx, slice, "..").to_string(), method, method_args: args_str }) + } else { + None + } + } +} + +impl LateLintPass<'_> for StableSortPrimitive { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if let Some(detection) = detect_stable_sort_primitive(cx, expr) { + span_lint_and_sugg( + cx, + STABLE_SORT_PRIMITIVE, + expr.span, + format!( + "Use {} instead of {}", + detection.method.unstable_name(), + detection.method.stable_name() + ) + .as_str(), + "try", + format!( + "{}.{}({})", + detection.slice_name, + detection.method.unstable_name(), + detection.method_args + ), + Applicability::MachineApplicable, + ); + } + } +} diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 655b1133cf7..c75f8042907 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -1378,6 +1378,36 @@ pub fn run_lints(cx: &LateContext<'_>, lints: &[&'static Lint], id: HirId) -> bo }) } +/// 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). +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, + ty::Ref(_, inner, _) if inner.kind == ty::Str => true, + ty::Array(inner_type, _) | ty::Slice(inner_type) => is_recursively_primitive_type(inner_type), + ty::Tuple(inner_types) => inner_types.types().all(is_recursively_primitive_type), + _ => false, + } +} + +/// Returns true iff the given expression is a slice of primitives (as defined in the +/// `is_recursively_primitive_type` function). +pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let expr_type = cx.typeck_results().expr_ty_adjusted(expr); + match expr_type.kind { + ty::Slice(ref element_type) + | ty::Ref( + _, + ty::TyS { + kind: ty::Slice(ref element_type), + .. + }, + _, + ) => is_recursively_primitive_type(element_type), + _ => false, + } +} + #[macro_export] macro_rules! unwrap_cargo_metadata { ($cx: ident, $lint: ident, $deps: expr) => {{ diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 1879aae77fb..41d06a67881 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2026,6 +2026,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "slow_vector_initialization", }, + Lint { + name: "stable_sort_primitive", + group: "perf", + desc: "use of sort() when sort_unstable() is equivalent", + deprecation: None, + module: "stable_sort_primitive", + }, Lint { name: "string_add", group: "restriction", diff --git a/tests/ui/stable_sort_primitive.fixed b/tests/ui/stable_sort_primitive.fixed new file mode 100644 index 00000000000..8f8f5665931 --- /dev/null +++ b/tests/ui/stable_sort_primitive.fixed @@ -0,0 +1,32 @@ +// run-rustfix +#![warn(clippy::stable_sort_primitive)] + +fn main() { + // positive examples + let mut vec = vec![1, 3, 2]; + vec.sort_unstable(); + let mut vec = vec![false, false, true]; + vec.sort_unstable(); + let mut vec = vec!['a', 'A', 'c']; + vec.sort_unstable(); + let mut vec = vec!["ab", "cd", "ab", "bc"]; + vec.sort_unstable(); + let mut vec = vec![(2, 1), (1, 2), (2, 5)]; + vec.sort_unstable(); + let mut vec = vec![[2, 1], [1, 2], [2, 5]]; + vec.sort_unstable(); + let mut arr = [1, 3, 2]; + arr.sort_unstable(); + // Negative examples: behavior changes if made unstable + let mut vec = vec![1, 3, 2]; + vec.sort_by_key(|i| i / 2); + vec.sort_by(|a, b| (a + b).cmp(&b)); + // negative examples - Not of a primitive type + let mut vec_of_complex = vec![String::from("hello"), String::from("world!")]; + vec_of_complex.sort(); + vec_of_complex.sort_by_key(String::len); + let mut vec = vec![(String::from("hello"), String::from("world"))]; + vec.sort(); + let mut vec = vec![[String::from("hello"), String::from("world")]]; + vec.sort(); +} diff --git a/tests/ui/stable_sort_primitive.rs b/tests/ui/stable_sort_primitive.rs new file mode 100644 index 00000000000..f9bd9779067 --- /dev/null +++ b/tests/ui/stable_sort_primitive.rs @@ -0,0 +1,32 @@ +// run-rustfix +#![warn(clippy::stable_sort_primitive)] + +fn main() { + // positive examples + let mut vec = vec![1, 3, 2]; + vec.sort(); + let mut vec = vec![false, false, true]; + vec.sort(); + let mut vec = vec!['a', 'A', 'c']; + vec.sort(); + let mut vec = vec!["ab", "cd", "ab", "bc"]; + vec.sort(); + let mut vec = vec![(2, 1), (1, 2), (2, 5)]; + vec.sort(); + let mut vec = vec![[2, 1], [1, 2], [2, 5]]; + vec.sort(); + let mut arr = [1, 3, 2]; + arr.sort(); + // Negative examples: behavior changes if made unstable + let mut vec = vec![1, 3, 2]; + vec.sort_by_key(|i| i / 2); + vec.sort_by(|a, b| (a + b).cmp(&b)); + // negative examples - Not of a primitive type + let mut vec_of_complex = vec![String::from("hello"), String::from("world!")]; + vec_of_complex.sort(); + vec_of_complex.sort_by_key(String::len); + let mut vec = vec![(String::from("hello"), String::from("world"))]; + vec.sort(); + let mut vec = vec![[String::from("hello"), String::from("world")]]; + vec.sort(); +} diff --git a/tests/ui/stable_sort_primitive.stderr b/tests/ui/stable_sort_primitive.stderr new file mode 100644 index 00000000000..b0b729ede48 --- /dev/null +++ b/tests/ui/stable_sort_primitive.stderr @@ -0,0 +1,46 @@ +error: Use sort_unstable instead of sort + --> $DIR/stable_sort_primitive.rs:7:5 + | +LL | vec.sort(); + | ^^^^^^^^^^ help: try: `vec.sort_unstable()` + | + = note: `-D clippy::stable-sort-primitive` implied by `-D warnings` + +error: Use sort_unstable instead of sort + --> $DIR/stable_sort_primitive.rs:9:5 + | +LL | vec.sort(); + | ^^^^^^^^^^ help: try: `vec.sort_unstable()` + +error: Use sort_unstable instead of sort + --> $DIR/stable_sort_primitive.rs:11:5 + | +LL | vec.sort(); + | ^^^^^^^^^^ help: try: `vec.sort_unstable()` + +error: Use sort_unstable instead of sort + --> $DIR/stable_sort_primitive.rs:13:5 + | +LL | vec.sort(); + | ^^^^^^^^^^ help: try: `vec.sort_unstable()` + +error: Use sort_unstable instead of sort + --> $DIR/stable_sort_primitive.rs:15:5 + | +LL | vec.sort(); + | ^^^^^^^^^^ help: try: `vec.sort_unstable()` + +error: Use sort_unstable instead of sort + --> $DIR/stable_sort_primitive.rs:17:5 + | +LL | vec.sort(); + | ^^^^^^^^^^ help: try: `vec.sort_unstable()` + +error: Use sort_unstable instead of sort + --> $DIR/stable_sort_primitive.rs:19:5 + | +LL | arr.sort(); + | ^^^^^^^^^^ help: try: `arr.sort_unstable()` + +error: aborting due to 7 previous errors + diff --git a/tests/ui/unnecessary_sort_by.fixed b/tests/ui/unnecessary_sort_by.fixed index c017d1cf9a4..31c2ba0f9c5 100644 --- a/tests/ui/unnecessary_sort_by.fixed +++ b/tests/ui/unnecessary_sort_by.fixed @@ -1,5 +1,7 @@ // run-rustfix +#![allow(clippy::stable_sort_primitive)] + use std::cmp::Reverse; fn unnecessary_sort_by() { diff --git a/tests/ui/unnecessary_sort_by.rs b/tests/ui/unnecessary_sort_by.rs index 1929c72b2f2..a3c8ae468ed 100644 --- a/tests/ui/unnecessary_sort_by.rs +++ b/tests/ui/unnecessary_sort_by.rs @@ -1,5 +1,7 @@ // run-rustfix +#![allow(clippy::stable_sort_primitive)] + use std::cmp::Reverse; fn unnecessary_sort_by() { diff --git a/tests/ui/unnecessary_sort_by.stderr b/tests/ui/unnecessary_sort_by.stderr index 903b6e5099c..70c6cf0a3b6 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:12:5 + --> $DIR/unnecessary_sort_by.rs:14:5 | LL | vec.sort_by(|a, b| a.cmp(b)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort()` @@ -7,37 +7,37 @@ 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:13:5 + --> $DIR/unnecessary_sort_by.rs:15: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:14:5 + --> $DIR/unnecessary_sort_by.rs:16: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:15:5 + --> $DIR/unnecessary_sort_by.rs:17: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:17:5 + --> $DIR/unnecessary_sort_by.rs:19:5 | LL | vec.sort_by(|a, b| b.cmp(a)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(b))` error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:18:5 + --> $DIR/unnecessary_sort_by.rs:20: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:19:5 + --> $DIR/unnecessary_sort_by.rs:21:5 | LL | vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|&b| Reverse(id(-b)))` -- cgit 1.4.1-3-g733a5 From e9677105bf85a2b0c57e8d67d2ed22a286333033 Mon Sep 17 00:00:00 2001 From: Tomasz Miąsko Date: Sun, 2 Aug 2020 00:00:00 +0000 Subject: try_err: Consider Try impl for Poll when generating suggestions There are two different implementation of Try trait for Poll type; Poll> and Poll>>. Take them into account when generating suggestions. For example, for Err(e)? suggest either return Poll::Ready(Err(e)) or return Poll::Ready(Some(Err(e))) as appropriate. --- clippy_lints/src/try_err.rs | 112 +++++++++++++++++++++++++++++++--------- clippy_lints/src/utils/paths.rs | 2 +- tests/ui/try_err.fixed | 21 ++++++++ tests/ui/try_err.rs | 21 ++++++++ tests/ui/try_err.stderr | 30 ++++++++--- 5 files changed, 155 insertions(+), 31 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/try_err.rs b/clippy_lints/src/try_err.rs index d3b351f30ef..3bd73d9f21a 100644 --- a/clippy_lints/src/try_err.rs +++ b/clippy_lints/src/try_err.rs @@ -1,10 +1,13 @@ -use crate::utils::{match_qpath, paths, snippet, snippet_with_macro_callsite, span_lint_and_sugg}; +use crate::utils::{ + is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet, snippet_with_macro_callsite, + span_lint_and_sugg, +}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Arm, Expr, ExprKind, MatchSource}; +use rustc_hir::{Expr, ExprKind, MatchSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::Ty; +use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -65,19 +68,39 @@ impl<'tcx> LateLintPass<'tcx> for TryErr { if let Some(ref err_arg) = err_args.get(0); if let ExprKind::Path(ref err_fun_path) = err_fun.kind; if match_qpath(err_fun_path, &paths::RESULT_ERR); - if let Some(return_type) = find_err_return_type(cx, &expr.kind); - + if let Some(return_ty) = find_return_type(cx, &expr.kind); then { - let err_type = cx.typeck_results().expr_ty(err_arg); + let prefix; + let suffix; + let err_ty; + + if let Some(ty) = result_error_type(cx, return_ty) { + prefix = "Err("; + suffix = ")"; + err_ty = ty; + } else if let Some(ty) = poll_result_error_type(cx, return_ty) { + prefix = "Poll::Ready(Err("; + suffix = "))"; + err_ty = ty; + } else if let Some(ty) = poll_option_result_error_type(cx, return_ty) { + prefix = "Poll::Ready(Some(Err("; + suffix = ")))"; + err_ty = ty; + } else { + return; + }; + + let expr_err_ty = cx.typeck_results().expr_ty(err_arg); + let origin_snippet = if err_arg.span.from_expansion() { snippet_with_macro_callsite(cx, err_arg.span, "_") } else { snippet(cx, err_arg.span, "_") }; - let suggestion = if err_type == return_type { - format!("return Err({})", origin_snippet) + let suggestion = if err_ty == expr_err_ty { + format!("return {}{}{}", prefix, origin_snippet, suffix) } else { - format!("return Err({}.into())", origin_snippet) + format!("return {}{}.into(){}", prefix, origin_snippet, suffix) }; span_lint_and_sugg( @@ -94,27 +117,68 @@ impl<'tcx> LateLintPass<'tcx> for TryErr { } } -// In order to determine whether to suggest `.into()` or not, we need to find the error type the -// function returns. To do that, we look for the From::from call (see tree above), and capture -// its output type. -fn find_err_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> Option> { +/// Finds function return type by examining return expressions in match arms. +fn find_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> Option> { if let ExprKind::Match(_, ref arms, MatchSource::TryDesugar) = expr { - arms.iter().find_map(|ty| find_err_return_type_arm(cx, ty)) - } else { - None + for arm in arms.iter() { + if let ExprKind::Ret(Some(ref ret)) = arm.body.kind { + return Some(cx.typeck_results().expr_ty(ret)); + } + } } + None } -// Check for From::from in one of the match arms. -fn find_err_return_type_arm<'tcx>(cx: &LateContext<'tcx>, arm: &'tcx Arm<'_>) -> Option> { +/// Extracts the error type from Result. +fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { if_chain! { - if let ExprKind::Ret(Some(ref err_ret)) = arm.body.kind; - if let ExprKind::Call(ref from_error_path, ref from_error_args) = err_ret.kind; - if let ExprKind::Path(ref from_error_fn) = from_error_path.kind; - if match_qpath(from_error_fn, &paths::TRY_FROM_ERROR); - if let Some(from_error_arg) = from_error_args.get(0); + if let ty::Adt(_, subst) = ty.kind; + if is_type_diagnostic_item(cx, ty, sym!(result_type)); + let err_ty = subst.type_at(1); + then { + Some(err_ty) + } else { + None + } + } +} + +/// Extracts the error type from Poll>. +fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { + if_chain! { + if let ty::Adt(def, subst) = ty.kind; + if match_def_path(cx, def.did, &paths::POLL); + let ready_ty = subst.type_at(0); + + if let ty::Adt(ready_def, ready_subst) = ready_ty.kind; + if cx.tcx.is_diagnostic_item(sym!(result_type), ready_def.did); + let err_ty = ready_subst.type_at(1); + + then { + Some(err_ty) + } else { + None + } + } +} + +/// Extracts the error type from Poll>>. +fn poll_option_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { + if_chain! { + if let ty::Adt(def, subst) = ty.kind; + if match_def_path(cx, def.did, &paths::POLL); + let ready_ty = subst.type_at(0); + + if let ty::Adt(ready_def, ready_subst) = ready_ty.kind; + if cx.tcx.is_diagnostic_item(sym!(option_type), ready_def.did); + let some_ty = ready_subst.type_at(0); + + if let ty::Adt(some_def, some_subst) = some_ty.kind; + if cx.tcx.is_diagnostic_item(sym!(result_type), some_def.did); + let err_ty = some_subst.type_at(1); + then { - Some(cx.typeck_results().expr_ty(from_error_arg)) + Some(err_ty) } else { None } diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index a515ee29c82..923b319d777 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -80,6 +80,7 @@ pub const PATH: [&str; 3] = ["std", "path", "Path"]; pub const PATH_BUF: [&str; 3] = ["std", "path", "PathBuf"]; pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"]; pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"]; +pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"]; pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"]; pub const PTR_NULL: [&str; 2] = ["ptr", "null"]; pub const PTR_NULL_MUT: [&str; 2] = ["ptr", "null_mut"]; @@ -129,7 +130,6 @@ pub const TO_STRING: [&str; 3] = ["alloc", "string", "ToString"]; pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"]; pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"]; pub const TRY_FROM: [&str; 4] = ["core", "convert", "TryFrom", "try_from"]; -pub const TRY_FROM_ERROR: [&str; 4] = ["std", "ops", "Try", "from_error"]; pub const TRY_INTO_RESULT: [&str; 4] = ["std", "ops", "Try", "into_result"]; pub const TRY_INTO_TRAIT: [&str; 3] = ["core", "convert", "TryInto"]; pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"]; diff --git a/tests/ui/try_err.fixed b/tests/ui/try_err.fixed index 29d9139d3e3..9e77dcd8731 100644 --- a/tests/ui/try_err.fixed +++ b/tests/ui/try_err.fixed @@ -6,6 +6,9 @@ #[macro_use] extern crate macro_rules; +use std::io; +use std::task::Poll; + // Tests that a simple case works // Should flag `Err(err)?` pub fn basic_test() -> Result { @@ -104,3 +107,21 @@ pub fn macro_inside(fail: bool) -> Result { } Ok(0) } + +pub fn poll_write(n: usize) -> Poll> { + if n == 0 { + return Poll::Ready(Err(io::ErrorKind::WriteZero.into())) + } else if n == 1 { + return Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput, "error"))) + }; + + Poll::Ready(Ok(n)) +} + +pub fn poll_next(ready: bool) -> Poll>> { + if !ready { + return Poll::Ready(Some(Err(io::ErrorKind::NotFound.into()))) + } + + Poll::Ready(None) +} diff --git a/tests/ui/try_err.rs b/tests/ui/try_err.rs index 5e85d091a2a..41bcb0a189e 100644 --- a/tests/ui/try_err.rs +++ b/tests/ui/try_err.rs @@ -6,6 +6,9 @@ #[macro_use] extern crate macro_rules; +use std::io; +use std::task::Poll; + // Tests that a simple case works // Should flag `Err(err)?` pub fn basic_test() -> Result { @@ -104,3 +107,21 @@ pub fn macro_inside(fail: bool) -> Result { } Ok(0) } + +pub fn poll_write(n: usize) -> Poll> { + if n == 0 { + Err(io::ErrorKind::WriteZero)? + } else if n == 1 { + Err(io::Error::new(io::ErrorKind::InvalidInput, "error"))? + }; + + Poll::Ready(Ok(n)) +} + +pub fn poll_next(ready: bool) -> Poll>> { + if !ready { + Err(io::ErrorKind::NotFound)? + } + + Poll::Ready(None) +} diff --git a/tests/ui/try_err.stderr b/tests/ui/try_err.stderr index 21e9d4048a5..3f1cbc17e72 100644 --- a/tests/ui/try_err.stderr +++ b/tests/ui/try_err.stderr @@ -1,5 +1,5 @@ error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:15:9 + --> $DIR/try_err.rs:18:9 | LL | Err(err)?; | ^^^^^^^^^ help: try this: `return Err(err)` @@ -11,28 +11,46 @@ LL | #![deny(clippy::try_err)] | ^^^^^^^^^^^^^^^ error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:25:9 + --> $DIR/try_err.rs:28:9 | LL | Err(err)?; | ^^^^^^^^^ help: try this: `return Err(err.into())` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:45:17 + --> $DIR/try_err.rs:48:17 | LL | Err(err)?; | ^^^^^^^^^ help: try this: `return Err(err)` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:64:17 + --> $DIR/try_err.rs:67:17 | LL | Err(err)?; | ^^^^^^^^^ help: try this: `return Err(err.into())` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:103:9 + --> $DIR/try_err.rs:106:9 | LL | Err(foo!())?; | ^^^^^^^^^^^^ help: try this: `return Err(foo!())` -error: aborting due to 5 previous errors +error: returning an `Err(_)` with the `?` operator + --> $DIR/try_err.rs:113:9 + | +LL | Err(io::ErrorKind::WriteZero)? + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Err(io::ErrorKind::WriteZero.into()))` + +error: returning an `Err(_)` with the `?` operator + --> $DIR/try_err.rs:115:9 + | +LL | Err(io::Error::new(io::ErrorKind::InvalidInput, "error"))? + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput, "error")))` + +error: returning an `Err(_)` with the `?` operator + --> $DIR/try_err.rs:123:9 + | +LL | Err(io::ErrorKind::NotFound)? + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Some(Err(io::ErrorKind::NotFound.into())))` + +error: aborting due to 8 previous errors -- cgit 1.4.1-3-g733a5 From 1e8ada3cab5470a4f2070c0aa9d1a94922476621 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Sat, 18 Jul 2020 23:28:31 +0900 Subject: Add lint `same_item_push` --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 3 + clippy_lints/src/loops.rs | 265 +++++++++++++++++++++++++++++++++++++++++ src/lintlist/mod.rs | 7 ++ tests/ui/same_item_push.rs | 77 ++++++++++++ tests/ui/same_item_push.stderr | 35 ++++++ 6 files changed, 388 insertions(+) create mode 100644 tests/ui/same_item_push.rs create mode 100644 tests/ui/same_item_push.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 43d83d978b8..fbc783f1c2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1687,6 +1687,7 @@ Released 2018-09-13 [`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn [`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges [`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition +[`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push [`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some [`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse [`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 26aff6af8cd..2bd5ae0ed98 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -610,6 +610,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &loops::WHILE_IMMUTABLE_CONDITION, &loops::WHILE_LET_LOOP, &loops::WHILE_LET_ON_ITERATOR, + &loops::SAME_ITEM_PUSH, ¯o_use::MACRO_USE_IMPORTS, &main_recursion::MAIN_RECURSION, &manual_async_fn::MANUAL_ASYNC_FN, @@ -1405,6 +1406,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&repeat_once::REPEAT_ONCE), LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&returns::UNUSED_UNIT), + LintId::of(&loops::SAME_ITEM_PUSH), LintId::of(&serde_api::SERDE_API_MISUSE), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), @@ -1543,6 +1545,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(®ex::TRIVIAL_REGEX), LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&returns::UNUSED_UNIT), + LintId::of(&loops::SAME_ITEM_PUSH), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 6359c20040c..48891a59c00 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -419,6 +419,39 @@ declare_clippy_lint! { "variables used within while expression are not mutated in the body" } +declare_clippy_lint! { + /// **What it does:** Checks whether a for loop is being used to push a constant + /// value into a Vec. + /// + /// **Why is this bad?** This kind of operation can be expressed more succinctly with + /// `vec![item;SIZE]` or `vec.resize(NEW_SIZE, item)` and using these alternatives may also + /// have better performance. + /// **Known problems:** None + /// + /// **Example:** + /// ```rust + /// let item1 = 2; + /// let item2 = 3; + /// let mut vec: Vec = Vec::new(); + /// for _ in 0..20 { + /// vec.push(item1); + /// } + /// for _ in 0..30 { + /// vec.push(item2); + /// } + /// ``` + /// could be written as + /// ```rust + /// let item1 = 2; + /// let item2 = 3; + /// let mut vec: Vec = vec![item1; 20]; + /// vec.resize(20 + 30, item2); + /// ``` + pub SAME_ITEM_PUSH, + style, + "the same item is pushed inside of a for loop" +} + declare_lint_pass!(Loops => [ MANUAL_MEMCPY, NEEDLESS_RANGE_LOOP, @@ -435,6 +468,7 @@ declare_lint_pass!(Loops => [ NEVER_LOOP, MUT_RANGE_BOUND, WHILE_IMMUTABLE_CONDITION, + SAME_ITEM_PUSH, ]); impl<'tcx> LateLintPass<'tcx> for Loops { @@ -740,6 +774,7 @@ fn check_for_loop<'tcx>( check_for_loop_over_map_kv(cx, pat, arg, body, expr); check_for_mut_range_bound(cx, arg, body); detect_manual_memcpy(cx, pat, arg, body, expr); + detect_same_item_push(cx, pat, arg, body, expr); } fn same_var<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, var: HirId) -> bool { @@ -1016,6 +1051,236 @@ fn detect_manual_memcpy<'tcx>( } } +// Delegate that traverses expression and detects mutable variables being used +struct UsesMutableDelegate { + found_mutable: bool, +} + +impl<'tcx> Delegate<'tcx> for UsesMutableDelegate { + fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: ConsumeMode) {} + + fn borrow(&mut self, _: &PlaceWithHirId<'tcx>, bk: ty::BorrowKind) { + // Mutable variable is found + if let ty::BorrowKind::MutBorrow = bk { + self.found_mutable = true; + } + } + + fn mutate(&mut self, _: &PlaceWithHirId<'tcx>) {} +} + +// Uses UsesMutableDelegate to find mutable variables in an expression expr +fn has_mutable_variables<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { + let mut delegate = UsesMutableDelegate { found_mutable: false }; + let def_id = expr.hir_id.owner.to_def_id(); + cx.tcx.infer_ctxt().enter(|infcx| { + ExprUseVisitor::new( + &mut delegate, + &infcx, + def_id.expect_local(), + cx.param_env, + cx.tables(), + ).walk_expr(expr); + }); + + delegate.found_mutable +} + +// Scans for the usage of the for loop pattern +struct ForPatternVisitor<'a, 'tcx> { + found_pattern: bool, + // Pattern that we are searching for + for_pattern: &'a Pat<'tcx>, + cx: &'a LateContext<'tcx>, +} + +impl<'a, 'tcx> Visitor<'tcx> for ForPatternVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + // Recursively explore an expression until a ExprKind::Path is found + match &expr.kind { + ExprKind::Array(expr_list) | ExprKind::MethodCall(_, _, expr_list, _) | ExprKind::Tup(expr_list) => { + for expr in *expr_list { + self.visit_expr(expr) + } + }, + ExprKind::Binary(_, lhs_expr, rhs_expr) => { + self.visit_expr(lhs_expr); + self.visit_expr(rhs_expr); + }, + ExprKind::Box(expr) + | ExprKind::Unary(_, expr) + | ExprKind::Cast(expr, _) + | ExprKind::Type(expr, _) + | ExprKind::AddrOf(_, _, expr) + | ExprKind::Struct(_, _, Some(expr)) => self.visit_expr(expr), + _ => { + // Exploration cannot continue ... calculate the hir_id of the current + // expr assuming it is a Path + if let Some(hir_id) = var_def_id(self.cx, &expr) { + // Pattern is found + if hir_id == self.for_pattern.hir_id { + self.found_pattern = true; + } + // If the for loop pattern is a tuple, determine whether the current + // expr is inside that tuple pattern + if let PatKind::Tuple(pat_list, _) = &self.for_pattern.kind { + let hir_id_list: Vec = pat_list.iter().map(|p| p.hir_id).collect(); + if hir_id_list.contains(&hir_id) { + self.found_pattern = true; + } + } + } + }, + } + } + + // This is triggered by walk_expr() for the case of vec.push(pat) + fn visit_qpath(&mut self, qpath: &'tcx QPath<'_>, _: HirId, _: Span) { + if_chain! { + if let QPath::Resolved(_, path) = qpath; + if let Res::Local(hir_id) = &path.res; + then { + if *hir_id == self.for_pattern.hir_id{ + self.found_pattern = true; + } + + if let PatKind::Tuple(pat_list, _) = &self.for_pattern.kind { + let hir_id_list: Vec = pat_list.iter().map(|p| p.hir_id).collect(); + if hir_id_list.contains(&hir_id) { + self.found_pattern = true; + } + } + } + } + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +// Scans the body of the for loop and determines whether lint should be given +struct SameItemPushVisitor<'a, 'tcx> { + should_lint: bool, + // this field holds the last vec push operation visited, which should be the only push seen + vec_push: Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)>, + cx: &'a LateContext<'tcx>, +} + +impl<'a, 'tcx> Visitor<'tcx> for SameItemPushVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + match &expr.kind { + // Non-determinism may occur ... don't give a lint + ExprKind::Loop(_, _, _) | ExprKind::Match(_, _, _) => self.should_lint = false, + ExprKind::Block(block, _) => self.visit_block(block), + _ => {}, + } + } + + fn visit_block(&mut self, b: &'tcx Block<'_>) { + for stmt in b.stmts.iter() { + self.visit_stmt(stmt); + } + } + + fn visit_stmt(&mut self, s: &'tcx Stmt<'_>) { + let vec_push_option = get_vec_push(self.cx, s); + if vec_push_option.is_none() { + // Current statement is not a push so visit inside + match &s.kind { + StmtKind::Expr(expr) | StmtKind::Semi(expr) => self.visit_expr(&expr), + _ => {}, + } + } else { + // Current statement is a push ...check whether another + // push had been previously done + if self.vec_push.is_none() { + self.vec_push = vec_push_option; + } else { + // There are multiple pushes ... don't lint + self.should_lint = false; + } + } + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +// Given some statement, determine if that statement is a push on a Vec. If it is, return +// the Vec being pushed into and the item being pushed +fn get_vec_push<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> { + if_chain! { + // Extract method being called + if let StmtKind::Semi(semi_stmt) = &stmt.kind; + if let ExprKind::MethodCall(path, _, args, _) = &semi_stmt.kind; + // Figure out the parameters for the method call + if let Some(self_expr) = args.get(0); + if let Some(pushed_item) = args.get(1); + // Check that the method being called is push() on a Vec + if match_type(cx, cx.tables().expr_ty(self_expr), &paths::VEC); + if path.ident.name.as_str() == "push"; + then { + return Some((self_expr, pushed_item)) + } + } + None +} + +/// Detects for loop pushing the same item into a Vec +fn detect_same_item_push<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + _: &'tcx Expr<'_>, + body: &'tcx Expr<'_>, + _: &'tcx Expr<'_>, +) { + // Determine whether it is safe to lint the body + let mut same_item_push_visitor = SameItemPushVisitor { + should_lint: true, + vec_push: None, + cx, + }; + walk_expr(&mut same_item_push_visitor, body); + if same_item_push_visitor.should_lint { + if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push { + // Make sure that the push does not involve possibly mutating values + if !has_mutable_variables(cx, pushed_item) { + // Walk through the expression being pushed and make sure that it + // does not contain the for loop pattern + let mut for_pat_visitor = ForPatternVisitor { + found_pattern: false, + for_pattern: pat, + cx, + }; + walk_expr(&mut for_pat_visitor, pushed_item); + + if !for_pat_visitor.found_pattern { + let vec_str = snippet(cx, vec.span, ""); + let item_str = snippet(cx, pushed_item.span, ""); + + span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ) + } + } + } + } +} + /// Checks for looping over a range and then indexing a sequence with it. /// The iteratee must be a range literal. #[allow(clippy::too_many_lines)] diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index a08d7da6dcb..1b10226c930 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1935,6 +1935,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "copies", }, + Lint { + name: "same_item_push", + group: "style", + desc: "default lint description", + deprecation: None, + module: "same_item_push", + }, Lint { name: "search_is_some", group: "complexity", diff --git a/tests/ui/same_item_push.rs b/tests/ui/same_item_push.rs new file mode 100644 index 00000000000..e3a5a647f76 --- /dev/null +++ b/tests/ui/same_item_push.rs @@ -0,0 +1,77 @@ +#![warn(clippy::same_item_push)] + +fn mutate_increment(x: &mut u8) -> u8 { + *x += 1; + *x +} + +fn increment(x: u8) -> u8 { + x + 1 +} + +fn main() { + // Test for basic case + let mut spaces = Vec::with_capacity(10); + for _ in 0..10 { + spaces.push(vec![b' ']); + } + + let mut vec2: Vec = Vec::new(); + let item = 2; + for _ in 5..=20 { + vec2.push(item); + } + + let mut vec3: Vec = Vec::new(); + for _ in 0..15 { + let item = 2; + vec3.push(item); + } + + let mut vec4: Vec = Vec::new(); + for _ in 0..15 { + vec4.push(13); + } + + // Suggestion should not be given as pushed variable can mutate + let mut vec5: Vec = Vec::new(); + let mut item: u8 = 2; + for _ in 0..30 { + vec5.push(mutate_increment(&mut item)); + } + + let mut vec6: Vec = Vec::new(); + let mut item: u8 = 2; + let mut item2 = &mut mutate_increment(&mut item); + for _ in 0..30 { + vec6.push(mutate_increment(item2)); + } + + let mut vec7: Vec = Vec::new(); + for (a, b) in [0, 1, 4, 9, 16].iter().enumerate() { + vec7.push(a); + } + + let mut vec8: Vec = Vec::new(); + for i in 0..30 { + vec8.push(increment(i)); + } + + let mut vec9: Vec = Vec::new(); + for i in 0..30 { + vec9.push(i + i * i); + } + + // Suggestion should not be given as there are multiple pushes that are not the same + let mut vec10: Vec = Vec::new(); + let item: u8 = 2; + for _ in 0..30 { + vec10.push(item); + vec10.push(item * 2); + } + + // Suggestion should not be given as Vec is not involved + for _ in 0..5 { + println!("Same Item Push"); + } +} diff --git a/tests/ui/same_item_push.stderr b/tests/ui/same_item_push.stderr new file mode 100644 index 00000000000..559cc450b9d --- /dev/null +++ b/tests/ui/same_item_push.stderr @@ -0,0 +1,35 @@ +error: it looks like the same item is being pushed into this Vec + --> $DIR/same_item_push.rs:16:9 + | +LL | spaces.push(vec![b' ']); + | ^^^^^^ + | + = note: `-D clippy::same-item-push` implied by `-D warnings` + = help: try using vec![<[_]>::into_vec(box [$($x),+]);SIZE] or spaces.resize(NEW_SIZE, <[_]>::into_vec(box [$($x),+])) + +error: it looks like the same item is being pushed into this Vec + --> $DIR/same_item_push.rs:22:9 + | +LL | vec2.push(item); + | ^^^^ + | + = help: try using vec![item;SIZE] or vec2.resize(NEW_SIZE, item) + +error: it looks like the same item is being pushed into this Vec + --> $DIR/same_item_push.rs:28:9 + | +LL | vec3.push(item); + | ^^^^ + | + = help: try using vec![item;SIZE] or vec3.resize(NEW_SIZE, item) + +error: it looks like the same item is being pushed into this Vec + --> $DIR/same_item_push.rs:33:9 + | +LL | vec4.push(13); + | ^^^^ + | + = help: try using vec![13;SIZE] or vec4.resize(NEW_SIZE, 13) + +error: aborting due to 4 previous errors + -- cgit 1.4.1-3-g733a5 From 161f47510076d36722546c3541a546f9b724fadd Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Sun, 19 Jul 2020 23:43:35 +0900 Subject: Add test case for `same_item_push` --- clippy_lints/src/loops.rs | 1 + tests/ui/same_item_push.rs | 12 ++++++++++++ 2 files changed, 13 insertions(+) (limited to 'tests') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 48891a59c00..1c2f1225497 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1114,6 +1114,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ForPatternVisitor<'a, 'tcx> { | ExprKind::Cast(expr, _) | ExprKind::Type(expr, _) | ExprKind::AddrOf(_, _, expr) + | ExprKind::Field(expr, _) | ExprKind::Struct(_, _, Some(expr)) => self.visit_expr(expr), _ => { // Exploration cannot continue ... calculate the hir_id of the current diff --git a/tests/ui/same_item_push.rs b/tests/ui/same_item_push.rs index e3a5a647f76..4bb5e73ff0d 100644 --- a/tests/ui/same_item_push.rs +++ b/tests/ui/same_item_push.rs @@ -74,4 +74,16 @@ fn main() { for _ in 0..5 { println!("Same Item Push"); } + + struct A { + kind: u32, + } + let mut vec_a: Vec = Vec::new(); + for i in 0..30 { + vec_a.push(A{kind: i}); + } + let mut vec12: Vec = Vec::new(); + for a in vec_a { + vec12.push(2u8.pow(a.kind)); + } } -- cgit 1.4.1-3-g733a5 From 14a4e3bcc8082b0323886ae15365ea2424b512cf Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 21 Jul 2020 08:15:13 +0900 Subject: Fix a lint message --- clippy_lints/src/loops.rs | 11 ++++++----- tests/ui/same_item_push.stderr | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 766c68df623..8ca67cae0e9 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -3,9 +3,10 @@ use crate::utils::paths; use crate::utils::sugg::Sugg; use crate::utils::usage::{is_unused, mutated_variables}; use crate::utils::{ - get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, - is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, match_trait_method, - match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_opt, snippet_with_applicability, span_lint, + get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, + implements_trait, is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, + last_path_segment, match_trait_method, match_type, match_var, multispan_sugg, qpath_res, + snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, SpanlessEq, }; use if_chain::if_chain; @@ -1262,8 +1263,8 @@ fn detect_same_item_push<'tcx>( walk_expr(&mut for_pat_visitor, pushed_item); if !for_pat_visitor.found_pattern { - let vec_str = snippet(cx, vec.span, ""); - let item_str = snippet(cx, pushed_item.span, ""); + let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); + let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); span_lint_and_help( cx, diff --git a/tests/ui/same_item_push.stderr b/tests/ui/same_item_push.stderr index 559cc450b9d..ddc5d48cd41 100644 --- a/tests/ui/same_item_push.stderr +++ b/tests/ui/same_item_push.stderr @@ -5,7 +5,7 @@ LL | spaces.push(vec![b' ']); | ^^^^^^ | = note: `-D clippy::same-item-push` implied by `-D warnings` - = help: try using vec![<[_]>::into_vec(box [$($x),+]);SIZE] or spaces.resize(NEW_SIZE, <[_]>::into_vec(box [$($x),+])) + = help: try using vec![vec![b' '];SIZE] or spaces.resize(NEW_SIZE, vec![b' ']) error: it looks like the same item is being pushed into this Vec --> $DIR/same_item_push.rs:22:9 -- cgit 1.4.1-3-g733a5 From b7ceb4d3d7ed3ea7039caf803073e86ad3643e21 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 21 Jul 2020 08:25:11 +0900 Subject: rustfmt --- clippy_lints/src/loops.rs | 5 +++-- tests/ui/same_item_push.rs | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 8ca67cae0e9..3a1fbc4bfed 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1081,7 +1081,8 @@ fn has_mutable_variables<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> def_id.expect_local(), cx.param_env, cx.typeck_results(), - ).walk_expr(expr); + ) + .walk_expr(expr); }); delegate.found_mutable @@ -1271,7 +1272,7 @@ fn detect_same_item_push<'tcx>( SAME_ITEM_PUSH, vec.span, "it looks like the same item is being pushed into this Vec", - None, + None, &format!( "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", item_str, vec_str, item_str diff --git a/tests/ui/same_item_push.rs b/tests/ui/same_item_push.rs index 4bb5e73ff0d..ff1088f86f6 100644 --- a/tests/ui/same_item_push.rs +++ b/tests/ui/same_item_push.rs @@ -80,7 +80,7 @@ fn main() { } let mut vec_a: Vec = Vec::new(); for i in 0..30 { - vec_a.push(A{kind: i}); + vec_a.push(A { kind: i }); } let mut vec12: Vec = Vec::new(); for a in vec_a { -- cgit 1.4.1-3-g733a5 From 50a86d492718f2ad5e653575d19324205fa007f1 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Thu, 6 Aug 2020 00:40:11 +0200 Subject: enable #[allow(clippy::unsafe_derive_deserialize)] --- clippy_lints/src/derive.rs | 8 +++++--- tests/ui/unsafe_derive_deserialize.rs | 10 ++++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 08d8100a885..80a06758982 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -1,7 +1,7 @@ use crate::utils::paths; use crate::utils::{ - get_trait_def_id, is_automatically_derived, is_copy, match_path, span_lint_and_help, span_lint_and_note, - span_lint_and_then, + get_trait_def_id, is_allowed, is_automatically_derived, is_copy, match_path, span_lint_and_help, + span_lint_and_note, span_lint_and_then, }; use if_chain::if_chain; use rustc_hir::def_id::DefId; @@ -354,7 +354,9 @@ fn check_unsafe_derive_deserialize<'tcx>( if_chain! { if match_path(&trait_ref.path, &paths::SERDE_DESERIALIZE); if let ty::Adt(def, _) = ty.kind; - if def.did.is_local(); + if let Some(local_def_id) = def.did.as_local(); + let adt_hir_id = cx.tcx.hir().as_local_hir_id(local_def_id); + if !is_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id); if cx.tcx.inherent_impls(def.did) .iter() .map(|imp_did| item_from_def_id(cx, *imp_did)) diff --git a/tests/ui/unsafe_derive_deserialize.rs b/tests/ui/unsafe_derive_deserialize.rs index 7bee9c499e1..690d705573d 100644 --- a/tests/ui/unsafe_derive_deserialize.rs +++ b/tests/ui/unsafe_derive_deserialize.rs @@ -57,4 +57,14 @@ impl E { #[derive(Deserialize)] pub struct F {} +// Check that we honor the `allow` attribute on the ADT +#[allow(clippy::unsafe_derive_deserialize)] +#[derive(Deserialize)] +pub struct G {} +impl G { + pub fn unsafe_block(&self) { + unsafe {} + } +} + fn main() {} -- cgit 1.4.1-3-g733a5 From 0abc4833e5dc8ec4da48d5b25e1d0df81cceec4d Mon Sep 17 00:00:00 2001 From: Christoph Walcher Date: Thu, 6 Aug 2020 02:42:40 +0200 Subject: Lint .min(x).max(y) with x < y Fixes #5854 --- clippy_lints/src/minmax.rs | 52 +++++++++++++++++++++++++++++----------------- tests/ui/min_max.rs | 14 +++++++++++++ tests/ui/min_max.stderr | 32 +++++++++++++++++++++++++++- 3 files changed, 78 insertions(+), 20 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index dae39aaf5e2..1f798fd1120 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -18,6 +18,10 @@ declare_clippy_lint! { /// ```ignore /// min(0, max(100, x)) /// ``` + /// or + /// ```ignore + /// x.max(100).min(0) + /// ``` /// It will always be equal to `0`. Probably the author meant to clamp the value /// between 0 and 100, but has erroneously swapped `min` and `max`. pub MIN_MAX, @@ -60,25 +64,35 @@ enum MinMax { } fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Constant, &'a Expr<'a>)> { - if let ExprKind::Call(ref path, ref args) = expr.kind { - if let ExprKind::Path(ref qpath) = path.kind { - cx.typeck_results() - .qpath_res(qpath, path.hir_id) - .opt_def_id() - .and_then(|def_id| { - if match_def_path(cx, def_id, &paths::CMP_MIN) { - fetch_const(cx, args, MinMax::Min) - } else if match_def_path(cx, def_id, &paths::CMP_MAX) { - fetch_const(cx, args, MinMax::Max) - } else { - None - } - }) - } else { - None - } - } else { - None + match expr.kind { + ExprKind::Call(ref path, ref args) => { + if let ExprKind::Path(ref qpath) = path.kind { + cx.typeck_results() + .qpath_res(qpath, path.hir_id) + .opt_def_id() + .and_then(|def_id| { + if match_def_path(cx, def_id, &paths::CMP_MIN) { + fetch_const(cx, args, MinMax::Min) + } else if match_def_path(cx, def_id, &paths::CMP_MAX) { + fetch_const(cx, args, MinMax::Max) + } else { + None + } + }) + } else { + None + } + }, + ExprKind::MethodCall(ref path, _, ref args, _) => { + if path.ident.as_str() == sym!(max).as_str() { + fetch_const(cx, args, MinMax::Max) + } else if path.ident.as_str() == sym!(min).as_str() { + fetch_const(cx, args, MinMax::Min) + } else { + None + } + }, + _ => None, } } diff --git a/tests/ui/min_max.rs b/tests/ui/min_max.rs index 8307d4b3019..90ec5676493 100644 --- a/tests/ui/min_max.rs +++ b/tests/ui/min_max.rs @@ -30,4 +30,18 @@ fn main() { max(min(s, "Apple"), "Zoo"); max("Apple", min(s, "Zoo")); // ok + + x.min(1).max(3); + x.max(3).min(1); + + x.max(1).min(3); // ok + x.min(3).max(1); // ok + + max(x.min(1), 3); + min(x.max(1), 3); // ok + + s.max("Zoo").min("Apple"); + s.min("Apple").max("Zoo"); + + s.min("Zoo").max("Apple"); // ok } diff --git a/tests/ui/min_max.stderr b/tests/ui/min_max.stderr index b552c137f7c..653946dc025 100644 --- a/tests/ui/min_max.stderr +++ b/tests/ui/min_max.stderr @@ -42,5 +42,35 @@ error: this `min`/`max` combination leads to constant result LL | max(min(s, "Apple"), "Zoo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 7 previous errors +error: this `min`/`max` combination leads to constant result + --> $DIR/min_max.rs:34:5 + | +LL | x.min(1).max(3); + | ^^^^^^^^^^^^^^^ + +error: this `min`/`max` combination leads to constant result + --> $DIR/min_max.rs:35:5 + | +LL | x.max(3).min(1); + | ^^^^^^^^^^^^^^^ + +error: this `min`/`max` combination leads to constant result + --> $DIR/min_max.rs:40:5 + | +LL | max(x.min(1), 3); + | ^^^^^^^^^^^^^^^^ + +error: this `min`/`max` combination leads to constant result + --> $DIR/min_max.rs:43:5 + | +LL | s.max("Zoo").min("Apple"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this `min`/`max` combination leads to constant result + --> $DIR/min_max.rs:44:5 + | +LL | s.min("Apple").max("Zoo"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 12 previous errors -- cgit 1.4.1-3-g733a5 From e0a4988fcc716e349fd801d98182c0d984a2ee3f Mon Sep 17 00:00:00 2001 From: Christoph Walcher Date: Tue, 4 Aug 2020 20:23:14 +0200 Subject: Lint against `Self` as an arbitrary self type Fixes #5861 --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 +++ clippy_lints/src/needless_fn_self_type.rs | 64 +++++++++++++++++++++++++++++++ clippy_lints/src/trait_bounds.rs | 2 +- src/lintlist/mod.rs | 7 ++++ tests/ui/needless_fn_self_type.rs | 26 +++++++++++++ tests/ui/needless_fn_self_type.stderr | 11 ++++++ 7 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/needless_fn_self_type.rs create mode 100644 tests/ui/needless_fn_self_type.rs create mode 100644 tests/ui/needless_fn_self_type.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index bfe896d5efa..179ecee03da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1622,6 +1622,7 @@ Released 2018-09-13 [`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect [`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue [`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main +[`needless_fn_self_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_fn_self_type [`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes [`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value [`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 81864340f1a..80c85e70e89 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -254,6 +254,7 @@ mod needless_bool; mod needless_borrow; mod needless_borrowed_ref; mod needless_continue; +mod needless_fn_self_type; mod needless_pass_by_value; mod needless_update; mod neg_cmp_op_on_partial_ord; @@ -722,6 +723,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &needless_borrow::NEEDLESS_BORROW, &needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE, &needless_continue::NEEDLESS_CONTINUE, + &needless_fn_self_type::NEEDLESS_FN_SELF_TYPE, &needless_pass_by_value::NEEDLESS_PASS_BY_VALUE, &needless_update::NEEDLESS_UPDATE, &neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD, @@ -1027,6 +1029,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box items_after_statements::ItemsAfterStatements); store.register_early_pass(|| box precedence::Precedence); store.register_early_pass(|| box needless_continue::NeedlessContinue); + store.register_early_pass(|| box needless_fn_self_type::NeedlessFnSelfType); store.register_early_pass(|| box redundant_static_lifetimes::RedundantStaticLifetimes); store.register_late_pass(|| box cargo_common_metadata::CargoCommonMetadata); store.register_late_pass(|| box multiple_crate_versions::MultipleCrateVersions); @@ -1374,6 +1377,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&needless_bool::BOOL_COMPARISON), LintId::of(&needless_bool::NEEDLESS_BOOL), LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), + LintId::of(&needless_fn_self_type::NEEDLESS_FN_SELF_TYPE), 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), @@ -1534,6 +1538,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&misc_early::MIXED_CASE_HEX_LITERALS), LintId::of(&misc_early::REDUNDANT_PATTERN), LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED), + LintId::of(&needless_fn_self_type::NEEDLESS_FN_SELF_TYPE), LintId::of(&neg_multiply::NEG_MULTIPLY), LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT), LintId::of(&non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), diff --git a/clippy_lints/src/needless_fn_self_type.rs b/clippy_lints/src/needless_fn_self_type.rs new file mode 100644 index 00000000000..050a03467fb --- /dev/null +++ b/clippy_lints/src/needless_fn_self_type.rs @@ -0,0 +1,64 @@ +use crate::utils::span_lint_and_help; +use if_chain::if_chain; +use rustc_ast::ast::{Param, TyKind}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** The lint checks for `self` fn fn parameters that explicitly + /// specify the `Self`-type explicitly + /// **Why is this bad?** Increases the amount and decreases the readability of code + /// + /// **Known problems:** None + /// + /// **Example:** + /// ```rust + /// impl ValType { + /// pub fn bytes(self: Self) -> usize { + /// match self { + /// Self::I32 | Self::F32 => 4, + /// Self::I64 | Self::F64 => 8, + /// } + /// } + /// } + /// ``` + /// + /// Could be rewritten as + /// + /// ```rust + /// impl ValType { + /// pub fn bytes(self) -> usize { + /// match self { + /// Self::I32 | Self::F32 => 4, + /// Self::I64 | Self::F64 => 8, + /// } + /// } + /// } + /// ``` + pub NEEDLESS_FN_SELF_TYPE, + style, + "type of `self` parameter is already by default `Self`" +} + +declare_lint_pass!(NeedlessFnSelfType => [NEEDLESS_FN_SELF_TYPE]); + +impl EarlyLintPass for NeedlessFnSelfType { + fn check_param(&mut self, cx: &EarlyContext<'_>, p: &Param) { + if_chain! { + if p.is_self(); + if let TyKind::Path(None, path) = &p.ty.kind; + if let Some(segment) = path.segments.first(); + if segment.ident.as_str() == sym!(Self).as_str(); + then { + span_lint_and_help( + cx, + NEEDLESS_FN_SELF_TYPE, + p.ty.span, + "the type of the `self` parameter is already by default `Self`", + None, + "consider removing the type specification", + ); + } + } + } +} diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 06631f89f27..d4acf8df46d 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -50,7 +50,7 @@ declare_clippy_lint! { /// fn func(arg: T) {} /// ``` /// or - /// /// + /// /// ```rust /// fn func(arg: T) where T: Clone + Default {} /// ``` diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index b64c6e54409..a20e410f79b 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1501,6 +1501,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "doc", }, + Lint { + name: "needless_fn_self_type", + group: "style", + desc: "type of `self` parameter is already by default `Self`", + deprecation: None, + module: "needless_fn_self_type", + }, Lint { name: "needless_lifetimes", group: "complexity", diff --git a/tests/ui/needless_fn_self_type.rs b/tests/ui/needless_fn_self_type.rs new file mode 100644 index 00000000000..12bb84582ff --- /dev/null +++ b/tests/ui/needless_fn_self_type.rs @@ -0,0 +1,26 @@ +#![warn(clippy::style, clippy::needless_fn_self_type)] + +pub enum ValType { + I32, + I64, + F32, + F64, +} + +impl ValType { + pub fn bytes_bad(self: Self) -> usize { + match self { + Self::I32 | Self::F32 => 4, + Self::I64 | Self::F64 => 8, + } + } + + pub fn bytes_good(self) -> usize { + match self { + Self::I32 | Self::F32 => 4, + Self::I64 | Self::F64 => 8, + } + } +} + +fn main() {} diff --git a/tests/ui/needless_fn_self_type.stderr b/tests/ui/needless_fn_self_type.stderr new file mode 100644 index 00000000000..703705c7842 --- /dev/null +++ b/tests/ui/needless_fn_self_type.stderr @@ -0,0 +1,11 @@ +error: the type of the `self` parameter is already by default `Self` + --> $DIR/needless_fn_self_type.rs:11:28 + | +LL | pub fn bytes_bad(self: Self) -> usize { + | ^^^^ + | + = note: `-D clippy::needless-fn-self-type` implied by `-D warnings` + = help: consider removing the type specification + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From d635b76eaf3435f9bdce1dcbdd315b0e770493f0 Mon Sep 17 00:00:00 2001 From: Christoph Walcher Date: Wed, 5 Aug 2020 02:59:30 +0200 Subject: adopt comments from review --- CHANGELOG.md | 2 +- clippy_lints/src/lib.rs | 10 +- clippy_lints/src/needless_arbitrary_self_type.rs | 114 +++++++++++++++++++++++ clippy_lints/src/needless_fn_self_type.rs | 78 ---------------- src/lintlist/mod.rs | 14 +-- tests/ui/needless_arbitrary_self_type.rs | 58 ++++++++++++ tests/ui/needless_arbitrary_self_type.stderr | 46 +++++++++ tests/ui/needless_fn_self_type.rs | 26 ------ tests/ui/needless_fn_self_type.stderr | 11 --- 9 files changed, 231 insertions(+), 128 deletions(-) create mode 100644 clippy_lints/src/needless_arbitrary_self_type.rs delete mode 100644 clippy_lints/src/needless_fn_self_type.rs create mode 100644 tests/ui/needless_arbitrary_self_type.rs create mode 100644 tests/ui/needless_arbitrary_self_type.stderr delete mode 100644 tests/ui/needless_fn_self_type.rs delete mode 100644 tests/ui/needless_fn_self_type.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 179ecee03da..3f470f601ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1616,13 +1616,13 @@ Released 2018-09-13 [`mutex_atomic`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_atomic [`mutex_integer`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_integer [`naive_bytecount`]: https://rust-lang.github.io/rust-clippy/master/index.html#naive_bytecount +[`needless_arbitrary_self_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_arbitrary_self_type [`needless_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool [`needless_borrow`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow [`needless_borrowed_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference [`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect [`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue [`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main -[`needless_fn_self_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_fn_self_type [`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes [`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value [`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 80c85e70e89..3c39de1abf1 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -250,11 +250,11 @@ mod mut_mut; mod mut_reference; mod mutable_debug_assertion; mod mutex_atomic; +mod needless_arbitrary_self_type; mod needless_bool; mod needless_borrow; mod needless_borrowed_ref; mod needless_continue; -mod needless_fn_self_type; mod needless_pass_by_value; mod needless_update; mod neg_cmp_op_on_partial_ord; @@ -718,12 +718,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &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_bool::BOOL_COMPARISON, &needless_bool::NEEDLESS_BOOL, &needless_borrow::NEEDLESS_BORROW, &needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE, &needless_continue::NEEDLESS_CONTINUE, - &needless_fn_self_type::NEEDLESS_FN_SELF_TYPE, &needless_pass_by_value::NEEDLESS_PASS_BY_VALUE, &needless_update::NEEDLESS_UPDATE, &neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD, @@ -1029,7 +1029,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box items_after_statements::ItemsAfterStatements); store.register_early_pass(|| box precedence::Precedence); store.register_early_pass(|| box needless_continue::NeedlessContinue); - store.register_early_pass(|| box needless_fn_self_type::NeedlessFnSelfType); + store.register_early_pass(|| box needless_arbitrary_self_type::NeedlessArbitrarySelfType); store.register_early_pass(|| box redundant_static_lifetimes::RedundantStaticLifetimes); store.register_late_pass(|| box cargo_common_metadata::CargoCommonMetadata); store.register_late_pass(|| box multiple_crate_versions::MultipleCrateVersions); @@ -1374,10 +1374,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&mut_key::MUTABLE_KEY_TYPE), 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_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), - LintId::of(&needless_fn_self_type::NEEDLESS_FN_SELF_TYPE), 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), @@ -1538,7 +1538,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&misc_early::MIXED_CASE_HEX_LITERALS), LintId::of(&misc_early::REDUNDANT_PATTERN), LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED), - LintId::of(&needless_fn_self_type::NEEDLESS_FN_SELF_TYPE), LintId::of(&neg_multiply::NEG_MULTIPLY), LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT), LintId::of(&non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), @@ -1607,6 +1606,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), diff --git a/clippy_lints/src/needless_arbitrary_self_type.rs b/clippy_lints/src/needless_arbitrary_self_type.rs new file mode 100644 index 00000000000..1b23ecd9ad2 --- /dev/null +++ b/clippy_lints/src/needless_arbitrary_self_type.rs @@ -0,0 +1,114 @@ +use crate::utils::span_lint_and_sugg; +use if_chain::if_chain; +use rustc_ast::ast::{BindingMode, Lifetime, Mutability, Param, PatKind, Path, TyKind}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::kw; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** The lint checks for `self` in fn parameters that + /// specify the `Self`-type explicitly + /// **Why is this bad?** Increases the amount and decreases the readability of code + /// + /// **Known problems:** None + /// + /// **Example:** + /// ```rust + /// enum ValType { + /// I32, + /// I64, + /// F32, + /// F64, + /// } + /// + /// impl ValType { + /// pub fn bytes(self: Self) -> usize { + /// match self { + /// Self::I32 | Self::F32 => 4, + /// Self::I64 | Self::F64 => 8, + /// } + /// } + /// } + /// ``` + /// + /// Could be rewritten as + /// + /// ```rust + /// enum ValType { + /// I32, + /// I64, + /// F32, + /// F64, + /// } + /// + /// impl ValType { + /// pub fn bytes(self) -> usize { + /// match self { + /// Self::I32 | Self::F32 => 4, + /// Self::I64 | Self::F64 => 8, + /// } + /// } + /// } + /// ``` + pub NEEDLESS_ARBITRARY_SELF_TYPE, + complexity, + "type of `self` parameter is already by default `Self`" +} + +declare_lint_pass!(NeedlessArbitrarySelfType => [NEEDLESS_ARBITRARY_SELF_TYPE]); + +enum Mode { + Ref(Option), + Value, +} + +fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mode: &Mode, mutbl: Mutability) { + if_chain! { + if let [segment] = &path.segments[..]; + if segment.ident.name == kw::SelfUpper; + then { + let self_param = match (binding_mode, mutbl) { + (Mode::Ref(None), Mutability::Mut) => "&mut self".to_string(), + (Mode::Ref(Some(lifetime)), Mutability::Mut) => format!("&{} mut self", &lifetime.ident.name), + (Mode::Ref(None), Mutability::Not) => "&self".to_string(), + (Mode::Ref(Some(lifetime)), Mutability::Not) => format!("&{} self", &lifetime.ident.name), + (Mode::Value, Mutability::Mut) => "mut self".to_string(), + (Mode::Value, Mutability::Not) => "self".to_string(), + }; + + span_lint_and_sugg( + cx, + NEEDLESS_ARBITRARY_SELF_TYPE, + span, + "the type of the `self` parameter is arbitrary", + "consider to change this parameter to", + self_param, + Applicability::MachineApplicable, + ) + } + } +} + +impl EarlyLintPass for NeedlessArbitrarySelfType { + fn check_param(&mut self, cx: &EarlyContext<'_>, p: &Param) { + if !p.is_self() { + return; + } + + match &p.ty.kind { + TyKind::Path(None, path) => { + if let PatKind::Ident(BindingMode::ByValue(mutbl), _, _) = p.pat.kind { + check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Value, mutbl) + } + }, + TyKind::Rptr(lifetime, mut_ty) => { + if let TyKind::Path(None, path) = &mut_ty.ty.kind { + check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Ref(*lifetime), mut_ty.mutbl) + } + }, + _ => {}, + } + } +} diff --git a/clippy_lints/src/needless_fn_self_type.rs b/clippy_lints/src/needless_fn_self_type.rs deleted file mode 100644 index b71f2fbbd46..00000000000 --- a/clippy_lints/src/needless_fn_self_type.rs +++ /dev/null @@ -1,78 +0,0 @@ -use crate::utils::span_lint_and_help; -use if_chain::if_chain; -use rustc_ast::ast::{Param, TyKind}; -use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; - -declare_clippy_lint! { - /// **What it does:** The lint checks for `self` fn fn parameters that explicitly - /// specify the `Self`-type explicitly - /// **Why is this bad?** Increases the amount and decreases the readability of code - /// - /// **Known problems:** None - /// - /// **Example:** - /// ```rust - /// enum ValType { - /// I32, - /// I64, - /// F32, - /// F64, - /// } - /// - /// impl ValType { - /// pub fn bytes(self: Self) -> usize { - /// match self { - /// Self::I32 | Self::F32 => 4, - /// Self::I64 | Self::F64 => 8, - /// } - /// } - /// } - /// ``` - /// - /// Could be rewritten as - /// - /// ```rust - /// enum ValType { - /// I32, - /// I64, - /// F32, - /// F64, - /// } - /// - /// impl ValType { - /// pub fn bytes(self) -> usize { - /// match self { - /// Self::I32 | Self::F32 => 4, - /// Self::I64 | Self::F64 => 8, - /// } - /// } - /// } - /// ``` - pub NEEDLESS_FN_SELF_TYPE, - style, - "type of `self` parameter is already by default `Self`" -} - -declare_lint_pass!(NeedlessFnSelfType => [NEEDLESS_FN_SELF_TYPE]); - -impl EarlyLintPass for NeedlessFnSelfType { - fn check_param(&mut self, cx: &EarlyContext<'_>, p: &Param) { - if_chain! { - if p.is_self(); - if let TyKind::Path(None, path) = &p.ty.kind; - if let Some(segment) = path.segments.first(); - if segment.ident.as_str() == sym!(Self).as_str(); - then { - span_lint_and_help( - cx, - NEEDLESS_FN_SELF_TYPE, - p.ty.span, - "the type of the `self` parameter is already by default `Self`", - None, - "consider removing the type specification", - ); - } - } - } -} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index a20e410f79b..91761e8a86d 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1459,6 +1459,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "bytecount", }, + Lint { + name: "needless_arbitrary_self_type", + group: "complexity", + desc: "type of `self` parameter is already by default `Self`", + deprecation: None, + module: "needless_arbitrary_self_type", + }, Lint { name: "needless_bool", group: "complexity", @@ -1501,13 +1508,6 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "doc", }, - Lint { - name: "needless_fn_self_type", - group: "style", - desc: "type of `self` parameter is already by default `Self`", - deprecation: None, - module: "needless_fn_self_type", - }, Lint { name: "needless_lifetimes", group: "complexity", diff --git a/tests/ui/needless_arbitrary_self_type.rs b/tests/ui/needless_arbitrary_self_type.rs new file mode 100644 index 00000000000..da4bbcf4a7d --- /dev/null +++ b/tests/ui/needless_arbitrary_self_type.rs @@ -0,0 +1,58 @@ +#![warn(clippy::needless_arbitrary_self_type)] + +pub enum ValType { + A, + B, +} + +impl ValType { + pub fn bad(self: Self) { + unimplemented!(); + } + + pub fn good(self) { + unimplemented!(); + } + + pub fn mut_bad(mut self: Self) { + unimplemented!(); + } + + pub fn mut_good(mut self) { + unimplemented!(); + } + + pub fn ref_bad(self: &Self) { + unimplemented!(); + } + + pub fn ref_bad_with_lifetime<'a>(self: &'a Self) { + unimplemented!(); + } + + pub fn ref_good(&self) { + unimplemented!(); + } + + pub fn mut_ref_bad(self: &mut Self) { + unimplemented!(); + } + + pub fn mut_ref_bad_with_lifetime<'a>(self: &'a mut Self) { + unimplemented!(); + } + + pub fn mut_ref_good(&mut self) { + unimplemented!(); + } + + pub fn mut_ref_mut_bad(mut self: &mut Self) { + unimplemented!(); + } + + pub fn mut_ref_mut_ref_good(self: &&mut &mut Self) { + unimplemented!(); + } +} + +fn main() {} diff --git a/tests/ui/needless_arbitrary_self_type.stderr b/tests/ui/needless_arbitrary_self_type.stderr new file mode 100644 index 00000000000..ee803b24071 --- /dev/null +++ b/tests/ui/needless_arbitrary_self_type.stderr @@ -0,0 +1,46 @@ +error: the type of the `self` parameter is arbitrary + --> $DIR/needless_arbitrary_self_type.rs:9:16 + | +LL | pub fn bad(self: Self) { + | ^^^^^^^^^^ help: consider to change this parameter to: `self` + | + = note: `-D clippy::needless-arbitrary-self-type` implied by `-D warnings` + +error: the type of the `self` parameter is arbitrary + --> $DIR/needless_arbitrary_self_type.rs:17:20 + | +LL | pub fn mut_bad(mut self: Self) { + | ^^^^^^^^^^^^^^ help: consider to change this parameter to: `mut self` + +error: the type of the `self` parameter is arbitrary + --> $DIR/needless_arbitrary_self_type.rs:25:20 + | +LL | pub fn ref_bad(self: &Self) { + | ^^^^^^^^^^^ help: consider to change this parameter to: `&self` + +error: the type of the `self` parameter is arbitrary + --> $DIR/needless_arbitrary_self_type.rs:29:38 + | +LL | pub fn ref_bad_with_lifetime<'a>(self: &'a Self) { + | ^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'a self` + +error: the type of the `self` parameter is arbitrary + --> $DIR/needless_arbitrary_self_type.rs:37:24 + | +LL | pub fn mut_ref_bad(self: &mut Self) { + | ^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&mut self` + +error: the type of the `self` parameter is arbitrary + --> $DIR/needless_arbitrary_self_type.rs:41:42 + | +LL | pub fn mut_ref_bad_with_lifetime<'a>(self: &'a mut Self) { + | ^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'a mut self` + +error: the type of the `self` parameter is arbitrary + --> $DIR/needless_arbitrary_self_type.rs:49:28 + | +LL | pub fn mut_ref_mut_bad(mut self: &mut Self) { + | ^^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&mut self` + +error: aborting due to 7 previous errors + diff --git a/tests/ui/needless_fn_self_type.rs b/tests/ui/needless_fn_self_type.rs deleted file mode 100644 index 12bb84582ff..00000000000 --- a/tests/ui/needless_fn_self_type.rs +++ /dev/null @@ -1,26 +0,0 @@ -#![warn(clippy::style, clippy::needless_fn_self_type)] - -pub enum ValType { - I32, - I64, - F32, - F64, -} - -impl ValType { - pub fn bytes_bad(self: Self) -> usize { - match self { - Self::I32 | Self::F32 => 4, - Self::I64 | Self::F64 => 8, - } - } - - pub fn bytes_good(self) -> usize { - match self { - Self::I32 | Self::F32 => 4, - Self::I64 | Self::F64 => 8, - } - } -} - -fn main() {} diff --git a/tests/ui/needless_fn_self_type.stderr b/tests/ui/needless_fn_self_type.stderr deleted file mode 100644 index 703705c7842..00000000000 --- a/tests/ui/needless_fn_self_type.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error: the type of the `self` parameter is already by default `Self` - --> $DIR/needless_fn_self_type.rs:11:28 - | -LL | pub fn bytes_bad(self: Self) -> usize { - | ^^^^ - | - = note: `-D clippy::needless-fn-self-type` implied by `-D warnings` - = help: consider removing the type specification - -error: aborting due to previous error - -- cgit 1.4.1-3-g733a5 From c87d999fa2f8e88f986aa5f4d76b708824e1fd3a Mon Sep 17 00:00:00 2001 From: Christoph Walcher Date: Wed, 5 Aug 2020 03:37:29 +0200 Subject: fix ui tests --- tests/ui/extra_unused_lifetimes.rs | 8 +++- tests/ui/extra_unused_lifetimes.stderr | 8 ++-- tests/ui/len_without_is_empty.rs | 40 +++++++++--------- tests/ui/len_without_is_empty.stderr | 8 ++-- tests/ui/len_zero.fixed | 20 ++++----- tests/ui/len_zero.rs | 20 ++++----- tests/ui/needless_arbitrary_self_type.fixed | 61 ++++++++++++++++++++++++++++ tests/ui/needless_arbitrary_self_type.rs | 3 ++ tests/ui/needless_arbitrary_self_type.stderr | 14 +++---- tests/ui/option_map_unit_fn_fixable.fixed | 4 +- tests/ui/option_map_unit_fn_fixable.rs | 4 +- tests/ui/result_map_unit_fn_fixable.fixed | 4 +- tests/ui/result_map_unit_fn_fixable.rs | 4 +- 13 files changed, 134 insertions(+), 64 deletions(-) create mode 100644 tests/ui/needless_arbitrary_self_type.fixed (limited to 'tests') diff --git a/tests/ui/extra_unused_lifetimes.rs b/tests/ui/extra_unused_lifetimes.rs index ddbf4e98c51..150acfbfee7 100644 --- a/tests/ui/extra_unused_lifetimes.rs +++ b/tests/ui/extra_unused_lifetimes.rs @@ -1,4 +1,10 @@ -#![allow(unused, dead_code, clippy::needless_lifetimes, clippy::needless_pass_by_value)] +#![allow( + unused, + dead_code, + clippy::needless_lifetimes, + clippy::needless_pass_by_value, + clippy::needless_arbitrary_self_type +)] #![warn(clippy::extra_unused_lifetimes)] fn empty() {} diff --git a/tests/ui/extra_unused_lifetimes.stderr b/tests/ui/extra_unused_lifetimes.stderr index 16bbb1c037d..ebdb8e74952 100644 --- a/tests/ui/extra_unused_lifetimes.stderr +++ b/tests/ui/extra_unused_lifetimes.stderr @@ -1,5 +1,5 @@ error: this lifetime isn't used in the function definition - --> $DIR/extra_unused_lifetimes.rs:8:14 + --> $DIR/extra_unused_lifetimes.rs:14:14 | LL | fn unused_lt<'a>(x: u8) {} | ^^ @@ -7,19 +7,19 @@ LL | fn unused_lt<'a>(x: u8) {} = note: `-D clippy::extra-unused-lifetimes` implied by `-D warnings` error: this lifetime isn't used in the function definition - --> $DIR/extra_unused_lifetimes.rs:10:25 + --> $DIR/extra_unused_lifetimes.rs:16:25 | LL | fn unused_lt_transitive<'a, 'b: 'a>(x: &'b u8) { | ^^ error: this lifetime isn't used in the function definition - --> $DIR/extra_unused_lifetimes.rs:35:10 + --> $DIR/extra_unused_lifetimes.rs:41:10 | LL | fn x<'a>(&self) {} | ^^ error: this lifetime isn't used in the function definition - --> $DIR/extra_unused_lifetimes.rs:61:22 + --> $DIR/extra_unused_lifetimes.rs:67:22 | LL | fn unused_lt<'a>(x: u8) {} | ^^ diff --git a/tests/ui/len_without_is_empty.rs b/tests/ui/len_without_is_empty.rs index 3ef29dd6388..b5211318a15 100644 --- a/tests/ui/len_without_is_empty.rs +++ b/tests/ui/len_without_is_empty.rs @@ -4,14 +4,14 @@ pub struct PubOne; impl PubOne { - pub fn len(self: &Self) -> isize { + pub fn len(&self) -> isize { 1 } } impl PubOne { // A second impl for this struct -- the error span shouldn't mention this. - pub fn irrelevant(self: &Self) -> bool { + pub fn irrelevant(&self) -> bool { false } } @@ -21,7 +21,7 @@ pub struct PubAllowed; #[allow(clippy::len_without_is_empty)] impl PubAllowed { - pub fn len(self: &Self) -> isize { + pub fn len(&self) -> isize { 1 } } @@ -29,17 +29,17 @@ impl PubAllowed { // No `allow` attribute on this impl block, but that doesn't matter -- we only require one on the // impl containing `len`. impl PubAllowed { - pub fn irrelevant(self: &Self) -> bool { + pub fn irrelevant(&self) -> bool { false } } pub trait PubTraitsToo { - fn len(self: &Self) -> isize; + fn len(&self) -> isize; } impl PubTraitsToo for One { - fn len(self: &Self) -> isize { + fn len(&self) -> isize { 0 } } @@ -47,11 +47,11 @@ impl PubTraitsToo for One { pub struct HasIsEmpty; impl HasIsEmpty { - pub fn len(self: &Self) -> isize { + pub fn len(&self) -> isize { 1 } - fn is_empty(self: &Self) -> bool { + fn is_empty(&self) -> bool { false } } @@ -59,11 +59,11 @@ impl HasIsEmpty { pub struct HasWrongIsEmpty; impl HasWrongIsEmpty { - pub fn len(self: &Self) -> isize { + pub fn len(&self) -> isize { 1 } - pub fn is_empty(self: &Self, x: u32) -> bool { + pub fn is_empty(&self, x: u32) -> bool { false } } @@ -71,7 +71,7 @@ impl HasWrongIsEmpty { struct NotPubOne; impl NotPubOne { - pub fn len(self: &Self) -> isize { + pub fn len(&self) -> isize { // No error; `len` is pub but `NotPubOne` is not exported anyway. 1 } @@ -80,19 +80,19 @@ impl NotPubOne { struct One; impl One { - fn len(self: &Self) -> isize { + fn len(&self) -> isize { // No error; `len` is private; see issue #1085. 1 } } trait TraitsToo { - fn len(self: &Self) -> isize; + fn len(&self) -> isize; // No error; `len` is private; see issue #1085. } impl TraitsToo for One { - fn len(self: &Self) -> isize { + fn len(&self) -> isize { 0 } } @@ -100,11 +100,11 @@ impl TraitsToo for One { struct HasPrivateIsEmpty; impl HasPrivateIsEmpty { - pub fn len(self: &Self) -> isize { + pub fn len(&self) -> isize { 1 } - fn is_empty(self: &Self) -> bool { + fn is_empty(&self) -> bool { false } } @@ -112,16 +112,16 @@ impl HasPrivateIsEmpty { struct Wither; pub trait WithIsEmpty { - fn len(self: &Self) -> isize; - fn is_empty(self: &Self) -> bool; + fn len(&self) -> isize; + fn is_empty(&self) -> bool; } impl WithIsEmpty for Wither { - fn len(self: &Self) -> isize { + fn len(&self) -> isize { 1 } - fn is_empty(self: &Self) -> bool { + fn is_empty(&self) -> bool { false } } diff --git a/tests/ui/len_without_is_empty.stderr b/tests/ui/len_without_is_empty.stderr index 4493b17a4b4..d79c300c074 100644 --- a/tests/ui/len_without_is_empty.stderr +++ b/tests/ui/len_without_is_empty.stderr @@ -2,7 +2,7 @@ error: item `PubOne` has a public `len` method but no corresponding `is_empty` m --> $DIR/len_without_is_empty.rs:6:1 | LL | / impl PubOne { -LL | | pub fn len(self: &Self) -> isize { +LL | | pub fn len(&self) -> isize { LL | | 1 LL | | } LL | | } @@ -14,7 +14,7 @@ error: trait `PubTraitsToo` has a `len` method but no (possibly inherited) `is_e --> $DIR/len_without_is_empty.rs:37:1 | LL | / pub trait PubTraitsToo { -LL | | fn len(self: &Self) -> isize; +LL | | fn len(&self) -> isize; LL | | } | |_^ @@ -22,7 +22,7 @@ error: item `HasIsEmpty` has a public `len` method but a private `is_empty` meth --> $DIR/len_without_is_empty.rs:49:1 | LL | / impl HasIsEmpty { -LL | | pub fn len(self: &Self) -> isize { +LL | | pub fn len(&self) -> isize { LL | | 1 LL | | } ... | @@ -34,7 +34,7 @@ error: item `HasWrongIsEmpty` has a public `len` method but no corresponding `is --> $DIR/len_without_is_empty.rs:61:1 | LL | / impl HasWrongIsEmpty { -LL | | pub fn len(self: &Self) -> isize { +LL | | pub fn len(&self) -> isize { LL | | 1 LL | | } ... | diff --git a/tests/ui/len_zero.fixed b/tests/ui/len_zero.fixed index a29b832eb60..d81676a3d9f 100644 --- a/tests/ui/len_zero.fixed +++ b/tests/ui/len_zero.fixed @@ -7,12 +7,12 @@ pub struct One; struct Wither; trait TraitsToo { - fn len(self: &Self) -> isize; + fn len(&self) -> isize; // No error; `len` is private; see issue #1085. } impl TraitsToo for One { - fn len(self: &Self) -> isize { + fn len(&self) -> isize { 0 } } @@ -20,11 +20,11 @@ impl TraitsToo for One { pub struct HasIsEmpty; impl HasIsEmpty { - pub fn len(self: &Self) -> isize { + pub fn len(&self) -> isize { 1 } - fn is_empty(self: &Self) -> bool { + fn is_empty(&self) -> bool { false } } @@ -32,26 +32,26 @@ impl HasIsEmpty { pub struct HasWrongIsEmpty; impl HasWrongIsEmpty { - pub fn len(self: &Self) -> isize { + pub fn len(&self) -> isize { 1 } - pub fn is_empty(self: &Self, x: u32) -> bool { + pub fn is_empty(&self, x: u32) -> bool { false } } pub trait WithIsEmpty { - fn len(self: &Self) -> isize; - fn is_empty(self: &Self) -> bool; + fn len(&self) -> isize; + fn is_empty(&self) -> bool; } impl WithIsEmpty for Wither { - fn len(self: &Self) -> isize { + fn len(&self) -> isize { 1 } - fn is_empty(self: &Self) -> bool { + fn is_empty(&self) -> bool { false } } diff --git a/tests/ui/len_zero.rs b/tests/ui/len_zero.rs index 8fd0093f4fd..ecdba921a5d 100644 --- a/tests/ui/len_zero.rs +++ b/tests/ui/len_zero.rs @@ -7,12 +7,12 @@ pub struct One; struct Wither; trait TraitsToo { - fn len(self: &Self) -> isize; + fn len(&self) -> isize; // No error; `len` is private; see issue #1085. } impl TraitsToo for One { - fn len(self: &Self) -> isize { + fn len(&self) -> isize { 0 } } @@ -20,11 +20,11 @@ impl TraitsToo for One { pub struct HasIsEmpty; impl HasIsEmpty { - pub fn len(self: &Self) -> isize { + pub fn len(&self) -> isize { 1 } - fn is_empty(self: &Self) -> bool { + fn is_empty(&self) -> bool { false } } @@ -32,26 +32,26 @@ impl HasIsEmpty { pub struct HasWrongIsEmpty; impl HasWrongIsEmpty { - pub fn len(self: &Self) -> isize { + pub fn len(&self) -> isize { 1 } - pub fn is_empty(self: &Self, x: u32) -> bool { + pub fn is_empty(&self, x: u32) -> bool { false } } pub trait WithIsEmpty { - fn len(self: &Self) -> isize; - fn is_empty(self: &Self) -> bool; + fn len(&self) -> isize; + fn is_empty(&self) -> bool; } impl WithIsEmpty for Wither { - fn len(self: &Self) -> isize { + fn len(&self) -> isize { 1 } - fn is_empty(self: &Self) -> bool { + fn is_empty(&self) -> bool { false } } diff --git a/tests/ui/needless_arbitrary_self_type.fixed b/tests/ui/needless_arbitrary_self_type.fixed new file mode 100644 index 00000000000..642e48fd131 --- /dev/null +++ b/tests/ui/needless_arbitrary_self_type.fixed @@ -0,0 +1,61 @@ +// run-rustfix + +#![warn(clippy::needless_arbitrary_self_type)] +#![allow(unused_mut, clippy::needless_lifetimes)] + +pub enum ValType { + A, + B, +} + +impl ValType { + pub fn bad(self) { + unimplemented!(); + } + + pub fn good(self) { + unimplemented!(); + } + + pub fn mut_bad(mut self) { + unimplemented!(); + } + + pub fn mut_good(mut self) { + unimplemented!(); + } + + pub fn ref_bad(&self) { + unimplemented!(); + } + + pub fn ref_bad_with_lifetime<'a>(&'a self) { + unimplemented!(); + } + + pub fn ref_good(&self) { + unimplemented!(); + } + + pub fn mut_ref_bad(&mut self) { + unimplemented!(); + } + + pub fn mut_ref_bad_with_lifetime<'a>(&'a mut self) { + unimplemented!(); + } + + pub fn mut_ref_good(&mut self) { + unimplemented!(); + } + + pub fn mut_ref_mut_bad(&mut self) { + unimplemented!(); + } + + pub fn mut_ref_mut_ref_good(self: &&mut &mut Self) { + unimplemented!(); + } +} + +fn main() {} diff --git a/tests/ui/needless_arbitrary_self_type.rs b/tests/ui/needless_arbitrary_self_type.rs index da4bbcf4a7d..178abc341a8 100644 --- a/tests/ui/needless_arbitrary_self_type.rs +++ b/tests/ui/needless_arbitrary_self_type.rs @@ -1,4 +1,7 @@ +// run-rustfix + #![warn(clippy::needless_arbitrary_self_type)] +#![allow(unused_mut, clippy::needless_lifetimes)] pub enum ValType { A, diff --git a/tests/ui/needless_arbitrary_self_type.stderr b/tests/ui/needless_arbitrary_self_type.stderr index ee803b24071..fc21d3d0afd 100644 --- a/tests/ui/needless_arbitrary_self_type.stderr +++ b/tests/ui/needless_arbitrary_self_type.stderr @@ -1,5 +1,5 @@ error: the type of the `self` parameter is arbitrary - --> $DIR/needless_arbitrary_self_type.rs:9:16 + --> $DIR/needless_arbitrary_self_type.rs:12:16 | LL | pub fn bad(self: Self) { | ^^^^^^^^^^ help: consider to change this parameter to: `self` @@ -7,37 +7,37 @@ LL | pub fn bad(self: Self) { = note: `-D clippy::needless-arbitrary-self-type` implied by `-D warnings` error: the type of the `self` parameter is arbitrary - --> $DIR/needless_arbitrary_self_type.rs:17:20 + --> $DIR/needless_arbitrary_self_type.rs:20:20 | LL | pub fn mut_bad(mut self: Self) { | ^^^^^^^^^^^^^^ help: consider to change this parameter to: `mut self` error: the type of the `self` parameter is arbitrary - --> $DIR/needless_arbitrary_self_type.rs:25:20 + --> $DIR/needless_arbitrary_self_type.rs:28:20 | LL | pub fn ref_bad(self: &Self) { | ^^^^^^^^^^^ help: consider to change this parameter to: `&self` error: the type of the `self` parameter is arbitrary - --> $DIR/needless_arbitrary_self_type.rs:29:38 + --> $DIR/needless_arbitrary_self_type.rs:32:38 | LL | pub fn ref_bad_with_lifetime<'a>(self: &'a Self) { | ^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'a self` error: the type of the `self` parameter is arbitrary - --> $DIR/needless_arbitrary_self_type.rs:37:24 + --> $DIR/needless_arbitrary_self_type.rs:40:24 | LL | pub fn mut_ref_bad(self: &mut Self) { | ^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&mut self` error: the type of the `self` parameter is arbitrary - --> $DIR/needless_arbitrary_self_type.rs:41:42 + --> $DIR/needless_arbitrary_self_type.rs:44:42 | LL | pub fn mut_ref_bad_with_lifetime<'a>(self: &'a mut Self) { | ^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'a mut self` error: the type of the `self` parameter is arbitrary - --> $DIR/needless_arbitrary_self_type.rs:49:28 + --> $DIR/needless_arbitrary_self_type.rs:52:28 | LL | pub fn mut_ref_mut_bad(mut self: &mut Self) { | ^^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&mut self` diff --git a/tests/ui/option_map_unit_fn_fixable.fixed b/tests/ui/option_map_unit_fn_fixable.fixed index 9a0da404cb6..96d1c54946c 100644 --- a/tests/ui/option_map_unit_fn_fixable.fixed +++ b/tests/ui/option_map_unit_fn_fixable.fixed @@ -22,9 +22,9 @@ struct HasOption { } impl HasOption { - fn do_option_nothing(self: &Self, value: usize) {} + fn do_option_nothing(&self, value: usize) {} - fn do_option_plus_one(self: &Self, value: usize) -> usize { + fn do_option_plus_one(&self, value: usize) -> usize { value + 1 } } diff --git a/tests/ui/option_map_unit_fn_fixable.rs b/tests/ui/option_map_unit_fn_fixable.rs index 58041b62df3..931ffc18665 100644 --- a/tests/ui/option_map_unit_fn_fixable.rs +++ b/tests/ui/option_map_unit_fn_fixable.rs @@ -22,9 +22,9 @@ struct HasOption { } impl HasOption { - fn do_option_nothing(self: &Self, value: usize) {} + fn do_option_nothing(&self, value: usize) {} - fn do_option_plus_one(self: &Self, value: usize) -> usize { + fn do_option_plus_one(&self, value: usize) -> usize { value + 1 } } diff --git a/tests/ui/result_map_unit_fn_fixable.fixed b/tests/ui/result_map_unit_fn_fixable.fixed index 1d0a3ecd0ff..631042c616b 100644 --- a/tests/ui/result_map_unit_fn_fixable.fixed +++ b/tests/ui/result_map_unit_fn_fixable.fixed @@ -18,9 +18,9 @@ struct HasResult { } impl HasResult { - fn do_result_nothing(self: &Self, value: usize) {} + fn do_result_nothing(&self, value: usize) {} - fn do_result_plus_one(self: &Self, value: usize) -> usize { + fn do_result_plus_one(&self, value: usize) -> usize { value + 1 } } diff --git a/tests/ui/result_map_unit_fn_fixable.rs b/tests/ui/result_map_unit_fn_fixable.rs index 2fe18f923f0..679eb232626 100644 --- a/tests/ui/result_map_unit_fn_fixable.rs +++ b/tests/ui/result_map_unit_fn_fixable.rs @@ -18,9 +18,9 @@ struct HasResult { } impl HasResult { - fn do_result_nothing(self: &Self, value: usize) {} + fn do_result_nothing(&self, value: usize) {} - fn do_result_plus_one(self: &Self, value: usize) -> usize { + fn do_result_plus_one(&self, value: usize) -> usize { value + 1 } } -- cgit 1.4.1-3-g733a5 From e03f73e627721c35459886781af281632cac299d Mon Sep 17 00:00:00 2001 From: Christoph Walcher Date: Wed, 5 Aug 2020 23:38:55 +0200 Subject: fix nits --- clippy_lints/src/needless_arbitrary_self_type.rs | 2 +- tests/ui/needless_arbitrary_self_type.fixed | 12 ++++++++++-- tests/ui/needless_arbitrary_self_type.rs | 12 ++++++++++-- tests/ui/needless_arbitrary_self_type.stderr | 22 +++++++++++----------- 4 files changed, 32 insertions(+), 16 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/needless_arbitrary_self_type.rs b/clippy_lints/src/needless_arbitrary_self_type.rs index 1b23ecd9ad2..4590128bedc 100644 --- a/clippy_lints/src/needless_arbitrary_self_type.rs +++ b/clippy_lints/src/needless_arbitrary_self_type.rs @@ -82,7 +82,7 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod cx, NEEDLESS_ARBITRARY_SELF_TYPE, span, - "the type of the `self` parameter is arbitrary", + "the type of the `self` parameter does not need to be arbitrary", "consider to change this parameter to", self_param, Applicability::MachineApplicable, diff --git a/tests/ui/needless_arbitrary_self_type.fixed b/tests/ui/needless_arbitrary_self_type.fixed index 642e48fd131..bc770d8bf68 100644 --- a/tests/ui/needless_arbitrary_self_type.fixed +++ b/tests/ui/needless_arbitrary_self_type.fixed @@ -29,11 +29,15 @@ impl ValType { unimplemented!(); } + pub fn ref_good(&self) { + unimplemented!(); + } + pub fn ref_bad_with_lifetime<'a>(&'a self) { unimplemented!(); } - pub fn ref_good(&self) { + pub fn ref_good_with_lifetime<'a>(&'a self) { unimplemented!(); } @@ -41,11 +45,15 @@ impl ValType { unimplemented!(); } + pub fn mut_ref_good(&mut self) { + unimplemented!(); + } + pub fn mut_ref_bad_with_lifetime<'a>(&'a mut self) { unimplemented!(); } - pub fn mut_ref_good(&mut self) { + pub fn mut_ref_good_with_lifetime<'a>(&'a mut self) { unimplemented!(); } diff --git a/tests/ui/needless_arbitrary_self_type.rs b/tests/ui/needless_arbitrary_self_type.rs index 178abc341a8..9074920b204 100644 --- a/tests/ui/needless_arbitrary_self_type.rs +++ b/tests/ui/needless_arbitrary_self_type.rs @@ -29,11 +29,15 @@ impl ValType { unimplemented!(); } + pub fn ref_good(&self) { + unimplemented!(); + } + pub fn ref_bad_with_lifetime<'a>(self: &'a Self) { unimplemented!(); } - pub fn ref_good(&self) { + pub fn ref_good_with_lifetime<'a>(&'a self) { unimplemented!(); } @@ -41,11 +45,15 @@ impl ValType { unimplemented!(); } + pub fn mut_ref_good(&mut self) { + unimplemented!(); + } + pub fn mut_ref_bad_with_lifetime<'a>(self: &'a mut Self) { unimplemented!(); } - pub fn mut_ref_good(&mut self) { + pub fn mut_ref_good_with_lifetime<'a>(&'a mut self) { unimplemented!(); } diff --git a/tests/ui/needless_arbitrary_self_type.stderr b/tests/ui/needless_arbitrary_self_type.stderr index fc21d3d0afd..227c6d73b62 100644 --- a/tests/ui/needless_arbitrary_self_type.stderr +++ b/tests/ui/needless_arbitrary_self_type.stderr @@ -1,4 +1,4 @@ -error: the type of the `self` parameter is arbitrary +error: the type of the `self` parameter does not need to be arbitrary --> $DIR/needless_arbitrary_self_type.rs:12:16 | LL | pub fn bad(self: Self) { @@ -6,38 +6,38 @@ LL | pub fn bad(self: Self) { | = note: `-D clippy::needless-arbitrary-self-type` implied by `-D warnings` -error: the type of the `self` parameter is arbitrary +error: the type of the `self` parameter does not need to be arbitrary --> $DIR/needless_arbitrary_self_type.rs:20:20 | LL | pub fn mut_bad(mut self: Self) { | ^^^^^^^^^^^^^^ help: consider to change this parameter to: `mut self` -error: the type of the `self` parameter is arbitrary +error: the type of the `self` parameter does not need to be arbitrary --> $DIR/needless_arbitrary_self_type.rs:28:20 | LL | pub fn ref_bad(self: &Self) { | ^^^^^^^^^^^ help: consider to change this parameter to: `&self` -error: the type of the `self` parameter is arbitrary - --> $DIR/needless_arbitrary_self_type.rs:32:38 +error: the type of the `self` parameter does not need to be arbitrary + --> $DIR/needless_arbitrary_self_type.rs:36:38 | LL | pub fn ref_bad_with_lifetime<'a>(self: &'a Self) { | ^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'a self` -error: the type of the `self` parameter is arbitrary - --> $DIR/needless_arbitrary_self_type.rs:40:24 +error: the type of the `self` parameter does not need to be arbitrary + --> $DIR/needless_arbitrary_self_type.rs:44:24 | LL | pub fn mut_ref_bad(self: &mut Self) { | ^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&mut self` -error: the type of the `self` parameter is arbitrary - --> $DIR/needless_arbitrary_self_type.rs:44:42 +error: the type of the `self` parameter does not need to be arbitrary + --> $DIR/needless_arbitrary_self_type.rs:52:42 | LL | pub fn mut_ref_bad_with_lifetime<'a>(self: &'a mut Self) { | ^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'a mut self` -error: the type of the `self` parameter is arbitrary - --> $DIR/needless_arbitrary_self_type.rs:52:28 +error: the type of the `self` parameter does not need to be arbitrary + --> $DIR/needless_arbitrary_self_type.rs:60:28 | LL | pub fn mut_ref_mut_bad(mut self: &mut Self) { | ^^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&mut self` -- cgit 1.4.1-3-g733a5 From bfe610cc8d7d380cfaf83f03629a23747fc54fad Mon Sep 17 00:00:00 2001 From: Christoph Walcher Date: Fri, 7 Aug 2020 18:03:12 +0200 Subject: ignore mutable self reference parameters --- clippy_lints/src/needless_arbitrary_self_type.rs | 8 ++++++-- tests/ui/needless_arbitrary_self_type.fixed | 2 +- tests/ui/needless_arbitrary_self_type.rs | 2 +- tests/ui/needless_arbitrary_self_type.stderr | 8 +------- 4 files changed, 9 insertions(+), 11 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/needless_arbitrary_self_type.rs b/clippy_lints/src/needless_arbitrary_self_type.rs index 4590128bedc..38bdd0f7ed2 100644 --- a/clippy_lints/src/needless_arbitrary_self_type.rs +++ b/clippy_lints/src/needless_arbitrary_self_type.rs @@ -104,8 +104,12 @@ impl EarlyLintPass for NeedlessArbitrarySelfType { } }, TyKind::Rptr(lifetime, mut_ty) => { - if let TyKind::Path(None, path) = &mut_ty.ty.kind { - check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Ref(*lifetime), mut_ty.mutbl) + if_chain! { + if let TyKind::Path(None, path) = &mut_ty.ty.kind; + if let PatKind::Ident(BindingMode::ByValue(Mutability::Not), _, _) = p.pat.kind; + then { + check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Ref(*lifetime), mut_ty.mutbl) + } } }, _ => {}, diff --git a/tests/ui/needless_arbitrary_self_type.fixed b/tests/ui/needless_arbitrary_self_type.fixed index bc770d8bf68..9da21eb6b29 100644 --- a/tests/ui/needless_arbitrary_self_type.fixed +++ b/tests/ui/needless_arbitrary_self_type.fixed @@ -57,7 +57,7 @@ impl ValType { unimplemented!(); } - pub fn mut_ref_mut_bad(&mut self) { + pub fn mut_ref_mut_good(mut self: &mut Self) { unimplemented!(); } diff --git a/tests/ui/needless_arbitrary_self_type.rs b/tests/ui/needless_arbitrary_self_type.rs index 9074920b204..17aeaaf97ac 100644 --- a/tests/ui/needless_arbitrary_self_type.rs +++ b/tests/ui/needless_arbitrary_self_type.rs @@ -57,7 +57,7 @@ impl ValType { unimplemented!(); } - pub fn mut_ref_mut_bad(mut self: &mut Self) { + pub fn mut_ref_mut_good(mut self: &mut Self) { unimplemented!(); } diff --git a/tests/ui/needless_arbitrary_self_type.stderr b/tests/ui/needless_arbitrary_self_type.stderr index 227c6d73b62..f4c645d35c8 100644 --- a/tests/ui/needless_arbitrary_self_type.stderr +++ b/tests/ui/needless_arbitrary_self_type.stderr @@ -36,11 +36,5 @@ error: the type of the `self` parameter does not need to be arbitrary LL | pub fn mut_ref_bad_with_lifetime<'a>(self: &'a mut Self) { | ^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'a mut self` -error: the type of the `self` parameter does not need to be arbitrary - --> $DIR/needless_arbitrary_self_type.rs:60:28 - | -LL | pub fn mut_ref_mut_bad(mut self: &mut Self) { - | ^^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&mut self` - -error: aborting due to 7 previous errors +error: aborting due to 6 previous errors -- cgit 1.4.1-3-g733a5 From 87e740921abd4132152f090545fa4c9ed9fa0d6d Mon Sep 17 00:00:00 2001 From: Christoph Walcher Date: Fri, 7 Aug 2020 17:55:25 +0200 Subject: check impl Ord / is_float --- clippy_lints/src/minmax.rs | 23 ++++++++++++++++------- tests/ui/min_max.rs | 18 ++++++++++++++++++ tests/ui/min_max.stderr | 32 +++++++++++++++++++------------- 3 files changed, 53 insertions(+), 20 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index 1f798fd1120..004dd50a31b 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -1,5 +1,6 @@ use crate::consts::{constant_simple, Constant}; -use crate::utils::{match_def_path, paths, span_lint}; +use crate::utils::{match_def_path, match_trait_method, paths, span_lint}; +use if_chain::if_chain; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -84,12 +85,20 @@ fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Cons } }, ExprKind::MethodCall(ref path, _, ref args, _) => { - if path.ident.as_str() == sym!(max).as_str() { - fetch_const(cx, args, MinMax::Max) - } else if path.ident.as_str() == sym!(min).as_str() { - fetch_const(cx, args, MinMax::Min) - } else { - None + if_chain! { + if let [obj, _] = args; + if cx.typeck_results().expr_ty(obj).is_floating_point() || match_trait_method(cx, expr, &paths::ORD); + then { + if path.ident.as_str() == sym!(max).as_str() { + fetch_const(cx, args, MinMax::Max) + } else if path.ident.as_str() == sym!(min).as_str() { + fetch_const(cx, args, MinMax::Min) + } else { + None + } + } else { + None + } } }, _ => None, diff --git a/tests/ui/min_max.rs b/tests/ui/min_max.rs index 90ec5676493..f7ed72a11cf 100644 --- a/tests/ui/min_max.rs +++ b/tests/ui/min_max.rs @@ -6,6 +6,18 @@ use std::cmp::{max, min}; const LARGE: usize = 3; +struct NotOrd(u64); + +impl NotOrd { + fn min(self, x: u64) -> NotOrd { + NotOrd(x) + } + + fn max(self, x: u64) -> NotOrd { + NotOrd(x) + } +} + fn main() { let x; x = 2usize; @@ -31,11 +43,14 @@ fn main() { max("Apple", min(s, "Zoo")); // ok + let f = 3f32; x.min(1).max(3); x.max(3).min(1); + f.max(3f32).min(1f32); x.max(1).min(3); // ok x.min(3).max(1); // ok + f.min(3f32).max(1f32); // ok max(x.min(1), 3); min(x.max(1), 3); // ok @@ -44,4 +59,7 @@ fn main() { s.min("Apple").max("Zoo"); s.min("Zoo").max("Apple"); // ok + + let not_ord = NotOrd(1); + not_ord.min(1).max(3); // ok } diff --git a/tests/ui/min_max.stderr b/tests/ui/min_max.stderr index 653946dc025..9f8e26fa406 100644 --- a/tests/ui/min_max.stderr +++ b/tests/ui/min_max.stderr @@ -1,5 +1,5 @@ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:12:5 + --> $DIR/min_max.rs:24:5 | LL | min(1, max(3, x)); | ^^^^^^^^^^^^^^^^^ @@ -7,70 +7,76 @@ LL | min(1, max(3, x)); = note: `-D clippy::min-max` implied by `-D warnings` error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:13:5 + --> $DIR/min_max.rs:25:5 | LL | min(max(3, x), 1); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:14:5 + --> $DIR/min_max.rs:26:5 | LL | max(min(x, 1), 3); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:15:5 + --> $DIR/min_max.rs:27:5 | LL | max(3, min(x, 1)); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:17:5 + --> $DIR/min_max.rs:29:5 | LL | my_max(3, my_min(x, 1)); | ^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:29:5 + --> $DIR/min_max.rs:41:5 | LL | min("Apple", max("Zoo", s)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:30:5 + --> $DIR/min_max.rs:42:5 | LL | max(min(s, "Apple"), "Zoo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:34:5 + --> $DIR/min_max.rs:47:5 | LL | x.min(1).max(3); | ^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:35:5 + --> $DIR/min_max.rs:48:5 | LL | x.max(3).min(1); | ^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:40:5 + --> $DIR/min_max.rs:49:5 + | +LL | f.max(3f32).min(1f32); + | ^^^^^^^^^^^^^^^^^^^^^ + +error: this `min`/`max` combination leads to constant result + --> $DIR/min_max.rs:55:5 | LL | max(x.min(1), 3); | ^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:43:5 + --> $DIR/min_max.rs:58:5 | LL | s.max("Zoo").min("Apple"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> $DIR/min_max.rs:44:5 + --> $DIR/min_max.rs:59:5 | LL | s.min("Apple").max("Zoo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 12 previous errors +error: aborting due to 13 previous errors -- cgit 1.4.1-3-g733a5 From 888657e09a2133fc105382136f61915086144e3f Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sat, 8 Aug 2020 18:13:43 +0200 Subject: Fix ICE in `loops` module --- clippy_lints/src/loops.rs | 20 ++++++++++---------- tests/ui/crashes/ice-5872.rs | 5 +++++ tests/ui/crashes/ice-5872.stderr | 10 ++++++++++ tests/ui/needless_collect.fixed | 4 ++-- tests/ui/needless_collect.rs | 2 +- tests/ui/needless_collect.stderr | 4 ++-- 6 files changed, 30 insertions(+), 15 deletions(-) create mode 100644 tests/ui/crashes/ice-5872.rs create mode 100644 tests/ui/crashes/ice-5872.stderr (limited to 'tests') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 6359c20040c..1729fea7bc8 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2374,7 +2374,7 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont match_type(cx, ty, &paths::BTREEMAP) || is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) { if method.ident.name == sym!(len) { - let span = shorten_span(expr, sym!(collect)); + let span = shorten_needless_collect_span(expr); span_lint_and_sugg( cx, NEEDLESS_COLLECT, @@ -2386,20 +2386,20 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont ); } if method.ident.name == sym!(is_empty) { - let span = shorten_span(expr, sym!(iter)); + let span = shorten_needless_collect_span(expr); span_lint_and_sugg( cx, NEEDLESS_COLLECT, span, NEEDLESS_COLLECT_MSG, "replace with", - "get(0).is_none()".to_string(), + "next().is_none()".to_string(), Applicability::MachineApplicable, ); } if method.ident.name == sym!(contains) { let contains_arg = snippet(cx, args[1].span, "??"); - let span = shorten_span(expr, sym!(collect)); + let span = shorten_needless_collect_span(expr); span_lint_and_then( cx, NEEDLESS_COLLECT, @@ -2579,13 +2579,13 @@ fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, identifier: Ident) } } -fn shorten_span(expr: &Expr<'_>, target_fn_name: Symbol) -> Span { - let mut current_expr = expr; - while let ExprKind::MethodCall(ref path, ref span, ref args, _) = current_expr.kind { - if path.ident.name == target_fn_name { +fn shorten_needless_collect_span(expr: &Expr<'_>) -> Span { + if_chain! { + if let ExprKind::MethodCall(.., args, _) = &expr.kind; + if let ExprKind::MethodCall(_, span, ..) = &args[0].kind; + then { return expr.span.with_lo(span.lo()); } - current_expr = &args[0]; } - unreachable!() + unreachable!(); } diff --git a/tests/ui/crashes/ice-5872.rs b/tests/ui/crashes/ice-5872.rs new file mode 100644 index 00000000000..68afa8f8c3a --- /dev/null +++ b/tests/ui/crashes/ice-5872.rs @@ -0,0 +1,5 @@ +#![warn(clippy::needless_collect)] + +fn main() { + let _ = vec![1, 2, 3].into_iter().collect::>().is_empty(); +} diff --git a/tests/ui/crashes/ice-5872.stderr b/tests/ui/crashes/ice-5872.stderr new file mode 100644 index 00000000000..a60ca345cf7 --- /dev/null +++ b/tests/ui/crashes/ice-5872.stderr @@ -0,0 +1,10 @@ +error: avoid using `collect()` when not needed + --> $DIR/ice-5872.rs:4:39 + | +LL | let _ = vec![1, 2, 3].into_iter().collect::>().is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` + | + = note: `-D clippy::needless-collect` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/needless_collect.fixed b/tests/ui/needless_collect.fixed index be37dc16b9a..7f2fcf02f6b 100644 --- a/tests/ui/needless_collect.fixed +++ b/tests/ui/needless_collect.fixed @@ -5,11 +5,11 @@ use std::collections::{BTreeSet, HashMap, HashSet}; #[warn(clippy::needless_collect)] -#[allow(unused_variables, clippy::iter_cloned_collect)] +#[allow(unused_variables, clippy::iter_cloned_collect, clippy::iter_next_slice)] fn main() { let sample = [1; 5]; let len = sample.iter().count(); - if sample.get(0).is_none() { + if sample.iter().next().is_none() { // Empty } sample.iter().cloned().any(|x| x == 1); diff --git a/tests/ui/needless_collect.rs b/tests/ui/needless_collect.rs index 7ee603afeb0..788a9eb3264 100644 --- a/tests/ui/needless_collect.rs +++ b/tests/ui/needless_collect.rs @@ -5,7 +5,7 @@ use std::collections::{BTreeSet, HashMap, HashSet}; #[warn(clippy::needless_collect)] -#[allow(unused_variables, clippy::iter_cloned_collect)] +#[allow(unused_variables, clippy::iter_cloned_collect, clippy::iter_next_slice)] fn main() { let sample = [1; 5]; let len = sample.iter().collect::>().len(); diff --git a/tests/ui/needless_collect.stderr b/tests/ui/needless_collect.stderr index 9113aad90dd..2a9539d5975 100644 --- a/tests/ui/needless_collect.stderr +++ b/tests/ui/needless_collect.stderr @@ -7,10 +7,10 @@ LL | let len = sample.iter().collect::>().len(); = note: `-D clippy::needless-collect` implied by `-D warnings` error: avoid using `collect()` when not needed - --> $DIR/needless_collect.rs:12:15 + --> $DIR/needless_collect.rs:12:22 | LL | if sample.iter().collect::>().is_empty() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `get(0).is_none()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` error: avoid using `collect()` when not needed --> $DIR/needless_collect.rs:15:28 -- cgit 1.4.1-3-g733a5 From a77e881ec9f324cdc544150f897d8b34281f92e4 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Wed, 17 Jun 2020 01:16:34 +0200 Subject: should_impl_trait - ignore methods with lifetime params --- clippy_lints/src/methods/mod.rs | 11 ++++++++++- tests/ui/methods.rs | 5 +++++ tests/ui/methods.stderr | 26 +++++++++++++------------- 3 files changed, 28 insertions(+), 14 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 570ae66d595..3009aa3a64e 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1497,11 +1497,20 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if cx.access_levels.is_exported(impl_item.hir_id) { // check missing trait implementations for &(method_name, n_args, fn_header, self_kind, out_type, trait_name) in &TRAIT_METHODS { + let no_lifetime_params = || { + impl_item.generics.params.iter().filter(|p| match p.kind { + hir::GenericParamKind::Lifetime { .. } => true, + _ => false, + }).count() == 0 + }; if name == method_name && sig.decl.inputs.len() == n_args && out_type.matches(cx, &sig.decl.output) && self_kind.matches(cx, self_ty, first_arg_ty) && - fn_header_equals(*fn_header, sig.header) { + fn_header_equals(*fn_header, sig.header) && + // ignore methods with lifetime params, risk of false positive + no_lifetime_params() + { span_lint(cx, SHOULD_IMPLEMENT_TRAIT, impl_item.span, &format!( "defining a method called `{}` on this type; consider implementing \ the `{}` trait or choosing a less ambiguous name", name, trait_name)); diff --git a/tests/ui/methods.rs b/tests/ui/methods.rs index 7880cf36415..3b267b0dab2 100644 --- a/tests/ui/methods.rs +++ b/tests/ui/methods.rs @@ -10,6 +10,7 @@ clippy::non_ascii_literal, clippy::new_without_default, clippy::needless_pass_by_value, + clippy::needless_lifetimes, clippy::print_stdout, clippy::must_use_candidate, clippy::use_self, @@ -82,6 +83,10 @@ impl T { fn new(self) -> Self { unimplemented!(); } + + pub fn next<'b>(&'b mut self) -> Option<&'b mut T> { + unimplemented!(); + } } pub struct T1; diff --git a/tests/ui/methods.stderr b/tests/ui/methods.stderr index 01cf487ac14..9b8ecaed692 100644 --- a/tests/ui/methods.stderr +++ b/tests/ui/methods.stderr @@ -1,5 +1,5 @@ error: defining a method called `add` on this type; consider implementing the `std::ops::Add` trait or choosing a less ambiguous name - --> $DIR/methods.rs:39:5 + --> $DIR/methods.rs:40:5 | LL | / pub fn add(self, other: T) -> T { LL | | self @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::should-implement-trait` implied by `-D warnings` error: methods called `new` usually return `Self` - --> $DIR/methods.rs:169:5 + --> $DIR/methods.rs:174:5 | LL | / fn new() -> i32 { LL | | 0 @@ -19,7 +19,7 @@ LL | | } = note: `-D clippy::new-ret-no-self` implied by `-D warnings` error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead. - --> $DIR/methods.rs:188:13 + --> $DIR/methods.rs:193:13 | LL | let _ = v.iter().filter(|&x| *x < 0).next(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -28,7 +28,7 @@ LL | let _ = v.iter().filter(|&x| *x < 0).next(); = note: replace `filter(|&x| *x < 0).next()` with `find(|&x| *x < 0)` error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead. - --> $DIR/methods.rs:191:13 + --> $DIR/methods.rs:196:13 | LL | let _ = v.iter().filter(|&x| { | _____________^ @@ -38,7 +38,7 @@ LL | | ).next(); | |___________________________^ error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:208:22 + --> $DIR/methods.rs:213:22 | LL | let _ = v.iter().find(|&x| *x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x < 0)` @@ -46,25 +46,25 @@ LL | let _ = v.iter().find(|&x| *x < 0).is_some(); = note: `-D clippy::search-is-some` implied by `-D warnings` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:209:20 + --> $DIR/methods.rs:214:20 | LL | let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| **y == x)` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:210:20 + --> $DIR/methods.rs:215:20 | LL | let _ = (0..1).find(|x| *x == 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| x == 0)` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:211:22 + --> $DIR/methods.rs:216:22 | LL | let _ = v.iter().find(|x| **x == 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x == 0)` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:214:13 + --> $DIR/methods.rs:219:13 | LL | let _ = v.iter().find(|&x| { | _____________^ @@ -74,13 +74,13 @@ LL | | ).is_some(); | |______________________________^ error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:220:22 + --> $DIR/methods.rs:225:22 | LL | let _ = v.iter().position(|&x| x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:223:13 + --> $DIR/methods.rs:228:13 | LL | let _ = v.iter().position(|&x| { | _____________^ @@ -90,13 +90,13 @@ LL | | ).is_some(); | |______________________________^ error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:229:22 + --> $DIR/methods.rs:234:22 | LL | let _ = v.iter().rposition(|&x| x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:232:13 + --> $DIR/methods.rs:237:13 | LL | let _ = v.iter().rposition(|&x| { | _____________^ -- cgit 1.4.1-3-g733a5 From 2bc0ecd44b4d09476eade641e02451d949a1c8e2 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Fri, 19 Jun 2020 22:12:51 +0200 Subject: should_implement_trait - add test cases for every checked trait method --- clippy_lints/src/methods/mod.rs | 1 + tests/ui/methods.rs | 146 ++++++++++++++++++++-- tests/ui/methods.stderr | 264 +++++++++++++++++++++++++++++++++++++--- 3 files changed, 386 insertions(+), 25 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 3009aa3a64e..c225a3bd359 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3424,6 +3424,7 @@ const TRAIT_METHODS: [(&str, usize, &hir::FnHeader, SelfKind, OutType, &str); 30 ("borrow_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::borrow::BorrowMut"), ("clone", 1, &FN_HEADER, SelfKind::Ref, OutType::Any, "std::clone::Clone"), ("cmp", 2, &FN_HEADER, SelfKind::Ref, OutType::Any, "std::cmp::Ord"), + // FIXME: default doesn't work ("default", 0, &FN_HEADER, SelfKind::No, OutType::Any, "std::default::Default"), ("deref", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::ops::Deref"), ("deref_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::ops::DerefMut"), diff --git a/tests/ui/methods.rs b/tests/ui/methods.rs index 3b267b0dab2..adf81607440 100644 --- a/tests/ui/methods.rs +++ b/tests/ui/methods.rs @@ -37,9 +37,136 @@ use option_helpers::IteratorFalsePositives; pub struct T; impl T { + // ******************************************* + // complete trait method list, should lint all + // ******************************************* pub fn add(self, other: T) -> T { - self + unimplemented!() + } + + pub fn as_mut(&mut self) -> &mut T { + unimplemented!() + } + + pub fn as_ref(&self) -> &T { + unimplemented!() + } + + pub fn bitand(self, rhs: T) -> T { + unimplemented!() + } + + pub fn bitor(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn bitxor(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn borrow(&self) -> &str { + unimplemented!() + } + + pub fn borrow_mut(&mut self) -> &mut str { + unimplemented!() + } + + pub fn clone(&self) -> Self { + unimplemented!() + } + + pub fn cmp(&self, other: &Self) -> Self { + unimplemented!() + } + + pub fn default() -> Self { + unimplemented!() + } + + pub fn deref(&self) -> &Self { + unimplemented!() + } + + pub fn deref_mut(&mut self) -> &mut Self { + unimplemented!() + } + + pub fn div(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn drop(&mut self) { + unimplemented!() + } + + pub fn eq(&self, other: &Self) -> bool { + unimplemented!() + } + + pub fn from_iter(iter: T) -> Self { + unimplemented!() + } + + pub fn from_str(s: &str) -> Result { + unimplemented!() + } + + pub fn hash(&self, state: &mut T) { + unimplemented!() + } + + pub fn index(&self, index: usize) -> &Self { + unimplemented!() + } + + pub fn index_mut(&mut self, index: usize) -> &mut Self { + unimplemented!() + } + + pub fn into_iter(self) -> Self { + unimplemented!() + } + + pub fn mul(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn neg(self) -> Self { + unimplemented!() + } + + pub fn next(&mut self) -> Option { + unimplemented!() + } + + pub fn not(self) -> Self { + unimplemented!() + } + + pub fn rem(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn shl(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn shr(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn sub(self, rhs: Self) -> Self { + unimplemented!() } + // ***************** + // complete list end + // ***************** +} + +pub struct T1; +impl T1 { + // corner cases: should not lint // no error, not public interface pub(crate) fn drop(&mut self) {} @@ -50,22 +177,22 @@ impl T { } // no error, private function - fn eq(&self, other: T) -> bool { + fn eq(&self, other: Self) -> bool { true } // No error; self is a ref. - fn sub(&self, other: T) -> &T { + fn sub(&self, other: Self) -> &Self { self } // No error; different number of arguments. - fn div(self) -> T { + fn div(self) -> Self { self } // No error; wrong return type. - fn rem(self, other: T) {} + fn rem(self, other: Self) {} // Fine fn into_u32(self) -> u32 { @@ -89,16 +216,15 @@ impl T { } } -pub struct T1; - -impl T1 { +pub struct T2; +impl T2 { // Shouldn't trigger lint as it is unsafe. - pub unsafe fn add(self, rhs: T1) -> T1 { + pub unsafe fn add(self, rhs: Self) -> Self { self } // Should not trigger lint since this is an async function. - pub async fn next(&mut self) -> Option { + pub async fn next(&mut self) -> Option { None } } diff --git a/tests/ui/methods.stderr b/tests/ui/methods.stderr index 9b8ecaed692..5105fff8f5b 100644 --- a/tests/ui/methods.stderr +++ b/tests/ui/methods.stderr @@ -1,15 +1,249 @@ error: defining a method called `add` on this type; consider implementing the `std::ops::Add` trait or choosing a less ambiguous name - --> $DIR/methods.rs:40:5 + --> $DIR/methods.rs:43:5 | LL | / pub fn add(self, other: T) -> T { -LL | | self +LL | | unimplemented!() LL | | } | |_____^ | = note: `-D clippy::should-implement-trait` implied by `-D warnings` +error: defining a method called `as_mut` on this type; consider implementing the `std::convert::AsMut` trait or choosing a less ambiguous name + --> $DIR/methods.rs:47:5 + | +LL | / pub fn as_mut(&mut self) -> &mut T { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `as_ref` on this type; consider implementing the `std::convert::AsRef` trait or choosing a less ambiguous name + --> $DIR/methods.rs:51:5 + | +LL | / pub fn as_ref(&self) -> &T { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `bitand` on this type; consider implementing the `std::ops::BitAnd` trait or choosing a less ambiguous name + --> $DIR/methods.rs:55:5 + | +LL | / pub fn bitand(self, rhs: T) -> T { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `bitor` on this type; consider implementing the `std::ops::BitOr` trait or choosing a less ambiguous name + --> $DIR/methods.rs:59:5 + | +LL | / pub fn bitor(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `bitxor` on this type; consider implementing the `std::ops::BitXor` trait or choosing a less ambiguous name + --> $DIR/methods.rs:63:5 + | +LL | / pub fn bitxor(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `borrow` on this type; consider implementing the `std::borrow::Borrow` trait or choosing a less ambiguous name + --> $DIR/methods.rs:67:5 + | +LL | / pub fn borrow(&self) -> &str { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `borrow_mut` on this type; consider implementing the `std::borrow::BorrowMut` trait or choosing a less ambiguous name + --> $DIR/methods.rs:71:5 + | +LL | / pub fn borrow_mut(&mut self) -> &mut str { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `clone` on this type; consider implementing the `std::clone::Clone` trait or choosing a less ambiguous name + --> $DIR/methods.rs:75:5 + | +LL | / pub fn clone(&self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `cmp` on this type; consider implementing the `std::cmp::Ord` trait or choosing a less ambiguous name + --> $DIR/methods.rs:79:5 + | +LL | / pub fn cmp(&self, other: &Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `deref` on this type; consider implementing the `std::ops::Deref` trait or choosing a less ambiguous name + --> $DIR/methods.rs:87:5 + | +LL | / pub fn deref(&self) -> &Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `deref_mut` on this type; consider implementing the `std::ops::DerefMut` trait or choosing a less ambiguous name + --> $DIR/methods.rs:91:5 + | +LL | / pub fn deref_mut(&mut self) -> &mut Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `div` on this type; consider implementing the `std::ops::Div` trait or choosing a less ambiguous name + --> $DIR/methods.rs:95:5 + | +LL | / pub fn div(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `drop` on this type; consider implementing the `std::ops::Drop` trait or choosing a less ambiguous name + --> $DIR/methods.rs:99:5 + | +LL | / pub fn drop(&mut self) { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `eq` on this type; consider implementing the `std::cmp::PartialEq` trait or choosing a less ambiguous name + --> $DIR/methods.rs:103:5 + | +LL | / pub fn eq(&self, other: &Self) -> bool { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `from_iter` on this type; consider implementing the `std::iter::FromIterator` trait or choosing a less ambiguous name + --> $DIR/methods.rs:107:5 + | +LL | / pub fn from_iter(iter: T) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `from_str` on this type; consider implementing the `std::str::FromStr` trait or choosing a less ambiguous name + --> $DIR/methods.rs:111:5 + | +LL | / pub fn from_str(s: &str) -> Result { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: docs for function returning `Result` missing `# Errors` section + --> $DIR/methods.rs:111:5 + | +LL | / pub fn from_str(s: &str) -> Result { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = note: `-D clippy::missing-errors-doc` implied by `-D warnings` + +error: defining a method called `hash` on this type; consider implementing the `std::hash::Hash` trait or choosing a less ambiguous name + --> $DIR/methods.rs:115:5 + | +LL | / pub fn hash(&self, state: &mut T) { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `index` on this type; consider implementing the `std::ops::Index` trait or choosing a less ambiguous name + --> $DIR/methods.rs:119:5 + | +LL | / pub fn index(&self, index: usize) -> &Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `index_mut` on this type; consider implementing the `std::ops::IndexMut` trait or choosing a less ambiguous name + --> $DIR/methods.rs:123:5 + | +LL | / pub fn index_mut(&mut self, index: usize) -> &mut Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `into_iter` on this type; consider implementing the `std::iter::IntoIterator` trait or choosing a less ambiguous name + --> $DIR/methods.rs:127:5 + | +LL | / pub fn into_iter(self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `mul` on this type; consider implementing the `std::ops::Mul` trait or choosing a less ambiguous name + --> $DIR/methods.rs:131:5 + | +LL | / pub fn mul(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `neg` on this type; consider implementing the `std::ops::Neg` trait or choosing a less ambiguous name + --> $DIR/methods.rs:135:5 + | +LL | / pub fn neg(self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `next` on this type; consider implementing the `std::iter::Iterator` trait or choosing a less ambiguous name + --> $DIR/methods.rs:139:5 + | +LL | / pub fn next(&mut self) -> Option { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `not` on this type; consider implementing the `std::ops::Not` trait or choosing a less ambiguous name + --> $DIR/methods.rs:143:5 + | +LL | / pub fn not(self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `rem` on this type; consider implementing the `std::ops::Rem` trait or choosing a less ambiguous name + --> $DIR/methods.rs:147:5 + | +LL | / pub fn rem(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `shl` on this type; consider implementing the `std::ops::Shl` trait or choosing a less ambiguous name + --> $DIR/methods.rs:151:5 + | +LL | / pub fn shl(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `shr` on this type; consider implementing the `std::ops::Shr` trait or choosing a less ambiguous name + --> $DIR/methods.rs:155:5 + | +LL | / pub fn shr(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + +error: defining a method called `sub` on this type; consider implementing the `std::ops::Sub` trait or choosing a less ambiguous name + --> $DIR/methods.rs:159:5 + | +LL | / pub fn sub(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + error: methods called `new` usually return `Self` - --> $DIR/methods.rs:174:5 + --> $DIR/methods.rs:300:5 | LL | / fn new() -> i32 { LL | | 0 @@ -19,7 +253,7 @@ LL | | } = note: `-D clippy::new-ret-no-self` implied by `-D warnings` error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead. - --> $DIR/methods.rs:193:13 + --> $DIR/methods.rs:319:13 | LL | let _ = v.iter().filter(|&x| *x < 0).next(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -28,7 +262,7 @@ LL | let _ = v.iter().filter(|&x| *x < 0).next(); = note: replace `filter(|&x| *x < 0).next()` with `find(|&x| *x < 0)` error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead. - --> $DIR/methods.rs:196:13 + --> $DIR/methods.rs:322:13 | LL | let _ = v.iter().filter(|&x| { | _____________^ @@ -38,7 +272,7 @@ LL | | ).next(); | |___________________________^ error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:213:22 + --> $DIR/methods.rs:339:22 | LL | let _ = v.iter().find(|&x| *x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x < 0)` @@ -46,25 +280,25 @@ LL | let _ = v.iter().find(|&x| *x < 0).is_some(); = note: `-D clippy::search-is-some` implied by `-D warnings` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:214:20 + --> $DIR/methods.rs:340:20 | LL | let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| **y == x)` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:215:20 + --> $DIR/methods.rs:341:20 | LL | let _ = (0..1).find(|x| *x == 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| x == 0)` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:216:22 + --> $DIR/methods.rs:342:22 | LL | let _ = v.iter().find(|x| **x == 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x == 0)` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:219:13 + --> $DIR/methods.rs:345:13 | LL | let _ = v.iter().find(|&x| { | _____________^ @@ -74,13 +308,13 @@ LL | | ).is_some(); | |______________________________^ error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:225:22 + --> $DIR/methods.rs:351:22 | LL | let _ = v.iter().position(|&x| x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:228:13 + --> $DIR/methods.rs:354:13 | LL | let _ = v.iter().position(|&x| { | _____________^ @@ -90,13 +324,13 @@ LL | | ).is_some(); | |______________________________^ error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:234:22 + --> $DIR/methods.rs:360:22 | LL | let _ = v.iter().rposition(|&x| x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:237:13 + --> $DIR/methods.rs:363:13 | LL | let _ = v.iter().rposition(|&x| { | _____________^ @@ -105,5 +339,5 @@ LL | | } LL | | ).is_some(); | |______________________________^ -error: aborting due to 13 previous errors +error: aborting due to 42 previous errors -- cgit 1.4.1-3-g733a5 From 166c520e9a8b1a45819255e75dee737136aa6ec8 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Thu, 30 Jul 2020 01:41:12 +0200 Subject: should_impl_trait - pr comments --- clippy_lints/src/methods/mod.rs | 150 ++++++++----- tests/ui/methods.rs | 197 +---------------- tests/ui/methods.stderr | 270 ++---------------------- tests/ui/should_impl_trait/corner_cases.rs | 83 ++++++++ tests/ui/should_impl_trait/method_list_1.rs | 87 ++++++++ tests/ui/should_impl_trait/method_list_1.stderr | 143 +++++++++++++ tests/ui/should_impl_trait/method_list_2.rs | 88 ++++++++ tests/ui/should_impl_trait/method_list_2.stderr | 153 ++++++++++++++ 8 files changed, 670 insertions(+), 501 deletions(-) create mode 100644 tests/ui/should_impl_trait/corner_cases.rs create mode 100644 tests/ui/should_impl_trait/method_list_1.rs create mode 100644 tests/ui/should_impl_trait/method_list_1.stderr create mode 100644 tests/ui/should_impl_trait/method_list_2.rs create mode 100644 tests/ui/should_impl_trait/method_list_2.stderr (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index bad1bb7224e..a549c3b78ef 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1495,25 +1495,31 @@ impl<'tcx> LateLintPass<'tcx> for Methods { then { if cx.access_levels.is_exported(impl_item.hir_id) { - // check missing trait implementations - for &(method_name, n_args, fn_header, self_kind, out_type, trait_name) in &TRAIT_METHODS { - let no_lifetime_params = || { - !impl_item.generics.params.iter() - .any(|p| matches!( - p.kind, - hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit })) - }; - if name == method_name && - sig.decl.inputs.len() == n_args && - out_type.matches(cx, &sig.decl.output) && - self_kind.matches(cx, self_ty, first_arg_ty) && - fn_header_equals(*fn_header, sig.header) && - // ignore methods with lifetime params, risk of false positive - no_lifetime_params() + // check missing trait implementations + for method_config in &TRAIT_METHODS { + if name == method_config.method_name && + sig.decl.inputs.len() == method_config.param_count && + method_config.output_type.matches(cx, &sig.decl.output) && + method_config.self_kind.matches(cx, self_ty, first_arg_ty) && + fn_header_equals(*method_config.fn_header, sig.header) && + method_config.lifetime_param_cond(&impl_item) { - span_lint(cx, SHOULD_IMPLEMENT_TRAIT, impl_item.span, &format!( - "defining a method called `{}` on this type; consider implementing \ - the `{}` trait or choosing a less ambiguous name", name, trait_name)); + span_lint_and_help( + cx, + SHOULD_IMPLEMENT_TRAIT, + impl_item.span, + &format!( + "method `{}` can be confused for the standard trait method `{}::{}`", + method_config.method_name, + method_config.trait_name, + method_config.method_name + ), + None, + &format!( + "consider implementing the trait `{}` or choosing a less ambiguous method name", + method_config.trait_name + ) + ); } } } @@ -3412,39 +3418,85 @@ const FN_HEADER: hir::FnHeader = hir::FnHeader { abi: rustc_target::spec::abi::Abi::Rust, }; +struct ShouldImplTraitCase { + trait_name: &'static str, + method_name: &'static str, + param_count: usize, + fn_header: &'static hir::FnHeader, + // implicit self kind expected (none, self, &self, ...) + self_kind: SelfKind, + // checks against the output type + output_type: OutType, + // certain methods with explicit lifetimes can't implement the equivalent trait method + lint_explicit_lifetime: bool, +} +impl ShouldImplTraitCase { + const fn new( + trait_name: &'static str, + method_name: &'static str, + param_count: usize, + fn_header: &'static hir::FnHeader, + self_kind: SelfKind, + output_type: OutType, + lint_explicit_lifetime: bool, + ) -> ShouldImplTraitCase { + ShouldImplTraitCase { + trait_name, + method_name, + param_count, + fn_header, + self_kind, + output_type, + lint_explicit_lifetime, + } + } + + fn lifetime_param_cond(&self, impl_item: &hir::ImplItem<'_>) -> bool { + self.lint_explicit_lifetime + || !impl_item.generics.params.iter().any(|p| { + matches!( + p.kind, + hir::GenericParamKind::Lifetime { + kind: hir::LifetimeParamKind::Explicit + } + ) + }) + } +} + #[rustfmt::skip] -const TRAIT_METHODS: [(&str, usize, &hir::FnHeader, SelfKind, OutType, &str); 30] = [ - ("add", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Add"), - ("as_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::convert::AsMut"), - ("as_ref", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::convert::AsRef"), - ("bitand", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::BitAnd"), - ("bitor", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::BitOr"), - ("bitxor", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::BitXor"), - ("borrow", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::borrow::Borrow"), - ("borrow_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::borrow::BorrowMut"), - ("clone", 1, &FN_HEADER, SelfKind::Ref, OutType::Any, "std::clone::Clone"), - ("cmp", 2, &FN_HEADER, SelfKind::Ref, OutType::Any, "std::cmp::Ord"), +const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [ + ShouldImplTraitCase::new("std::ops::Add", "add", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::convert::AsMut", "as_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, true), + ShouldImplTraitCase::new("std::convert::AsRef", "as_ref", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, true), + ShouldImplTraitCase::new("std::ops::BitAnd", "bitand", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::BitOr", "bitor", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::BitXor", "bitxor", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::borrow::Borrow", "borrow", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, true), + ShouldImplTraitCase::new("std::borrow::BorrowMut", "borrow_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, true), + ShouldImplTraitCase::new("std::clone::Clone", "clone", 1, &FN_HEADER, SelfKind::Ref, OutType::Any, true), + ShouldImplTraitCase::new("std::cmp::Ord", "cmp", 2, &FN_HEADER, SelfKind::Ref, OutType::Any, true), // FIXME: default doesn't work - ("default", 0, &FN_HEADER, SelfKind::No, OutType::Any, "std::default::Default"), - ("deref", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::ops::Deref"), - ("deref_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::ops::DerefMut"), - ("div", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Div"), - ("drop", 1, &FN_HEADER, SelfKind::RefMut, OutType::Unit, "std::ops::Drop"), - ("eq", 2, &FN_HEADER, SelfKind::Ref, OutType::Bool, "std::cmp::PartialEq"), - ("from_iter", 1, &FN_HEADER, SelfKind::No, OutType::Any, "std::iter::FromIterator"), - ("from_str", 1, &FN_HEADER, SelfKind::No, OutType::Any, "std::str::FromStr"), - ("hash", 2, &FN_HEADER, SelfKind::Ref, OutType::Unit, "std::hash::Hash"), - ("index", 2, &FN_HEADER, SelfKind::Ref, OutType::Ref, "std::ops::Index"), - ("index_mut", 2, &FN_HEADER, SelfKind::RefMut, OutType::Ref, "std::ops::IndexMut"), - ("into_iter", 1, &FN_HEADER, SelfKind::Value, OutType::Any, "std::iter::IntoIterator"), - ("mul", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Mul"), - ("neg", 1, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Neg"), - ("next", 1, &FN_HEADER, SelfKind::RefMut, OutType::Any, "std::iter::Iterator"), - ("not", 1, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Not"), - ("rem", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Rem"), - ("shl", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Shl"), - ("shr", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Shr"), - ("sub", 2, &FN_HEADER, SelfKind::Value, OutType::Any, "std::ops::Sub"), + ShouldImplTraitCase::new("std::default::Default", "default", 0, &FN_HEADER, SelfKind::No, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Deref", "deref", 1, &FN_HEADER, SelfKind::Ref, OutType::Ref, true), + ShouldImplTraitCase::new("std::ops::DerefMut", "deref_mut", 1, &FN_HEADER, SelfKind::RefMut, OutType::Ref, true), + ShouldImplTraitCase::new("std::ops::Div", "div", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Drop", "drop", 1, &FN_HEADER, SelfKind::RefMut, OutType::Unit, true), + ShouldImplTraitCase::new("std::cmp::PartialEq", "eq", 2, &FN_HEADER, SelfKind::Ref, OutType::Bool, true), + ShouldImplTraitCase::new("std::iter::FromIterator", "from_iter", 1, &FN_HEADER, SelfKind::No, OutType::Any, true), + ShouldImplTraitCase::new("std::str::FromStr", "from_str", 1, &FN_HEADER, SelfKind::No, OutType::Any, true), + ShouldImplTraitCase::new("std::hash::Hash", "hash", 2, &FN_HEADER, SelfKind::Ref, OutType::Unit, true), + ShouldImplTraitCase::new("std::ops::Index", "index", 2, &FN_HEADER, SelfKind::Ref, OutType::Ref, true), + ShouldImplTraitCase::new("std::ops::IndexMut", "index_mut", 2, &FN_HEADER, SelfKind::RefMut, OutType::Ref, true), + ShouldImplTraitCase::new("std::iter::IntoIterator", "into_iter", 1, &FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Mul", "mul", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Neg", "neg", 1, &FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::iter::Iterator", "next", 1, &FN_HEADER, SelfKind::RefMut, OutType::Any, false), + ShouldImplTraitCase::new("std::ops::Not", "not", 1, &FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Rem", "rem", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Shl", "shl", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Shr", "shr", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), + ShouldImplTraitCase::new("std::ops::Sub", "sub", 2, &FN_HEADER, SelfKind::Value, OutType::Any, true), ]; #[rustfmt::skip] diff --git a/tests/ui/methods.rs b/tests/ui/methods.rs index adf81607440..80dd2f744b3 100644 --- a/tests/ui/methods.rs +++ b/tests/ui/methods.rs @@ -34,201 +34,6 @@ use std::sync::{self, Arc}; use option_helpers::IteratorFalsePositives; -pub struct T; - -impl T { - // ******************************************* - // complete trait method list, should lint all - // ******************************************* - pub fn add(self, other: T) -> T { - unimplemented!() - } - - pub fn as_mut(&mut self) -> &mut T { - unimplemented!() - } - - pub fn as_ref(&self) -> &T { - unimplemented!() - } - - pub fn bitand(self, rhs: T) -> T { - unimplemented!() - } - - pub fn bitor(self, rhs: Self) -> Self { - unimplemented!() - } - - pub fn bitxor(self, rhs: Self) -> Self { - unimplemented!() - } - - pub fn borrow(&self) -> &str { - unimplemented!() - } - - pub fn borrow_mut(&mut self) -> &mut str { - unimplemented!() - } - - pub fn clone(&self) -> Self { - unimplemented!() - } - - pub fn cmp(&self, other: &Self) -> Self { - unimplemented!() - } - - pub fn default() -> Self { - unimplemented!() - } - - pub fn deref(&self) -> &Self { - unimplemented!() - } - - pub fn deref_mut(&mut self) -> &mut Self { - unimplemented!() - } - - pub fn div(self, rhs: Self) -> Self { - unimplemented!() - } - - pub fn drop(&mut self) { - unimplemented!() - } - - pub fn eq(&self, other: &Self) -> bool { - unimplemented!() - } - - pub fn from_iter(iter: T) -> Self { - unimplemented!() - } - - pub fn from_str(s: &str) -> Result { - unimplemented!() - } - - pub fn hash(&self, state: &mut T) { - unimplemented!() - } - - pub fn index(&self, index: usize) -> &Self { - unimplemented!() - } - - pub fn index_mut(&mut self, index: usize) -> &mut Self { - unimplemented!() - } - - pub fn into_iter(self) -> Self { - unimplemented!() - } - - pub fn mul(self, rhs: Self) -> Self { - unimplemented!() - } - - pub fn neg(self) -> Self { - unimplemented!() - } - - pub fn next(&mut self) -> Option { - unimplemented!() - } - - pub fn not(self) -> Self { - unimplemented!() - } - - pub fn rem(self, rhs: Self) -> Self { - unimplemented!() - } - - pub fn shl(self, rhs: Self) -> Self { - unimplemented!() - } - - pub fn shr(self, rhs: Self) -> Self { - unimplemented!() - } - - pub fn sub(self, rhs: Self) -> Self { - unimplemented!() - } - // ***************** - // complete list end - // ***************** -} - -pub struct T1; -impl T1 { - // corner cases: should not lint - - // no error, not public interface - pub(crate) fn drop(&mut self) {} - - // no error, private function - fn neg(self) -> Self { - self - } - - // no error, private function - fn eq(&self, other: Self) -> bool { - true - } - - // No error; self is a ref. - fn sub(&self, other: Self) -> &Self { - self - } - - // No error; different number of arguments. - fn div(self) -> Self { - self - } - - // No error; wrong return type. - fn rem(self, other: Self) {} - - // Fine - fn into_u32(self) -> u32 { - 0 - } - - fn into_u16(&self) -> u16 { - 0 - } - - fn to_something(self) -> u32 { - 0 - } - - fn new(self) -> Self { - unimplemented!(); - } - - pub fn next<'b>(&'b mut self) -> Option<&'b mut T> { - unimplemented!(); - } -} - -pub struct T2; -impl T2 { - // Shouldn't trigger lint as it is unsafe. - pub unsafe fn add(self, rhs: Self) -> Self { - self - } - - // Should not trigger lint since this is an async function. - pub async fn next(&mut self) -> Option { - None - } -} - struct Lt<'a> { foo: &'a u32, } @@ -302,6 +107,8 @@ impl BadNew { } } +struct T; + impl Mul for T { type Output = T; // No error, obviously. diff --git a/tests/ui/methods.stderr b/tests/ui/methods.stderr index 5105fff8f5b..2a0a43e83a6 100644 --- a/tests/ui/methods.stderr +++ b/tests/ui/methods.stderr @@ -1,249 +1,5 @@ -error: defining a method called `add` on this type; consider implementing the `std::ops::Add` trait or choosing a less ambiguous name - --> $DIR/methods.rs:43:5 - | -LL | / pub fn add(self, other: T) -> T { -LL | | unimplemented!() -LL | | } - | |_____^ - | - = note: `-D clippy::should-implement-trait` implied by `-D warnings` - -error: defining a method called `as_mut` on this type; consider implementing the `std::convert::AsMut` trait or choosing a less ambiguous name - --> $DIR/methods.rs:47:5 - | -LL | / pub fn as_mut(&mut self) -> &mut T { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `as_ref` on this type; consider implementing the `std::convert::AsRef` trait or choosing a less ambiguous name - --> $DIR/methods.rs:51:5 - | -LL | / pub fn as_ref(&self) -> &T { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `bitand` on this type; consider implementing the `std::ops::BitAnd` trait or choosing a less ambiguous name - --> $DIR/methods.rs:55:5 - | -LL | / pub fn bitand(self, rhs: T) -> T { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `bitor` on this type; consider implementing the `std::ops::BitOr` trait or choosing a less ambiguous name - --> $DIR/methods.rs:59:5 - | -LL | / pub fn bitor(self, rhs: Self) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `bitxor` on this type; consider implementing the `std::ops::BitXor` trait or choosing a less ambiguous name - --> $DIR/methods.rs:63:5 - | -LL | / pub fn bitxor(self, rhs: Self) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `borrow` on this type; consider implementing the `std::borrow::Borrow` trait or choosing a less ambiguous name - --> $DIR/methods.rs:67:5 - | -LL | / pub fn borrow(&self) -> &str { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `borrow_mut` on this type; consider implementing the `std::borrow::BorrowMut` trait or choosing a less ambiguous name - --> $DIR/methods.rs:71:5 - | -LL | / pub fn borrow_mut(&mut self) -> &mut str { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `clone` on this type; consider implementing the `std::clone::Clone` trait or choosing a less ambiguous name - --> $DIR/methods.rs:75:5 - | -LL | / pub fn clone(&self) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `cmp` on this type; consider implementing the `std::cmp::Ord` trait or choosing a less ambiguous name - --> $DIR/methods.rs:79:5 - | -LL | / pub fn cmp(&self, other: &Self) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `deref` on this type; consider implementing the `std::ops::Deref` trait or choosing a less ambiguous name - --> $DIR/methods.rs:87:5 - | -LL | / pub fn deref(&self) -> &Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `deref_mut` on this type; consider implementing the `std::ops::DerefMut` trait or choosing a less ambiguous name - --> $DIR/methods.rs:91:5 - | -LL | / pub fn deref_mut(&mut self) -> &mut Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `div` on this type; consider implementing the `std::ops::Div` trait or choosing a less ambiguous name - --> $DIR/methods.rs:95:5 - | -LL | / pub fn div(self, rhs: Self) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `drop` on this type; consider implementing the `std::ops::Drop` trait or choosing a less ambiguous name - --> $DIR/methods.rs:99:5 - | -LL | / pub fn drop(&mut self) { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `eq` on this type; consider implementing the `std::cmp::PartialEq` trait or choosing a less ambiguous name - --> $DIR/methods.rs:103:5 - | -LL | / pub fn eq(&self, other: &Self) -> bool { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `from_iter` on this type; consider implementing the `std::iter::FromIterator` trait or choosing a less ambiguous name - --> $DIR/methods.rs:107:5 - | -LL | / pub fn from_iter(iter: T) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `from_str` on this type; consider implementing the `std::str::FromStr` trait or choosing a less ambiguous name - --> $DIR/methods.rs:111:5 - | -LL | / pub fn from_str(s: &str) -> Result { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: docs for function returning `Result` missing `# Errors` section - --> $DIR/methods.rs:111:5 - | -LL | / pub fn from_str(s: &str) -> Result { -LL | | unimplemented!() -LL | | } - | |_____^ - | - = note: `-D clippy::missing-errors-doc` implied by `-D warnings` - -error: defining a method called `hash` on this type; consider implementing the `std::hash::Hash` trait or choosing a less ambiguous name - --> $DIR/methods.rs:115:5 - | -LL | / pub fn hash(&self, state: &mut T) { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `index` on this type; consider implementing the `std::ops::Index` trait or choosing a less ambiguous name - --> $DIR/methods.rs:119:5 - | -LL | / pub fn index(&self, index: usize) -> &Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `index_mut` on this type; consider implementing the `std::ops::IndexMut` trait or choosing a less ambiguous name - --> $DIR/methods.rs:123:5 - | -LL | / pub fn index_mut(&mut self, index: usize) -> &mut Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `into_iter` on this type; consider implementing the `std::iter::IntoIterator` trait or choosing a less ambiguous name - --> $DIR/methods.rs:127:5 - | -LL | / pub fn into_iter(self) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `mul` on this type; consider implementing the `std::ops::Mul` trait or choosing a less ambiguous name - --> $DIR/methods.rs:131:5 - | -LL | / pub fn mul(self, rhs: Self) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `neg` on this type; consider implementing the `std::ops::Neg` trait or choosing a less ambiguous name - --> $DIR/methods.rs:135:5 - | -LL | / pub fn neg(self) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `next` on this type; consider implementing the `std::iter::Iterator` trait or choosing a less ambiguous name - --> $DIR/methods.rs:139:5 - | -LL | / pub fn next(&mut self) -> Option { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `not` on this type; consider implementing the `std::ops::Not` trait or choosing a less ambiguous name - --> $DIR/methods.rs:143:5 - | -LL | / pub fn not(self) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `rem` on this type; consider implementing the `std::ops::Rem` trait or choosing a less ambiguous name - --> $DIR/methods.rs:147:5 - | -LL | / pub fn rem(self, rhs: Self) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `shl` on this type; consider implementing the `std::ops::Shl` trait or choosing a less ambiguous name - --> $DIR/methods.rs:151:5 - | -LL | / pub fn shl(self, rhs: Self) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `shr` on this type; consider implementing the `std::ops::Shr` trait or choosing a less ambiguous name - --> $DIR/methods.rs:155:5 - | -LL | / pub fn shr(self, rhs: Self) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - -error: defining a method called `sub` on this type; consider implementing the `std::ops::Sub` trait or choosing a less ambiguous name - --> $DIR/methods.rs:159:5 - | -LL | / pub fn sub(self, rhs: Self) -> Self { -LL | | unimplemented!() -LL | | } - | |_____^ - error: methods called `new` usually return `Self` - --> $DIR/methods.rs:300:5 + --> $DIR/methods.rs:105:5 | LL | / fn new() -> i32 { LL | | 0 @@ -253,7 +9,7 @@ LL | | } = note: `-D clippy::new-ret-no-self` implied by `-D warnings` error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead. - --> $DIR/methods.rs:319:13 + --> $DIR/methods.rs:126:13 | LL | let _ = v.iter().filter(|&x| *x < 0).next(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -262,7 +18,7 @@ LL | let _ = v.iter().filter(|&x| *x < 0).next(); = note: replace `filter(|&x| *x < 0).next()` with `find(|&x| *x < 0)` error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead. - --> $DIR/methods.rs:322:13 + --> $DIR/methods.rs:129:13 | LL | let _ = v.iter().filter(|&x| { | _____________^ @@ -272,7 +28,7 @@ LL | | ).next(); | |___________________________^ error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:339:22 + --> $DIR/methods.rs:146:22 | LL | let _ = v.iter().find(|&x| *x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x < 0)` @@ -280,25 +36,25 @@ LL | let _ = v.iter().find(|&x| *x < 0).is_some(); = note: `-D clippy::search-is-some` implied by `-D warnings` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:340:20 + --> $DIR/methods.rs:147:20 | LL | let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| **y == x)` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:341:20 + --> $DIR/methods.rs:148:20 | LL | let _ = (0..1).find(|x| *x == 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| x == 0)` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:342:22 + --> $DIR/methods.rs:149:22 | LL | let _ = v.iter().find(|x| **x == 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x == 0)` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:345:13 + --> $DIR/methods.rs:152:13 | LL | let _ = v.iter().find(|&x| { | _____________^ @@ -308,13 +64,13 @@ LL | | ).is_some(); | |______________________________^ error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:351:22 + --> $DIR/methods.rs:158:22 | LL | let _ = v.iter().position(|&x| x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:354:13 + --> $DIR/methods.rs:161:13 | LL | let _ = v.iter().position(|&x| { | _____________^ @@ -324,13 +80,13 @@ LL | | ).is_some(); | |______________________________^ error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:360:22 + --> $DIR/methods.rs:167:22 | LL | let _ = v.iter().rposition(|&x| x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:363:13 + --> $DIR/methods.rs:170:13 | LL | let _ = v.iter().rposition(|&x| { | _____________^ @@ -339,5 +95,5 @@ LL | | } LL | | ).is_some(); | |______________________________^ -error: aborting due to 42 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/should_impl_trait/corner_cases.rs b/tests/ui/should_impl_trait/corner_cases.rs new file mode 100644 index 00000000000..6c5ffe6aba8 --- /dev/null +++ b/tests/ui/should_impl_trait/corner_cases.rs @@ -0,0 +1,83 @@ +// edition:2018 + +#![warn(clippy::all, clippy::pedantic)] +#![allow( + clippy::missing_errors_doc, + clippy::needless_pass_by_value, + clippy::must_use_candidate, + clippy::unused_self, + clippy::needless_lifetimes, + clippy::missing_safety_doc, + clippy::wrong_self_convention +)] + +use std::ops::Mul; +use std::rc::{self, Rc}; +use std::sync::{self, Arc}; + +fn main() {} + +pub struct T1; +impl T1 { + // corner cases: should not lint + + // no error, not public interface + pub(crate) fn drop(&mut self) {} + + // no error, private function + fn neg(self) -> Self { + self + } + + // no error, private function + fn eq(&self, other: Self) -> bool { + true + } + + // No error; self is a ref. + fn sub(&self, other: Self) -> &Self { + self + } + + // No error; different number of arguments. + fn div(self) -> Self { + self + } + + // No error; wrong return type. + fn rem(self, other: Self) {} + + // Fine + fn into_u32(self) -> u32 { + 0 + } + + fn into_u16(&self) -> u16 { + 0 + } + + fn to_something(self) -> u32 { + 0 + } + + fn new(self) -> Self { + unimplemented!(); + } + + pub fn next<'b>(&'b mut self) -> Option<&'b mut T1> { + unimplemented!(); + } +} + +pub struct T2; +impl T2 { + // Shouldn't trigger lint as it is unsafe. + pub unsafe fn add(self, rhs: Self) -> Self { + self + } + + // Should not trigger lint since this is an async function. + pub async fn next(&mut self) -> Option { + None + } +} diff --git a/tests/ui/should_impl_trait/method_list_1.rs b/tests/ui/should_impl_trait/method_list_1.rs new file mode 100644 index 00000000000..f8d248fc98d --- /dev/null +++ b/tests/ui/should_impl_trait/method_list_1.rs @@ -0,0 +1,87 @@ +// edition:2018 + +#![warn(clippy::all, clippy::pedantic)] +#![allow( + clippy::missing_errors_doc, + clippy::needless_pass_by_value, + clippy::must_use_candidate, + clippy::unused_self, + clippy::needless_lifetimes, + clippy::missing_safety_doc, + clippy::wrong_self_convention +)] + +use std::ops::Mul; +use std::rc::{self, Rc}; +use std::sync::{self, Arc}; + +fn main() {} +pub struct T; + +impl T { + // ***************************************** + // trait method list part 1, should lint all + // ***************************************** + pub fn add(self, other: T) -> T { + unimplemented!() + } + + pub fn as_mut(&mut self) -> &mut T { + unimplemented!() + } + + pub fn as_ref(&self) -> &T { + unimplemented!() + } + + pub fn bitand(self, rhs: T) -> T { + unimplemented!() + } + + pub fn bitor(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn bitxor(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn borrow(&self) -> &str { + unimplemented!() + } + + pub fn borrow_mut(&mut self) -> &mut str { + unimplemented!() + } + + pub fn clone(&self) -> Self { + unimplemented!() + } + + pub fn cmp(&self, other: &Self) -> Self { + unimplemented!() + } + + pub fn default() -> Self { + unimplemented!() + } + + pub fn deref(&self) -> &Self { + unimplemented!() + } + + pub fn deref_mut(&mut self) -> &mut Self { + unimplemented!() + } + + pub fn div(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn drop(&mut self) { + unimplemented!() + } + // ********** + // part 1 end + // ********** +} diff --git a/tests/ui/should_impl_trait/method_list_1.stderr b/tests/ui/should_impl_trait/method_list_1.stderr new file mode 100644 index 00000000000..2b7d4628c3f --- /dev/null +++ b/tests/ui/should_impl_trait/method_list_1.stderr @@ -0,0 +1,143 @@ +error: method `add` can be confused for the standard trait method `std::ops::Add::add` + --> $DIR/method_list_1.rs:25:5 + | +LL | / pub fn add(self, other: T) -> T { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = note: `-D clippy::should-implement-trait` implied by `-D warnings` + = help: consider implementing the trait `std::ops::Add` or choosing a less ambiguous method name + +error: method `as_mut` can be confused for the standard trait method `std::convert::AsMut::as_mut` + --> $DIR/method_list_1.rs:29:5 + | +LL | / pub fn as_mut(&mut self) -> &mut T { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::convert::AsMut` or choosing a less ambiguous method name + +error: method `as_ref` can be confused for the standard trait method `std::convert::AsRef::as_ref` + --> $DIR/method_list_1.rs:33:5 + | +LL | / pub fn as_ref(&self) -> &T { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::convert::AsRef` or choosing a less ambiguous method name + +error: method `bitand` can be confused for the standard trait method `std::ops::BitAnd::bitand` + --> $DIR/method_list_1.rs:37:5 + | +LL | / pub fn bitand(self, rhs: T) -> T { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::BitAnd` or choosing a less ambiguous method name + +error: method `bitor` can be confused for the standard trait method `std::ops::BitOr::bitor` + --> $DIR/method_list_1.rs:41:5 + | +LL | / pub fn bitor(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::BitOr` or choosing a less ambiguous method name + +error: method `bitxor` can be confused for the standard trait method `std::ops::BitXor::bitxor` + --> $DIR/method_list_1.rs:45:5 + | +LL | / pub fn bitxor(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::BitXor` or choosing a less ambiguous method name + +error: method `borrow` can be confused for the standard trait method `std::borrow::Borrow::borrow` + --> $DIR/method_list_1.rs:49:5 + | +LL | / pub fn borrow(&self) -> &str { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::borrow::Borrow` or choosing a less ambiguous method name + +error: method `borrow_mut` can be confused for the standard trait method `std::borrow::BorrowMut::borrow_mut` + --> $DIR/method_list_1.rs:53:5 + | +LL | / pub fn borrow_mut(&mut self) -> &mut str { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::borrow::BorrowMut` or choosing a less ambiguous method name + +error: method `clone` can be confused for the standard trait method `std::clone::Clone::clone` + --> $DIR/method_list_1.rs:57:5 + | +LL | / pub fn clone(&self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::clone::Clone` or choosing a less ambiguous method name + +error: method `cmp` can be confused for the standard trait method `std::cmp::Ord::cmp` + --> $DIR/method_list_1.rs:61:5 + | +LL | / pub fn cmp(&self, other: &Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::cmp::Ord` or choosing a less ambiguous method name + +error: method `deref` can be confused for the standard trait method `std::ops::Deref::deref` + --> $DIR/method_list_1.rs:69:5 + | +LL | / pub fn deref(&self) -> &Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Deref` or choosing a less ambiguous method name + +error: method `deref_mut` can be confused for the standard trait method `std::ops::DerefMut::deref_mut` + --> $DIR/method_list_1.rs:73:5 + | +LL | / pub fn deref_mut(&mut self) -> &mut Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::DerefMut` or choosing a less ambiguous method name + +error: method `div` can be confused for the standard trait method `std::ops::Div::div` + --> $DIR/method_list_1.rs:77:5 + | +LL | / pub fn div(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Div` or choosing a less ambiguous method name + +error: method `drop` can be confused for the standard trait method `std::ops::Drop::drop` + --> $DIR/method_list_1.rs:81:5 + | +LL | / pub fn drop(&mut self) { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Drop` or choosing a less ambiguous method name + +error: aborting due to 14 previous errors + diff --git a/tests/ui/should_impl_trait/method_list_2.rs b/tests/ui/should_impl_trait/method_list_2.rs new file mode 100644 index 00000000000..ed5e0d384bf --- /dev/null +++ b/tests/ui/should_impl_trait/method_list_2.rs @@ -0,0 +1,88 @@ +// edition:2018 + +#![warn(clippy::all, clippy::pedantic)] +#![allow( + clippy::missing_errors_doc, + clippy::needless_pass_by_value, + clippy::must_use_candidate, + clippy::unused_self, + clippy::needless_lifetimes, + clippy::missing_safety_doc, + clippy::wrong_self_convention +)] + +use std::ops::Mul; +use std::rc::{self, Rc}; +use std::sync::{self, Arc}; + +fn main() {} +pub struct T; + +impl T { + // ***************************************** + // trait method list part 2, should lint all + // ***************************************** + + pub fn eq(&self, other: &Self) -> bool { + unimplemented!() + } + + pub fn from_iter(iter: T) -> Self { + unimplemented!() + } + + pub fn from_str(s: &str) -> Result { + unimplemented!() + } + + pub fn hash(&self, state: &mut T) { + unimplemented!() + } + + pub fn index(&self, index: usize) -> &Self { + unimplemented!() + } + + pub fn index_mut(&mut self, index: usize) -> &mut Self { + unimplemented!() + } + + pub fn into_iter(self) -> Self { + unimplemented!() + } + + pub fn mul(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn neg(self) -> Self { + unimplemented!() + } + + pub fn next(&mut self) -> Option { + unimplemented!() + } + + pub fn not(self) -> Self { + unimplemented!() + } + + pub fn rem(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn shl(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn shr(self, rhs: Self) -> Self { + unimplemented!() + } + + pub fn sub(self, rhs: Self) -> Self { + unimplemented!() + } + // ********** + // part 2 end + // ********** +} diff --git a/tests/ui/should_impl_trait/method_list_2.stderr b/tests/ui/should_impl_trait/method_list_2.stderr new file mode 100644 index 00000000000..b6fd4356956 --- /dev/null +++ b/tests/ui/should_impl_trait/method_list_2.stderr @@ -0,0 +1,153 @@ +error: method `eq` can be confused for the standard trait method `std::cmp::PartialEq::eq` + --> $DIR/method_list_2.rs:26:5 + | +LL | / pub fn eq(&self, other: &Self) -> bool { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = note: `-D clippy::should-implement-trait` implied by `-D warnings` + = help: consider implementing the trait `std::cmp::PartialEq` or choosing a less ambiguous method name + +error: method `from_iter` can be confused for the standard trait method `std::iter::FromIterator::from_iter` + --> $DIR/method_list_2.rs:30:5 + | +LL | / pub fn from_iter(iter: T) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::iter::FromIterator` or choosing a less ambiguous method name + +error: method `from_str` can be confused for the standard trait method `std::str::FromStr::from_str` + --> $DIR/method_list_2.rs:34:5 + | +LL | / pub fn from_str(s: &str) -> Result { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::str::FromStr` or choosing a less ambiguous method name + +error: method `hash` can be confused for the standard trait method `std::hash::Hash::hash` + --> $DIR/method_list_2.rs:38:5 + | +LL | / pub fn hash(&self, state: &mut T) { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::hash::Hash` or choosing a less ambiguous method name + +error: method `index` can be confused for the standard trait method `std::ops::Index::index` + --> $DIR/method_list_2.rs:42:5 + | +LL | / pub fn index(&self, index: usize) -> &Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Index` or choosing a less ambiguous method name + +error: method `index_mut` can be confused for the standard trait method `std::ops::IndexMut::index_mut` + --> $DIR/method_list_2.rs:46:5 + | +LL | / pub fn index_mut(&mut self, index: usize) -> &mut Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::IndexMut` or choosing a less ambiguous method name + +error: method `into_iter` can be confused for the standard trait method `std::iter::IntoIterator::into_iter` + --> $DIR/method_list_2.rs:50:5 + | +LL | / pub fn into_iter(self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::iter::IntoIterator` or choosing a less ambiguous method name + +error: method `mul` can be confused for the standard trait method `std::ops::Mul::mul` + --> $DIR/method_list_2.rs:54:5 + | +LL | / pub fn mul(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Mul` or choosing a less ambiguous method name + +error: method `neg` can be confused for the standard trait method `std::ops::Neg::neg` + --> $DIR/method_list_2.rs:58:5 + | +LL | / pub fn neg(self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Neg` or choosing a less ambiguous method name + +error: method `next` can be confused for the standard trait method `std::iter::Iterator::next` + --> $DIR/method_list_2.rs:62:5 + | +LL | / pub fn next(&mut self) -> Option { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::iter::Iterator` or choosing a less ambiguous method name + +error: method `not` can be confused for the standard trait method `std::ops::Not::not` + --> $DIR/method_list_2.rs:66:5 + | +LL | / pub fn not(self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Not` or choosing a less ambiguous method name + +error: method `rem` can be confused for the standard trait method `std::ops::Rem::rem` + --> $DIR/method_list_2.rs:70:5 + | +LL | / pub fn rem(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Rem` or choosing a less ambiguous method name + +error: method `shl` can be confused for the standard trait method `std::ops::Shl::shl` + --> $DIR/method_list_2.rs:74:5 + | +LL | / pub fn shl(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Shl` or choosing a less ambiguous method name + +error: method `shr` can be confused for the standard trait method `std::ops::Shr::shr` + --> $DIR/method_list_2.rs:78:5 + | +LL | / pub fn shr(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Shr` or choosing a less ambiguous method name + +error: method `sub` can be confused for the standard trait method `std::ops::Sub::sub` + --> $DIR/method_list_2.rs:82:5 + | +LL | / pub fn sub(self, rhs: Self) -> Self { +LL | | unimplemented!() +LL | | } + | |_____^ + | + = help: consider implementing the trait `std::ops::Sub` or choosing a less ambiguous method name + +error: aborting due to 15 previous errors + -- cgit 1.4.1-3-g733a5 From e57aafe33f338208e612ecb816a948d28f2c3741 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 23 Jul 2020 15:06:33 +0200 Subject: too-many-lines: make lint adhere to lint message convention --- clippy_lints/src/functions.rs | 2 +- tests/ui-toml/functions_maxlines/test.stderr | 4 ++-- tests/ui/functions_maxlines.stderr | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/functions.rs b/clippy_lints/src/functions.rs index 28b276967bc..ac1c7aa9bbb 100644 --- a/clippy_lints/src/functions.rs +++ b/clippy_lints/src/functions.rs @@ -374,7 +374,7 @@ impl<'tcx> Functions { } if line_count > self.max_lines { - span_lint(cx, TOO_MANY_LINES, span, "This function has a large number of lines.") + span_lint(cx, TOO_MANY_LINES, span, "this function has a large number of lines") } } diff --git a/tests/ui-toml/functions_maxlines/test.stderr b/tests/ui-toml/functions_maxlines/test.stderr index 4b77ac551e7..fb12257021a 100644 --- a/tests/ui-toml/functions_maxlines/test.stderr +++ b/tests/ui-toml/functions_maxlines/test.stderr @@ -1,4 +1,4 @@ -error: This function has a large number of lines. +error: this function has a large number of lines --> $DIR/test.rs:18:1 | LL | / fn too_many_lines() { @@ -9,7 +9,7 @@ LL | | } | = note: `-D clippy::too-many-lines` implied by `-D warnings` -error: This function has a large number of lines. +error: this function has a large number of lines --> $DIR/test.rs:38:1 | LL | / fn comment_before_code() { diff --git a/tests/ui/functions_maxlines.stderr b/tests/ui/functions_maxlines.stderr index 9b0e7550cc3..c640c82d6d7 100644 --- a/tests/ui/functions_maxlines.stderr +++ b/tests/ui/functions_maxlines.stderr @@ -1,4 +1,4 @@ -error: This function has a large number of lines. +error: this function has a large number of lines --> $DIR/functions_maxlines.rs:58:1 | LL | / fn bad_lines() { -- cgit 1.4.1-3-g733a5 From 0876f17d77e8747f4cba889bd29fb64a0dc1a63f Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 23 Jul 2020 15:14:12 +0200 Subject: bool-comparison: make lint adhere to lint message convention --- clippy_lints/src/needless_bool.rs | 2 +- tests/ui/bool_comparison.stderr | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs index 8e44f2ec240..dc5aa669139 100644 --- a/clippy_lints/src/needless_bool.rs +++ b/clippy_lints/src/needless_bool.rs @@ -243,7 +243,7 @@ fn check_comparison<'a, 'tcx>( cx, BOOL_COMPARISON, e.span, - "This comparison might be written more concisely", + "this comparison might be written more concisely", "try simplifying it as shown", format!( "{} != {}", diff --git a/tests/ui/bool_comparison.stderr b/tests/ui/bool_comparison.stderr index eeb1f20ee89..55d94b8257d 100644 --- a/tests/ui/bool_comparison.stderr +++ b/tests/ui/bool_comparison.stderr @@ -84,25 +84,25 @@ error: order comparisons between booleans can be simplified LL | if x > y { | ^^^^^ help: try simplifying it as shown: `x & !y` -error: This comparison might be written more concisely +error: this comparison might be written more concisely --> $DIR/bool_comparison.rs:120:8 | LL | if a == !b {}; | ^^^^^^^ help: try simplifying it as shown: `a != b` -error: This comparison might be written more concisely +error: this comparison might be written more concisely --> $DIR/bool_comparison.rs:121:8 | LL | if !a == b {}; | ^^^^^^^ help: try simplifying it as shown: `a != b` -error: This comparison might be written more concisely +error: this comparison might be written more concisely --> $DIR/bool_comparison.rs:125:8 | LL | if b == !a {}; | ^^^^^^^ help: try simplifying it as shown: `b != a` -error: This comparison might be written more concisely +error: this comparison might be written more concisely --> $DIR/bool_comparison.rs:126:8 | LL | if !b == a {}; -- cgit 1.4.1-3-g733a5 From fd379a889e25c94c0568fe6fc08d0783bcbc3dd7 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 23 Jul 2020 15:16:10 +0200 Subject: builtin-type-shadow: make lint adhere to lint message convention --- clippy_lints/src/misc_early.rs | 2 +- tests/ui/builtin-type-shadow.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/misc_early.rs b/clippy_lints/src/misc_early.rs index 29aba7c1218..38b11c5e733 100644 --- a/clippy_lints/src/misc_early.rs +++ b/clippy_lints/src/misc_early.rs @@ -271,7 +271,7 @@ impl EarlyLintPass for MiscEarlyLints { cx, BUILTIN_TYPE_SHADOW, param.ident.span, - &format!("This generic shadows the built-in type `{}`", name), + &format!("this generic shadows the built-in type `{}`", name), ); } } diff --git a/tests/ui/builtin-type-shadow.stderr b/tests/ui/builtin-type-shadow.stderr index bc785b075e0..f42b246afd2 100644 --- a/tests/ui/builtin-type-shadow.stderr +++ b/tests/ui/builtin-type-shadow.stderr @@ -1,4 +1,4 @@ -error: This generic shadows the built-in type `u32` +error: this generic shadows the built-in type `u32` --> $DIR/builtin-type-shadow.rs:4:8 | LL | fn foo(a: u32) -> u32 { -- cgit 1.4.1-3-g733a5 From 40416c0fa8409da63fb27f065e82cabb51ec17d3 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 23 Jul 2020 15:18:13 +0200 Subject: naive_bytecount: make lint adhere to lint message convention --- clippy_lints/src/bytecount.rs | 4 ++-- tests/ui/bytecount.stderr | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/bytecount.rs b/clippy_lints/src/bytecount.rs index dde799fcae4..cdb49d777d8 100644 --- a/clippy_lints/src/bytecount.rs +++ b/clippy_lints/src/bytecount.rs @@ -82,8 +82,8 @@ impl<'tcx> LateLintPass<'tcx> for ByteCount { cx, NAIVE_BYTECOUNT, expr.span, - "You appear to be counting bytes the naive way", - "Consider using the bytecount crate", + "you appear to be counting bytes the naive way", + "consider using the bytecount crate", format!("bytecount::count({}, {})", snippet_with_applicability(cx, haystack.span, "..", &mut applicability), snippet_with_applicability(cx, needle.span, "..", &mut applicability)), diff --git a/tests/ui/bytecount.stderr b/tests/ui/bytecount.stderr index 436f5d86a06..1dc37fc8b25 100644 --- a/tests/ui/bytecount.stderr +++ b/tests/ui/bytecount.stderr @@ -1,8 +1,8 @@ -error: You appear to be counting bytes the naive way +error: you appear to be counting bytes the naive way --> $DIR/bytecount.rs:5:13 | LL | let _ = x.iter().filter(|&&a| a == 0).count(); // naive byte count - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider using the bytecount crate: `bytecount::count(x, 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the bytecount crate: `bytecount::count(x, 0)` | note: the lint level is defined here --> $DIR/bytecount.rs:1:8 @@ -10,17 +10,17 @@ note: the lint level is defined here LL | #[deny(clippy::naive_bytecount)] | ^^^^^^^^^^^^^^^^^^^^^^^ -error: You appear to be counting bytes the naive way +error: you appear to be counting bytes the naive way --> $DIR/bytecount.rs:7:13 | LL | let _ = (&x[..]).iter().filter(|&a| *a == 0).count(); // naive byte count - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider using the bytecount crate: `bytecount::count((&x[..]), 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the bytecount crate: `bytecount::count((&x[..]), 0)` -error: You appear to be counting bytes the naive way +error: you appear to be counting bytes the naive way --> $DIR/bytecount.rs:19:13 | LL | let _ = x.iter().filter(|a| b + 1 == **a).count(); // naive byte count - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider using the bytecount crate: `bytecount::count(x, b + 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the bytecount crate: `bytecount::count(x, b + 1)` error: aborting due to 3 previous errors -- cgit 1.4.1-3-g733a5 From 5d66bd7bb3fd701d70ec11217e3f89fabe5cb0a7 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Mon, 10 Aug 2020 23:38:58 +0200 Subject: Avoid or_fun_call for const_fn with no args --- clippy_lints/src/utils/mod.rs | 9 +++++++++ tests/ui/or_fun_call.fixed | 8 ++++++++ tests/ui/or_fun_call.rs | 8 ++++++++ 3 files changed, 25 insertions(+) (limited to 'tests') diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 9f967d59c8b..223628cc610 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -43,6 +43,7 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::ty::{self, layout::IntegerExt, subst::GenericArg, Ty, TyCtxt, TypeFoldable}; +use rustc_mir::const_eval; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::original_sp; use rustc_span::symbol::{self, kw, Symbol}; @@ -868,11 +869,19 @@ pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { /// Checks if an expression is constructing a tuple-like enum variant or struct pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + fn has_no_arguments(cx: &LateContext<'_>, def_id: DefId) -> bool { + cx.tcx.fn_sig(def_id).skip_binder().inputs().is_empty() + } + if let ExprKind::Call(ref fun, _) = expr.kind { if let ExprKind::Path(ref qp) = fun.kind { let res = cx.qpath_res(qp, fun.hir_id); return match res { def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true, + // FIXME: check the constness of the arguments, see https://github.com/rust-lang/rust-clippy/pull/5682#issuecomment-638681210 + def::Res::Def(DefKind::Fn, def_id) if has_no_arguments(cx, def_id) => { + const_eval::is_const_fn(cx.tcx, def_id) + }, def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id), _ => false, }; diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 2045ffdb5f0..67faa8bd4a0 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -116,4 +116,12 @@ fn f() -> Option<()> { Some(()) } +// Issue 5886 - const fn (with no arguments) +pub fn skip_const_fn_with_no_args() { + const fn foo() -> Option { + Some(42) + } + let _ = None.or(foo()); +} + fn main() {} diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 522f31b72d0..9867e2eedcf 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -116,4 +116,12 @@ fn f() -> Option<()> { Some(()) } +// Issue 5886 - const fn (with no arguments) +pub fn skip_const_fn_with_no_args() { + const fn foo() -> Option { + Some(42) + } + let _ = None.or(foo()); +} + fn main() {} -- cgit 1.4.1-3-g733a5 From 9b7ab1d38b13ad8af555793cdf7ce08c12c22595 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 23 Jul 2020 16:58:20 +0200 Subject: checked-conversions: make lint adhere to lint message convention --- clippy_lints/src/checked_conversions.rs | 2 +- tests/ui/checked_conversions.stderr | 32 ++++++++++++++++---------------- 2 files changed, 17 insertions(+), 17 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index 841902943f0..28c1a54d2c5 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -66,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for CheckedConversions { cx, CHECKED_CONVERSIONS, item.span, - "Checked cast can be simplified.", + "checked cast can be simplified", "try", format!("{}::try_from({}).is_ok()", to_type, snippet), applicability, diff --git a/tests/ui/checked_conversions.stderr b/tests/ui/checked_conversions.stderr index 648ba3ccd01..18518def0ac 100644 --- a/tests/ui/checked_conversions.stderr +++ b/tests/ui/checked_conversions.stderr @@ -1,4 +1,4 @@ -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:17:13 | LL | let _ = value <= (u32::max_value() as i64) && value >= 0; @@ -6,91 +6,91 @@ LL | let _ = value <= (u32::max_value() as i64) && value >= 0; | = note: `-D clippy::checked-conversions` implied by `-D warnings` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:18:13 | LL | let _ = value <= (u32::MAX as i64) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:22:13 | LL | let _ = value <= i64::from(u16::max_value()) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:23:13 | LL | let _ = value <= i64::from(u16::MAX) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:27:13 | LL | let _ = value <= (u8::max_value() as isize) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:28:13 | LL | let _ = value <= (u8::MAX as isize) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:34:13 | LL | let _ = value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:35:13 | LL | let _ = value <= (i32::MAX as i64) && value >= (i32::MIN as i64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:39:13 | LL | let _ = value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:40:13 | LL | let _ = value <= i64::from(i16::MAX) && value >= i64::from(i16::MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:46:13 | LL | let _ = value <= i32::max_value() as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:47:13 | LL | let _ = value <= i32::MAX as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:51:13 | LL | let _ = value <= isize::max_value() as usize && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:52:13 | LL | let _ = value <= isize::MAX as usize && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:56:13 | LL | let _ = value <= u16::max_value() as u32 && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` -error: Checked cast can be simplified. +error: checked cast can be simplified --> $DIR/checked_conversions.rs:57:13 | LL | let _ = value <= u16::MAX as u32 && value as i32 == 5; -- cgit 1.4.1-3-g733a5 From 8679dd375b928a3a5d8420401ed80057eea6f198 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 23 Jul 2020 17:06:29 +0200 Subject: unnecessary_unwrap, panicking_unwrap: make lints adhere to lint message convention --- clippy_lints/src/unwrap.rs | 6 ++-- .../ui/checked_unwrap/complex_conditionals.stderr | 40 +++++++++++----------- .../complex_conditionals_nested.stderr | 4 +-- tests/ui/checked_unwrap/simple_conditionals.stderr | 26 +++++++------- 4 files changed, 38 insertions(+), 38 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index f2bbde28c2a..fd755dcc790 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -181,8 +181,8 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { self.cx, UNNECESSARY_UNWRAP, expr.span, - &format!("You checked before that `{}()` cannot fail. \ - Instead of checking and unwrapping, it's better to use `if let` or `match`.", + &format!("you checked before that `{}()` cannot fail. \ + Instead of checking and unwrapping, it's better to use `if let` or `match`", method_name.ident.name), |diag| { diag.span_label(unwrappable.check.span, "the check is happening here"); }, ); @@ -191,7 +191,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { self.cx, PANICKING_UNWRAP, expr.span, - &format!("This call to `{}()` will always panic.", + &format!("this call to `{}()` will always panic", method_name.ident.name), |diag| { diag.span_label(unwrappable.check.span, "because of this check"); }, ); diff --git a/tests/ui/checked_unwrap/complex_conditionals.stderr b/tests/ui/checked_unwrap/complex_conditionals.stderr index dc666bab460..5b62dca629f 100644 --- a/tests/ui/checked_unwrap/complex_conditionals.stderr +++ b/tests/ui/checked_unwrap/complex_conditionals.stderr @@ -1,4 +1,4 @@ -error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:8:9 | LL | if x.is_ok() && y.is_err() { @@ -12,7 +12,7 @@ note: the lint level is defined here LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: This call to `unwrap_err()` will always panic. +error: this call to `unwrap_err()` will always panic --> $DIR/complex_conditionals.rs:9:9 | LL | if x.is_ok() && y.is_err() { @@ -27,7 +27,7 @@ note: the lint level is defined here LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/complex_conditionals.rs:10:9 | LL | if x.is_ok() && y.is_err() { @@ -36,7 +36,7 @@ LL | if x.is_ok() && y.is_err() { LL | y.unwrap(); // will panic | ^^^^^^^^^^ -error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:11:9 | LL | if x.is_ok() && y.is_err() { @@ -45,7 +45,7 @@ LL | if x.is_ok() && y.is_err() { LL | y.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/complex_conditionals.rs:25:9 | LL | if x.is_ok() || y.is_ok() { @@ -54,7 +54,7 @@ LL | if x.is_ok() || y.is_ok() { LL | x.unwrap(); // will panic | ^^^^^^^^^^ -error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:26:9 | LL | if x.is_ok() || y.is_ok() { @@ -63,7 +63,7 @@ LL | if x.is_ok() || y.is_ok() { LL | x.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/complex_conditionals.rs:27:9 | LL | if x.is_ok() || y.is_ok() { @@ -72,7 +72,7 @@ LL | if x.is_ok() || y.is_ok() { LL | y.unwrap(); // will panic | ^^^^^^^^^^ -error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:28:9 | LL | if x.is_ok() || y.is_ok() { @@ -81,7 +81,7 @@ LL | if x.is_ok() || y.is_ok() { LL | y.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ -error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:32:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { @@ -89,7 +89,7 @@ LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { LL | x.unwrap(); // unnecessary | ^^^^^^^^^^ -error: This call to `unwrap_err()` will always panic. +error: this call to `unwrap_err()` will always panic --> $DIR/complex_conditionals.rs:33:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { @@ -98,7 +98,7 @@ LL | x.unwrap(); // unnecessary LL | x.unwrap_err(); // will panic | ^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/complex_conditionals.rs:34:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { @@ -107,7 +107,7 @@ LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { LL | y.unwrap(); // will panic | ^^^^^^^^^^ -error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:35:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { @@ -116,7 +116,7 @@ LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { LL | y.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ -error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:36:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { @@ -125,7 +125,7 @@ LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { LL | z.unwrap(); // unnecessary | ^^^^^^^^^^ -error: This call to `unwrap_err()` will always panic. +error: this call to `unwrap_err()` will always panic --> $DIR/complex_conditionals.rs:37:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { @@ -134,7 +134,7 @@ LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { LL | z.unwrap_err(); // will panic | ^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/complex_conditionals.rs:45:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { @@ -143,7 +143,7 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { LL | x.unwrap(); // will panic | ^^^^^^^^^^ -error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:46:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { @@ -152,7 +152,7 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { LL | x.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ -error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:47:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { @@ -161,7 +161,7 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { LL | y.unwrap(); // unnecessary | ^^^^^^^^^^ -error: This call to `unwrap_err()` will always panic. +error: this call to `unwrap_err()` will always panic --> $DIR/complex_conditionals.rs:48:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { @@ -170,7 +170,7 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { LL | y.unwrap_err(); // will panic | ^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/complex_conditionals.rs:49:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { @@ -179,7 +179,7 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { LL | z.unwrap(); // will panic | ^^^^^^^^^^ -error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:50:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { diff --git a/tests/ui/checked_unwrap/complex_conditionals_nested.stderr b/tests/ui/checked_unwrap/complex_conditionals_nested.stderr index e4d085470c3..46ffc16c23e 100644 --- a/tests/ui/checked_unwrap/complex_conditionals_nested.stderr +++ b/tests/ui/checked_unwrap/complex_conditionals_nested.stderr @@ -1,4 +1,4 @@ -error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals_nested.rs:8:13 | LL | if x.is_some() { @@ -12,7 +12,7 @@ note: the lint level is defined here LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/complex_conditionals_nested.rs:10:13 | LL | if x.is_some() { diff --git a/tests/ui/checked_unwrap/simple_conditionals.stderr b/tests/ui/checked_unwrap/simple_conditionals.stderr index 4013d1ed667..bf4b6c93098 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.stderr +++ b/tests/ui/checked_unwrap/simple_conditionals.stderr @@ -1,4 +1,4 @@ -error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:39:9 | LL | if x.is_some() { @@ -12,7 +12,7 @@ note: the lint level is defined here LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/simple_conditionals.rs:41:9 | LL | if x.is_some() { @@ -27,7 +27,7 @@ note: the lint level is defined here LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/simple_conditionals.rs:44:9 | LL | if x.is_none() { @@ -35,7 +35,7 @@ LL | if x.is_none() { LL | x.unwrap(); // will panic | ^^^^^^^^^^ -error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:46:9 | LL | if x.is_none() { @@ -44,7 +44,7 @@ LL | if x.is_none() { LL | x.unwrap(); // unnecessary | ^^^^^^^^^^ -error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:7:13 | LL | if $a.is_some() { @@ -57,7 +57,7 @@ LL | m!(x); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:54:9 | LL | if x.is_ok() { @@ -65,7 +65,7 @@ LL | if x.is_ok() { LL | x.unwrap(); // unnecessary | ^^^^^^^^^^ -error: This call to `unwrap_err()` will always panic. +error: this call to `unwrap_err()` will always panic --> $DIR/simple_conditionals.rs:55:9 | LL | if x.is_ok() { @@ -74,7 +74,7 @@ LL | x.unwrap(); // unnecessary LL | x.unwrap_err(); // will panic | ^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/simple_conditionals.rs:57:9 | LL | if x.is_ok() { @@ -83,7 +83,7 @@ LL | if x.is_ok() { LL | x.unwrap(); // will panic | ^^^^^^^^^^ -error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:58:9 | LL | if x.is_ok() { @@ -92,7 +92,7 @@ LL | if x.is_ok() { LL | x.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ -error: This call to `unwrap()` will always panic. +error: this call to `unwrap()` will always panic --> $DIR/simple_conditionals.rs:61:9 | LL | if x.is_err() { @@ -100,7 +100,7 @@ LL | if x.is_err() { LL | x.unwrap(); // will panic | ^^^^^^^^^^ -error: You checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:62:9 | LL | if x.is_err() { @@ -109,7 +109,7 @@ LL | x.unwrap(); // will panic LL | x.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ -error: You checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match`. +error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:64:9 | LL | if x.is_err() { @@ -118,7 +118,7 @@ LL | if x.is_err() { LL | x.unwrap(); // unnecessary | ^^^^^^^^^^ -error: This call to `unwrap_err()` will always panic. +error: this call to `unwrap_err()` will always panic --> $DIR/simple_conditionals.rs:65:9 | LL | if x.is_err() { -- cgit 1.4.1-3-g733a5 From 3d592b515492c8d054583078163c1986c44e222a Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 23 Jul 2020 19:21:31 +0200 Subject: cmp_null: make lint adhere to lint message convention --- clippy_lints/src/ptr.rs | 2 +- tests/ui/cmp_null.stderr | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 7b6bd69ffca..460d631fab0 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -145,7 +145,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { cx, CMP_NULL, expr.span, - "Comparing with null is better expressed by the `.is_null()` method", + "comparing with null is better expressed by the `.is_null()` method", ); } } diff --git a/tests/ui/cmp_null.stderr b/tests/ui/cmp_null.stderr index b563a2ebec2..a1f4c70fb27 100644 --- a/tests/ui/cmp_null.stderr +++ b/tests/ui/cmp_null.stderr @@ -1,4 +1,4 @@ -error: Comparing with null is better expressed by the `.is_null()` method +error: comparing with null is better expressed by the `.is_null()` method --> $DIR/cmp_null.rs:9:8 | LL | if p == ptr::null() { @@ -6,7 +6,7 @@ LL | if p == ptr::null() { | = note: `-D clippy::cmp-null` implied by `-D warnings` -error: Comparing with null is better expressed by the `.is_null()` method +error: comparing with null is better expressed by the `.is_null()` method --> $DIR/cmp_null.rs:14:8 | LL | if m == ptr::null_mut() { -- cgit 1.4.1-3-g733a5 From 6b0a6a70f8dfa743707c440fdf908b9aceb1b8ae Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 23 Jul 2020 19:39:35 +0200 Subject: default-trait-access: make lint adhere to lint message convention --- clippy_lints/src/default_trait_access.rs | 2 +- tests/ui/default_trait_access.stderr | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/default_trait_access.rs b/clippy_lints/src/default_trait_access.rs index ea244768129..874e19d9e9f 100644 --- a/clippy_lints/src/default_trait_access.rs +++ b/clippy_lints/src/default_trait_access.rs @@ -61,7 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for DefaultTraitAccess { cx, DEFAULT_TRAIT_ACCESS, expr.span, - &format!("Calling `{}` is more clear than this expression", replacement), + &format!("calling `{}` is more clear than this expression", replacement), "try", replacement, Applicability::Unspecified, // First resolve the TODO above diff --git a/tests/ui/default_trait_access.stderr b/tests/ui/default_trait_access.stderr index 453760c6b92..26b2057548b 100644 --- a/tests/ui/default_trait_access.stderr +++ b/tests/ui/default_trait_access.stderr @@ -1,4 +1,4 @@ -error: Calling `std::string::String::default()` is more clear than this expression +error: calling `std::string::String::default()` is more clear than this expression --> $DIR/default_trait_access.rs:8:22 | LL | let s1: String = Default::default(); @@ -6,43 +6,43 @@ LL | let s1: String = Default::default(); | = note: `-D clippy::default-trait-access` implied by `-D warnings` -error: Calling `std::string::String::default()` is more clear than this expression +error: calling `std::string::String::default()` is more clear than this expression --> $DIR/default_trait_access.rs:12:22 | LL | let s3: String = D2::default(); | ^^^^^^^^^^^^^ help: try: `std::string::String::default()` -error: Calling `std::string::String::default()` is more clear than this expression +error: calling `std::string::String::default()` is more clear than this expression --> $DIR/default_trait_access.rs:14:22 | LL | let s4: String = std::default::Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()` -error: Calling `std::string::String::default()` is more clear than this expression +error: calling `std::string::String::default()` is more clear than this expression --> $DIR/default_trait_access.rs:18:22 | LL | let s6: String = default::Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()` -error: Calling `GenericDerivedDefault::default()` is more clear than this expression +error: calling `GenericDerivedDefault::default()` is more clear than this expression --> $DIR/default_trait_access.rs:28:46 | LL | let s11: GenericDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `GenericDerivedDefault::default()` -error: Calling `TupleDerivedDefault::default()` is more clear than this expression +error: calling `TupleDerivedDefault::default()` is more clear than this expression --> $DIR/default_trait_access.rs:34:36 | LL | let s14: TupleDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `TupleDerivedDefault::default()` -error: Calling `ArrayDerivedDefault::default()` is more clear than this expression +error: calling `ArrayDerivedDefault::default()` is more clear than this expression --> $DIR/default_trait_access.rs:36:36 | LL | let s15: ArrayDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `ArrayDerivedDefault::default()` -error: Calling `TupleStructDerivedDefault::default()` is more clear than this expression +error: calling `TupleStructDerivedDefault::default()` is more clear than this expression --> $DIR/default_trait_access.rs:40:42 | LL | let s17: TupleStructDerivedDefault = Default::default(); -- cgit 1.4.1-3-g733a5 From ba7a01a6a8709eaff32f88d47382c940b24a3c9e Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 23 Jul 2020 19:50:28 +0200 Subject: double-comparisons: make lint adhere to lint message convention --- clippy_lints/src/double_comparison.rs | 2 +- tests/ui/double_comparison.stderr | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/double_comparison.rs b/clippy_lints/src/double_comparison.rs index 5d16192b754..bae7c4647d4 100644 --- a/clippy_lints/src/double_comparison.rs +++ b/clippy_lints/src/double_comparison.rs @@ -60,7 +60,7 @@ impl<'tcx> DoubleComparisons { cx, DOUBLE_COMPARISONS, span, - "This binary expression can be simplified", + "this binary expression can be simplified", "try", sugg, applicability, diff --git a/tests/ui/double_comparison.stderr b/tests/ui/double_comparison.stderr index 5dcda7b3af4..05ef4e25f7f 100644 --- a/tests/ui/double_comparison.stderr +++ b/tests/ui/double_comparison.stderr @@ -1,4 +1,4 @@ -error: This binary expression can be simplified +error: this binary expression can be simplified --> $DIR/double_comparison.rs:6:8 | LL | if x == y || x < y { @@ -6,43 +6,43 @@ LL | if x == y || x < y { | = note: `-D clippy::double-comparisons` implied by `-D warnings` -error: This binary expression can be simplified +error: this binary expression can be simplified --> $DIR/double_comparison.rs:9:8 | LL | if x < y || x == y { | ^^^^^^^^^^^^^^^ help: try: `x <= y` -error: This binary expression can be simplified +error: this binary expression can be simplified --> $DIR/double_comparison.rs:12:8 | LL | if x == y || x > y { | ^^^^^^^^^^^^^^^ help: try: `x >= y` -error: This binary expression can be simplified +error: this binary expression can be simplified --> $DIR/double_comparison.rs:15:8 | LL | if x > y || x == y { | ^^^^^^^^^^^^^^^ help: try: `x >= y` -error: This binary expression can be simplified +error: this binary expression can be simplified --> $DIR/double_comparison.rs:18:8 | LL | if x < y || x > y { | ^^^^^^^^^^^^^^ help: try: `x != y` -error: This binary expression can be simplified +error: this binary expression can be simplified --> $DIR/double_comparison.rs:21:8 | LL | if x > y || x < y { | ^^^^^^^^^^^^^^ help: try: `x != y` -error: This binary expression can be simplified +error: this binary expression can be simplified --> $DIR/double_comparison.rs:24:8 | LL | if x <= y && x >= y { | ^^^^^^^^^^^^^^^^ help: try: `x == y` -error: This binary expression can be simplified +error: this binary expression can be simplified --> $DIR/double_comparison.rs:27:8 | LL | if x >= y && x <= y { -- cgit 1.4.1-3-g733a5 From 590b91d8d4250bd6e30e2396a36c54cdbae3780f Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 23 Jul 2020 19:56:01 +0200 Subject: double-parens: make lint adhere to lint message convention and do minor refactoring --- clippy_lints/src/double_parens.rs | 23 +++++------------------ tests/ui/double_parens.stderr | 12 ++++++------ 2 files changed, 11 insertions(+), 24 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/double_parens.rs b/clippy_lints/src/double_parens.rs index 1eb380a22cc..abbcaf43f41 100644 --- a/clippy_lints/src/double_parens.rs +++ b/clippy_lints/src/double_parens.rs @@ -45,15 +45,12 @@ impl EarlyLintPass for DoubleParens { return; } + let msg: &str = "consider removing unnecessary double parentheses"; + match expr.kind { ExprKind::Paren(ref in_paren) => match in_paren.kind { ExprKind::Paren(_) | ExprKind::Tup(_) => { - span_lint( - cx, - DOUBLE_PARENS, - expr.span, - "Consider removing unnecessary double parentheses", - ); + span_lint(cx, DOUBLE_PARENS, expr.span, &msg); }, _ => {}, }, @@ -61,12 +58,7 @@ impl EarlyLintPass for DoubleParens { if params.len() == 1 { let param = ¶ms[0]; if let ExprKind::Paren(_) = param.kind { - span_lint( - cx, - DOUBLE_PARENS, - param.span, - "Consider removing unnecessary double parentheses", - ); + span_lint(cx, DOUBLE_PARENS, param.span, &msg); } } }, @@ -74,12 +66,7 @@ impl EarlyLintPass for DoubleParens { if params.len() == 2 { let param = ¶ms[1]; if let ExprKind::Paren(_) = param.kind { - span_lint( - cx, - DOUBLE_PARENS, - param.span, - "Consider removing unnecessary double parentheses", - ); + span_lint(cx, DOUBLE_PARENS, param.span, &msg); } } }, diff --git a/tests/ui/double_parens.stderr b/tests/ui/double_parens.stderr index 0e4c9b5682d..40fcad2ab1d 100644 --- a/tests/ui/double_parens.stderr +++ b/tests/ui/double_parens.stderr @@ -1,4 +1,4 @@ -error: Consider removing unnecessary double parentheses +error: consider removing unnecessary double parentheses --> $DIR/double_parens.rs:15:5 | LL | ((0)) @@ -6,31 +6,31 @@ LL | ((0)) | = note: `-D clippy::double-parens` implied by `-D warnings` -error: Consider removing unnecessary double parentheses +error: consider removing unnecessary double parentheses --> $DIR/double_parens.rs:19:14 | LL | dummy_fn((0)); | ^^^ -error: Consider removing unnecessary double parentheses +error: consider removing unnecessary double parentheses --> $DIR/double_parens.rs:23:20 | LL | x.dummy_method((0)); | ^^^ -error: Consider removing unnecessary double parentheses +error: consider removing unnecessary double parentheses --> $DIR/double_parens.rs:27:5 | LL | ((1, 2)) | ^^^^^^^^ -error: Consider removing unnecessary double parentheses +error: consider removing unnecessary double parentheses --> $DIR/double_parens.rs:31:5 | LL | (()) | ^^^^ -error: Consider removing unnecessary double parentheses +error: consider removing unnecessary double parentheses --> $DIR/double_parens.rs:53:16 | LL | assert_eq!(((1, 2)), (1, 2), "Error"); -- cgit 1.4.1-3-g733a5 From 0db5cb13934bff7a561dde2e13c3455120dc1bd4 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 23 Jul 2020 20:05:42 +0200 Subject: drop_bounds: make lint adhere to lint message convention --- clippy_lints/src/drop_bounds.rs | 6 +++--- tests/ui/drop_bounds.stderr | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/drop_bounds.rs b/clippy_lints/src/drop_bounds.rs index 4afbd1ed0e5..ec3b6afa630 100644 --- a/clippy_lints/src/drop_bounds.rs +++ b/clippy_lints/src/drop_bounds.rs @@ -33,11 +33,11 @@ declare_clippy_lint! { /// ``` pub DROP_BOUNDS, correctness, - "Bounds of the form `T: Drop` are useless" + "bounds of the form `T: Drop` are useless" } -const DROP_BOUNDS_SUMMARY: &str = "Bounds of the form `T: Drop` are useless. \ - Use `std::mem::needs_drop` to detect if a type has drop glue."; +const DROP_BOUNDS_SUMMARY: &str = "bounds of the form `T: Drop` are useless, \ + use `std::mem::needs_drop` to detect if a type has drop glue"; declare_lint_pass!(DropBounds => [DROP_BOUNDS]); diff --git a/tests/ui/drop_bounds.stderr b/tests/ui/drop_bounds.stderr index 5d360ef30a1..8208c0ed7e3 100644 --- a/tests/ui/drop_bounds.stderr +++ b/tests/ui/drop_bounds.stderr @@ -1,4 +1,4 @@ -error: Bounds of the form `T: Drop` are useless. Use `std::mem::needs_drop` to detect if a type has drop glue. +error: bounds of the form `T: Drop` are useless, use `std::mem::needs_drop` to detect if a type has drop glue --> $DIR/drop_bounds.rs:2:11 | LL | fn foo() {} @@ -6,7 +6,7 @@ LL | fn foo() {} | = note: `#[deny(clippy::drop_bounds)]` on by default -error: Bounds of the form `T: Drop` are useless. Use `std::mem::needs_drop` to detect if a type has drop glue. +error: bounds of the form `T: Drop` are useless, use `std::mem::needs_drop` to detect if a type has drop glue --> $DIR/drop_bounds.rs:5:8 | LL | T: Drop, -- cgit 1.4.1-3-g733a5 From 2792260636f720a44921c5f6571535e887aa6047 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 23 Jul 2020 22:40:50 +0200 Subject: empty-liner-after-outer-attr: make lint adhere to lint message convention --- clippy_lints/src/attrs.rs | 2 +- tests/ui/empty_line_after_outer_attribute.stderr | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 3ce110e8e0f..376ac55f9c9 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -605,7 +605,7 @@ fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::as cx, EMPTY_LINE_AFTER_OUTER_ATTR, begin_of_attr_to_item, - "Found an empty line after an outer attribute. \ + "found an empty line after an outer attribute. \ Perhaps you forgot to add a `!` to make it an inner attribute?", ); } diff --git a/tests/ui/empty_line_after_outer_attribute.stderr b/tests/ui/empty_line_after_outer_attribute.stderr index bf753a732f0..594fca44a32 100644 --- a/tests/ui/empty_line_after_outer_attribute.stderr +++ b/tests/ui/empty_line_after_outer_attribute.stderr @@ -1,4 +1,4 @@ -error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? +error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? --> $DIR/empty_line_after_outer_attribute.rs:11:1 | LL | / #[crate_type = "lib"] @@ -9,7 +9,7 @@ LL | | fn with_one_newline_and_comment() { assert!(true) } | = note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings` -error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? +error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? --> $DIR/empty_line_after_outer_attribute.rs:23:1 | LL | / #[crate_type = "lib"] @@ -17,7 +17,7 @@ LL | | LL | | fn with_one_newline() { assert!(true) } | |_ -error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? +error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? --> $DIR/empty_line_after_outer_attribute.rs:28:1 | LL | / #[crate_type = "lib"] @@ -26,7 +26,7 @@ LL | | LL | | fn with_two_newlines() { assert!(true) } | |_ -error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? +error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? --> $DIR/empty_line_after_outer_attribute.rs:35:1 | LL | / #[crate_type = "lib"] @@ -34,7 +34,7 @@ LL | | LL | | enum Baz { | |_ -error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? +error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? --> $DIR/empty_line_after_outer_attribute.rs:43:1 | LL | / #[crate_type = "lib"] @@ -42,7 +42,7 @@ LL | | LL | | struct Foo { | |_ -error: Found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? +error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? --> $DIR/empty_line_after_outer_attribute.rs:51:1 | LL | / #[crate_type = "lib"] -- cgit 1.4.1-3-g733a5 From 4418ff122fdc65d642dd4adb709634c4f879171e Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 23 Jul 2020 23:31:33 +0200 Subject: unneeded-field-pattern: make lint adhere to lint message convention --- clippy_lints/src/misc_early.rs | 10 +++++----- tests/ui/unneeded_field_pattern.stderr | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/misc_early.rs b/clippy_lints/src/misc_early.rs index 38b11c5e733..02789735c17 100644 --- a/clippy_lints/src/misc_early.rs +++ b/clippy_lints/src/misc_early.rs @@ -298,9 +298,9 @@ impl EarlyLintPass for MiscEarlyLints { cx, UNNEEDED_FIELD_PATTERN, pat.span, - "All the struct fields are matched to a wildcard pattern, consider using `..`.", + "all the struct fields are matched to a wildcard pattern, consider using `..`", None, - &format!("Try with `{} {{ .. }}` instead", type_name), + &format!("try with `{} {{ .. }}` instead", type_name), ); return; } @@ -313,7 +313,7 @@ impl EarlyLintPass for MiscEarlyLints { cx, UNNEEDED_FIELD_PATTERN, field.span, - "You matched a field with a wildcard pattern. Consider using `..` instead", + "you matched a field with a wildcard pattern, consider using `..` instead", ); } else { let mut normal = vec![]; @@ -333,10 +333,10 @@ impl EarlyLintPass for MiscEarlyLints { cx, UNNEEDED_FIELD_PATTERN, field.span, - "You matched a field with a wildcard pattern. Consider using `..` \ + "you matched a field with a wildcard pattern, consider using `..` \ instead", None, - &format!("Try with `{} {{ {}, .. }}`", type_name, normal[..].join(", ")), + &format!("try with `{} {{ {}, .. }}`", type_name, normal[..].join(", ")), ); } } diff --git a/tests/ui/unneeded_field_pattern.stderr b/tests/ui/unneeded_field_pattern.stderr index e7b92ce1e19..b8d3c294532 100644 --- a/tests/ui/unneeded_field_pattern.stderr +++ b/tests/ui/unneeded_field_pattern.stderr @@ -1,19 +1,19 @@ -error: You matched a field with a wildcard pattern. Consider using `..` instead +error: you matched a field with a wildcard pattern, consider using `..` instead --> $DIR/unneeded_field_pattern.rs:14:15 | LL | Foo { a: _, b: 0, .. } => {}, | ^^^^ | = note: `-D clippy::unneeded-field-pattern` implied by `-D warnings` - = help: Try with `Foo { b: 0, .. }` + = help: try with `Foo { b: 0, .. }` -error: All the struct fields are matched to a wildcard pattern, consider using `..`. +error: all the struct fields are matched to a wildcard pattern, consider using `..` --> $DIR/unneeded_field_pattern.rs:16:9 | LL | Foo { a: _, b: _, c: _ } => {}, | ^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: Try with `Foo { .. }` instead + = help: try with `Foo { .. }` instead error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From b36a6c9594ffb7e225a3d8872d8de2889ea0bac1 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 23 Jul 2020 23:36:20 +0200 Subject: ref_in_deref: make lint adhere to lint message convention --- clippy_lints/src/reference.rs | 2 +- tests/ui/unnecessary_ref.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index fe457aad50e..3fda00403c6 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -92,7 +92,7 @@ impl EarlyLintPass for RefInDeref { cx, REF_IN_DEREF, object.span, - "Creating a reference that is immediately dereferenced.", + "creating a reference that is immediately dereferenced", "try this", snippet_with_applicability(cx, inner.span, "_", &mut applicability).to_string(), applicability, diff --git a/tests/ui/unnecessary_ref.stderr b/tests/ui/unnecessary_ref.stderr index 34ba167a947..d0a0f219097 100644 --- a/tests/ui/unnecessary_ref.stderr +++ b/tests/ui/unnecessary_ref.stderr @@ -1,4 +1,4 @@ -error: Creating a reference that is immediately dereferenced. +error: creating a reference that is immediately dereferenced --> $DIR/unnecessary_ref.rs:13:17 | LL | let inner = (&outer).inner; -- cgit 1.4.1-3-g733a5 From 7954c22a99275a8b7be79c14d2bb882750de53ea Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 23 Jul 2020 23:37:16 +0200 Subject: unknown: make lint adhere to lint message convention --- clippy_lints/src/utils/attrs.rs | 2 +- tests/ui/unknown_attribute.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/utils/attrs.rs b/clippy_lints/src/utils/attrs.rs index 407527251da..234ae37612b 100644 --- a/clippy_lints/src/utils/attrs.rs +++ b/clippy_lints/src/utils/attrs.rs @@ -75,7 +75,7 @@ pub fn get_attr<'a>( }) .map_or_else( || { - sess.span_err(attr_segments[1].ident.span, "Usage of unknown attribute"); + sess.span_err(attr_segments[1].ident.span, "usage of unknown attribute"); false }, |deprecation_status| { diff --git a/tests/ui/unknown_attribute.stderr b/tests/ui/unknown_attribute.stderr index 47e37aed246..618c5980d64 100644 --- a/tests/ui/unknown_attribute.stderr +++ b/tests/ui/unknown_attribute.stderr @@ -1,4 +1,4 @@ -error: Usage of unknown attribute +error: usage of unknown attribute --> $DIR/unknown_attribute.rs:1:11 | LL | #[clippy::unknown] -- cgit 1.4.1-3-g733a5 From fe37ddbd11dd3f2cb2e529846492b312e44ed1b9 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 23 Jul 2020 23:45:24 +0200 Subject: suspicious-arithmetic-impl: make lint adhere to lint message convention --- clippy_lints/src/suspicious_trait_impl.rs | 4 ++-- tests/ui/suspicious_arithmetic_impl.stderr | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/suspicious_trait_impl.rs b/clippy_lints/src/suspicious_trait_impl.rs index 502fffc5e6c..4e335a0222f 100644 --- a/clippy_lints/src/suspicious_trait_impl.rs +++ b/clippy_lints/src/suspicious_trait_impl.rs @@ -98,7 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl { cx, SUSPICIOUS_ARITHMETIC_IMPL, binop.span, - &format!(r#"Suspicious use of binary operator in `{}` impl"#, impl_trait), + &format!("suspicious use of binary operator in `{}` impl", impl_trait), ); } @@ -135,7 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl { cx, SUSPICIOUS_OP_ASSIGN_IMPL, binop.span, - &format!(r#"Suspicious use of binary operator in `{}` impl"#, impl_trait), + &format!("suspicious use of binary operator in `{}` impl", impl_trait), ); } } diff --git a/tests/ui/suspicious_arithmetic_impl.stderr b/tests/ui/suspicious_arithmetic_impl.stderr index 7e42d72c30b..23d47e3f1ff 100644 --- a/tests/ui/suspicious_arithmetic_impl.stderr +++ b/tests/ui/suspicious_arithmetic_impl.stderr @@ -1,4 +1,4 @@ -error: Suspicious use of binary operator in `Add` impl +error: suspicious use of binary operator in `Add` impl --> $DIR/suspicious_arithmetic_impl.rs:11:20 | LL | Foo(self.0 - other.0) @@ -6,7 +6,7 @@ LL | Foo(self.0 - other.0) | = note: `-D clippy::suspicious-arithmetic-impl` implied by `-D warnings` -error: Suspicious use of binary operator in `AddAssign` impl +error: suspicious use of binary operator in `AddAssign` impl --> $DIR/suspicious_arithmetic_impl.rs:17:23 | LL | *self = *self - other; @@ -14,7 +14,7 @@ LL | *self = *self - other; | = note: `#[deny(clippy::suspicious_op_assign_impl)]` on by default -error: Suspicious use of binary operator in `MulAssign` impl +error: suspicious use of binary operator in `MulAssign` impl --> $DIR/suspicious_arithmetic_impl.rs:30:16 | LL | self.0 /= other.0; -- cgit 1.4.1-3-g733a5 From 5d69ca5e114a1e44be08c835394b82ffc9cc7f14 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 23 Jul 2020 23:46:52 +0200 Subject: also change "deprecated-attribute" message --- clippy_lints/src/utils/attrs.rs | 2 +- tests/ui/renamed_builtin_attr.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/utils/attrs.rs b/clippy_lints/src/utils/attrs.rs index 234ae37612b..a3975683cb3 100644 --- a/clippy_lints/src/utils/attrs.rs +++ b/clippy_lints/src/utils/attrs.rs @@ -80,7 +80,7 @@ pub fn get_attr<'a>( }, |deprecation_status| { let mut diag = - sess.struct_span_err(attr_segments[1].ident.span, "Usage of deprecated attribute"); + sess.struct_span_err(attr_segments[1].ident.span, "usage of deprecated attribute"); match *deprecation_status { DeprecationStatus::Deprecated => { diag.emit(); diff --git a/tests/ui/renamed_builtin_attr.stderr b/tests/ui/renamed_builtin_attr.stderr index a399ff52fb8..88046762483 100644 --- a/tests/ui/renamed_builtin_attr.stderr +++ b/tests/ui/renamed_builtin_attr.stderr @@ -1,4 +1,4 @@ -error: Usage of deprecated attribute +error: usage of deprecated attribute --> $DIR/renamed_builtin_attr.rs:3:11 | LL | #[clippy::cyclomatic_complexity = "1"] -- cgit 1.4.1-3-g733a5 From 3e1e0c9bdb582b15c7804e354085b17f8b6c62d5 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 23 Jul 2020 23:54:34 +0200 Subject: redundant-static-lifetimes: make lint adhere to lint message convention --- clippy_lints/src/redundant_static_lifetimes.rs | 4 +-- tests/ui/redundant_static_lifetimes.stderr | 32 +++++++++++----------- .../ui/redundant_static_lifetimes_multiple.stderr | 20 +++++++------- 3 files changed, 28 insertions(+), 28 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/redundant_static_lifetimes.rs b/clippy_lints/src/redundant_static_lifetimes.rs index c6f57298c26..7bbcc67aa2d 100644 --- a/clippy_lints/src/redundant_static_lifetimes.rs +++ b/clippy_lints/src/redundant_static_lifetimes.rs @@ -86,13 +86,13 @@ impl EarlyLintPass for RedundantStaticLifetimes { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { if !item.span.from_expansion() { if let ItemKind::Const(_, ref var_type, _) = item.kind { - self.visit_type(var_type, cx, "Constants have by default a `'static` lifetime"); + self.visit_type(var_type, cx, "constants have by default a `'static` lifetime"); // Don't check associated consts because `'static` cannot be elided on those (issue // #2438) } if let ItemKind::Static(ref var_type, _, _) = item.kind { - self.visit_type(var_type, cx, "Statics have by default a `'static` lifetime"); + self.visit_type(var_type, cx, "statics have by default a `'static` lifetime"); } } } diff --git a/tests/ui/redundant_static_lifetimes.stderr b/tests/ui/redundant_static_lifetimes.stderr index 3c3d2eacd8d..649831f9c06 100644 --- a/tests/ui/redundant_static_lifetimes.stderr +++ b/tests/ui/redundant_static_lifetimes.stderr @@ -1,4 +1,4 @@ -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:8:17 | LL | const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removing 'static. @@ -6,91 +6,91 @@ LL | const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removin | = note: `-D clippy::redundant-static-lifetimes` implied by `-D warnings` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:12:21 | LL | const VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:14:32 | LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:14:47 | LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:16:17 | LL | const VAR_SIX: &'static u8 = &5; | -^^^^^^^--- help: consider removing `'static`: `&u8` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:18:20 | LL | const VAR_HEIGHT: &'static Foo = &Foo {}; | -^^^^^^^---- help: consider removing `'static`: `&Foo` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:20:19 | LL | const VAR_SLICE: &'static [u8] = b"Test constant #1"; // ERROR Consider removing 'static. | -^^^^^^^----- help: consider removing `'static`: `&[u8]` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:22:19 | LL | const VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static. | -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:24:19 | LL | const VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static. | -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:26:25 | LL | static STATIC_VAR_ONE: &'static str = "Test static #1"; // ERROR Consider removing 'static. | -^^^^^^^---- help: consider removing `'static`: `&str` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:30:29 | LL | static STATIC_VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:32:25 | LL | static STATIC_VAR_SIX: &'static u8 = &5; | -^^^^^^^--- help: consider removing `'static`: `&u8` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:34:28 | LL | static STATIC_VAR_HEIGHT: &'static Foo = &Foo {}; | -^^^^^^^---- help: consider removing `'static`: `&Foo` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:36:27 | LL | static STATIC_VAR_SLICE: &'static [u8] = b"Test static #3"; // ERROR Consider removing 'static. | -^^^^^^^----- help: consider removing `'static`: `&[u8]` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:38:27 | LL | static STATIC_VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static. | -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes.rs:40:27 | LL | static STATIC_VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static. diff --git a/tests/ui/redundant_static_lifetimes_multiple.stderr b/tests/ui/redundant_static_lifetimes_multiple.stderr index afc853dcfce..cc7e55a757a 100644 --- a/tests/ui/redundant_static_lifetimes_multiple.stderr +++ b/tests/ui/redundant_static_lifetimes_multiple.stderr @@ -1,4 +1,4 @@ -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes_multiple.rs:3:18 | LL | const VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static @@ -6,55 +6,55 @@ LL | const VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; | = note: `-D clippy::redundant-static-lifetimes` implied by `-D warnings` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes_multiple.rs:3:30 | LL | const VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes_multiple.rs:5:29 | LL | const VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])]; | -^^^^^^^--------------- help: consider removing `'static`: `&[&'static str]` -error: Constants have by default a `'static` lifetime +error: constants have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes_multiple.rs:5:39 | LL | const VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])]; | -^^^^^^^---- help: consider removing `'static`: `&str` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes_multiple.rs:7:40 | LL | static STATIC_VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes_multiple.rs:7:55 | LL | static STATIC_VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes_multiple.rs:9:26 | LL | static STATIC_VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static | -^^^^^^^------------------ help: consider removing `'static`: `&[&[&'static str]]` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes_multiple.rs:9:38 | LL | static STATIC_VAR_FIVE: &'static [&[&'static str]] = &[&["test"], &["other one"]]; // ERROR Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes_multiple.rs:11:37 | LL | static STATIC_VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])]; | -^^^^^^^--------------- help: consider removing `'static`: `&[&'static str]` -error: Statics have by default a `'static` lifetime +error: statics have by default a `'static` lifetime --> $DIR/redundant_static_lifetimes_multiple.rs:11:47 | LL | static STATIC_VAR_SEVEN: &[&(&str, &'static [&'static str])] = &[&("one", &["other one"])]; -- cgit 1.4.1-3-g733a5 From 81f77a411e844ca553ba93adcc8be617d372ac30 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Fri, 24 Jul 2020 00:12:21 +0200 Subject: range-zip-with-len: make lint adhere to lint message convention --- clippy_lints/src/ranges.rs | 2 +- tests/ui/range.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 4c1f2e8e01a..f88075798ca 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -160,7 +160,7 @@ impl<'tcx> LateLintPass<'tcx> for Ranges { span_lint(cx, RANGE_ZIP_WITH_LEN, expr.span, - &format!("It is more idiomatic to use `{}.iter().enumerate()`", + &format!("it is more idiomatic to use `{}.iter().enumerate()`", snippet(cx, iter_args[0].span, "_"))); } } diff --git a/tests/ui/range.stderr b/tests/ui/range.stderr index d53c1edecac..dcb5061371f 100644 --- a/tests/ui/range.stderr +++ b/tests/ui/range.stderr @@ -1,4 +1,4 @@ -error: It is more idiomatic to use `v1.iter().enumerate()` +error: it is more idiomatic to use `v1.iter().enumerate()` --> $DIR/range.rs:5:14 | LL | let _x = v1.iter().zip(0..v1.len()); -- cgit 1.4.1-3-g733a5 From 9178363574625a6185ea779c4a231b8f18205261 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Fri, 24 Jul 2020 00:16:28 +0200 Subject: path-buf-push-overwrite: make lint adhere to lint message convention --- clippy_lints/src/path_buf_push_overwrite.rs | 2 +- tests/ui/path_buf_push_overwrite.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/path_buf_push_overwrite.rs b/clippy_lints/src/path_buf_push_overwrite.rs index 66a145a7f14..b8583402928 100644 --- a/clippy_lints/src/path_buf_push_overwrite.rs +++ b/clippy_lints/src/path_buf_push_overwrite.rs @@ -60,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for PathBufPushOverwrite { cx, PATH_BUF_PUSH_OVERWRITE, lit.span, - "Calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition", + "calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition", "try", format!("\"{}\"", pushed_path_lit.trim_start_matches(|c| c == '/' || c == '\\')), Applicability::MachineApplicable, diff --git a/tests/ui/path_buf_push_overwrite.stderr b/tests/ui/path_buf_push_overwrite.stderr index 09b18d71baf..bb8dce2bbba 100644 --- a/tests/ui/path_buf_push_overwrite.stderr +++ b/tests/ui/path_buf_push_overwrite.stderr @@ -1,4 +1,4 @@ -error: Calling `push` with '/' or '/' (file system root) will overwrite the previous path definition +error: calling `push` with '/' or '/' (file system root) will overwrite the previous path definition --> $DIR/path_buf_push_overwrite.rs:7:12 | LL | x.push("/bar"); -- cgit 1.4.1-3-g733a5 From e519bb3c850199d03eed7f2bd29637b3d5479551 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Fri, 24 Jul 2020 00:18:34 +0200 Subject: overflow-check-conditional: make lint adhere to lint message convention --- clippy_lints/src/overflow_check_conditional.rs | 8 ++++---- tests/ui/overflow_check_conditional.stderr | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/overflow_check_conditional.rs b/clippy_lints/src/overflow_check_conditional.rs index 4d4a9676654..3c041bac234 100644 --- a/clippy_lints/src/overflow_check_conditional.rs +++ b/clippy_lints/src/overflow_check_conditional.rs @@ -42,13 +42,13 @@ impl<'tcx> LateLintPass<'tcx> for OverflowCheckConditional { 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."); + "you are trying to use classic C overflow conditions that will fail in Rust"); } } 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."); + "you are trying to use classic C underflow conditions that will fail in Rust"); } } } @@ -67,13 +67,13 @@ impl<'tcx> LateLintPass<'tcx> for OverflowCheckConditional { 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."); + "you are trying to use classic C overflow conditions that will fail in Rust"); } } 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."); + "you are trying to use classic C underflow conditions that will fail in Rust"); } } } diff --git a/tests/ui/overflow_check_conditional.stderr b/tests/ui/overflow_check_conditional.stderr index ad66135d326..19e843c2c0a 100644 --- a/tests/ui/overflow_check_conditional.stderr +++ b/tests/ui/overflow_check_conditional.stderr @@ -1,4 +1,4 @@ -error: You are trying to use classic C overflow conditions that will fail in Rust. +error: you are trying to use classic C overflow conditions that will fail in Rust --> $DIR/overflow_check_conditional.rs:8:8 | LL | if a + b < a {} @@ -6,43 +6,43 @@ LL | if a + b < a {} | = note: `-D clippy::overflow-check-conditional` implied by `-D warnings` -error: You are trying to use classic C overflow conditions that will fail in Rust. +error: you are trying to use classic C overflow conditions that will fail in Rust --> $DIR/overflow_check_conditional.rs:9:8 | LL | if a > a + b {} | ^^^^^^^^^ -error: You are trying to use classic C overflow conditions that will fail in Rust. +error: you are trying to use classic C overflow conditions that will fail in Rust --> $DIR/overflow_check_conditional.rs:10:8 | LL | if a + b < b {} | ^^^^^^^^^ -error: You are trying to use classic C overflow conditions that will fail in Rust. +error: you are trying to use classic C overflow conditions that will fail in Rust --> $DIR/overflow_check_conditional.rs:11:8 | LL | if b > a + b {} | ^^^^^^^^^ -error: You are trying to use classic C underflow conditions that will fail in Rust. +error: you are trying to use classic C underflow conditions that will fail in Rust --> $DIR/overflow_check_conditional.rs:12:8 | LL | if a - b > b {} | ^^^^^^^^^ -error: You are trying to use classic C underflow conditions that will fail in Rust. +error: you are trying to use classic C underflow conditions that will fail in Rust --> $DIR/overflow_check_conditional.rs:13:8 | LL | if b < a - b {} | ^^^^^^^^^ -error: You are trying to use classic C underflow conditions that will fail in Rust. +error: you are trying to use classic C underflow conditions that will fail in Rust --> $DIR/overflow_check_conditional.rs:14:8 | LL | if a - b > a {} | ^^^^^^^^^ -error: You are trying to use classic C underflow conditions that will fail in Rust. +error: you are trying to use classic C underflow conditions that will fail in Rust --> $DIR/overflow_check_conditional.rs:15:8 | LL | if a < a - b {} -- cgit 1.4.1-3-g733a5 From 178da9b2ef9e8c94ab0fbe7812e11445664f67b0 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Fri, 24 Jul 2020 00:24:11 +0200 Subject: neg-multiply: make lint adhere to lint message convention --- clippy_lints/src/neg_multiply.rs | 2 +- tests/ui/neg_multiply.stderr | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/neg_multiply.rs b/clippy_lints/src/neg_multiply.rs index 6b6c950e0ab..aa550510867 100644 --- a/clippy_lints/src/neg_multiply.rs +++ b/clippy_lints/src/neg_multiply.rs @@ -47,7 +47,7 @@ fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) { if let Constant::Int(1) = consts::lit_to_constant(&l.node, cx.typeck_results().expr_ty_opt(lit)); if cx.typeck_results().expr_ty(exp).is_integral(); then { - span_lint(cx, NEG_MULTIPLY, span, "Negation by multiplying with `-1`"); + span_lint(cx, NEG_MULTIPLY, span, "negation by multiplying with `-1`"); } } } diff --git a/tests/ui/neg_multiply.stderr b/tests/ui/neg_multiply.stderr index f08bbd6a12c..ad677f6d6fb 100644 --- a/tests/ui/neg_multiply.stderr +++ b/tests/ui/neg_multiply.stderr @@ -1,4 +1,4 @@ -error: Negation by multiplying with `-1` +error: negation by multiplying with `-1` --> $DIR/neg_multiply.rs:27:5 | LL | x * -1; @@ -6,7 +6,7 @@ LL | x * -1; | = note: `-D clippy::neg-multiply` implied by `-D warnings` -error: Negation by multiplying with `-1` +error: negation by multiplying with `-1` --> $DIR/neg_multiply.rs:29:5 | LL | -1 * x; -- cgit 1.4.1-3-g733a5 From dabf9891954aa0d0c59b230ab0e7afcfd4142be0 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Tue, 28 Jul 2020 12:13:22 +0200 Subject: neg-cmp-op-on-partial-ord: make lint adhere to lint message convention --- clippy_lints/src/neg_cmp_op_on_partial_ord.rs | 2 +- tests/ui/neg_cmp_op_on_partial_ord.stderr | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs index 95613a1b82e..0f5d5ce3495 100644 --- a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs +++ b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs @@ -79,7 +79,7 @@ impl<'tcx> LateLintPass<'tcx> for NoNegCompOpForPartialOrd { cx, NEG_CMP_OP_ON_PARTIAL_ORD, expr.span, - "The use of negated comparison operators on partially ordered \ + "the use of negated comparison operators on partially ordered \ types produces code that is hard to read and refactor. Please \ consider using the `partial_cmp` method instead, to make it \ clear that the two values could be incomparable." diff --git a/tests/ui/neg_cmp_op_on_partial_ord.stderr b/tests/ui/neg_cmp_op_on_partial_ord.stderr index 8c5d548222e..193d9f9bcea 100644 --- a/tests/ui/neg_cmp_op_on_partial_ord.stderr +++ b/tests/ui/neg_cmp_op_on_partial_ord.stderr @@ -1,4 +1,4 @@ -error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. +error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. --> $DIR/neg_cmp_op_on_partial_ord.rs:16:21 | LL | let _not_less = !(a_value < another_value); @@ -6,19 +6,19 @@ LL | let _not_less = !(a_value < another_value); | = note: `-D clippy::neg-cmp-op-on-partial-ord` implied by `-D warnings` -error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. +error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. --> $DIR/neg_cmp_op_on_partial_ord.rs:19:30 | LL | let _not_less_or_equal = !(a_value <= another_value); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. +error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. --> $DIR/neg_cmp_op_on_partial_ord.rs:22:24 | LL | let _not_greater = !(a_value > another_value); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: The use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. +error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. --> $DIR/neg_cmp_op_on_partial_ord.rs:25:33 | LL | let _not_greater_or_equal = !(a_value >= another_value); -- cgit 1.4.1-3-g733a5 From 6d0b5e24dfc8232123984fcefface485aa7fbc3c Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Tue, 11 Aug 2020 00:27:55 +0200 Subject: update test stderr --- tests/ui/checked_unwrap/complex_conditionals.stderr | 20 ++++++++++---------- .../complex_conditionals_nested.stderr | 2 +- tests/ui/checked_unwrap/simple_conditionals.stderr | 14 +++++++------- tests/ui/neg_cmp_op_on_partial_ord.stderr | 8 ++++---- 4 files changed, 22 insertions(+), 22 deletions(-) (limited to 'tests') diff --git a/tests/ui/checked_unwrap/complex_conditionals.stderr b/tests/ui/checked_unwrap/complex_conditionals.stderr index 5b62dca629f..33bb5136ef8 100644 --- a/tests/ui/checked_unwrap/complex_conditionals.stderr +++ b/tests/ui/checked_unwrap/complex_conditionals.stderr @@ -1,4 +1,4 @@ -error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` +error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:8:9 | LL | if x.is_ok() && y.is_err() { @@ -36,7 +36,7 @@ LL | if x.is_ok() && y.is_err() { LL | y.unwrap(); // will panic | ^^^^^^^^^^ -error: you checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` +error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:11:9 | LL | if x.is_ok() && y.is_err() { @@ -54,7 +54,7 @@ LL | if x.is_ok() || y.is_ok() { LL | x.unwrap(); // will panic | ^^^^^^^^^^ -error: you checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` +error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:26:9 | LL | if x.is_ok() || y.is_ok() { @@ -72,7 +72,7 @@ LL | if x.is_ok() || y.is_ok() { LL | y.unwrap(); // will panic | ^^^^^^^^^^ -error: you checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` +error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:28:9 | LL | if x.is_ok() || y.is_ok() { @@ -81,7 +81,7 @@ LL | if x.is_ok() || y.is_ok() { LL | y.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ -error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` +error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:32:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { @@ -107,7 +107,7 @@ LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { LL | y.unwrap(); // will panic | ^^^^^^^^^^ -error: you checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` +error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:35:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { @@ -116,7 +116,7 @@ LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { LL | y.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ -error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` +error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:36:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { @@ -143,7 +143,7 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { LL | x.unwrap(); // will panic | ^^^^^^^^^^ -error: you checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` +error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:46:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { @@ -152,7 +152,7 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { LL | x.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ -error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` +error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:47:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { @@ -179,7 +179,7 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { LL | z.unwrap(); // will panic | ^^^^^^^^^^ -error: you checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` +error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals.rs:50:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { diff --git a/tests/ui/checked_unwrap/complex_conditionals_nested.stderr b/tests/ui/checked_unwrap/complex_conditionals_nested.stderr index 46ffc16c23e..a01f7f956f6 100644 --- a/tests/ui/checked_unwrap/complex_conditionals_nested.stderr +++ b/tests/ui/checked_unwrap/complex_conditionals_nested.stderr @@ -1,4 +1,4 @@ -error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` +error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/complex_conditionals_nested.rs:8:13 | LL | if x.is_some() { diff --git a/tests/ui/checked_unwrap/simple_conditionals.stderr b/tests/ui/checked_unwrap/simple_conditionals.stderr index bf4b6c93098..416ec1a01ab 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.stderr +++ b/tests/ui/checked_unwrap/simple_conditionals.stderr @@ -1,4 +1,4 @@ -error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` +error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:39:9 | LL | if x.is_some() { @@ -35,7 +35,7 @@ LL | if x.is_none() { LL | x.unwrap(); // will panic | ^^^^^^^^^^ -error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` +error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:46:9 | LL | if x.is_none() { @@ -44,7 +44,7 @@ LL | if x.is_none() { LL | x.unwrap(); // unnecessary | ^^^^^^^^^^ -error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` +error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:7:13 | LL | if $a.is_some() { @@ -57,7 +57,7 @@ LL | m!(x); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` +error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:54:9 | LL | if x.is_ok() { @@ -83,7 +83,7 @@ LL | if x.is_ok() { LL | x.unwrap(); // will panic | ^^^^^^^^^^ -error: you checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` +error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:58:9 | LL | if x.is_ok() { @@ -100,7 +100,7 @@ LL | if x.is_err() { LL | x.unwrap(); // will panic | ^^^^^^^^^^ -error: you checked before that `unwrap_err()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` +error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:62:9 | LL | if x.is_err() { @@ -109,7 +109,7 @@ LL | x.unwrap(); // will panic LL | x.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ -error: you checked before that `unwrap()` cannot fail. Instead of checking and unwrapping, it's better to use `if let` or `match` +error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` --> $DIR/simple_conditionals.rs:64:9 | LL | if x.is_err() { diff --git a/tests/ui/neg_cmp_op_on_partial_ord.stderr b/tests/ui/neg_cmp_op_on_partial_ord.stderr index 193d9f9bcea..c7856000721 100644 --- a/tests/ui/neg_cmp_op_on_partial_ord.stderr +++ b/tests/ui/neg_cmp_op_on_partial_ord.stderr @@ -1,4 +1,4 @@ -error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. +error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor, please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable --> $DIR/neg_cmp_op_on_partial_ord.rs:16:21 | LL | let _not_less = !(a_value < another_value); @@ -6,19 +6,19 @@ LL | let _not_less = !(a_value < another_value); | = note: `-D clippy::neg-cmp-op-on-partial-ord` implied by `-D warnings` -error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. +error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor, please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable --> $DIR/neg_cmp_op_on_partial_ord.rs:19:30 | LL | let _not_less_or_equal = !(a_value <= another_value); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. +error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor, please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable --> $DIR/neg_cmp_op_on_partial_ord.rs:22:24 | LL | let _not_greater = !(a_value > another_value); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor. Please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable. +error: the use of negated comparison operators on partially ordered types produces code that is hard to read and refactor, please consider using the `partial_cmp` method instead, to make it clear that the two values could be incomparable --> $DIR/neg_cmp_op_on_partial_ord.rs:25:33 | LL | let _not_greater_or_equal = !(a_value >= another_value); -- cgit 1.4.1-3-g733a5 From b8713e3854cb90b974eceaa1d50484831591619c Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Tue, 11 Aug 2020 12:35:55 +0200 Subject: unnecessary-mut-passed: make lint message say if fn is a function or a method. --- clippy_lints/src/mut_reference.rs | 13 ++++++++++--- tests/ui/mut_reference.stderr | 6 +++--- 2 files changed, 13 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/mut_reference.rs b/clippy_lints/src/mut_reference.rs index b8dc5081632..be3ae7ab380 100644 --- a/clippy_lints/src/mut_reference.rs +++ b/clippy_lints/src/mut_reference.rs @@ -39,6 +39,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed { arguments, cx.typeck_results().expr_ty(fn_expr), &rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false)), + "function", ); } }, @@ -46,14 +47,20 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed { let def_id = cx.typeck_results().type_dependent_def_id(e.hir_id).unwrap(); let substs = cx.typeck_results().node_substs(e.hir_id); let method_type = cx.tcx.type_of(def_id).subst(cx.tcx, substs); - check_arguments(cx, arguments, method_type, &path.ident.as_str()) + check_arguments(cx, arguments, method_type, &path.ident.as_str(), "method") }, _ => (), } } } -fn check_arguments<'tcx>(cx: &LateContext<'tcx>, arguments: &[Expr<'_>], type_definition: Ty<'tcx>, name: &str) { +fn check_arguments<'tcx>( + cx: &LateContext<'tcx>, + arguments: &[Expr<'_>], + type_definition: Ty<'tcx>, + name: &str, + fn_kind: &str, +) { match type_definition.kind { ty::FnDef(..) | ty::FnPtr(_) => { let parameters = type_definition.fn_sig(cx.tcx).skip_binder().inputs(); @@ -68,7 +75,7 @@ fn check_arguments<'tcx>(cx: &LateContext<'tcx>, arguments: &[Expr<'_>], type_de cx, UNNECESSARY_MUT_PASSED, argument.span, - &format!("The function/method `{}` doesn't need a mutable reference", name), + &format!("the {} `{}` doesn't need a mutable reference", fn_kind, name), ); } }, diff --git a/tests/ui/mut_reference.stderr b/tests/ui/mut_reference.stderr index fa8c82ae0f3..062d30b262c 100644 --- a/tests/ui/mut_reference.stderr +++ b/tests/ui/mut_reference.stderr @@ -1,4 +1,4 @@ -error: The function/method `takes_an_immutable_reference` doesn't need a mutable reference +error: the function `takes_an_immutable_reference` doesn't need a mutable reference --> $DIR/mut_reference.rs:17:34 | LL | takes_an_immutable_reference(&mut 42); @@ -6,13 +6,13 @@ LL | takes_an_immutable_reference(&mut 42); | = note: `-D clippy::unnecessary-mut-passed` implied by `-D warnings` -error: The function/method `as_ptr` doesn't need a mutable reference +error: the function `as_ptr` doesn't need a mutable reference --> $DIR/mut_reference.rs:19:12 | LL | as_ptr(&mut 42); | ^^^^^^^ -error: The function/method `takes_an_immutable_reference` doesn't need a mutable reference +error: the method `takes_an_immutable_reference` doesn't need a mutable reference --> $DIR/mut_reference.rs:23:44 | LL | my_struct.takes_an_immutable_reference(&mut 42); -- cgit 1.4.1-3-g733a5 From 9311c11d7c01d64d22dc7914e9dff4c5167adb49 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Tue, 11 Aug 2020 13:57:32 +0200 Subject: Fix sync fallout --- clippy_lints/src/transmute.rs | 12 +++--- tests/compile-test.rs | 7 ++-- tests/ui/transmutes_expressible_as_ptr_casts.fixed | 38 +++++++------------ tests/ui/transmutes_expressible_as_ptr_casts.rs | 38 +++++++------------ .../ui/transmutes_expressible_as_ptr_casts.stderr | 44 +++++++++++----------- 5 files changed, 59 insertions(+), 80 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/transmute.rs b/clippy_lints/src/transmute.rs index f077c146183..7b5e92eb5ee 100644 --- a/clippy_lints/src/transmute.rs +++ b/clippy_lints/src/transmute.rs @@ -61,12 +61,14 @@ declare_clippy_lint! { /// /// **Example:** /// - /// ```rust,ignore - /// core::intrinsics::transmute::<*const [i32], *const [u16]>(p) + /// ```rust + /// # let p: *const [i32] = &[]; + /// unsafe { std::mem::transmute::<*const [i32], *const [u16]>(p) }; /// ``` /// Use instead: /// ```rust - /// p as *const [u16] + /// # let p: *const [i32] = &[]; + /// p as *const [u16]; /// ``` pub TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, complexity, @@ -704,14 +706,14 @@ fn can_be_expressed_as_pointer_cast<'tcx>( from_ty: Ty<'tcx>, to_ty: Ty<'tcx>, ) -> bool { - use CastKind::*; + use CastKind::{AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast}; matches!( check_cast(cx, e, from_ty, to_ty), Some(PtrPtrCast | PtrAddrCast | AddrPtrCast | ArrayPtrCast | FnPtrPtrCast | FnPtrAddrCast) ) } -/// If a cast from from_ty to to_ty is valid, returns an Ok containing the kind of +/// If a cast from `from_ty` to `to_ty` is valid, returns an Ok containing the kind of /// the cast. In certain cases, including some invalid casts from array references /// to pointers, this may cause additional errors to be emitted and/or ICE error /// messages. This function will panic if that occurs. diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 26a47d23706..e662d608edf 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -147,9 +147,6 @@ fn run_ui_toml(config: &mut compiletest::Config) { } fn run_ui_cargo(config: &mut compiletest::Config) { - if cargo::is_rustc_test_suite() { - return; - } fn run_tests( config: &compiletest::Config, filter: &Option, @@ -217,6 +214,10 @@ fn run_ui_cargo(config: &mut compiletest::Config) { Ok(result) } + if cargo::is_rustc_test_suite() { + return; + } + config.mode = TestMode::Ui; config.src_base = Path::new("tests").join("ui-cargo").canonicalize().unwrap(); diff --git a/tests/ui/transmutes_expressible_as_ptr_casts.fixed b/tests/ui/transmutes_expressible_as_ptr_casts.fixed index 98288dde6d8..b6f1e83181c 100644 --- a/tests/ui/transmutes_expressible_as_ptr_casts.fixed +++ b/tests/ui/transmutes_expressible_as_ptr_casts.fixed @@ -9,60 +9,48 @@ use std::mem::{size_of, transmute}; -// rustc_typeck::check::cast contains documentation about when a cast `e as U` is +// rustc_typeck::check::cast contains documentation about when a cast `e as U` is // valid, which we quote from below. fn main() { // We should see an error message for each transmute, and no error messages for // the casts, since the casts are the recommended fixes. // e is an integer and U is *U_0, while U_0: Sized; addr-ptr-cast - let _ptr_i32_transmute = unsafe { - usize::MAX as *const i32 - }; + let _ptr_i32_transmute = unsafe { usize::MAX as *const i32 }; let ptr_i32 = usize::MAX as *const i32; // e has type *T, U is *U_0, and either U_0: Sized ... - let _ptr_i8_transmute = unsafe { - ptr_i32 as *const i8 - }; + let _ptr_i8_transmute = unsafe { ptr_i32 as *const i8 }; let _ptr_i8 = ptr_i32 as *const i8; - let slice_ptr = &[0,1,2,3] as *const [i32]; + let slice_ptr = &[0, 1, 2, 3] as *const [i32]; // ... or pointer_kind(T) = pointer_kind(U_0); ptr-ptr-cast - let _ptr_to_unsized_transmute = unsafe { - slice_ptr as *const [u16] - }; + let _ptr_to_unsized_transmute = unsafe { slice_ptr as *const [u16] }; let _ptr_to_unsized = slice_ptr as *const [u16]; // TODO: We could try testing vtable casts here too, but maybe // we should wait until std::raw::TraitObject is stabilized? // e has type *T and U is a numeric type, while T: Sized; ptr-addr-cast - let _usize_from_int_ptr_transmute = unsafe { - ptr_i32 as usize - }; + let _usize_from_int_ptr_transmute = unsafe { ptr_i32 as usize }; let _usize_from_int_ptr = ptr_i32 as usize; - let array_ref: &[i32; 4] = &[1,2,3,4]; + let array_ref: &[i32; 4] = &[1, 2, 3, 4]; // e has type &[T; n] and U is *const T; array-ptr-cast - let _array_ptr_transmute = unsafe { - array_ref as *const [i32; 4] - }; + let _array_ptr_transmute = unsafe { array_ref as *const [i32; 4] }; let _array_ptr = array_ref as *const [i32; 4]; - fn foo(_: usize) -> u8 { 42 } + fn foo(_: usize) -> u8 { + 42 + } // e is a function pointer type and U has type *T, while T: Sized; fptr-ptr-cast - let _usize_ptr_transmute = unsafe { - foo as *const usize - }; + let _usize_ptr_transmute = unsafe { foo as *const usize }; let _usize_ptr_transmute = foo as *const usize; // e is a function pointer type and U is an integer; fptr-addr-cast - let _usize_from_fn_ptr_transmute = unsafe { - foo as usize - }; + let _usize_from_fn_ptr_transmute = unsafe { foo as usize }; let _usize_from_fn_ptr = foo as *const usize; } diff --git a/tests/ui/transmutes_expressible_as_ptr_casts.rs b/tests/ui/transmutes_expressible_as_ptr_casts.rs index fd5055c08f6..0205d1ece60 100644 --- a/tests/ui/transmutes_expressible_as_ptr_casts.rs +++ b/tests/ui/transmutes_expressible_as_ptr_casts.rs @@ -9,60 +9,48 @@ use std::mem::{size_of, transmute}; -// rustc_typeck::check::cast contains documentation about when a cast `e as U` is +// rustc_typeck::check::cast contains documentation about when a cast `e as U` is // valid, which we quote from below. fn main() { // We should see an error message for each transmute, and no error messages for // the casts, since the casts are the recommended fixes. // e is an integer and U is *U_0, while U_0: Sized; addr-ptr-cast - let _ptr_i32_transmute = unsafe { - transmute::(usize::MAX) - }; + let _ptr_i32_transmute = unsafe { transmute::(usize::MAX) }; let ptr_i32 = usize::MAX as *const i32; // e has type *T, U is *U_0, and either U_0: Sized ... - let _ptr_i8_transmute = unsafe { - transmute::<*const i32, *const i8>(ptr_i32) - }; + let _ptr_i8_transmute = unsafe { transmute::<*const i32, *const i8>(ptr_i32) }; let _ptr_i8 = ptr_i32 as *const i8; - let slice_ptr = &[0,1,2,3] as *const [i32]; + let slice_ptr = &[0, 1, 2, 3] as *const [i32]; // ... or pointer_kind(T) = pointer_kind(U_0); ptr-ptr-cast - let _ptr_to_unsized_transmute = unsafe { - transmute::<*const [i32], *const [u16]>(slice_ptr) - }; + let _ptr_to_unsized_transmute = unsafe { transmute::<*const [i32], *const [u16]>(slice_ptr) }; let _ptr_to_unsized = slice_ptr as *const [u16]; // TODO: We could try testing vtable casts here too, but maybe // we should wait until std::raw::TraitObject is stabilized? // e has type *T and U is a numeric type, while T: Sized; ptr-addr-cast - let _usize_from_int_ptr_transmute = unsafe { - transmute::<*const i32, usize>(ptr_i32) - }; + let _usize_from_int_ptr_transmute = unsafe { transmute::<*const i32, usize>(ptr_i32) }; let _usize_from_int_ptr = ptr_i32 as usize; - let array_ref: &[i32; 4] = &[1,2,3,4]; + let array_ref: &[i32; 4] = &[1, 2, 3, 4]; // e has type &[T; n] and U is *const T; array-ptr-cast - let _array_ptr_transmute = unsafe { - transmute::<&[i32; 4], *const [i32; 4]>(array_ref) - }; + let _array_ptr_transmute = unsafe { transmute::<&[i32; 4], *const [i32; 4]>(array_ref) }; let _array_ptr = array_ref as *const [i32; 4]; - fn foo(_: usize) -> u8 { 42 } + fn foo(_: usize) -> u8 { + 42 + } // e is a function pointer type and U has type *T, while T: Sized; fptr-ptr-cast - let _usize_ptr_transmute = unsafe { - transmute:: u8, *const usize>(foo) - }; + let _usize_ptr_transmute = unsafe { transmute:: u8, *const usize>(foo) }; let _usize_ptr_transmute = foo as *const usize; // e is a function pointer type and U is an integer; fptr-addr-cast - let _usize_from_fn_ptr_transmute = unsafe { - transmute:: u8, usize>(foo) - }; + let _usize_from_fn_ptr_transmute = unsafe { transmute:: u8, usize>(foo) }; let _usize_from_fn_ptr = foo as *const usize; } diff --git a/tests/ui/transmutes_expressible_as_ptr_casts.stderr b/tests/ui/transmutes_expressible_as_ptr_casts.stderr index 46597acc6c0..1157b179317 100644 --- a/tests/ui/transmutes_expressible_as_ptr_casts.stderr +++ b/tests/ui/transmutes_expressible_as_ptr_casts.stderr @@ -1,53 +1,53 @@ error: transmute from an integer to a pointer - --> $DIR/transmutes_expressible_as_ptr_casts.rs:20:9 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:19:39 | -LL | transmute::(usize::MAX) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `usize::MAX as *const i32` +LL | let _ptr_i32_transmute = unsafe { transmute::(usize::MAX) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `usize::MAX as *const i32` | = note: `-D clippy::useless-transmute` implied by `-D warnings` error: transmute from a pointer to a pointer - --> $DIR/transmutes_expressible_as_ptr_casts.rs:26:9 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:23:38 | -LL | transmute::<*const i32, *const i8>(ptr_i32) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_i32 as *const i8` +LL | let _ptr_i8_transmute = unsafe { transmute::<*const i32, *const i8>(ptr_i32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_i32 as *const i8` | = note: `-D clippy::transmute-ptr-to-ptr` implied by `-D warnings` error: transmute from a pointer to a pointer - --> $DIR/transmutes_expressible_as_ptr_casts.rs:34:9 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:29:46 | -LL | transmute::<*const [i32], *const [u16]>(slice_ptr) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `slice_ptr as *const [u16]` +LL | let _ptr_to_unsized_transmute = unsafe { transmute::<*const [i32], *const [u16]>(slice_ptr) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `slice_ptr as *const [u16]` error: transmute from `*const i32` to `usize` which could be expressed as a pointer cast instead - --> $DIR/transmutes_expressible_as_ptr_casts.rs:42:9 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:35:50 | -LL | transmute::<*const i32, usize>(ptr_i32) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_i32 as usize` +LL | let _usize_from_int_ptr_transmute = unsafe { transmute::<*const i32, usize>(ptr_i32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_i32 as usize` | = note: `-D clippy::transmutes-expressible-as-ptr-casts` implied by `-D warnings` error: transmute from a reference to a pointer - --> $DIR/transmutes_expressible_as_ptr_casts.rs:50:9 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:41:41 | -LL | transmute::<&[i32; 4], *const [i32; 4]>(array_ref) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `array_ref as *const [i32; 4]` +LL | let _array_ptr_transmute = unsafe { transmute::<&[i32; 4], *const [i32; 4]>(array_ref) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `array_ref as *const [i32; 4]` error: transmute from `fn(usize) -> u8 {main::foo}` to `*const usize` which could be expressed as a pointer cast instead - --> $DIR/transmutes_expressible_as_ptr_casts.rs:58:9 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:49:41 | -LL | transmute:: u8, *const usize>(foo) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as *const usize` +LL | let _usize_ptr_transmute = unsafe { transmute:: u8, *const usize>(foo) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as *const usize` error: transmute from `fn(usize) -> u8 {main::foo}` to `usize` which could be expressed as a pointer cast instead - --> $DIR/transmutes_expressible_as_ptr_casts.rs:64:9 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:53:49 | -LL | transmute:: u8, usize>(foo) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as usize` +LL | let _usize_from_fn_ptr_transmute = unsafe { transmute:: u8, usize>(foo) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as usize` error: transmute from a reference to a pointer - --> $DIR/transmutes_expressible_as_ptr_casts.rs:77:14 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:65:14 | LL | unsafe { transmute::<&[i32; 1], *const u8>(in_param) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `in_param as *const [i32; 1] as *const u8` -- cgit 1.4.1-3-g733a5 From c0a9d64818d7076b72fd6c3a9e6172eca659034b Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Tue, 11 Aug 2020 11:50:26 +0200 Subject: stable-sort-primitive: make lint adhere to lint message convention --- clippy_lints/src/stable_sort_primitive.rs | 6 +++--- tests/ui/stable_sort_primitive.stderr | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/stable_sort_primitive.rs b/clippy_lints/src/stable_sort_primitive.rs index cd7056620a2..22c49a20451 100644 --- a/clippy_lints/src/stable_sort_primitive.rs +++ b/clippy_lints/src/stable_sort_primitive.rs @@ -111,9 +111,9 @@ impl LateLintPass<'_> for StableSortPrimitive { STABLE_SORT_PRIMITIVE, expr.span, format!( - "Use {} instead of {}", - detection.method.unstable_name(), - detection.method.stable_name() + "used {} instead of {}", + detection.method.stable_name(), + detection.method.unstable_name() ) .as_str(), "try", diff --git a/tests/ui/stable_sort_primitive.stderr b/tests/ui/stable_sort_primitive.stderr index b0b729ede48..b73012a4691 100644 --- a/tests/ui/stable_sort_primitive.stderr +++ b/tests/ui/stable_sort_primitive.stderr @@ -1,4 +1,4 @@ -error: Use sort_unstable instead of sort +error: used sort instead of sort_unstable --> $DIR/stable_sort_primitive.rs:7:5 | LL | vec.sort(); @@ -6,37 +6,37 @@ LL | vec.sort(); | = note: `-D clippy::stable-sort-primitive` implied by `-D warnings` -error: Use sort_unstable instead of sort +error: used sort instead of sort_unstable --> $DIR/stable_sort_primitive.rs:9:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` -error: Use sort_unstable instead of sort +error: used sort instead of sort_unstable --> $DIR/stable_sort_primitive.rs:11:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` -error: Use sort_unstable instead of sort +error: used sort instead of sort_unstable --> $DIR/stable_sort_primitive.rs:13:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` -error: Use sort_unstable instead of sort +error: used sort instead of sort_unstable --> $DIR/stable_sort_primitive.rs:15:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` -error: Use sort_unstable instead of sort +error: used sort instead of sort_unstable --> $DIR/stable_sort_primitive.rs:17:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` -error: Use sort_unstable instead of sort +error: used sort instead of sort_unstable --> $DIR/stable_sort_primitive.rs:19:5 | LL | arr.sort(); -- cgit 1.4.1-3-g733a5 From ac194cafc124276d4614bf023ca7ea6e9be9c6ed Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Tue, 11 Aug 2020 11:53:21 +0200 Subject: map_clone: make lint adhere to lint message convention --- clippy_lints/src/map_clone.rs | 12 ++++++------ tests/ui/map_clone.stderr | 24 ++++++++++++------------ 2 files changed, 18 insertions(+), 18 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs index 641e6a17043..1cd5b201292 100644 --- a/clippy_lints/src/map_clone.rs +++ b/clippy_lints/src/map_clone.rs @@ -111,8 +111,8 @@ fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) { cx, MAP_CLONE, root.trim_start(receiver).unwrap(), - "You are needlessly cloning iterator elements", - "Remove the `map` call", + "you are needlessly cloning iterator elements", + "remove the `map` call", String::new(), Applicability::MachineApplicable, ) @@ -125,8 +125,8 @@ fn lint(cx: &LateContext<'_>, replace: Span, root: Span, copied: bool) { cx, MAP_CLONE, replace, - "You are using an explicit closure for copying elements", - "Consider calling the dedicated `copied` method", + "you are using an explicit closure for copying elements", + "consider calling the dedicated `copied` method", format!( "{}.copied()", snippet_with_applicability(cx, root, "..", &mut applicability) @@ -138,8 +138,8 @@ fn lint(cx: &LateContext<'_>, replace: Span, root: Span, copied: bool) { cx, MAP_CLONE, replace, - "You are using an explicit closure for cloning elements", - "Consider calling the dedicated `cloned` method", + "you are using an explicit closure for cloning elements", + "consider calling the dedicated `cloned` method", format!( "{}.cloned()", snippet_with_applicability(cx, root, "..", &mut applicability) diff --git a/tests/ui/map_clone.stderr b/tests/ui/map_clone.stderr index 9eec6928e8c..4f43cff5024 100644 --- a/tests/ui/map_clone.stderr +++ b/tests/ui/map_clone.stderr @@ -1,40 +1,40 @@ -error: You are using an explicit closure for copying elements +error: you are using an explicit closure for copying elements --> $DIR/map_clone.rs:10:22 | LL | let _: Vec = vec![5_i8; 6].iter().map(|x| *x).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `vec![5_i8; 6].iter().copied()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `vec![5_i8; 6].iter().copied()` | = note: `-D clippy::map-clone` implied by `-D warnings` -error: You are using an explicit closure for cloning elements +error: you are using an explicit closure for cloning elements --> $DIR/map_clone.rs:11:26 | LL | let _: Vec = vec![String::new()].iter().map(|x| x.clone()).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `cloned` method: `vec![String::new()].iter().cloned()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `vec![String::new()].iter().cloned()` -error: You are using an explicit closure for copying elements +error: you are using an explicit closure for copying elements --> $DIR/map_clone.rs:12:23 | LL | let _: Vec = vec![42, 43].iter().map(|&x| x).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `vec![42, 43].iter().copied()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `vec![42, 43].iter().copied()` -error: You are using an explicit closure for copying elements +error: you are using an explicit closure for copying elements --> $DIR/map_clone.rs:14:26 | LL | let _: Option = Some(&16).map(|b| *b); - | ^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `Some(&16).copied()` + | ^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `Some(&16).copied()` -error: You are using an explicit closure for copying elements +error: you are using an explicit closure for copying elements --> $DIR/map_clone.rs:15:25 | LL | let _: Option = Some(&1).map(|x| x.clone()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Consider calling the dedicated `copied` method: `Some(&1).copied()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `Some(&1).copied()` -error: You are needlessly cloning iterator elements +error: you are needlessly cloning iterator elements --> $DIR/map_clone.rs:26:29 | LL | let _ = std::env::args().map(|v| v.clone()); - | ^^^^^^^^^^^^^^^^^^^ help: Remove the `map` call + | ^^^^^^^^^^^^^^^^^^^ help: remove the `map` call error: aborting due to 6 previous errors -- cgit 1.4.1-3-g733a5 From 04867e004ebc0f2edf66d0a457e785848451f13a Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Tue, 11 Aug 2020 12:10:42 +0200 Subject: mutex-atomic: make lint adhere to lint message convention --- clippy_lints/src/mutex_atomic.rs | 4 ++-- tests/ui/mutex_atomic.stderr | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs index 568898aa5c9..21efee71269 100644 --- a/clippy_lints/src/mutex_atomic.rs +++ b/clippy_lints/src/mutex_atomic.rs @@ -72,8 +72,8 @@ impl<'tcx> LateLintPass<'tcx> for Mutex { let mutex_param = subst.type_at(0); if let Some(atomic_name) = get_atomic_name(mutex_param) { let msg = format!( - "Consider using an `{}` instead of a `Mutex` here. If you just want the locking \ - behavior and not the internal type, consider using `Mutex<()>`.", + "consider using an `{}` instead of a `Mutex` here; if you just want the locking \ + behavior and not the internal type, consider using `Mutex<()>`", atomic_name ); match mutex_param.kind { diff --git a/tests/ui/mutex_atomic.stderr b/tests/ui/mutex_atomic.stderr index 7dac0865855..a3511ba708a 100644 --- a/tests/ui/mutex_atomic.stderr +++ b/tests/ui/mutex_atomic.stderr @@ -1,4 +1,4 @@ -error: Consider using an `AtomicBool` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. +error: consider using an `AtomicBool` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` --> $DIR/mutex_atomic.rs:6:5 | LL | Mutex::new(true); @@ -6,31 +6,31 @@ LL | Mutex::new(true); | = note: `-D clippy::mutex-atomic` implied by `-D warnings` -error: Consider using an `AtomicUsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. +error: consider using an `AtomicUsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` --> $DIR/mutex_atomic.rs:7:5 | LL | Mutex::new(5usize); | ^^^^^^^^^^^^^^^^^^ -error: Consider using an `AtomicIsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. +error: consider using an `AtomicIsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` --> $DIR/mutex_atomic.rs:8:5 | LL | Mutex::new(9isize); | ^^^^^^^^^^^^^^^^^^ -error: Consider using an `AtomicPtr` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. +error: consider using an `AtomicPtr` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` --> $DIR/mutex_atomic.rs:10:5 | LL | Mutex::new(&x as *const u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: Consider using an `AtomicPtr` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. +error: consider using an `AtomicPtr` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` --> $DIR/mutex_atomic.rs:11:5 | LL | Mutex::new(&mut x as *mut u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: Consider using an `AtomicUsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. +error: consider using an `AtomicUsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` --> $DIR/mutex_atomic.rs:12:5 | LL | Mutex::new(0u32); @@ -38,7 +38,7 @@ LL | Mutex::new(0u32); | = note: `-D clippy::mutex-integer` implied by `-D warnings` -error: Consider using an `AtomicIsize` instead of a `Mutex` here. If you just want the locking behavior and not the internal type, consider using `Mutex<()>`. +error: consider using an `AtomicIsize` instead of a `Mutex` here; if you just want the locking behavior and not the internal type, consider using `Mutex<()>` --> $DIR/mutex_atomic.rs:13:5 | LL | Mutex::new(0i32); -- cgit 1.4.1-3-g733a5 From 6af297f80e59050c87078f1ba6f05c97d6f90fd7 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Tue, 11 Aug 2020 12:42:50 +0200 Subject: iter-next-slice: make lint adhere to lint message convention --- clippy_lints/src/methods/mod.rs | 4 ++-- tests/ui/iter_next_slice.stderr | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 570ae66d595..f4eb9c4516f 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2280,7 +2280,7 @@ fn lint_iter_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, iter_ cx, ITER_NEXT_SLICE, expr.span, - "Using `.iter().next()` on a Slice without end index.", + "using `.iter().next()` on a Slice without end index", "try calling", format!("{}.get({})", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability), start_idx), applicability, @@ -2299,7 +2299,7 @@ fn lint_iter_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, iter_ cx, ITER_NEXT_SLICE, expr.span, - "Using `.iter().next()` on an array", + "using `.iter().next()` on an array", "try calling", format!( "{}.get(0)", diff --git a/tests/ui/iter_next_slice.stderr b/tests/ui/iter_next_slice.stderr index bbf61df0cda..8c10a252ee0 100644 --- a/tests/ui/iter_next_slice.stderr +++ b/tests/ui/iter_next_slice.stderr @@ -1,4 +1,4 @@ -error: Using `.iter().next()` on an array +error: using `.iter().next()` on an array --> $DIR/iter_next_slice.rs:9:5 | LL | s.iter().next(); @@ -6,19 +6,19 @@ LL | s.iter().next(); | = note: `-D clippy::iter-next-slice` implied by `-D warnings` -error: Using `.iter().next()` on a Slice without end index. +error: using `.iter().next()` on a Slice without end index --> $DIR/iter_next_slice.rs:12:5 | LL | s[2..].iter().next(); | ^^^^^^^^^^^^^^^^^^^^ help: try calling: `s.get(2)` -error: Using `.iter().next()` on a Slice without end index. +error: using `.iter().next()` on a Slice without end index --> $DIR/iter_next_slice.rs:15:5 | LL | v[5..].iter().next(); | ^^^^^^^^^^^^^^^^^^^^ help: try calling: `v.get(5)` -error: Using `.iter().next()` on an array +error: using `.iter().next()` on an array --> $DIR/iter_next_slice.rs:18:5 | LL | v.iter().next(); -- cgit 1.4.1-3-g733a5 From f171f89aed11043e459c3baab305e7f859debb94 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Tue, 11 Aug 2020 15:14:32 +0200 Subject: int_plus_one: make lint adhere to lint message convention --- clippy_lints/src/int_plus_one.rs | 6 +++--- tests/ui/int_plus_one.stderr | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/int_plus_one.rs b/clippy_lints/src/int_plus_one.rs index e91fb0c2f27..c629ee05ab9 100644 --- a/clippy_lints/src/int_plus_one.rs +++ b/clippy_lints/src/int_plus_one.rs @@ -152,7 +152,7 @@ impl IntPlusOne { cx, INT_PLUS_ONE, block.span, - "Unnecessary `>= y + 1` or `x - 1 >=`", + "unnecessary `>= y + 1` or `x - 1 >=`", "change it to", recommendation, Applicability::MachineApplicable, // snippet @@ -163,8 +163,8 @@ impl IntPlusOne { impl EarlyLintPass for IntPlusOne { fn check_expr(&mut self, cx: &EarlyContext<'_>, item: &Expr) { if let ExprKind::Binary(ref kind, ref lhs, ref rhs) = item.kind { - if let Some(ref rec) = Self::check_binop(cx, kind.node, lhs, rhs) { - Self::emit_warning(cx, item, rec.clone()); + if let Some(rec) = Self::check_binop(cx, kind.node, lhs, rhs) { + Self::emit_warning(cx, item, rec); } } } diff --git a/tests/ui/int_plus_one.stderr b/tests/ui/int_plus_one.stderr index 29a6914761c..c5b020ba8ce 100644 --- a/tests/ui/int_plus_one.stderr +++ b/tests/ui/int_plus_one.stderr @@ -1,4 +1,4 @@ -error: Unnecessary `>= y + 1` or `x - 1 >=` +error: unnecessary `>= y + 1` or `x - 1 >=` --> $DIR/int_plus_one.rs:9:13 | LL | let _ = x >= y + 1; @@ -6,19 +6,19 @@ LL | let _ = x >= y + 1; | = note: `-D clippy::int-plus-one` implied by `-D warnings` -error: Unnecessary `>= y + 1` or `x - 1 >=` +error: unnecessary `>= y + 1` or `x - 1 >=` --> $DIR/int_plus_one.rs:10:13 | LL | let _ = y + 1 <= x; | ^^^^^^^^^^ help: change it to: `y < x` -error: Unnecessary `>= y + 1` or `x - 1 >=` +error: unnecessary `>= y + 1` or `x - 1 >=` --> $DIR/int_plus_one.rs:12:13 | LL | let _ = x - 1 >= y; | ^^^^^^^^^^ help: change it to: `x > y` -error: Unnecessary `>= y + 1` or `x - 1 >=` +error: unnecessary `>= y + 1` or `x - 1 >=` --> $DIR/int_plus_one.rs:13:13 | LL | let _ = y <= x - 1; -- cgit 1.4.1-3-g733a5 From bdf4dc3abd9a49f699d9de209a1f4d55ce770191 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Tue, 11 Aug 2020 15:22:59 +0200 Subject: implicit-saturating-sub: make lint adhere to lint message convention --- clippy_lints/src/implicit_saturating_sub.rs | 4 +-- tests/ui/implicit_saturating_sub.stderr | 46 ++++++++++++++--------------- 2 files changed, 25 insertions(+), 25 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index 5f931a0adde..b57fe8dc426 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -158,9 +158,9 @@ fn print_lint_and_sugg(cx: &LateContext<'_>, var_name: &str, expr: &Expr<'_>) { cx, IMPLICIT_SATURATING_SUB, expr.span, - "Implicitly performing saturating subtraction", + "implicitly performing saturating subtraction", "try", - format!("{} = {}.saturating_sub({});", var_name, var_name, 1.to_string()), + format!("{} = {}.saturating_sub({});", var_name, var_name, '1'), Applicability::MachineApplicable, ); } diff --git a/tests/ui/implicit_saturating_sub.stderr b/tests/ui/implicit_saturating_sub.stderr index 2eb2023b3b9..5bb9a606422 100644 --- a/tests/ui/implicit_saturating_sub.stderr +++ b/tests/ui/implicit_saturating_sub.stderr @@ -1,4 +1,4 @@ -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:13:5 | LL | / if u_8 > 0 { @@ -8,7 +8,7 @@ LL | | } | = note: `-D clippy::implicit-saturating-sub` implied by `-D warnings` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:20:13 | LL | / if u_8 > 0 { @@ -16,7 +16,7 @@ LL | | u_8 -= 1; LL | | } | |_____________^ help: try: `u_8 = u_8.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:34:5 | LL | / if u_16 > 0 { @@ -24,7 +24,7 @@ LL | | u_16 -= 1; LL | | } | |_____^ help: try: `u_16 = u_16.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:44:5 | LL | / if u_32 != 0 { @@ -32,7 +32,7 @@ LL | | u_32 -= 1; LL | | } | |_____^ help: try: `u_32 = u_32.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:65:5 | LL | / if u_64 > 0 { @@ -40,7 +40,7 @@ LL | | u_64 -= 1; LL | | } | |_____^ help: try: `u_64 = u_64.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:70:5 | LL | / if 0 < u_64 { @@ -48,7 +48,7 @@ LL | | u_64 -= 1; LL | | } | |_____^ help: try: `u_64 = u_64.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:75:5 | LL | / if 0 != u_64 { @@ -56,7 +56,7 @@ LL | | u_64 -= 1; LL | | } | |_____^ help: try: `u_64 = u_64.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:96:5 | LL | / if u_usize > 0 { @@ -64,7 +64,7 @@ LL | | u_usize -= 1; LL | | } | |_____^ help: try: `u_usize = u_usize.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:108:5 | LL | / if i_8 > i8::MIN { @@ -72,7 +72,7 @@ LL | | i_8 -= 1; LL | | } | |_____^ help: try: `i_8 = i_8.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:113:5 | LL | / if i_8 > i8::MIN { @@ -80,7 +80,7 @@ LL | | i_8 -= 1; LL | | } | |_____^ help: try: `i_8 = i_8.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:118:5 | LL | / if i_8 != i8::MIN { @@ -88,7 +88,7 @@ LL | | i_8 -= 1; LL | | } | |_____^ help: try: `i_8 = i_8.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:123:5 | LL | / if i_8 != i8::MIN { @@ -96,7 +96,7 @@ LL | | i_8 -= 1; LL | | } | |_____^ help: try: `i_8 = i_8.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:133:5 | LL | / if i_16 > i16::MIN { @@ -104,7 +104,7 @@ LL | | i_16 -= 1; LL | | } | |_____^ help: try: `i_16 = i_16.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:138:5 | LL | / if i_16 > i16::MIN { @@ -112,7 +112,7 @@ LL | | i_16 -= 1; LL | | } | |_____^ help: try: `i_16 = i_16.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:143:5 | LL | / if i_16 != i16::MIN { @@ -120,7 +120,7 @@ LL | | i_16 -= 1; LL | | } | |_____^ help: try: `i_16 = i_16.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:148:5 | LL | / if i_16 != i16::MIN { @@ -128,7 +128,7 @@ LL | | i_16 -= 1; LL | | } | |_____^ help: try: `i_16 = i_16.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:158:5 | LL | / if i_32 > i32::MIN { @@ -136,7 +136,7 @@ LL | | i_32 -= 1; LL | | } | |_____^ help: try: `i_32 = i_32.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:163:5 | LL | / if i_32 > i32::MIN { @@ -144,7 +144,7 @@ LL | | i_32 -= 1; LL | | } | |_____^ help: try: `i_32 = i_32.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:168:5 | LL | / if i_32 != i32::MIN { @@ -152,7 +152,7 @@ LL | | i_32 -= 1; LL | | } | |_____^ help: try: `i_32 = i_32.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:173:5 | LL | / if i_32 != i32::MIN { @@ -160,7 +160,7 @@ LL | | i_32 -= 1; LL | | } | |_____^ help: try: `i_32 = i_32.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:183:5 | LL | / if i64::MIN < i_64 { @@ -168,7 +168,7 @@ LL | | i_64 -= 1; LL | | } | |_____^ help: try: `i_64 = i_64.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:188:5 | LL | / if i64::MIN != i_64 { @@ -176,7 +176,7 @@ LL | | i_64 -= 1; LL | | } | |_____^ help: try: `i_64 = i_64.saturating_sub(1);` -error: Implicitly performing saturating subtraction +error: implicitly performing saturating subtraction --> $DIR/implicit_saturating_sub.rs:193:5 | LL | / if i64::MIN < i_64 { -- cgit 1.4.1-3-g733a5 From 1f17c3b02bce95c7c95a320e9e6e8e88b216d235 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Tue, 11 Aug 2020 15:28:51 +0200 Subject: multiple_inherent_impl: make lint adhere to lint message convention --- clippy_lints/src/inherent_impl.rs | 4 ++-- tests/ui/impl.stderr | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/inherent_impl.rs b/clippy_lints/src/inherent_impl.rs index 9fb10c7f627..4e6bb604785 100644 --- a/clippy_lints/src/inherent_impl.rs +++ b/clippy_lints/src/inherent_impl.rs @@ -81,9 +81,9 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl { cx, MULTIPLE_INHERENT_IMPL, *additional_span, - "Multiple implementations of this structure", + "multiple implementations of this structure", |diag| { - diag.span_note(*initial_span, "First implementation here"); + diag.span_note(*initial_span, "first implementation here"); }, ) }) diff --git a/tests/ui/impl.stderr b/tests/ui/impl.stderr index 585d32845d2..aab688cc2d8 100644 --- a/tests/ui/impl.stderr +++ b/tests/ui/impl.stderr @@ -1,4 +1,4 @@ -error: Multiple implementations of this structure +error: multiple implementations of this structure --> $DIR/impl.rs:10:1 | LL | / impl MyStruct { @@ -7,7 +7,7 @@ LL | | } | |_^ | = note: `-D clippy::multiple-inherent-impl` implied by `-D warnings` -note: First implementation here +note: first implementation here --> $DIR/impl.rs:6:1 | LL | / impl MyStruct { @@ -15,7 +15,7 @@ LL | | fn first() {} LL | | } | |_^ -error: Multiple implementations of this structure +error: multiple implementations of this structure --> $DIR/impl.rs:24:5 | LL | / impl super::MyStruct { @@ -23,7 +23,7 @@ LL | | fn third() {} LL | | } | |_____^ | -note: First implementation here +note: first implementation here --> $DIR/impl.rs:6:1 | LL | / impl MyStruct { -- cgit 1.4.1-3-g733a5 From 423615693ba27f77f2e01a82948bbe592f48f6d0 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Tue, 11 Aug 2020 16:28:05 +0200 Subject: pub-enum-variant-names: make lint adhere to lint message convention --- clippy_lints/src/enum_variants.rs | 2 +- tests/ui/enum_variants.stderr | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs index cb0fd59a2d4..d73d0f1752e 100644 --- a/clippy_lints/src/enum_variants.rs +++ b/clippy_lints/src/enum_variants.rs @@ -227,7 +227,7 @@ fn check_variant( cx, lint, span, - &format!("All variants have the same {}fix: `{}`", what, value), + &format!("all variants have the same {}fix: `{}`", what, value), None, &format!( "remove the {}fixes and use full paths to \ diff --git a/tests/ui/enum_variants.stderr b/tests/ui/enum_variants.stderr index 2835391de7f..3aa0e4ddcfe 100644 --- a/tests/ui/enum_variants.stderr +++ b/tests/ui/enum_variants.stderr @@ -24,7 +24,7 @@ error: Variant name starts with the enum's name LL | FoodBad, | ^^^^^^^ -error: All variants have the same prefix: `Food` +error: all variants have the same prefix: `Food` --> $DIR/enum_variants.rs:26:1 | LL | / enum Food { @@ -36,7 +36,7 @@ LL | | } | = help: remove the prefixes and use full paths to the variants instead of glob imports -error: All variants have the same prefix: `CallType` +error: all variants have the same prefix: `CallType` --> $DIR/enum_variants.rs:36:1 | LL | / enum BadCallType { @@ -48,7 +48,7 @@ LL | | } | = help: remove the prefixes and use full paths to the variants instead of glob imports -error: All variants have the same prefix: `Constant` +error: all variants have the same prefix: `Constant` --> $DIR/enum_variants.rs:48:1 | LL | / enum Consts { @@ -60,7 +60,7 @@ LL | | } | = help: remove the prefixes and use full paths to the variants instead of glob imports -error: All variants have the same prefix: `With` +error: all variants have the same prefix: `With` --> $DIR/enum_variants.rs:82:1 | LL | / enum Seallll { @@ -72,7 +72,7 @@ LL | | } | = help: remove the prefixes and use full paths to the variants instead of glob imports -error: All variants have the same prefix: `Prefix` +error: all variants have the same prefix: `Prefix` --> $DIR/enum_variants.rs:88:1 | LL | / enum NonCaps { @@ -84,7 +84,7 @@ LL | | } | = help: remove the prefixes and use full paths to the variants instead of glob imports -error: All variants have the same prefix: `With` +error: all variants have the same prefix: `With` --> $DIR/enum_variants.rs:94:1 | LL | / pub enum PubSeall { -- cgit 1.4.1-3-g733a5 From 2de290d5c5e1d2f0c0f112a51f0cba2e0cb91636 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Tue, 11 Aug 2020 16:31:02 +0200 Subject: duration-subsec: make lint adhere to lint message convention --- clippy_lints/src/duration_subsec.rs | 2 +- tests/ui/duration_subsec.stderr | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/duration_subsec.rs b/clippy_lints/src/duration_subsec.rs index 1dfb2eaa579..8ece44878fe 100644 --- a/clippy_lints/src/duration_subsec.rs +++ b/clippy_lints/src/duration_subsec.rs @@ -56,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for DurationSubsec { cx, DURATION_SUBSEC, expr.span, - &format!("Calling `{}()` is more concise than this calculation", suggested_fn), + &format!("calling `{}()` is more concise than this calculation", suggested_fn), "try", format!( "{}.{}()", diff --git a/tests/ui/duration_subsec.stderr b/tests/ui/duration_subsec.stderr index bd8adc2c570..cdbeff6a037 100644 --- a/tests/ui/duration_subsec.stderr +++ b/tests/ui/duration_subsec.stderr @@ -1,4 +1,4 @@ -error: Calling `subsec_millis()` is more concise than this calculation +error: calling `subsec_millis()` is more concise than this calculation --> $DIR/duration_subsec.rs:10:24 | LL | let bad_millis_1 = dur.subsec_micros() / 1_000; @@ -6,25 +6,25 @@ LL | let bad_millis_1 = dur.subsec_micros() / 1_000; | = note: `-D clippy::duration-subsec` implied by `-D warnings` -error: Calling `subsec_millis()` is more concise than this calculation +error: calling `subsec_millis()` is more concise than this calculation --> $DIR/duration_subsec.rs:11:24 | LL | let bad_millis_2 = dur.subsec_nanos() / 1_000_000; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_millis()` -error: Calling `subsec_micros()` is more concise than this calculation +error: calling `subsec_micros()` is more concise than this calculation --> $DIR/duration_subsec.rs:16:22 | LL | let bad_micros = dur.subsec_nanos() / 1_000; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `dur.subsec_micros()` -error: Calling `subsec_micros()` is more concise than this calculation +error: calling `subsec_micros()` is more concise than this calculation --> $DIR/duration_subsec.rs:21:13 | LL | let _ = (&dur).subsec_nanos() / 1_000; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(&dur).subsec_micros()` -error: Calling `subsec_micros()` is more concise than this calculation +error: calling `subsec_micros()` is more concise than this calculation --> $DIR/duration_subsec.rs:25:13 | LL | let _ = dur.subsec_nanos() / NANOS_IN_MICRO; -- cgit 1.4.1-3-g733a5 From db390f5e6a2e68a0f9e0d235f2b734e907cafef9 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Tue, 11 Aug 2020 16:35:09 +0200 Subject: enum-clike-unportable-variant: tweak message a bit (Clike -> C-like) --- clippy_lints/src/enum_clike.rs | 2 +- tests/ui/enum_clike_unportable_variant.stderr | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/enum_clike.rs b/clippy_lints/src/enum_clike.rs index 91214f277be..48caf48dbdb 100644 --- a/clippy_lints/src/enum_clike.rs +++ b/clippy_lints/src/enum_clike.rs @@ -72,7 +72,7 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant { cx, ENUM_CLIKE_UNPORTABLE_VARIANT, var.span, - "Clike enum variant discriminant is not portable to 32-bit targets", + "C-like enum variant discriminant is not portable to 32-bit targets", ); }; } diff --git a/tests/ui/enum_clike_unportable_variant.stderr b/tests/ui/enum_clike_unportable_variant.stderr index 71f3f5e083e..5935eea5e03 100644 --- a/tests/ui/enum_clike_unportable_variant.stderr +++ b/tests/ui/enum_clike_unportable_variant.stderr @@ -1,4 +1,4 @@ -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:8:5 | LL | X = 0x1_0000_0000, @@ -6,49 +6,49 @@ LL | X = 0x1_0000_0000, | = note: `-D clippy::enum-clike-unportable-variant` implied by `-D warnings` -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:15:5 | LL | X = 0x1_0000_0000, | ^^^^^^^^^^^^^^^^^ -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:18:5 | LL | A = 0xFFFF_FFFF, | ^^^^^^^^^^^^^^^ -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:25:5 | LL | Z = 0xFFFF_FFFF, | ^^^^^^^^^^^^^^^ -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:26:5 | LL | A = 0x1_0000_0000, | ^^^^^^^^^^^^^^^^^ -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:28:5 | LL | C = (i32::MIN as isize) - 1, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:34:5 | LL | Z = 0xFFFF_FFFF, | ^^^^^^^^^^^^^^^ -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:35:5 | LL | A = 0x1_0000_0000, | ^^^^^^^^^^^^^^^^^ -error: Clike enum variant discriminant is not portable to 32-bit targets +error: C-like enum variant discriminant is not portable to 32-bit targets --> $DIR/enum_clike_unportable_variant.rs:40:5 | LL | X = ::Number, -- cgit 1.4.1-3-g733a5 From 89591a78b83df30830bcc8f6fe57f6fe1fbf918e Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Tue, 11 Aug 2020 16:38:20 +0200 Subject: enum-variant-names: make lint adhere to lint message convention --- clippy_lints/src/enum_variants.rs | 4 ++-- tests/ui/enum_variants.stderr | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs index d73d0f1752e..a9294a87f15 100644 --- a/clippy_lints/src/enum_variants.rs +++ b/clippy_lints/src/enum_variants.rs @@ -183,10 +183,10 @@ fn check_variant( && name.chars().nth(item_name_chars).map_or(false, |c| !c.is_lowercase()) && name.chars().nth(item_name_chars + 1).map_or(false, |c| !c.is_numeric()) { - span_lint(cx, lint, var.span, "Variant name starts with the enum's name"); + span_lint(cx, lint, var.span, "variant name starts with the enum's name"); } if partial_rmatch(item_name, &name) == item_name_chars { - span_lint(cx, lint, var.span, "Variant name ends with the enum's name"); + span_lint(cx, lint, var.span, "variant name ends with the enum's name"); } } let first = &def.variants[0].ident.name.as_str(); diff --git a/tests/ui/enum_variants.stderr b/tests/ui/enum_variants.stderr index 3aa0e4ddcfe..b1d481190ff 100644 --- a/tests/ui/enum_variants.stderr +++ b/tests/ui/enum_variants.stderr @@ -1,4 +1,4 @@ -error: Variant name ends with the enum's name +error: variant name ends with the enum's name --> $DIR/enum_variants.rs:16:5 | LL | cFoo, @@ -6,19 +6,19 @@ LL | cFoo, | = note: `-D clippy::enum-variant-names` implied by `-D warnings` -error: Variant name starts with the enum's name +error: variant name starts with the enum's name --> $DIR/enum_variants.rs:27:5 | LL | FoodGood, | ^^^^^^^^ -error: Variant name starts with the enum's name +error: variant name starts with the enum's name --> $DIR/enum_variants.rs:28:5 | LL | FoodMiddle, | ^^^^^^^^^^ -error: Variant name starts with the enum's name +error: variant name starts with the enum's name --> $DIR/enum_variants.rs:29:5 | LL | FoodBad, -- cgit 1.4.1-3-g733a5 From 605e027fda4420669784864940abcbabef5b0efe Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Tue, 11 Aug 2020 16:40:45 +0200 Subject: if_let_some_result: make lint adhere to lint message convention --- clippy_lints/src/if_let_some_result.rs | 4 ++-- tests/ui/if_let_some_result.stderr | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/if_let_some_result.rs b/clippy_lints/src/if_let_some_result.rs index 5b22df5fe49..28b20cdeac3 100644 --- a/clippy_lints/src/if_let_some_result.rs +++ b/clippy_lints/src/if_let_some_result.rs @@ -61,8 +61,8 @@ impl<'tcx> LateLintPass<'tcx> for OkIfLet { cx, IF_LET_SOME_RESULT, expr.span.with_hi(op.span.hi()), - "Matching on `Some` with `ok()` is redundant", - &format!("Consider matching on `Ok({})` and removing the call to `ok` instead", some_expr_string), + "matching on `Some` with `ok()` is redundant", + &format!("consider matching on `Ok({})` and removing the call to `ok` instead", some_expr_string), sugg, applicability, ); diff --git a/tests/ui/if_let_some_result.stderr b/tests/ui/if_let_some_result.stderr index 334ccb01016..6afee0f36b9 100644 --- a/tests/ui/if_let_some_result.stderr +++ b/tests/ui/if_let_some_result.stderr @@ -1,22 +1,22 @@ -error: Matching on `Some` with `ok()` is redundant +error: matching on `Some` with `ok()` is redundant --> $DIR/if_let_some_result.rs:6:5 | LL | if let Some(y) = x.parse().ok() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::if-let-some-result` implied by `-D warnings` -help: Consider matching on `Ok(y)` and removing the call to `ok` instead +help: consider matching on `Ok(y)` and removing the call to `ok` instead | LL | if let Ok(y) = x.parse() { | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: Matching on `Some` with `ok()` is redundant +error: matching on `Some` with `ok()` is redundant --> $DIR/if_let_some_result.rs:24:9 | LL | if let Some(y) = x . parse() . ok () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: Consider matching on `Ok(y)` and removing the call to `ok` instead +help: consider matching on `Ok(y)` and removing the call to `ok` instead | LL | if let Ok(y) = x . parse() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From be3e695b60b07e911a88f6cb660c5617836c5365 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Tue, 11 Aug 2020 16:43:53 +0200 Subject: if_not_else: make lint adhere to lint message convention --- clippy_lints/src/if_not_else.rs | 4 ++-- clippy_lints/src/use_self.rs | 2 +- src/lintlist/mod.rs | 2 +- tests/ui/if_not_else.stderr | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/if_not_else.rs b/clippy_lints/src/if_not_else.rs index c11e291f98e..b86d2e76656 100644 --- a/clippy_lints/src/if_not_else.rs +++ b/clippy_lints/src/if_not_else.rs @@ -60,7 +60,7 @@ impl EarlyLintPass for IfNotElse { cx, IF_NOT_ELSE, item.span, - "Unnecessary boolean `not` operation", + "unnecessary boolean `not` operation", None, "remove the `!` and swap the blocks of the `if`/`else`", ); @@ -70,7 +70,7 @@ impl EarlyLintPass for IfNotElse { cx, IF_NOT_ELSE, item.span, - "Unnecessary `!=` operation", + "unnecessary `!=` operation", None, "change to `==` and swap the blocks of the `if`/`else`", ); diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 776c6bc57ca..427a1b65773 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -50,7 +50,7 @@ declare_clippy_lint! { /// ``` pub USE_SELF, nursery, - "Unnecessary structure name repetition whereas `Self` is applicable" + "unnecessary structure name repetition whereas `Self` is applicable" } declare_lint_pass!(UseSelf => [USE_SELF]); diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index bbb300296be..ccc9e250952 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2498,7 +2498,7 @@ pub static ref ALL_LINTS: Vec = vec![ Lint { name: "use_self", group: "nursery", - desc: "Unnecessary structure name repetition whereas `Self` is applicable", + desc: "unnecessary structure name repetition whereas `Self` is applicable", deprecation: None, module: "use_self", }, diff --git a/tests/ui/if_not_else.stderr b/tests/ui/if_not_else.stderr index 78bc4d4bd20..53d1b86d02a 100644 --- a/tests/ui/if_not_else.stderr +++ b/tests/ui/if_not_else.stderr @@ -1,4 +1,4 @@ -error: Unnecessary boolean `not` operation +error: unnecessary boolean `not` operation --> $DIR/if_not_else.rs:9:5 | LL | / if !bla() { @@ -11,7 +11,7 @@ LL | | } = note: `-D clippy::if-not-else` implied by `-D warnings` = help: remove the `!` and swap the blocks of the `if`/`else` -error: Unnecessary `!=` operation +error: unnecessary `!=` operation --> $DIR/if_not_else.rs:14:5 | LL | / if 4 != 5 { -- cgit 1.4.1-3-g733a5 From f4eeff99b6f2d5a01f7af1eae46e1b84525bf95a Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Wed, 12 Aug 2020 09:17:40 -0600 Subject: add tests for Rem, BitAnd, BitOr, BitXor, Shl, and Shr --- tests/ui/suspicious_arithmetic_impl.rs | 52 +++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/ui/suspicious_arithmetic_impl.rs b/tests/ui/suspicious_arithmetic_impl.rs index 60c2f3ec9b6..5c280efac1a 100644 --- a/tests/ui/suspicious_arithmetic_impl.rs +++ b/tests/ui/suspicious_arithmetic_impl.rs @@ -1,5 +1,7 @@ #![warn(clippy::suspicious_arithmetic_impl)] -use std::ops::{Add, AddAssign, BitOrAssign, Div, DivAssign, Mul, MulAssign, Sub}; +use std::ops::{ + Add, AddAssign, BitAnd, BitOr, BitOrAssign, BitXor, Div, DivAssign, Mul, MulAssign, Rem, Shl, Shr, Sub, +}; #[derive(Copy, Clone)] struct Foo(u32); @@ -61,6 +63,54 @@ impl Div for Foo { } } +impl Rem for Foo { + type Output = Foo; + + fn rem(self, other: Self) -> Self { + Foo(self.0 / other.0) + } +} + +impl BitAnd for Foo { + type Output = Foo; + + fn bitand(self, other: Self) -> Self { + Foo(self.0 | other.0) + } +} + +impl BitOr for Foo { + type Output = Foo; + + fn bitor(self, other: Self) -> Self { + Foo(self.0 ^ other.0) + } +} + +impl BitXor for Foo { + type Output = Foo; + + fn bitxor(self, other: Self) -> Self { + Foo(self.0 & other.0) + } +} + +impl Shl for Foo { + type Output = Foo; + + fn shl(self, other: Self) -> Self { + Foo(self.0 >> other.0) + } +} + +impl Shr for Foo { + type Output = Foo; + + fn shr(self, other: Self) -> Self { + Foo(self.0 << other.0) + } +} + struct Bar(i32); impl Add for Bar { -- cgit 1.4.1-3-g733a5 From 7bd7a46331fbaa8b8ebbaacf178c988498df9f13 Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Wed, 12 Aug 2020 10:49:12 -0600 Subject: run tests/ui/update-references.sh to update 'suspicious_arithmetic_impl.rs' --- tests/ui/suspicious_arithmetic_impl.stderr | 44 +++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/tests/ui/suspicious_arithmetic_impl.stderr b/tests/ui/suspicious_arithmetic_impl.stderr index 23d47e3f1ff..388fc740082 100644 --- a/tests/ui/suspicious_arithmetic_impl.stderr +++ b/tests/ui/suspicious_arithmetic_impl.stderr @@ -1,5 +1,5 @@ error: suspicious use of binary operator in `Add` impl - --> $DIR/suspicious_arithmetic_impl.rs:11:20 + --> $DIR/suspicious_arithmetic_impl.rs:13:20 | LL | Foo(self.0 - other.0) | ^ @@ -7,7 +7,7 @@ LL | Foo(self.0 - other.0) = note: `-D clippy::suspicious-arithmetic-impl` implied by `-D warnings` error: suspicious use of binary operator in `AddAssign` impl - --> $DIR/suspicious_arithmetic_impl.rs:17:23 + --> $DIR/suspicious_arithmetic_impl.rs:19:23 | LL | *self = *self - other; | ^ @@ -15,10 +15,46 @@ LL | *self = *self - other; = note: `#[deny(clippy::suspicious_op_assign_impl)]` on by default error: suspicious use of binary operator in `MulAssign` impl - --> $DIR/suspicious_arithmetic_impl.rs:30:16 + --> $DIR/suspicious_arithmetic_impl.rs:32:16 | LL | self.0 /= other.0; | ^^ -error: aborting due to 3 previous errors +error: suspicious use of binary operator in `Rem` impl + --> $DIR/suspicious_arithmetic_impl.rs:70:20 + | +LL | Foo(self.0 / other.0) + | ^ + +error: suspicious use of binary operator in `BitAnd` impl + --> $DIR/suspicious_arithmetic_impl.rs:78:20 + | +LL | Foo(self.0 | other.0) + | ^ + +error: suspicious use of binary operator in `BitOr` impl + --> $DIR/suspicious_arithmetic_impl.rs:86:20 + | +LL | Foo(self.0 ^ other.0) + | ^ + +error: suspicious use of binary operator in `BitXor` impl + --> $DIR/suspicious_arithmetic_impl.rs:94:20 + | +LL | Foo(self.0 & other.0) + | ^ + +error: suspicious use of binary operator in `Shl` impl + --> $DIR/suspicious_arithmetic_impl.rs:102:20 + | +LL | Foo(self.0 >> other.0) + | ^^ + +error: suspicious use of binary operator in `Shr` impl + --> $DIR/suspicious_arithmetic_impl.rs:110:20 + | +LL | Foo(self.0 << other.0) + | ^^ + +error: aborting due to 9 previous errors -- cgit 1.4.1-3-g733a5 From 480ccc3dbec4440bea0aa1f47d2ad21ebcdd578e Mon Sep 17 00:00:00 2001 From: JarredAllen Date: Tue, 11 Aug 2020 18:01:10 -0700 Subject: Change Rc> recommendation to be Rc instead of Box --- clippy_lints/src/types.rs | 15 +++++++++++++-- tests/ui/redundant_allocation.fixed | 2 +- tests/ui/redundant_allocation.stderr | 2 +- 3 files changed, 15 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index c3dea447521..78cebc30472 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -353,14 +353,25 @@ impl Types { ); return; // don't recurse into the type } - if let Some(span) = match_type_parameter(cx, qpath, &paths::BOX) { + if match_type_parameter(cx, qpath, &paths::BOX).is_some() { + let box_ty = match &last_path_segment(qpath).args.unwrap().args[0] { + GenericArg::Type(ty) => match &ty.kind { + TyKind::Path(qpath) => qpath, + _ => panic!("Box that isn't a type"), + }, + _ => panic!("Rc without type argument"), + }; + let inner_span = match &last_path_segment(&box_ty).args.unwrap().args[0] { + GenericArg::Type(ty) => ty.span, + _ => panic!("Box without type argument"), + }; span_lint_and_sugg( cx, REDUNDANT_ALLOCATION, hir_ty.span, "usage of `Rc>`", "try", - snippet(cx, span, "..").to_string(), + format!("Rc<{}>", snippet(cx, inner_span, "..")), Applicability::MachineApplicable, ); return; // don't recurse into the type diff --git a/tests/ui/redundant_allocation.fixed b/tests/ui/redundant_allocation.fixed index 26635833458..6514fd6d1ac 100644 --- a/tests/ui/redundant_allocation.fixed +++ b/tests/ui/redundant_allocation.fixed @@ -33,7 +33,7 @@ pub fn test5(a: Rc) {} // Rc> -pub fn test6(a: Box) {} +pub fn test6(a: Rc) {} // Box<&T> diff --git a/tests/ui/redundant_allocation.stderr b/tests/ui/redundant_allocation.stderr index eaa57ce3024..92e4f67f5db 100644 --- a/tests/ui/redundant_allocation.stderr +++ b/tests/ui/redundant_allocation.stderr @@ -28,7 +28,7 @@ error: usage of `Rc>` --> $DIR/redundant_allocation.rs:36:17 | LL | pub fn test6(a: Rc>) {} - | ^^^^^^^^^^^^^ help: try: `Box` + | ^^^^^^^^^^^^^ help: try: `Rc` error: usage of `Box<&T>` --> $DIR/redundant_allocation.rs:40:22 -- cgit 1.4.1-3-g733a5 From 5634c8da02862653be557c6ab1242a6c9ce86ce8 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Wed, 12 Aug 2020 21:37:27 +0200 Subject: Fix: keep parenthesis for suggestion in `useless_conversion` lint --- clippy_lints/src/useless_conversion.rs | 5 +++-- tests/ui/useless_conversion.fixed | 5 +++++ tests/ui/useless_conversion.rs | 5 +++++ tests/ui/useless_conversion.stderr | 8 +++++++- 4 files changed, 20 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 1bf37632e32..4ab2b5e796d 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,3 +1,4 @@ +use crate::utils::sugg::Sugg; use crate::utils::{ get_parent_expr, is_type_diagnostic_item, match_def_path, match_trait_method, paths, snippet, snippet_with_macro_callsite, span_lint_and_help, span_lint_and_sugg, @@ -158,7 +159,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { if TyS::same_type(a, b); then { - let sugg = snippet(cx, args[0].span.source_callsite(), "").into_owned(); + let sugg = Sugg::hir_with_macro_callsite(cx, &args[0], "").maybe_par(); let sugg_msg = format!("consider removing `{}()`", snippet(cx, path.span, "From::from")); span_lint_and_sugg( @@ -167,7 +168,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { e.span, "useless conversion to the same type", &sugg_msg, - sugg, + sugg.to_string(), Applicability::MachineApplicable, // snippet ); } diff --git a/tests/ui/useless_conversion.fixed b/tests/ui/useless_conversion.fixed index 813cdaecaa9..8a9b0cd3cf0 100644 --- a/tests/ui/useless_conversion.fixed +++ b/tests/ui/useless_conversion.fixed @@ -64,4 +64,9 @@ fn main() { let _ = "".lines(); let _ = vec![1, 2, 3].into_iter(); let _: String = format!("Hello {}", "world"); + + // keep parenthesis 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 540fea23b36..4faa1572973 100644 --- a/tests/ui/useless_conversion.rs +++ b/tests/ui/useless_conversion.rs @@ -64,4 +64,9 @@ fn main() { let _ = "".lines().into_iter(); let _ = vec![1, 2, 3].into_iter().into_iter(); let _: String = format!("Hello {}", "world").into(); + + // keep parenthesis around `a + b` for suggestion (see #4750) + let a: i32 = 1; + let b: i32 = 1; + let _ = i32::from(a + b) * 3; } diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index b958b035452..f1e880d2696 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -64,5 +64,11 @@ error: useless conversion to the same type LL | let _: String = format!("Hello {}", "world").into(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `format!("Hello {}", "world")` -error: aborting due to 10 previous errors +error: useless conversion to the same type + --> $DIR/useless_conversion.rs:71:13 + | +LL | let _ = i32::from(a + b) * 3; + | ^^^^^^^^^^^^^^^^ help: consider removing `i32::from()`: `(a + b)` + +error: aborting due to 11 previous errors -- cgit 1.4.1-3-g733a5 From 8514b8407ac83dc02532c82c9188c49967d9a5d6 Mon Sep 17 00:00:00 2001 From: Christoph Walcher Date: Fri, 14 Aug 2020 14:13:35 +0200 Subject: appreciative too_large_for_stack in useless `vec!` Fixes: #5847 --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/utils/conf.rs | 2 +- clippy_lints/src/vec.rs | 97 ++++++++++++++++++++++++++---------------- tests/ui/vec.fixed | 7 +++ tests/ui/vec.rs | 7 +++ 5 files changed, 76 insertions(+), 39 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 6ad525d7620..4a4445200a1 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -930,11 +930,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box cognitive_complexity::CognitiveComplexity::new(cognitive_complexity_threshold)); let too_large_for_stack = conf.too_large_for_stack; store.register_late_pass(move || box escape::BoxedLocal{too_large_for_stack}); + store.register_late_pass(move || box vec::UselessVec{too_large_for_stack}); store.register_late_pass(|| box panic_unimplemented::PanicUnimplemented); store.register_late_pass(|| box strings::StringLitAsBytes); store.register_late_pass(|| box derive::Derive); store.register_late_pass(|| box types::CharLitAsU8); - store.register_late_pass(|| box vec::UselessVec); store.register_late_pass(|| box drop_bounds::DropBounds); store.register_late_pass(|| box get_last_with_len::GetLastWithLen); store.register_late_pass(|| box drop_forget_ref::DropForgetRef); diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index ba3492a6fff..292dbd7ad6b 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -138,7 +138,7 @@ define_Conf! { (type_complexity_threshold, "type_complexity_threshold": u64, 250), /// Lint: MANY_SINGLE_CHAR_NAMES. The maximum number of single char bindings a scope may have (single_char_binding_names_threshold, "single_char_binding_names_threshold": u64, 4), - /// Lint: BOXED_LOCAL. The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap + /// Lint: BOXED_LOCAL, USELESS_VEC. The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap (too_large_for_stack, "too_large_for_stack": u64, 200), /// Lint: ENUM_VARIANT_NAMES. The minimum number of enum variants for the lints about variant names to trigger (enum_variant_name_threshold, "enum_variant_name_threshold": u64, 3), diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index f2e76442a19..84e907d7125 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -1,13 +1,20 @@ -use crate::consts::constant; +use crate::consts::{constant, Constant}; +use crate::rustc_target::abi::LayoutOf; use crate::utils::{higher, is_copy, snippet_with_applicability, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; +#[allow(clippy::module_name_repetitions)] +#[derive(Copy, Clone)] +pub struct UselessVec { + pub too_large_for_stack: u64, +} + declare_clippy_lint! { /// **What it does:** Checks for usage of `&vec![..]` when using `&[..]` would /// be possible. @@ -31,7 +38,7 @@ declare_clippy_lint! { "useless `vec!`" } -declare_lint_pass!(UselessVec => [USELESS_VEC]); +impl_lint_pass!(UselessVec => [USELESS_VEC]); impl<'tcx> LateLintPass<'tcx> for UselessVec { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { @@ -42,7 +49,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { if let ExprKind::AddrOf(BorrowKind::Ref, _, ref addressee) = expr.kind; if let Some(vec_args) = higher::vec_macro(cx, addressee); then { - check_vec_macro(cx, &vec_args, expr.span); + self.check_vec_macro(cx, &vec_args, expr.span); } } @@ -60,46 +67,62 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { .ctxt() .outer_expn_data() .call_site; - check_vec_macro(cx, &vec_args, span); + self.check_vec_macro(cx, &vec_args, span); } } } } -fn check_vec_macro<'tcx>(cx: &LateContext<'tcx>, vec_args: &higher::VecArgs<'tcx>, span: Span) { - let mut applicability = Applicability::MachineApplicable; - let snippet = match *vec_args { - higher::VecArgs::Repeat(elem, len) => { - if constant(cx, cx.typeck_results(), len).is_some() { - format!( - "&[{}; {}]", - snippet_with_applicability(cx, elem.span, "elem", &mut applicability), - snippet_with_applicability(cx, len.span, "len", &mut applicability) - ) - } else { - return; - } - }, - higher::VecArgs::Vec(args) => { - if let Some(last) = args.iter().last() { - let span = args[0].span.to(last.span); +impl UselessVec { + fn check_vec_macro<'tcx>(self, cx: &LateContext<'tcx>, vec_args: &higher::VecArgs<'tcx>, span: Span) { + let mut applicability = Applicability::MachineApplicable; + let snippet = match *vec_args { + higher::VecArgs::Repeat(elem, len) => { + if let Some((Constant::Int(len_constant), _)) = constant(cx, cx.typeck_results(), len) { + #[allow(clippy::cast_possible_truncation)] + if len_constant as u64 * size_of(cx, elem) > self.too_large_for_stack { + return; + } - format!("&[{}]", snippet_with_applicability(cx, span, "..", &mut applicability)) - } else { - "&[]".into() - } - }, - }; + format!( + "&[{}; {}]", + snippet_with_applicability(cx, elem.span, "elem", &mut applicability), + snippet_with_applicability(cx, len.span, "len", &mut applicability) + ) + } else { + return; + } + }, + higher::VecArgs::Vec(args) => { + if let Some(last) = args.iter().last() { + #[allow(clippy::cast_possible_truncation)] + if args.len() as u64 * size_of(cx, last) > self.too_large_for_stack { + return; + } + let span = args[0].span.to(last.span); + + format!("&[{}]", snippet_with_applicability(cx, span, "..", &mut applicability)) + } else { + "&[]".into() + } + }, + }; + + span_lint_and_sugg( + cx, + USELESS_VEC, + span, + "useless use of `vec!`", + "you can use a slice directly", + snippet, + applicability, + ); + } +} - span_lint_and_sugg( - cx, - USELESS_VEC, - span, - "useless use of `vec!`", - "you can use a slice directly", - snippet, - applicability, - ); +fn size_of(cx: &LateContext<'_>, expr: &Expr<'_>) -> u64 { + let ty = cx.typeck_results().expr_ty_adjusted(expr); + cx.layout_of(ty).map_or(0, |l| l.size.bytes()) } /// Returns the item type of the vector (i.e., the `T` in `Vec`). diff --git a/tests/ui/vec.fixed b/tests/ui/vec.fixed index e73a791891f..85677159620 100644 --- a/tests/ui/vec.fixed +++ b/tests/ui/vec.fixed @@ -52,4 +52,11 @@ fn main() { for a in vec![NonCopy, NonCopy] { println!("{:?}", a); } + + on_vec(&vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack` + + // Ok + for a in vec![1; 201] { + println!("{:?}", a); + } } diff --git a/tests/ui/vec.rs b/tests/ui/vec.rs index 3eb960f53d7..03b8ee81665 100644 --- a/tests/ui/vec.rs +++ b/tests/ui/vec.rs @@ -52,4 +52,11 @@ fn main() { for a in vec![NonCopy, NonCopy] { println!("{:?}", a); } + + on_vec(&vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack` + + // Ok + for a in vec![1; 201] { + println!("{:?}", a); + } } -- cgit 1.4.1-3-g733a5 From 8e549978e58fe724c4637ab71808ff8785743c61 Mon Sep 17 00:00:00 2001 From: chansuke Date: Wed, 22 Jul 2020 21:44:53 +0900 Subject: Don't use `to_string` in impl Display --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 ++ clippy_lints/src/to_string_in_display.rs | 100 +++++++++++++++++++++++++++++++ src/lintlist/mod.rs | 7 +++ tests/ui/to_string_in_display.rs | 55 +++++++++++++++++ tests/ui/to_string_in_display.stderr | 10 ++++ 6 files changed, 178 insertions(+) create mode 100644 clippy_lints/src/to_string_in_display.rs create mode 100644 tests/ui/to_string_in_display.rs create mode 100644 tests/ui/to_string_in_display.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e9ed54c848..71433773254 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1723,6 +1723,7 @@ Released 2018-09-13 [`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment [`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr [`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 [`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/lib.rs b/clippy_lints/src/lib.rs index 6ad525d7620..7ae185103a3 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -296,6 +296,7 @@ mod swap; mod tabs_in_doc_comments; mod temporary_assignment; mod to_digit_is_some; +mod to_string_in_display; mod trait_bounds; mod transmute; mod transmuting_null; @@ -788,6 +789,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &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, @@ -1017,6 +1019,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box reference::DerefAddrOf); store.register_early_pass(|| box reference::RefInDeref); store.register_early_pass(|| box double_parens::DoubleParens); + store.register_late_pass(|| box to_string_in_display::ToStringInDisplay::new()); store.register_early_pass(|| box unsafe_removed_from_name::UnsafeNameRemoval); store.register_early_pass(|| box if_not_else::IfNotElse); store.register_early_pass(|| box else_if_without_else::ElseIfWithoutElse); @@ -1427,6 +1430,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), @@ -1708,6 +1712,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), 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), diff --git a/clippy_lints/src/to_string_in_display.rs b/clippy_lints/src/to_string_in_display.rs new file mode 100644 index 00000000000..11bdd27d9b1 --- /dev/null +++ b/clippy_lints/src/to_string_in_display.rs @@ -0,0 +1,100 @@ +use crate::utils::{match_def_path, match_trait_method, paths, span_lint}; +use if_chain::if_chain; +use rustc_hir::{Expr, ExprKind, Item, ItemKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +declare_clippy_lint! { + /// **What it does:** Checks for uses of `to_string()` in `Display` traits. + /// + /// **Why is this bad?** Usually `to_string` is implemented indirectly + /// via `Display`. Hence using it while implementing `Display` would + /// lead to infinite recursion. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// use std::fmt; + /// + /// struct Structure(i32); + /// impl fmt::Display for Structure { + /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + /// write!(f, "{}", self.to_string()) + /// } + /// } + /// + /// ``` + /// Use instead: + /// ```rust + /// use std::fmt; + /// + /// struct Structure(i32); + /// impl fmt::Display for Structure { + /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + /// write!(f, "{}", self.0) + /// } + /// } + /// ``` + pub TO_STRING_IN_DISPLAY, + correctness, + "to_string method used while implementing Display trait" +} + +#[derive(Default)] +pub struct ToStringInDisplay { + in_display_impl: bool, +} + +impl ToStringInDisplay { + pub fn new() -> Self { + Self { in_display_impl: false } + } +} + +impl_lint_pass!(ToStringInDisplay => [TO_STRING_IN_DISPLAY]); + +impl LateLintPass<'_> for ToStringInDisplay { + fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + if is_display_impl(cx, item) { + self.in_display_impl = true; + } + } + + fn check_item_post(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + if is_display_impl(cx, item) { + self.in_display_impl = false; + } + } + + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if_chain! { + if let ExprKind::MethodCall(ref path, _, _, _) = expr.kind; + if path.ident.name == sym!(to_string); + if match_trait_method(cx, expr, &paths::TO_STRING); + if self.in_display_impl; + + then { + span_lint( + cx, + TO_STRING_IN_DISPLAY, + expr.span, + "Using to_string in fmt::Display implementation might lead to infinite recursion", + ); + } + } + } +} + +fn is_display_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool { + if_chain! { + if let ItemKind::Impl { of_trait: Some(trait_ref), .. } = &item.kind; + if let Some(did) = trait_ref.trait_def_id(); + then { + match_def_path(cx, did, &paths::DISPLAY_TRAIT) + } else { + false + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index ccc9e250952..46827084a60 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2166,6 +2166,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "to_digit_is_some", }, + Lint { + name: "to_string_in_display", + group: "correctness", + desc: "to_string method used while implementing Display trait", + deprecation: None, + module: "to_string_in_display", + }, Lint { name: "todo", group: "restriction", diff --git a/tests/ui/to_string_in_display.rs b/tests/ui/to_string_in_display.rs new file mode 100644 index 00000000000..3b46324704e --- /dev/null +++ b/tests/ui/to_string_in_display.rs @@ -0,0 +1,55 @@ +#![warn(clippy::to_string_in_display)] +#![allow(clippy::inherent_to_string_shadow_display)] + +use std::fmt; + +struct A; +impl A { + fn fmt(&self) { + self.to_string(); + } +} + +trait B { + fn fmt(&self) {} +} + +impl B for A { + fn fmt(&self) { + self.to_string(); + } +} + +impl fmt::Display for A { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.to_string()) + } +} + +fn fmt(a: A) { + a.to_string(); +} + +struct C; + +impl C { + fn to_string(&self) -> String { + String::from("I am C") + } +} + +impl fmt::Display for C { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.to_string()) + } +} + +fn main() { + let a = A; + a.to_string(); + a.fmt(); + fmt(a); + + let c = C; + c.to_string(); +} diff --git a/tests/ui/to_string_in_display.stderr b/tests/ui/to_string_in_display.stderr new file mode 100644 index 00000000000..cbc0a41036b --- /dev/null +++ b/tests/ui/to_string_in_display.stderr @@ -0,0 +1,10 @@ +error: Using to_string in fmt::Display implementation might lead to infinite recursion + --> $DIR/to_string_in_display.rs:25:25 + | +LL | write!(f, "{}", self.to_string()) + | ^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::to-string-in-display` implied by `-D warnings` + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From f98ffa271d0112d04b482e1d61228d99bf006ccf Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Fri, 14 Aug 2020 22:54:12 +0900 Subject: Fix FP for `same_item_push` Don't emit a lint when `pushed_item` was declared as mutable variable. --- clippy_lints/src/loops.rs | 35 ++++++++++++++++++++++++++++++----- tests/ui/same_item_push.rs | 8 ++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 8352a8a3d2c..f7db2563d2b 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1141,11 +1141,36 @@ fn detect_same_item_push<'tcx>( if same_item_push_visitor.should_lint { if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push { // Make sure that the push does not involve possibly mutating values - if mutated_variables(pushed_item, cx).map_or(false, |mutvars| mutvars.is_empty()) { - if let PatKind::Wild = pat.kind { - let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); - let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); - + if let PatKind::Wild = pat.kind { + let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); + let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); + if let ExprKind::Path(ref qpath) = pushed_item.kind { + if let Res::Local(hir_id) = qpath_res(cx, qpath, pushed_item.hir_id) { + let node = cx.tcx.hir().get(hir_id); + if_chain! { + if let Node::Binding(pat) = node; + if let PatKind::Binding(bind_ann, ..) = pat.kind; + then { + match bind_ann { + BindingAnnotation::RefMut | BindingAnnotation::Mutable => {}, + _ => { + span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ) + } + } + } + } + } + } else if mutated_variables(pushed_item, cx).map_or(false, |mutvars| mutvars.is_empty()) { span_lint_and_help( cx, SAME_ITEM_PUSH, diff --git a/tests/ui/same_item_push.rs b/tests/ui/same_item_push.rs index ff1088f86f6..bfe27e02044 100644 --- a/tests/ui/same_item_push.rs +++ b/tests/ui/same_item_push.rs @@ -86,4 +86,12 @@ fn main() { for a in vec_a { vec12.push(2u8.pow(a.kind)); } + + // Fix #5902 + let mut vec13: Vec = Vec::new(); + let mut item = 0; + for _ in 0..10 { + vec13.push(item); + item += 10; + } } -- cgit 1.4.1-3-g733a5 From 72d2c2eab42fe8c7247e4a45b01a6e7411898443 Mon Sep 17 00:00:00 2001 From: Christoph Walcher Date: Sun, 9 Aug 2020 17:47:11 +0200 Subject: Lint `push_str` with a single-character string literal Fixes #5875 --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 +++ clippy_lints/src/single_char_push_str.rs | 62 ++++++++++++++++++++++++++++++++ clippy_lints/src/utils/paths.rs | 1 + src/lintlist/mod.rs | 7 ++++ tests/ui/single_char_push_str.fixed | 10 ++++++ tests/ui/single_char_push_str.rs | 10 ++++++ tests/ui/single_char_push_str.stderr | 16 +++++++++ 8 files changed, 112 insertions(+) create mode 100644 clippy_lints/src/single_char_push_str.rs create mode 100644 tests/ui/single_char_push_str.fixed create mode 100644 tests/ui/single_char_push_str.rs create mode 100644 tests/ui/single_char_push_str.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e9ed54c848..3b9cbbf0dcd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1699,6 +1699,7 @@ Released 2018-09-13 [`should_implement_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait [`similar_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#similar_names [`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern +[`single_char_push_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_push_str [`single_component_path_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports [`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match [`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 6ad525d7620..4f261ba932e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -287,6 +287,7 @@ mod repeat_once; mod returns; mod serde_api; mod shadow; +mod single_char_push_str; mod single_component_path_imports; mod slow_vector_initialization; mod stable_sort_primitive; @@ -775,6 +776,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &shadow::SHADOW_REUSE, &shadow::SHADOW_SAME, &shadow::SHADOW_UNRELATED, + &single_char_push_str::SINGLE_CHAR_PUSH_STR, &single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, &slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, &stable_sort_primitive::STABLE_SORT_PRIMITIVE, @@ -932,6 +934,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box escape::BoxedLocal{too_large_for_stack}); store.register_late_pass(|| box panic_unimplemented::PanicUnimplemented); store.register_late_pass(|| box strings::StringLitAsBytes); + store.register_late_pass(|| box single_char_push_str::SingleCharPushStrPass); store.register_late_pass(|| box derive::Derive); store.register_late_pass(|| box types::CharLitAsU8); store.register_late_pass(|| box vec::UselessVec); @@ -1416,6 +1419,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&returns::UNUSED_UNIT), LintId::of(&serde_api::SERDE_API_MISUSE), + LintId::of(&single_char_push_str::SINGLE_CHAR_PUSH_STR), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE), @@ -1556,6 +1560,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(®ex::TRIVIAL_REGEX), LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&returns::UNUSED_UNIT), + LintId::of(&single_char_push_str::SINGLE_CHAR_PUSH_STR), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), diff --git a/clippy_lints/src/single_char_push_str.rs b/clippy_lints/src/single_char_push_str.rs new file mode 100644 index 00000000000..68bbef7261a --- /dev/null +++ b/clippy_lints/src/single_char_push_str.rs @@ -0,0 +1,62 @@ +use crate::utils::{match_def_path, paths, snippet_with_applicability, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Warns when using push_str with a single-character string literal, + /// and push with a char would work fine. + /// + /// **Why is this bad?** This is in all probability not the intended outcome. At + /// the least it hurts readability of the code. + /// + /// **Known problems:** None + /// + /// **Example:** + /// ``` + /// let mut string = String::new(); + /// string.push_str("R"); + /// ``` + /// Could be written as + /// ``` + /// let mut string = String::new(); + /// string.push('R'); + /// ``` + pub SINGLE_CHAR_PUSH_STR, + style, + "`push_str()` used with a single-character string literal as parameter" +} + +declare_lint_pass!(SingleCharPushStrPass => [SINGLE_CHAR_PUSH_STR]); + +impl<'tcx> LateLintPass<'tcx> for SingleCharPushStrPass { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + if let ExprKind::MethodCall(_, _, ref args, _) = expr.kind; + if let [base_string, extension_string] = args; + if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); + if match_def_path(cx, fn_def_id, &paths::PUSH_STR); + if let ExprKind::Lit(ref lit) = extension_string.kind; + if let LitKind::Str(symbol,_) = lit.node; + let extension_string_val = symbol.as_str().to_string(); + if extension_string_val.len() == 1; + then { + let mut applicability = Applicability::MachineApplicable; + let base_string_snippet = snippet_with_applicability(cx, base_string.span, "_", &mut applicability); + let sugg = format!("{}.push({:?})", base_string_snippet, extension_string_val.chars().next().unwrap()); + span_lint_and_sugg( + cx, + SINGLE_CHAR_PUSH_STR, + expr.span, + "calling `push_str()` using a single-character string literal", + "consider using `push` with a character literal", + sugg, + applicability + ); + } + } + } +} diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 923b319d777..ffab0395120 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -84,6 +84,7 @@ pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"]; pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"]; pub const PTR_NULL: [&str; 2] = ["ptr", "null"]; pub const PTR_NULL_MUT: [&str; 2] = ["ptr", "null_mut"]; +pub const PUSH_STR: [&str; 4] = ["alloc", "string", "String", "push_str"]; pub const RANGE: [&str; 3] = ["core", "ops", "Range"]; pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"]; pub const RANGE_FROM: [&str; 3] = ["core", "ops", "RangeFrom"]; diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index ccc9e250952..000ab8b8f36 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2012,6 +2012,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "methods", }, + Lint { + name: "single_char_push_str", + group: "style", + desc: "`push_str()` used with a single-character string literal as parameter", + deprecation: None, + module: "single_char_push_str", + }, Lint { name: "single_component_path_imports", group: "style", diff --git a/tests/ui/single_char_push_str.fixed b/tests/ui/single_char_push_str.fixed new file mode 100644 index 00000000000..49607c49218 --- /dev/null +++ b/tests/ui/single_char_push_str.fixed @@ -0,0 +1,10 @@ +// run-rustfix +#![warn(clippy::single_char_push_str)] + +fn main() { + let mut string = String::new(); + string.push('R'); + string.push('\''); + + string.push('u'); +} diff --git a/tests/ui/single_char_push_str.rs b/tests/ui/single_char_push_str.rs new file mode 100644 index 00000000000..bbeebd027b1 --- /dev/null +++ b/tests/ui/single_char_push_str.rs @@ -0,0 +1,10 @@ +// run-rustfix +#![warn(clippy::single_char_push_str)] + +fn main() { + let mut string = String::new(); + string.push_str("R"); + string.push_str("'"); + + string.push('u'); +} diff --git a/tests/ui/single_char_push_str.stderr b/tests/ui/single_char_push_str.stderr new file mode 100644 index 00000000000..453ec2d42f1 --- /dev/null +++ b/tests/ui/single_char_push_str.stderr @@ -0,0 +1,16 @@ +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_push_str.rs:6:5 + | +LL | string.push_str("R"); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('R')` + | + = note: `-D clippy::single-char-push-str` implied by `-D warnings` + +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_push_str.rs:7:5 + | +LL | string.push_str("'"); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/'')` + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From ae56e988a2ae7b59c684cbbc5c326cb8097b3688 Mon Sep 17 00:00:00 2001 From: Christoph Walcher Date: Fri, 14 Aug 2020 12:52:19 +0200 Subject: Merge lint with `single_char_pattern` --- clippy_lints/src/lib.rs | 8 ++- clippy_lints/src/methods/mod.rs | 89 +++++++++++++++++++++++++++----- clippy_lints/src/single_char_push_str.rs | 62 ---------------------- src/lintlist/mod.rs | 2 +- tests/ui/single_char_push_str.fixed | 5 ++ tests/ui/single_char_push_str.rs | 5 ++ tests/ui/single_char_push_str.stderr | 20 ++++++- 7 files changed, 108 insertions(+), 83 deletions(-) delete mode 100644 clippy_lints/src/single_char_push_str.rs (limited to 'tests') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 4f261ba932e..5e4a4a4f49c 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -287,7 +287,6 @@ mod repeat_once; mod returns; mod serde_api; mod shadow; -mod single_char_push_str; mod single_component_path_imports; mod slow_vector_initialization; mod stable_sort_primitive; @@ -678,6 +677,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::SEARCH_IS_SOME, &methods::SHOULD_IMPLEMENT_TRAIT, &methods::SINGLE_CHAR_PATTERN, + &methods::SINGLE_CHAR_PUSH_STR, &methods::SKIP_WHILE_NEXT, &methods::STRING_EXTEND_CHARS, &methods::SUSPICIOUS_MAP, @@ -776,7 +776,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &shadow::SHADOW_REUSE, &shadow::SHADOW_SAME, &shadow::SHADOW_UNRELATED, - &single_char_push_str::SINGLE_CHAR_PUSH_STR, &single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, &slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, &stable_sort_primitive::STABLE_SORT_PRIMITIVE, @@ -934,7 +933,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box escape::BoxedLocal{too_large_for_stack}); store.register_late_pass(|| box panic_unimplemented::PanicUnimplemented); store.register_late_pass(|| box strings::StringLitAsBytes); - store.register_late_pass(|| box single_char_push_str::SingleCharPushStrPass); store.register_late_pass(|| box derive::Derive); store.register_late_pass(|| box types::CharLitAsU8); store.register_late_pass(|| box vec::UselessVec); @@ -1352,6 +1350,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::SEARCH_IS_SOME), LintId::of(&methods::SHOULD_IMPLEMENT_TRAIT), LintId::of(&methods::SINGLE_CHAR_PATTERN), + LintId::of(&methods::SINGLE_CHAR_PUSH_STR), LintId::of(&methods::SKIP_WHILE_NEXT), LintId::of(&methods::STRING_EXTEND_CHARS), LintId::of(&methods::SUSPICIOUS_MAP), @@ -1419,7 +1418,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&returns::UNUSED_UNIT), LintId::of(&serde_api::SERDE_API_MISUSE), - LintId::of(&single_char_push_str::SINGLE_CHAR_PUSH_STR), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE), @@ -1536,6 +1534,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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_PUSH_STR), LintId::of(&methods::STRING_EXTEND_CHARS), LintId::of(&methods::UNNECESSARY_FOLD), LintId::of(&methods::WRONG_SELF_CONVENTION), @@ -1560,7 +1559,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(®ex::TRIVIAL_REGEX), LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&returns::UNUSED_UNIT), - LintId::of(&single_char_push_str::SINGLE_CHAR_PUSH_STR), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index b6266ef2ba1..2986a5a5944 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1306,6 +1306,29 @@ declare_clippy_lint! { "using `.iter().next()` on a sliced array, which can be shortened to just `.get()`" } +declare_clippy_lint! { + /// **What it does:** Warns when using push_str with a single-character string literal, + /// and push with a char would work fine. + /// + /// **Why is this bad?** it's less clear that we are pushing a single character + /// + /// **Known problems:** None + /// + /// **Example:** + /// ``` + /// let mut string = String::new(); + /// string.push_str("R"); + /// ``` + /// Could be written as + /// ``` + /// let mut string = String::new(); + /// string.push('R'); + /// ``` + pub SINGLE_CHAR_PUSH_STR, + style, + "`push_str()` used with a single-character string literal as parameter" +} + declare_lint_pass!(Methods => [ UNWRAP_USED, EXPECT_USED, @@ -1327,6 +1350,7 @@ declare_lint_pass!(Methods => [ INEFFICIENT_TO_STRING, NEW_RET_NO_SELF, SINGLE_CHAR_PATTERN, + SINGLE_CHAR_PUSH_STR, SEARCH_IS_SOME, TEMPORARY_CSTRING_AS_PTR, FILTER_NEXT, @@ -1441,6 +1465,12 @@ impl<'tcx> LateLintPass<'tcx> for Methods { inefficient_to_string::lint(cx, expr, &args[0], self_ty); } + if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { + if match_def_path(cx, fn_def_id, &paths::PUSH_STR) { + lint_single_char_push_string(cx, expr, args); + } + } + match self_ty.kind { ty::Ref(_, ty, _) if ty.kind == ty::Str => { for &(method, pos) in &PATTERN_METHODS { @@ -3124,15 +3154,18 @@ fn lint_chars_last_cmp_with_unwrap<'tcx>(cx: &LateContext<'tcx>, info: &BinaryEx } } -/// lint for length-1 `str`s for methods in `PATTERN_METHODS` -fn lint_single_char_pattern<'tcx>(cx: &LateContext<'tcx>, _expr: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>) { +fn get_hint_if_single_char_arg<'tcx>( + cx: &LateContext<'tcx>, + arg: &'tcx hir::Expr<'_>, + applicability: &mut Applicability, +) -> Option { if_chain! { if let hir::ExprKind::Lit(lit) = &arg.kind; if let ast::LitKind::Str(r, style) = lit.node; - if r.as_str().len() == 1; + let string = r.as_str(); + if string.len() == 1; then { - let mut applicability = Applicability::MachineApplicable; - let snip = snippet_with_applicability(cx, arg.span, "..", &mut applicability); + let snip = snippet_with_applicability(cx, arg.span, &string, applicability); let ch = if let ast::StrStyle::Raw(nhash) = style { let nhash = nhash as usize; // for raw string: r##"a"## @@ -3142,19 +3175,47 @@ fn lint_single_char_pattern<'tcx>(cx: &LateContext<'tcx>, _expr: &'tcx hir::Expr &snip[1..(snip.len() - 1)] }; let hint = format!("'{}'", if ch == "'" { "\\'" } else { ch }); - span_lint_and_sugg( - cx, - SINGLE_CHAR_PATTERN, - arg.span, - "single-character string constant used as pattern", - "try using a `char` instead", - hint, - applicability, - ); + Some(hint) + } else { + None } } } +/// lint for length-1 `str`s for methods in `PATTERN_METHODS` +fn lint_single_char_pattern<'tcx>(cx: &LateContext<'tcx>, _expr: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>) { + let mut applicability = Applicability::MachineApplicable; + if let Some(hint) = get_hint_if_single_char_arg(cx, arg, &mut applicability) { + span_lint_and_sugg( + cx, + SINGLE_CHAR_PATTERN, + arg.span, + "single-character string constant used as pattern", + "try using a `char` instead", + hint, + applicability, + ); + } +} + +/// lint for length-1 `str`s as argument for `push_str` +fn lint_single_char_push_string<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, args: &'tcx [hir::Expr<'_>]) { + let mut applicability = Applicability::MachineApplicable; + if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[1], &mut applicability) { + let base_string_snippet = snippet_with_applicability(cx, args[0].span, "_", &mut applicability); + let sugg = format!("{}.push({})", base_string_snippet, extension_string); + span_lint_and_sugg( + cx, + SINGLE_CHAR_PUSH_STR, + expr.span, + "calling `push_str()` using a single-character string literal", + "consider using `push` with a character literal", + sugg, + applicability, + ); + } +} + /// Checks for the `USELESS_ASREF` lint. fn lint_asref(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, as_ref_args: &[hir::Expr<'_>]) { // when we get here, we've already checked that the call name is "as_ref" or "as_mut" diff --git a/clippy_lints/src/single_char_push_str.rs b/clippy_lints/src/single_char_push_str.rs deleted file mode 100644 index 68bbef7261a..00000000000 --- a/clippy_lints/src/single_char_push_str.rs +++ /dev/null @@ -1,62 +0,0 @@ -use crate::utils::{match_def_path, paths, snippet_with_applicability, span_lint_and_sugg}; -use if_chain::if_chain; -use rustc_ast::ast::LitKind; -use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; - -declare_clippy_lint! { - /// **What it does:** Warns when using push_str with a single-character string literal, - /// and push with a char would work fine. - /// - /// **Why is this bad?** This is in all probability not the intended outcome. At - /// the least it hurts readability of the code. - /// - /// **Known problems:** None - /// - /// **Example:** - /// ``` - /// let mut string = String::new(); - /// string.push_str("R"); - /// ``` - /// Could be written as - /// ``` - /// let mut string = String::new(); - /// string.push('R'); - /// ``` - pub SINGLE_CHAR_PUSH_STR, - style, - "`push_str()` used with a single-character string literal as parameter" -} - -declare_lint_pass!(SingleCharPushStrPass => [SINGLE_CHAR_PUSH_STR]); - -impl<'tcx> LateLintPass<'tcx> for SingleCharPushStrPass { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - if let ExprKind::MethodCall(_, _, ref args, _) = expr.kind; - if let [base_string, extension_string] = args; - if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if match_def_path(cx, fn_def_id, &paths::PUSH_STR); - if let ExprKind::Lit(ref lit) = extension_string.kind; - if let LitKind::Str(symbol,_) = lit.node; - let extension_string_val = symbol.as_str().to_string(); - if extension_string_val.len() == 1; - then { - let mut applicability = Applicability::MachineApplicable; - let base_string_snippet = snippet_with_applicability(cx, base_string.span, "_", &mut applicability); - let sugg = format!("{}.push({:?})", base_string_snippet, extension_string_val.chars().next().unwrap()); - span_lint_and_sugg( - cx, - SINGLE_CHAR_PUSH_STR, - expr.span, - "calling `push_str()` using a single-character string literal", - "consider using `push` with a character literal", - sugg, - applicability - ); - } - } - } -} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 000ab8b8f36..4fd32776874 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2017,7 +2017,7 @@ pub static ref ALL_LINTS: Vec = vec![ group: "style", desc: "`push_str()` used with a single-character string literal as parameter", deprecation: None, - module: "single_char_push_str", + module: "methods", }, Lint { name: "single_component_path_imports", diff --git a/tests/ui/single_char_push_str.fixed b/tests/ui/single_char_push_str.fixed index 49607c49218..0812c026a64 100644 --- a/tests/ui/single_char_push_str.fixed +++ b/tests/ui/single_char_push_str.fixed @@ -7,4 +7,9 @@ fn main() { string.push('\''); string.push('u'); + string.push_str("st"); + string.push_str(""); + string.push('\x52'); + string.push('\u{0052}'); + string.push('a'); } diff --git a/tests/ui/single_char_push_str.rs b/tests/ui/single_char_push_str.rs index bbeebd027b1..ab293bbe4ee 100644 --- a/tests/ui/single_char_push_str.rs +++ b/tests/ui/single_char_push_str.rs @@ -7,4 +7,9 @@ fn main() { string.push_str("'"); string.push('u'); + string.push_str("st"); + string.push_str(""); + string.push_str("\x52"); + string.push_str("\u{0052}"); + string.push_str(r##"a"##); } diff --git a/tests/ui/single_char_push_str.stderr b/tests/ui/single_char_push_str.stderr index 453ec2d42f1..0e9bdaa23e7 100644 --- a/tests/ui/single_char_push_str.stderr +++ b/tests/ui/single_char_push_str.stderr @@ -12,5 +12,23 @@ error: calling `push_str()` using a single-character string literal LL | string.push_str("'"); | ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/'')` -error: aborting due to 2 previous errors +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_push_str.rs:12:5 + | +LL | string.push_str("/x52"); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/x52')` + +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_push_str.rs:13:5 + | +LL | string.push_str("/u{0052}"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/u{0052}')` + +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_push_str.rs:14:5 + | +LL | string.push_str(r##"a"##); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('a')` + +error: aborting due to 5 previous errors -- cgit 1.4.1-3-g733a5 From 65d10c7abf4752923f040264c79433da8fc234ea Mon Sep 17 00:00:00 2001 From: jrqc Date: Thu, 13 Aug 2020 15:14:08 +0300 Subject: Borrow checker added --- clippy_lints/src/needless_return.rs | 99 ++++++++++++++++++++++--------------- clippy_lints/src/returns.rs | 5 +- tests/ui/needless_return.fixed | 10 ++++ tests/ui/needless_return.rs | 10 ++++ 4 files changed, 79 insertions(+), 45 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/needless_return.rs b/clippy_lints/src/needless_return.rs index 861a7ec558c..ba280cd5a61 100644 --- a/clippy_lints/src/needless_return.rs +++ b/clippy_lints/src/needless_return.rs @@ -1,13 +1,13 @@ -use rustc_lint::{LateLintPass, LateContext}; use rustc_ast::ast::Attribute; -use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_errors::Applicability; -use rustc_hir::intravisit::{FnKind, walk_expr, NestedVisitorMap, Visitor}; -use rustc_span::source_map::Span; -use rustc_middle::lint::in_external_macro; +use rustc_hir::intravisit::{walk_expr, FnKind, NestedVisitorMap, Visitor}; +use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; +use rustc_middle::lint::in_external_macro; use rustc_middle::ty::subst::GenericArgKind; -use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, StmtKind}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; use crate::utils::{fn_def_id, snippet_opt, span_lint_and_sugg, span_lint_and_then}; @@ -46,16 +46,39 @@ enum RetReplacement { declare_lint_pass!(NeedlessReturn => [NEEDLESS_RETURN]); impl<'tcx> LateLintPass<'tcx> for NeedlessReturn { - fn check_fn(&mut self, cx: &LateContext<'tcx>, kind: FnKind<'tcx>, _: &'tcx FnDecl<'tcx>, body: &'tcx Body<'tcx>, _: Span, _: HirId) { + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + kind: FnKind<'tcx>, + _: &'tcx FnDecl<'tcx>, + body: &'tcx Body<'tcx>, + _: Span, + _: HirId, + ) { match kind { FnKind::Closure(_) => { - check_final_expr(cx, &body.value, Some(body.value.span), RetReplacement::Empty) - } + if !last_statement_borrows(cx, &body.value) { + check_final_expr(cx, &body.value, Some(body.value.span), RetReplacement::Empty) + } + }, FnKind::ItemFn(..) | FnKind::Method(..) => { if let ExprKind::Block(ref block, _) = body.value.kind { - check_block_return(cx, block) + if let Some(expr) = block.expr { + if !last_statement_borrows(cx, expr) { + check_final_expr(cx, expr, Some(expr.span), RetReplacement::Empty); + } + } else if let Some(stmt) = block.stmts.iter().last() { + match stmt.kind { + StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => { + if !last_statement_borrows(cx, expr) { + check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty); + } + }, + _ => (), + } + } } - } + }, } } } @@ -71,23 +94,16 @@ fn check_block_return(cx: &LateContext<'_>, block: &Block<'_>) { match stmt.kind { StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => { check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty); - } + }, _ => (), } } } - -fn check_final_expr( - cx: &LateContext<'_>, - expr: &Expr<'_>, - span: Option, - replacement: RetReplacement, -) { +fn check_final_expr(cx: &LateContext<'_>, expr: &Expr<'_>, span: Option, replacement: RetReplacement) { match expr.kind { // simple return is always "bad" ExprKind::Ret(ref inner) => { - // allow `#[cfg(a)] return a; #[cfg(b)] return b;` if !expr.attrs.iter().any(attr_is_cfg) { emit_return_lint( @@ -97,32 +113,34 @@ fn check_final_expr( replacement, ); } - } + }, // a whole block? check it! ExprKind::Block(ref block, _) => { check_block_return(cx, block); - } + }, // a match expr, check all arms // an if/if let expr, check both exprs // note, if without else is going to be a type checking error anyways // (except for unit type functions) so we don't match it - - ExprKind::Match(_, ref arms, source) => { - match source { - MatchSource::Normal => { - for arm in arms.iter() { - check_final_expr(cx, &arm.body, Some(arm.body.span), RetReplacement::Block); - } - } - MatchSource::IfDesugar { contains_else_clause: true } | MatchSource::IfLetDesugar { contains_else_clause: true } => { - if let ExprKind::Block(ref ifblock, _) = arms[0].body.kind { - check_block_return(cx, ifblock); - } - check_final_expr(cx, arms[1].body, None, RetReplacement::Empty); + ExprKind::Match(_, ref arms, source) => match source { + MatchSource::Normal => { + for arm in arms.iter() { + check_final_expr(cx, &arm.body, Some(arm.body.span), RetReplacement::Block); } - _ => () + }, + MatchSource::IfDesugar { + contains_else_clause: true, } - } + | MatchSource::IfLetDesugar { + contains_else_clause: true, + } => { + if let ExprKind::Block(ref ifblock, _) = arms[0].body.kind { + check_block_return(cx, ifblock); + } + check_final_expr(cx, arms[1].body, None, RetReplacement::Empty); + }, + _ => (), + }, _ => (), } } @@ -139,7 +157,7 @@ fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, inner_span: Option match replacement { RetReplacement::Empty => { span_lint_and_sugg( @@ -151,7 +169,7 @@ fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, inner_span: Option { span_lint_and_sugg( cx, @@ -162,7 +180,7 @@ fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, inner_span: Option Visitor<'tcx> for BorrowVisitor<'_, 'tcx> { NestedVisitorMap::None } } - diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 2bd0cccd39d..593e2f6c74b 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -7,7 +7,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::BytePos; -use crate::utils::{span_lint_and_sugg}; +use crate::utils::span_lint_and_sugg; declare_clippy_lint! { /// **What it does:** Checks for unit (`()`) expressions that can be removed. @@ -30,12 +30,10 @@ declare_clippy_lint! { "needless unit expression" } - declare_lint_pass!(Return => [UNUSED_UNIT]); impl EarlyLintPass for Return { fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, span: Span, _: ast::NodeId) { - if_chain! { if let ast::FnRetTy::Ty(ref ty) = kind.decl().output; if let ast::TyKind::Tup(ref vals) = ty.kind; @@ -102,7 +100,6 @@ impl EarlyLintPass for Return { } } - // get the def site #[must_use] fn get_def(span: Span) -> Option { diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index ad20e238107..6b5cf2626df 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -69,6 +69,16 @@ fn test_void_match(x: u32) { } } +mod no_lint_if_stmt_borrows { + mod issue_5858 { + fn read_line() -> String { + use std::io::BufRead; + let stdin = ::std::io::stdin(); + return stdin.lock().lines().next().unwrap().unwrap(); + } + } +} + fn main() { let _ = test_end_of_fn(); let _ = test_no_semicolon(); diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index af0cdfb207f..1a693c9aa53 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -69,6 +69,16 @@ fn test_void_match(x: u32) { } } +mod no_lint_if_stmt_borrows { + mod issue_5858 { + fn read_line() -> String { + use std::io::BufRead; + let stdin = ::std::io::stdin(); + return stdin.lock().lines().next().unwrap().unwrap(); + } + } +} + fn main() { let _ = test_end_of_fn(); let _ = test_no_semicolon(); -- cgit 1.4.1-3-g733a5 From a7d5c2f967dd1f075ba5f8c4ca05c4b2ca2d22b4 Mon Sep 17 00:00:00 2001 From: jrqc Date: Thu, 13 Aug 2020 19:24:34 +0300 Subject: Modifications according to the code review --- clippy_lints/src/let_and_return.rs | 124 ------------- clippy_lints/src/lib.rs | 26 ++- clippy_lints/src/needless_return.rs | 223 ----------------------- clippy_lints/src/returns.rs | 347 +++++++++++++++++++++++++----------- clippy_lints/src/unused_unit.rs | 145 +++++++++++++++ tests/ui/needless_return.fixed | 11 ++ tests/ui/needless_return.rs | 11 ++ 7 files changed, 425 insertions(+), 462 deletions(-) delete mode 100644 clippy_lints/src/let_and_return.rs delete mode 100644 clippy_lints/src/needless_return.rs create mode 100644 clippy_lints/src/unused_unit.rs (limited to 'tests') diff --git a/clippy_lints/src/let_and_return.rs b/clippy_lints/src/let_and_return.rs deleted file mode 100644 index fa560ffb980..00000000000 --- a/clippy_lints/src/let_and_return.rs +++ /dev/null @@ -1,124 +0,0 @@ -use if_chain::if_chain; -use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; -use rustc_hir::{Block, Expr, ExprKind, PatKind, StmtKind}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::hir::map::Map; -use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::subst::GenericArgKind; -use rustc_session::{declare_lint_pass, declare_tool_lint}; - -use crate::utils::{fn_def_id, in_macro, match_qpath, snippet_opt, span_lint_and_then}; - -declare_clippy_lint! { - /// **What it does:** Checks for `let`-bindings, which are subsequently - /// returned. - /// - /// **Why is this bad?** It is just extraneous code. Remove it to make your code - /// more rusty. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// fn foo() -> String { - /// let x = String::new(); - /// x - /// } - /// ``` - /// instead, use - /// ``` - /// fn foo() -> String { - /// String::new() - /// } - /// ``` - pub LET_AND_RETURN, - style, - "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block" -} - -declare_lint_pass!(LetReturn => [LET_AND_RETURN]); - -impl<'tcx> LateLintPass<'tcx> for LetReturn { - fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) { - // we need both a let-binding stmt and an expr - if_chain! { - if let Some(retexpr) = block.expr; - if let Some(stmt) = block.stmts.iter().last(); - if let StmtKind::Local(local) = &stmt.kind; - if local.ty.is_none(); - if local.attrs.is_empty(); - if let Some(initexpr) = &local.init; - if let PatKind::Binding(.., ident, _) = local.pat.kind; - if let ExprKind::Path(qpath) = &retexpr.kind; - if match_qpath(qpath, &[&*ident.name.as_str()]); - if !last_statement_borrows(cx, initexpr); - if !in_external_macro(cx.sess(), initexpr.span); - if !in_external_macro(cx.sess(), retexpr.span); - if !in_external_macro(cx.sess(), local.span); - if !in_macro(local.span); - then { - span_lint_and_then( - cx, - LET_AND_RETURN, - retexpr.span, - "returning the result of a `let` binding from a block", - |err| { - err.span_label(local.span, "unnecessary `let` binding"); - - if let Some(snippet) = snippet_opt(cx, initexpr.span) { - err.multipart_suggestion( - "return the expression directly", - vec![ - (local.span, String::new()), - (retexpr.span, snippet), - ], - Applicability::MachineApplicable, - ); - } else { - err.span_help(initexpr.span, "this expression can be directly returned"); - } - }, - ); - } - } - } -} - -fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { - let mut visitor = BorrowVisitor { cx, borrows: false }; - walk_expr(&mut visitor, expr); - visitor.borrows -} - -struct BorrowVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - borrows: bool, -} - -impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if self.borrows { - return; - } - - if let Some(def_id) = fn_def_id(self.cx, expr) { - self.borrows = self - .cx - .tcx - .fn_sig(def_id) - .output() - .skip_binder() - .walk() - .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))); - } - - walk_expr(self, expr); - } - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } -} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d2f66cf9bd0..63de0f8a0c2 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -218,7 +218,6 @@ mod large_const_arrays; mod large_enum_variant; mod large_stack_arrays; mod len_zero; -mod let_and_return; mod let_if_seq; mod let_underscore; mod lifetimes; @@ -256,7 +255,6 @@ mod needless_borrow; mod needless_borrowed_ref; mod needless_continue; mod needless_pass_by_value; -mod needless_return; mod needless_update; mod neg_cmp_op_on_partial_ord; mod neg_multiply; @@ -311,6 +309,7 @@ mod unnested_or_patterns; mod unsafe_removed_from_name; mod unused_io_amount; mod unused_self; +mod unused_unit; mod unwrap; mod use_self; mod useless_conversion; @@ -587,7 +586,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &large_stack_arrays::LARGE_STACK_ARRAYS, &len_zero::LEN_WITHOUT_IS_EMPTY, &len_zero::LEN_ZERO, - &let_and_return::LET_AND_RETURN, + &returns::LET_AND_RETURN, &let_if_seq::USELESS_LET_IF_SEQ, &let_underscore::LET_UNDERSCORE_LOCK, &let_underscore::LET_UNDERSCORE_MUST_USE, @@ -727,7 +726,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE, &needless_continue::NEEDLESS_CONTINUE, &needless_pass_by_value::NEEDLESS_PASS_BY_VALUE, - &needless_return::NEEDLESS_RETURN, + &returns::NEEDLESS_RETURN, &needless_update::NEEDLESS_UPDATE, &neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD, &neg_multiply::NEG_MULTIPLY, @@ -771,7 +770,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ®ex::INVALID_REGEX, ®ex::TRIVIAL_REGEX, &repeat_once::REPEAT_ONCE, - &returns::UNUSED_UNIT, + &unused_unit::UNUSED_UNIT, &serde_api::SERDE_API_MISUSE, &shadow::SHADOW_REUSE, &shadow::SHADOW_SAME, @@ -1026,9 +1025,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box misc_early::MiscEarlyLints); store.register_early_pass(|| box redundant_closure_call::RedundantClosureCall); store.register_late_pass(|| box redundant_closure_call::RedundantClosureCall); - store.register_early_pass(|| box returns::Return); - store.register_late_pass(|| box let_and_return::LetReturn); - store.register_late_pass(|| box needless_return::NeedlessReturn); + store.register_early_pass(|| box unused_unit::UnusedUnit); + store.register_late_pass(|| box returns::Return); store.register_early_pass(|| box collapsible_if::CollapsibleIf); store.register_early_pass(|| box items_after_statements::ItemsAfterStatements); store.register_early_pass(|| box precedence::Precedence); @@ -1286,7 +1284,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&large_enum_variant::LARGE_ENUM_VARIANT), LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), LintId::of(&len_zero::LEN_ZERO), - LintId::of(&let_and_return::LET_AND_RETURN), + LintId::of(&returns::LET_AND_RETURN), LintId::of(&let_underscore::LET_UNDERSCORE_LOCK), LintId::of(&lifetimes::EXTRA_UNUSED_LIFETIMES), LintId::of(&lifetimes::NEEDLESS_LIFETIMES), @@ -1383,7 +1381,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&needless_bool::BOOL_COMPARISON), LintId::of(&needless_bool::NEEDLESS_BOOL), LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), - LintId::of(&needless_return::NEEDLESS_RETURN), + LintId::of(&returns::NEEDLESS_RETURN), 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), @@ -1416,7 +1414,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(®ex::INVALID_REGEX), LintId::of(®ex::TRIVIAL_REGEX), LintId::of(&repeat_once::REPEAT_ONCE), - LintId::of(&returns::UNUSED_UNIT), + LintId::of(&unused_unit::UNUSED_UNIT), LintId::of(&serde_api::SERDE_API_MISUSE), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), @@ -1502,7 +1500,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&inherent_to_string::INHERENT_TO_STRING), LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), LintId::of(&len_zero::LEN_ZERO), - LintId::of(&let_and_return::LET_AND_RETURN), + LintId::of(&returns::LET_AND_RETURN), LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING), LintId::of(&loops::EMPTY_LOOP), LintId::of(&loops::FOR_KV_MAP), @@ -1545,7 +1543,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&misc_early::MIXED_CASE_HEX_LITERALS), LintId::of(&misc_early::REDUNDANT_PATTERN), LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED), - LintId::of(&needless_return::NEEDLESS_RETURN), + LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&neg_multiply::NEG_MULTIPLY), LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT), LintId::of(&non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), @@ -1557,7 +1555,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), LintId::of(®ex::TRIVIAL_REGEX), - LintId::of(&returns::UNUSED_UNIT), + LintId::of(&unused_unit::UNUSED_UNIT), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), diff --git a/clippy_lints/src/needless_return.rs b/clippy_lints/src/needless_return.rs deleted file mode 100644 index eb0bf12c0ab..00000000000 --- a/clippy_lints/src/needless_return.rs +++ /dev/null @@ -1,223 +0,0 @@ -use rustc_ast::ast::Attribute; -use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_expr, FnKind, NestedVisitorMap, Visitor}; -use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, StmtKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::hir::map::Map; -use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::subst::GenericArgKind; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Span; - -use crate::utils::{fn_def_id, snippet_opt, span_lint_and_sugg, span_lint_and_then}; - -declare_clippy_lint! { - /// **What it does:** Checks for return statements at the end of a block. - /// - /// **Why is this bad?** Removing the `return` and semicolon will make the code - /// more rusty. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// fn foo(x: usize) -> usize { - /// return x; - /// } - /// ``` - /// simplify to - /// ```rust - /// fn foo(x: usize) -> usize { - /// x - /// } - /// ``` - pub NEEDLESS_RETURN, - style, - "using a return statement like `return expr;` where an expression would suffice" -} - -#[derive(PartialEq, Eq, Copy, Clone)] -enum RetReplacement { - Empty, - Block, -} - -declare_lint_pass!(NeedlessReturn => [NEEDLESS_RETURN]); - -impl<'tcx> LateLintPass<'tcx> for NeedlessReturn { - fn check_fn( - &mut self, - cx: &LateContext<'tcx>, - kind: FnKind<'tcx>, - _: &'tcx FnDecl<'tcx>, - body: &'tcx Body<'tcx>, - _: Span, - _: HirId, - ) { - match kind { - FnKind::Closure(_) => { - if !last_statement_borrows(cx, &body.value) { - check_final_expr(cx, &body.value, Some(body.value.span), RetReplacement::Empty) - } - }, - FnKind::ItemFn(..) | FnKind::Method(..) => { - if let ExprKind::Block(ref block, _) = body.value.kind { - if let Some(expr) = block.expr { - if !last_statement_borrows(cx, expr) { - check_final_expr(cx, expr, Some(expr.span), RetReplacement::Empty); - } - } else if let Some(stmt) = block.stmts.iter().last() { - match stmt.kind { - StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => { - if !last_statement_borrows(cx, expr) { - check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty); - } - }, - _ => (), - } - } - } - }, - } - } -} - -fn attr_is_cfg(attr: &Attribute) -> bool { - attr.meta_item_list().is_some() && attr.has_name(sym!(cfg)) -} - -fn check_block_return(cx: &LateContext<'_>, block: &Block<'_>) { - if let Some(expr) = block.expr { - check_final_expr(cx, expr, Some(expr.span), RetReplacement::Empty); - } else if let Some(stmt) = block.stmts.iter().last() { - match stmt.kind { - StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => { - check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty); - }, - _ => (), - } - } -} - -fn check_final_expr(cx: &LateContext<'_>, expr: &Expr<'_>, span: Option, replacement: RetReplacement) { - match expr.kind { - // simple return is always "bad" - ExprKind::Ret(ref inner) => { - // allow `#[cfg(a)] return a; #[cfg(b)] return b;` - if !expr.attrs.iter().any(attr_is_cfg) { - emit_return_lint( - cx, - span.expect("`else return` is not possible"), - inner.as_ref().map(|i| i.span), - replacement, - ); - } - }, - // a whole block? check it! - ExprKind::Block(ref block, _) => { - check_block_return(cx, block); - }, - // a match expr, check all arms - // an if/if let expr, check both exprs - // note, if without else is going to be a type checking error anyways - // (except for unit type functions) so we don't match it - ExprKind::Match(_, ref arms, source) => match source { - MatchSource::Normal => { - for arm in arms.iter() { - check_final_expr(cx, &arm.body, Some(arm.body.span), RetReplacement::Block); - } - }, - MatchSource::IfDesugar { - contains_else_clause: true, - } - | MatchSource::IfLetDesugar { - contains_else_clause: true, - } => { - if let ExprKind::Block(ref ifblock, _) = arms[0].body.kind { - check_block_return(cx, ifblock); - } - check_final_expr(cx, arms[1].body, None, RetReplacement::Empty); - }, - _ => (), - }, - _ => (), - } -} - -fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, inner_span: Option, replacement: RetReplacement) { - match inner_span { - Some(inner_span) => { - if in_external_macro(cx.tcx.sess, inner_span) || inner_span.from_expansion() { - return; - } - - span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| { - if let Some(snippet) = snippet_opt(cx, inner_span) { - diag.span_suggestion(ret_span, "remove `return`", snippet, Applicability::MachineApplicable); - } - }) - }, - None => match replacement { - RetReplacement::Empty => { - span_lint_and_sugg( - cx, - NEEDLESS_RETURN, - ret_span, - "unneeded `return` statement", - "remove `return`", - String::new(), - Applicability::MachineApplicable, - ); - }, - RetReplacement::Block => { - span_lint_and_sugg( - cx, - NEEDLESS_RETURN, - ret_span, - "unneeded `return` statement", - "replace `return` with an empty block", - "{}".to_string(), - Applicability::MachineApplicable, - ); - }, - }, - } -} - -fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { - let mut visitor = BorrowVisitor { cx, borrows: false }; - walk_expr(&mut visitor, expr); - visitor.borrows -} - -struct BorrowVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - borrows: bool, -} - -impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if self.borrows { - return; - } - - if let Some(def_id) = fn_def_id(self.cx, expr) { - self.borrows = self - .cx - .tcx - .fn_sig(def_id) - .output() - .skip_binder() - .walk() - .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))); - } - - walk_expr(self, expr); - } - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } -} diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 593e2f6c74b..4d91f9be999 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -1,145 +1,290 @@ use if_chain::if_chain; -use rustc_ast::ast; -use rustc_ast::visit::FnKind; +use rustc_ast::ast::Attribute; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_hir::intravisit::{walk_expr, FnKind, NestedVisitorMap, Visitor}; +use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, PatKind, StmtKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::hir::map::Map; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use rustc_span::BytePos; -use crate::utils::span_lint_and_sugg; +use crate::utils::{fn_def_id, in_macro, match_qpath, snippet_opt, span_lint_and_sugg, span_lint_and_then}; declare_clippy_lint! { - /// **What it does:** Checks for unit (`()`) expressions that can be removed. + /// **What it does:** Checks for `let`-bindings, which are subsequently + /// returned. /// - /// **Why is this bad?** Such expressions add no value, but can make the code - /// less readable. Depending on formatting they can make a `break` or `return` - /// statement look like a function call. + /// **Why is this bad?** It is just extraneous code. Remove it to make your code + /// more rusty. /// - /// **Known problems:** The lint currently misses unit return types in types, - /// e.g., the `F` in `fn generic_unit ()>(f: F) { .. }`. + /// **Known problems:** None. /// /// **Example:** /// ```rust - /// fn return_unit() -> () { - /// () + /// fn foo() -> String { + /// let x = String::new(); + /// x /// } /// ``` - pub UNUSED_UNIT, + /// instead, use + /// ``` + /// fn foo() -> String { + /// String::new() + /// } + /// ``` + pub LET_AND_RETURN, style, - "needless unit expression" + "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block" } -declare_lint_pass!(Return => [UNUSED_UNIT]); +declare_clippy_lint! { + /// **What it does:** Checks for return statements at the end of a block. + /// + /// **Why is this bad?** Removing the `return` and semicolon will make the code + /// more rusty. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// fn foo(x: usize) -> usize { + /// return x; + /// } + /// ``` + /// simplify to + /// ```rust + /// fn foo(x: usize) -> usize { + /// x + /// } + /// ``` + pub NEEDLESS_RETURN, + style, + "using a return statement like `return expr;` where an expression would suffice" +} -impl EarlyLintPass for Return { - fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, span: Span, _: ast::NodeId) { - if_chain! { - if let ast::FnRetTy::Ty(ref ty) = kind.decl().output; - if let ast::TyKind::Tup(ref vals) = ty.kind; - if vals.is_empty() && !ty.span.from_expansion() && get_def(span) == get_def(ty.span); - then { - lint_unneeded_unit_return(cx, ty, span); - } - } - } +#[derive(PartialEq, Eq, Copy, Clone)] +enum RetReplacement { + Empty, + Block, +} + +declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN]); - fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) { +impl<'tcx> LateLintPass<'tcx> for Return { + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) { + // we need both a let-binding stmt and an expr if_chain! { - if let Some(ref stmt) = block.stmts.last(); - if let ast::StmtKind::Expr(ref expr) = stmt.kind; - if is_unit_expr(expr) && !stmt.span.from_expansion(); + if let Some(retexpr) = block.expr; + if let Some(stmt) = block.stmts.iter().last(); + if let StmtKind::Local(local) = &stmt.kind; + if local.ty.is_none(); + if local.attrs.is_empty(); + if let Some(initexpr) = &local.init; + if let PatKind::Binding(.., ident, _) = local.pat.kind; + if let ExprKind::Path(qpath) = &retexpr.kind; + if match_qpath(qpath, &[&*ident.name.as_str()]); + if !last_statement_borrows(cx, initexpr); + if !in_external_macro(cx.sess(), initexpr.span); + if !in_external_macro(cx.sess(), retexpr.span); + if !in_external_macro(cx.sess(), local.span); + if !in_macro(local.span); then { - let sp = expr.span; - span_lint_and_sugg( + span_lint_and_then( cx, - UNUSED_UNIT, - sp, - "unneeded unit expression", - "remove the final `()`", - String::new(), - Applicability::MachineApplicable, + LET_AND_RETURN, + retexpr.span, + "returning the result of a `let` binding from a block", + |err| { + err.span_label(local.span, "unnecessary `let` binding"); + + if let Some(snippet) = snippet_opt(cx, initexpr.span) { + err.multipart_suggestion( + "return the expression directly", + vec![ + (local.span, String::new()), + (retexpr.span, snippet), + ], + Applicability::MachineApplicable, + ); + } else { + err.span_help(initexpr.span, "this expression can be directly returned"); + } + }, ); } } } - fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { - match e.kind { - ast::ExprKind::Ret(Some(ref expr)) | ast::ExprKind::Break(_, Some(ref expr)) => { - if is_unit_expr(expr) && !expr.span.from_expansion() { - span_lint_and_sugg( - cx, - UNUSED_UNIT, - expr.span, - "unneeded `()`", - "remove the `()`", - String::new(), - Applicability::MachineApplicable, - ); + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + kind: FnKind<'tcx>, + _: &'tcx FnDecl<'tcx>, + body: &'tcx Body<'tcx>, + _: Span, + _: HirId, + ) { + match kind { + FnKind::Closure(_) => check_final_expr(cx, &body.value, Some(body.value.span), RetReplacement::Empty), + FnKind::ItemFn(..) | FnKind::Method(..) => { + if let ExprKind::Block(ref block, _) = body.value.kind { + check_block_return(cx, block); } }, - _ => (), } } +} - fn check_poly_trait_ref(&mut self, cx: &EarlyContext<'_>, poly: &ast::PolyTraitRef, _: &ast::TraitBoundModifier) { - let segments = &poly.trait_ref.path.segments; +fn attr_is_cfg(attr: &Attribute) -> bool { + attr.meta_item_list().is_some() && attr.has_name(sym!(cfg)) +} - if_chain! { - if segments.len() == 1; - if ["Fn", "FnMut", "FnOnce"].contains(&&*segments[0].ident.name.as_str()); - if let Some(args) = &segments[0].args; - if let ast::GenericArgs::Parenthesized(generic_args) = &**args; - if let ast::FnRetTy::Ty(ty) = &generic_args.output; - if ty.kind.is_unit(); - then { - lint_unneeded_unit_return(cx, ty, generic_args.span); - } +fn check_block_return<'tcx>(cx: &LateContext<'tcx>, block: &Block<'tcx>) { + if let Some(expr) = block.expr { + check_final_expr(cx, expr, Some(expr.span), RetReplacement::Empty); + } else if let Some(stmt) = block.stmts.iter().last() { + match stmt.kind { + StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => { + check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty); + }, + _ => (), } } } -// get the def site -#[must_use] -fn get_def(span: Span) -> Option { - if span.from_expansion() { - Some(span.ctxt().outer_expn_data().def_site) - } else { - None +fn check_final_expr<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + span: Option, + replacement: RetReplacement, +) { + if last_statement_borrows(cx, expr) { + return; } -} -// is this expr a `()` unit? -fn is_unit_expr(expr: &ast::Expr) -> bool { - if let ast::ExprKind::Tup(ref vals) = expr.kind { - vals.is_empty() - } else { - false + match expr.kind { + // simple return is always "bad" + ExprKind::Ret(ref inner) => { + // allow `#[cfg(a)] return a; #[cfg(b)] return b;` + if !expr.attrs.iter().any(attr_is_cfg) { + let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner)); + if !borrows { + emit_return_lint( + cx, + span.expect("`else return` is not possible"), + inner.as_ref().map(|i| i.span), + replacement, + ); + } + } + }, + // a whole block? check it! + ExprKind::Block(ref block, _) => { + check_block_return(cx, block); + }, + // a match expr, check all arms + // an if/if let expr, check both exprs + // note, if without else is going to be a type checking error anyways + // (except for unit type functions) so we don't match it + ExprKind::Match(_, ref arms, source) => match source { + MatchSource::Normal => { + for arm in arms.iter() { + check_final_expr(cx, &arm.body, Some(arm.body.span), RetReplacement::Block); + } + }, + MatchSource::IfDesugar { + contains_else_clause: true, + } + | MatchSource::IfLetDesugar { + contains_else_clause: true, + } => { + if let ExprKind::Block(ref ifblock, _) = arms[0].body.kind { + check_block_return(cx, ifblock); + } + check_final_expr(cx, arms[1].body, None, RetReplacement::Empty); + }, + _ => (), + }, + _ => (), } } -fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) { - let (ret_span, appl) = if let Ok(fn_source) = cx.sess().source_map().span_to_snippet(span.with_hi(ty.span.hi())) { - fn_source - .rfind("->") - .map_or((ty.span, Applicability::MaybeIncorrect), |rpos| { - ( - #[allow(clippy::cast_possible_truncation)] - ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), - Applicability::MachineApplicable, - ) +fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, inner_span: Option, replacement: RetReplacement) { + match inner_span { + Some(inner_span) => { + if in_external_macro(cx.tcx.sess, inner_span) || inner_span.from_expansion() { + return; + } + + span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| { + if let Some(snippet) = snippet_opt(cx, inner_span) { + diag.span_suggestion(ret_span, "remove `return`", snippet, Applicability::MachineApplicable); + } }) - } else { - (ty.span, Applicability::MaybeIncorrect) - }; - span_lint_and_sugg( - cx, - UNUSED_UNIT, - ret_span, - "unneeded unit return type", - "remove the `-> ()`", - String::new(), - appl, - ); + }, + None => match replacement { + RetReplacement::Empty => { + span_lint_and_sugg( + cx, + NEEDLESS_RETURN, + ret_span, + "unneeded `return` statement", + "remove `return`", + String::new(), + Applicability::MachineApplicable, + ); + }, + RetReplacement::Block => { + span_lint_and_sugg( + cx, + NEEDLESS_RETURN, + ret_span, + "unneeded `return` statement", + "replace `return` with an empty block", + "{}".to_string(), + Applicability::MachineApplicable, + ); + }, + }, + } +} + +fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + let mut visitor = BorrowVisitor { cx, borrows: false }; + walk_expr(&mut visitor, expr); + visitor.borrows +} + +struct BorrowVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + borrows: bool, +} + +impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if self.borrows { + return; + } + + if let Some(def_id) = fn_def_id(self.cx, expr) { + self.borrows = self + .cx + .tcx + .fn_sig(def_id) + .output() + .skip_binder() + .walk() + .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))); + } + + walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } } diff --git a/clippy_lints/src/unused_unit.rs b/clippy_lints/src/unused_unit.rs new file mode 100644 index 00000000000..e322e402535 --- /dev/null +++ b/clippy_lints/src/unused_unit.rs @@ -0,0 +1,145 @@ +use if_chain::if_chain; +use rustc_ast::ast; +use rustc_ast::visit::FnKind; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; +use rustc_span::BytePos; + +use crate::utils::span_lint_and_sugg; + +declare_clippy_lint! { + /// **What it does:** Checks for unit (`()`) expressions that can be removed. + /// + /// **Why is this bad?** Such expressions add no value, but can make the code + /// less readable. Depending on formatting they can make a `break` or `return` + /// statement look like a function call. + /// + /// **Known problems:** The lint currently misses unit return types in types, + /// e.g., the `F` in `fn generic_unit ()>(f: F) { .. }`. + /// + /// **Example:** + /// ```rust + /// fn return_unit() -> () { + /// () + /// } + /// ``` + pub UNUSED_UNIT, + style, + "needless unit expression" +} + +declare_lint_pass!(UnusedUnit => [UNUSED_UNIT]); + +impl EarlyLintPass for UnusedUnit { + fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, span: Span, _: ast::NodeId) { + if_chain! { + if let ast::FnRetTy::Ty(ref ty) = kind.decl().output; + if let ast::TyKind::Tup(ref vals) = ty.kind; + if vals.is_empty() && !ty.span.from_expansion() && get_def(span) == get_def(ty.span); + then { + lint_unneeded_unit_return(cx, ty, span); + } + } + } + + fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) { + if_chain! { + if let Some(ref stmt) = block.stmts.last(); + if let ast::StmtKind::Expr(ref expr) = stmt.kind; + if is_unit_expr(expr) && !stmt.span.from_expansion(); + then { + let sp = expr.span; + span_lint_and_sugg( + cx, + UNUSED_UNIT, + sp, + "unneeded unit expression", + "remove the final `()`", + String::new(), + Applicability::MachineApplicable, + ); + } + } + } + + fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { + match e.kind { + ast::ExprKind::Ret(Some(ref expr)) | ast::ExprKind::Break(_, Some(ref expr)) => { + if is_unit_expr(expr) && !expr.span.from_expansion() { + span_lint_and_sugg( + cx, + UNUSED_UNIT, + expr.span, + "unneeded `()`", + "remove the `()`", + String::new(), + Applicability::MachineApplicable, + ); + } + }, + _ => (), + } + } + + fn check_poly_trait_ref(&mut self, cx: &EarlyContext<'_>, poly: &ast::PolyTraitRef, _: &ast::TraitBoundModifier) { + let segments = &poly.trait_ref.path.segments; + + if_chain! { + if segments.len() == 1; + if ["Fn", "FnMut", "FnOnce"].contains(&&*segments[0].ident.name.as_str()); + if let Some(args) = &segments[0].args; + if let ast::GenericArgs::Parenthesized(generic_args) = &**args; + if let ast::FnRetTy::Ty(ty) = &generic_args.output; + if ty.kind.is_unit(); + then { + lint_unneeded_unit_return(cx, ty, generic_args.span); + } + } + } +} + +// get the def site +#[must_use] +fn get_def(span: Span) -> Option { + if span.from_expansion() { + Some(span.ctxt().outer_expn_data().def_site) + } else { + None + } +} + +// is this expr a `()` unit? +fn is_unit_expr(expr: &ast::Expr) -> bool { + if let ast::ExprKind::Tup(ref vals) = expr.kind { + vals.is_empty() + } else { + false + } +} + +fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) { + let (ret_span, appl) = if let Ok(fn_source) = cx.sess().source_map().span_to_snippet(span.with_hi(ty.span.hi())) { + fn_source + .rfind("->") + .map_or((ty.span, Applicability::MaybeIncorrect), |rpos| { + ( + #[allow(clippy::cast_possible_truncation)] + ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), + Applicability::MachineApplicable, + ) + }) + } else { + (ty.span, Applicability::MaybeIncorrect) + }; + span_lint_and_sugg( + cx, + UNUSED_UNIT, + ret_span, + "unneeded unit return type", + "remove the `-> ()`", + String::new(), + appl, + ); +} diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index 6b5cf2626df..b795516f999 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -76,6 +76,17 @@ mod no_lint_if_stmt_borrows { let stdin = ::std::io::stdin(); return stdin.lock().lines().next().unwrap().unwrap(); } + + fn read_line2(value: bool) -> String { + if value { + use std::io::BufRead; + let stdin = ::std::io::stdin(); + let _a = stdin.lock().lines().next().unwrap().unwrap(); + return String::from("test"); + } else { + return String::new(); + } + } } } diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index 1a693c9aa53..3547991935d 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -76,6 +76,17 @@ mod no_lint_if_stmt_borrows { let stdin = ::std::io::stdin(); return stdin.lock().lines().next().unwrap().unwrap(); } + + fn read_line2(value: bool) -> String { + if value { + use std::io::BufRead; + let stdin = ::std::io::stdin(); + let _a = stdin.lock().lines().next().unwrap().unwrap(); + return String::from("test"); + } else { + return String::new(); + } + } } } -- cgit 1.4.1-3-g733a5 From baa4cb1cddc3a8ce1f47c4006e236edf082ee858 Mon Sep 17 00:00:00 2001 From: jrqc Date: Fri, 14 Aug 2020 09:25:26 +0300 Subject: early return removed --- clippy_lints/src/returns.rs | 4 ---- clippy_lints/src/unused_unit.rs | 3 +-- tests/ui/needless_return.fixed | 30 +++++++++++++----------------- tests/ui/needless_return.rs | 30 +++++++++++++----------------- tests/ui/needless_return.stderr | 14 +++++++++++++- 5 files changed, 40 insertions(+), 41 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 4d91f9be999..3c5541e64b4 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -160,10 +160,6 @@ fn check_final_expr<'tcx>( span: Option, replacement: RetReplacement, ) { - if last_statement_borrows(cx, expr) { - return; - } - match expr.kind { // simple return is always "bad" ExprKind::Ret(ref inner) => { diff --git a/clippy_lints/src/unused_unit.rs b/clippy_lints/src/unused_unit.rs index e322e402535..7548c6afa97 100644 --- a/clippy_lints/src/unused_unit.rs +++ b/clippy_lints/src/unused_unit.rs @@ -16,8 +16,7 @@ declare_clippy_lint! { /// less readable. Depending on formatting they can make a `break` or `return` /// statement look like a function call. /// - /// **Known problems:** The lint currently misses unit return types in types, - /// e.g., the `F` in `fn generic_unit ()>(f: F) { .. }`. + /// **Known problems:** None. /// /// **Example:** /// ```rust diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index b795516f999..d849e093da7 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -69,24 +69,20 @@ fn test_void_match(x: u32) { } } -mod no_lint_if_stmt_borrows { - mod issue_5858 { - fn read_line() -> String { - use std::io::BufRead; - let stdin = ::std::io::stdin(); - return stdin.lock().lines().next().unwrap().unwrap(); - } +fn read_line() -> String { + use std::io::BufRead; + let stdin = ::std::io::stdin(); + return stdin.lock().lines().next().unwrap().unwrap(); +} - fn read_line2(value: bool) -> String { - if value { - use std::io::BufRead; - let stdin = ::std::io::stdin(); - let _a = stdin.lock().lines().next().unwrap().unwrap(); - return String::from("test"); - } else { - return String::new(); - } - } +fn borrows_but_not_last(value: bool) -> String { + if value { + use std::io::BufRead; + let stdin = ::std::io::stdin(); + let _a = stdin.lock().lines().next().unwrap().unwrap(); + String::from("test") + } else { + String::new() } } diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index 3547991935d..29f2bd1852a 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -69,24 +69,20 @@ fn test_void_match(x: u32) { } } -mod no_lint_if_stmt_borrows { - mod issue_5858 { - fn read_line() -> String { - use std::io::BufRead; - let stdin = ::std::io::stdin(); - return stdin.lock().lines().next().unwrap().unwrap(); - } +fn read_line() -> String { + use std::io::BufRead; + let stdin = ::std::io::stdin(); + return stdin.lock().lines().next().unwrap().unwrap(); +} - fn read_line2(value: bool) -> String { - if value { - use std::io::BufRead; - let stdin = ::std::io::stdin(); - let _a = stdin.lock().lines().next().unwrap().unwrap(); - return String::from("test"); - } else { - return String::new(); - } - } +fn borrows_but_not_last(value: bool) -> String { + if value { + use std::io::BufRead; + let stdin = ::std::io::stdin(); + let _a = stdin.lock().lines().next().unwrap().unwrap(); + return String::from("test"); + } else { + return String::new(); } } diff --git a/tests/ui/needless_return.stderr b/tests/ui/needless_return.stderr index c34eecbcbb6..f73c833a801 100644 --- a/tests/ui/needless_return.stderr +++ b/tests/ui/needless_return.stderr @@ -72,5 +72,17 @@ error: unneeded `return` statement LL | _ => return, | ^^^^^^ help: replace `return` with an empty block: `{}` -error: aborting due to 12 previous errors +error: unneeded `return` statement + --> $DIR/needless_return.rs:83:9 + | +LL | return String::from("test"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:85:9 + | +LL | return String::new(); + | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()` + +error: aborting due to 14 previous errors -- cgit 1.4.1-3-g733a5 From 1a140dcc1c933ae84365bd1153372a7430f6f647 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sun, 16 Aug 2020 00:25:54 +0200 Subject: Improve needless_doctest_main by using the parser --- clippy_lints/src/doc.rs | 75 +++++++++++++++++++++++++++++++++++---- tests/ui/needless_doc_main.rs | 68 ++++++++++++++++++++++++++++++++--- tests/ui/needless_doc_main.stderr | 12 +++++-- 3 files changed, 142 insertions(+), 13 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 6ce36fd2360..9555459e240 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -1,16 +1,22 @@ use crate::utils::{implements_trait, is_entrypoint_fn, is_type_diagnostic_item, return_ty, span_lint}; use if_chain::if_chain; use itertools::Itertools; -use rustc_ast::ast::{AttrKind, Attribute}; +use rustc_ast::ast::{Async, AttrKind, Attribute, FnRetTy, ItemKind}; use rustc_ast::token::CommentKind; use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::sync::Lrc; +use rustc_errors::emitter::EmitterWriter; +use rustc_errors::Handler; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty; +use rustc_parse::maybe_new_parser_from_source_str; +use rustc_session::parse::ParseSess; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::source_map::{BytePos, MultiSpan, Span}; -use rustc_span::Pos; +use rustc_span::source_map::{BytePos, FilePathMapping, MultiSpan, SourceMap, Span}; +use rustc_span::{FileName, Pos}; +use std::io; use std::ops::Range; use url::Url; @@ -431,10 +437,67 @@ fn check_doc<'a, Events: Iterator, Range, text: &str, span: Span) { - if text.contains("fn main() {") && !LEAVE_MAIN_PATTERNS.iter().any(|p| text.contains(p)) { + fn has_needless_main(code: &str) -> bool { + let filename = FileName::anon_source_code(code); + + let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let emitter = EmitterWriter::new(box io::sink(), None, false, false, false, None, false); + let handler = Handler::with_emitter(false, None, box emitter); + let sess = ParseSess::with_span_handler(handler, sm); + + let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code.into()) { + Ok(p) => p, + Err(errs) => { + for mut err in errs { + err.cancel(); + } + return false; + }, + }; + + let mut relevant_main_found = false; + loop { + match parser.parse_item() { + Ok(Some(item)) => match &item.kind { + // Tests with one of these items are ignored + ItemKind::Static(..) + | ItemKind::Const(..) + | ItemKind::ExternCrate(..) + | ItemKind::ForeignMod(..) => return false, + // We found a main function ... + ItemKind::Fn(_, sig, _, Some(block)) if item.ident.name == sym!(main) => { + let is_async = matches!(sig.header.asyncness, Async::Yes{..}); + let returns_nothing = match &sig.decl.output { + FnRetTy::Default(..) => true, + FnRetTy::Ty(ty) if ty.kind.is_unit() => true, + _ => false, + }; + + if returns_nothing && !is_async && !block.stmts.is_empty() { + // This main function should be linted, but only if there are no other functions + relevant_main_found = true; + } else { + // This main function should not be linted, we're done + return false; + } + }, + // Another function was found; this case is ignored too + ItemKind::Fn(..) => return false, + _ => {}, + }, + Ok(None) => break, + Err(mut e) => { + e.cancel(); + return false; + }, + } + } + + relevant_main_found + } + + if has_needless_main(text) { span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest"); } } diff --git a/tests/ui/needless_doc_main.rs b/tests/ui/needless_doc_main.rs index 682d7b3c4ce..883683e08a2 100644 --- a/tests/ui/needless_doc_main.rs +++ b/tests/ui/needless_doc_main.rs @@ -9,8 +9,14 @@ /// } /// ``` /// -/// This should, too. +/// With an explicit return type it should lint too +/// ``` +/// fn main() -> () { +/// unimplemented!(); +/// } +/// ``` /// +/// This should, too. /// ```rust /// fn main() { /// unimplemented!(); @@ -18,7 +24,6 @@ /// ``` /// /// This one too. -/// /// ```no_run /// fn main() { /// unimplemented!(); @@ -33,6 +38,20 @@ fn bad_doctests() {} /// fn main(){} /// ``` /// +/// This shouldn't lint either, because main is async: +/// ``` +/// async fn main() { +/// assert_eq!(42, ANSWER); +/// } +/// ``` +/// +/// Same here, because the return type is not the unit type: +/// ``` +/// fn main() -> Result<()> { +/// Ok(()) +/// } +/// ``` +/// /// This shouldn't lint either, because there's a `static`: /// ``` /// static ANSWER: i32 = 42; @@ -42,6 +61,15 @@ fn bad_doctests() {} /// } /// ``` /// +/// This shouldn't lint either, because there's a `const`: +/// ``` +/// fn main() { +/// assert_eq!(42, ANSWER); +/// } +/// +/// const ANSWER: i32 = 42; +/// ``` +/// /// Neither should this lint because of `extern crate`: /// ``` /// #![feature(test)] @@ -51,8 +79,41 @@ fn bad_doctests() {} /// } /// ``` /// -/// We should not lint ignored examples: +/// Neither should this lint because it has an extern block: +/// ``` +/// extern {} +/// fn main() { +/// unimplemented!(); +/// } +/// ``` +/// +/// This should not lint because there is another function defined: +/// ``` +/// fn fun() {} +/// +/// fn main() { +/// unimplemented!(); +/// } +/// ``` /// +/// We should not lint inside raw strings ... +/// ``` +/// let string = r#" +/// fn main() { +/// unimplemented!(); +/// } +/// "#; +/// ``` +/// +/// ... or comments +/// ``` +/// // fn main() { +/// // let _inception = 42; +/// // } +/// let _inception = 42; +/// ``` +/// +/// We should not lint ignored examples: /// ```rust,ignore /// fn main() { /// unimplemented!(); @@ -60,7 +121,6 @@ fn bad_doctests() {} /// ``` /// /// Or even non-rust examples: -/// /// ```text /// fn main() { /// is what starts the program diff --git a/tests/ui/needless_doc_main.stderr b/tests/ui/needless_doc_main.stderr index 65d40ee6832..05c7f9d33a7 100644 --- a/tests/ui/needless_doc_main.stderr +++ b/tests/ui/needless_doc_main.stderr @@ -7,16 +7,22 @@ LL | /// fn main() { = note: `-D clippy::needless-doctest-main` implied by `-D warnings` error: needless `fn main` in doctest - --> $DIR/needless_doc_main.rs:15:4 + --> $DIR/needless_doc_main.rs:14:4 + | +LL | /// fn main() -> () { + | ^^^^^^^^^^^^^^^^^^ + +error: needless `fn main` in doctest + --> $DIR/needless_doc_main.rs:21:4 | LL | /// fn main() { | ^^^^^^^^^^^^ error: needless `fn main` in doctest - --> $DIR/needless_doc_main.rs:23:4 + --> $DIR/needless_doc_main.rs:28:4 | LL | /// fn main() { | ^^^^^^^^^^^^ -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From a3ea65c2d9bc52f308745ae488435388fce753a2 Mon Sep 17 00:00:00 2001 From: Dániel Buga Date: Mon, 15 Jun 2020 11:21:56 +0200 Subject: Implement new lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 3 + clippy_lints/src/methods/mod.rs | 130 +++++++++++++++++++++++++++++- src/lintlist/mod.rs | 7 ++ tests/ui/unnecessary_lazy_eval.fixed | 105 +++++++++++++++++++++++++ tests/ui/unnecessary_lazy_eval.rs | 105 +++++++++++++++++++++++++ tests/ui/unnecessary_lazy_eval.stderr | 144 ++++++++++++++++++++++++++++++++++ 7 files changed, 494 insertions(+), 1 deletion(-) create mode 100644 tests/ui/unnecessary_lazy_eval.fixed create mode 100644 tests/ui/unnecessary_lazy_eval.rs create mode 100644 tests/ui/unnecessary_lazy_eval.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 50fe7612909..c21190e9b9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1646,6 +1646,7 @@ Released 2018-09-13 [`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none [`option_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn [`option_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_option +[`option_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_unwrap_or_else [`or_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_fun_call [`out_of_bounds_indexing`]: https://rust-lang.github.io/rust-clippy/master/index.html#out_of_bounds_indexing [`overflow_check_conditional`]: https://rust-lang.github.io/rust-clippy/master/index.html#overflow_check_conditional diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 986e9d9bee4..cb29f71387b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -672,6 +672,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::OK_EXPECT, &methods::OPTION_AS_REF_DEREF, &methods::OPTION_MAP_OR_NONE, + &methods::UNNECESSARY_LAZY_EVALUATION, &methods::OR_FUN_CALL, &methods::RESULT_MAP_OR_INTO_OPTION, &methods::SEARCH_IS_SOME, @@ -1360,6 +1361,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::UNINIT_ASSUMED_INIT), LintId::of(&methods::UNNECESSARY_FILTER_MAP), LintId::of(&methods::UNNECESSARY_FOLD), + LintId::of(&methods::UNNECESSARY_LAZY_EVALUATION), LintId::of(&methods::USELESS_ASREF), LintId::of(&methods::WRONG_SELF_CONVENTION), LintId::of(&methods::ZST_OFFSET), @@ -1610,6 +1612,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::FILTER_NEXT), LintId::of(&methods::FLAT_MAP_IDENTITY), LintId::of(&methods::OPTION_AS_REF_DEREF), + LintId::of(&methods::UNNECESSARY_LAZY_EVALUATION), LintId::of(&methods::SEARCH_IS_SOME), LintId::of(&methods::SKIP_WHILE_NEXT), LintId::of(&methods::SUSPICIOUS_MAP), diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 2330978e67f..61b7f2647ee 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1329,6 +1329,32 @@ declare_clippy_lint! { "`push_str()` used with a single-character string literal as parameter" } +declare_clippy_lint! { + /// **What it does:** Looks for unnecessary lazily evaluated closures on `Option` and `Result`. + /// + /// **Why is this bad?** Using eager evaluation is shorter and simpler in some cases. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // example code where clippy issues a warning + /// let opt: Option = None; + /// + /// opt.unwrap_or_else(|| 42); + /// ``` + /// Use instead: + /// ```rust + /// let opt: Option = None; + /// + /// opt.unwrap_or(42); + /// ``` + pub UNNECESSARY_LAZY_EVALUATION, + style, + "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation" +} + declare_lint_pass!(Methods => [ UNWRAP_USED, EXPECT_USED, @@ -1378,6 +1404,7 @@ declare_lint_pass!(Methods => [ ZST_OFFSET, FILETYPE_IS_FILE, OPTION_AS_REF_DEREF, + UNNECESSARY_LAZY_EVALUATION, ]); impl<'tcx> LateLintPass<'tcx> for Methods { @@ -1398,13 +1425,18 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["expect", "ok"] => lint_ok_expect(cx, expr, arg_lists[1]), ["expect", ..] => lint_expect(cx, expr, arg_lists[0]), ["unwrap_or", "map"] => option_map_unwrap_or::lint(cx, expr, arg_lists[1], arg_lists[0], method_spans[1]), - ["unwrap_or_else", "map"] => lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]), + ["unwrap_or_else", "map"] => { + lint_lazy_eval(cx, expr, arg_lists[0], true, "unwrap_or"); + lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]); + }, ["map_or", ..] => lint_map_or_none(cx, expr, arg_lists[0]), ["and_then", ..] => { + lint_lazy_eval(cx, expr, arg_lists[0], false, "and"); bind_instead_of_map::OptionAndThenSome::lint(cx, expr, arg_lists[0]); bind_instead_of_map::ResultAndThenOk::lint(cx, expr, arg_lists[0]); }, ["or_else", ..] => { + lint_lazy_eval(cx, expr, arg_lists[0], false, "or"); bind_instead_of_map::ResultOrElseErrInfo::lint(cx, expr, arg_lists[0]); }, ["next", "filter"] => lint_filter_next(cx, expr, arg_lists[1]), @@ -1448,6 +1480,9 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["is_file", ..] => lint_filetype_is_file(cx, expr, arg_lists[0]), ["map", "as_ref"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], false), ["map", "as_mut"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], true), + ["unwrap_or_else", ..] => lint_lazy_eval(cx, expr, arg_lists[0], true, "unwrap_or"), + ["get_or_insert_with", ..] => lint_lazy_eval(cx, expr, arg_lists[0], true, "get_or_insert"), + ["ok_or_else", ..] => lint_lazy_eval(cx, expr, arg_lists[0], true, "ok_or"), _ => {}, } @@ -2663,6 +2698,99 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map } } +/// lint use of `_else(simple closure)` for `Option`s and `Result`s that can be +/// replaced with `(return value of simple closure)` +fn lint_lazy_eval<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + expr: &'tcx hir::Expr<'_>, + args: &'tcx [hir::Expr<'_>], + allow_variant_calls: bool, + simplify_using: &str, +) { + let is_option = is_type_diagnostic_item(cx, cx.tables.expr_ty(&args[0]), sym!(option_type)); + let is_result = is_type_diagnostic_item(cx, cx.tables.expr_ty(&args[0]), sym!(result_type)); + + if !is_option && !is_result { + return; + } + + // Return true if the expression is an accessor of any of the arguments + fn expr_uses_argument(expr: &hir::Expr<'_>, params: &[hir::Param<'_>]) -> bool { + params.iter().any(|arg| { + if_chain! { + if let hir::PatKind::Binding(_, _, ident, _) = arg.pat.kind; + if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = expr.kind; + if let [p, ..] = path.segments; + then { + ident.name == p.ident.name + } else { + false + } + } + }) + } + + fn match_any_qpath(path: &hir::QPath<'_>, paths: &[&[&str]]) -> bool { + paths.iter().any(|candidate| match_qpath(path, candidate)) + } + + if let hir::ExprKind::Closure(_, _, eid, _, _) = args[1].kind { + let body = cx.tcx.hir().body(eid); + let ex = &body.value; + let params = &body.params; + + let simplify = match ex.kind { + // Closures returning literals can be unconditionally simplified + hir::ExprKind::Lit(_) => true, + + // Reading fields can be simplified if the object is not an argument of the closure + hir::ExprKind::Field(ref object, _) => !expr_uses_argument(object, params), + + // Paths can be simplified if the root is not the argument, this also covers None + hir::ExprKind::Path(_) => !expr_uses_argument(ex, params), + + // Calls to Some, Ok, Err can be considered literals if they don't derive an argument + hir::ExprKind::Call(ref func, ref args) => if_chain! { + if allow_variant_calls; // Disable lint when rules conflict with bind_instead_of_map + if let hir::ExprKind::Path(ref path) = func.kind; + if match_any_qpath(path, &[&["Some"], &["Ok"], &["Err"]]); + then { + !args.iter().any(|arg| expr_uses_argument(arg, params)) + } else { + false + } + }, + + // For anything more complex than the above, a closure is probably the right solution, + // or the case is handled by an other lint + _ => false, + }; + + if simplify { + let msg = if is_option { + "unnecessary closure used to substitute value for `Option::None`" + } else { + "unnecessary closure used to substitute value for `Result::Err`" + }; + + span_lint_and_sugg( + cx, + UNNECESSARY_LAZY_EVALUATION, + expr.span, + msg, + &format!("Use `{}` instead", simplify_using), + format!( + "{0}.{1}({2})", + snippet(cx, args[0].span, ".."), + simplify_using, + snippet(cx, ex.span, ".."), + ), + Applicability::MachineApplicable, + ); + } + } +} + /// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s fn lint_map_unwrap_or_else<'tcx>( cx: &LateContext<'tcx>, diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 233f95deedd..1e76163f946 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2383,6 +2383,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "methods", }, + Lint { + name: "unnecessary_lazy_eval", + group: "style", + desc: "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation", + deprecation: None, + module: "methods", + }, Lint { name: "unnecessary_mut_passed", group: "style", diff --git a/tests/ui/unnecessary_lazy_eval.fixed b/tests/ui/unnecessary_lazy_eval.fixed new file mode 100644 index 00000000000..fcfa6dfe12d --- /dev/null +++ b/tests/ui/unnecessary_lazy_eval.fixed @@ -0,0 +1,105 @@ +// run-rustfix +#![warn(clippy::unnecessary_lazy_evaluation)] +#![allow(clippy::redundant_closure)] +#![allow(clippy::bind_instead_of_map)] + +struct Deep(Option); + +#[derive(Copy, Clone)] +struct SomeStruct { + some_field: u32, +} + +impl SomeStruct { + fn return_some_field(&self) -> u32 { + self.some_field + } +} + +fn some_call() -> T { + T::default() +} + +fn main() { + let astronomers_pi = 10; + let ext_str = SomeStruct { some_field: 10 }; + + // Should lint - Option + let mut opt = Some(42); + let ext_opt = Some(42); + let _ = opt.unwrap_or(2); + let _ = opt.unwrap_or(astronomers_pi); + let _ = opt.unwrap_or(ext_str.some_field); + let _ = opt.and(ext_opt); + let _ = opt.or(ext_opt); + let _ = opt.or(None); + let _ = opt.get_or_insert(2); + let _ = opt.ok_or(2); + + // Cases when unwrap is not called on a simple variable + let _ = Some(10).unwrap_or(2); + let _ = Some(10).and(ext_opt); + let _: Option = None.or(ext_opt); + let _ = None.get_or_insert(2); + let _: Result = None.ok_or(2); + let _: Option = None.or(None); + + let mut deep = Deep(Some(42)); + let _ = deep.0.unwrap_or(2); + let _ = deep.0.and(ext_opt); + let _ = deep.0.or(None); + let _ = deep.0.get_or_insert(2); + let _ = deep.0.ok_or(2); + + // Should not lint - Option + let _ = opt.unwrap_or_else(|| ext_str.return_some_field()); + let _ = opt.or_else(some_call); + let _ = opt.or_else(|| some_call()); + let _: Result = opt.ok_or_else(|| some_call()); + let _: Result = opt.ok_or_else(some_call); + let _ = deep.0.get_or_insert_with(|| some_call()); + let _ = deep.0.or_else(some_call); + let _ = deep.0.or_else(|| some_call()); + + // These are handled by bind_instead_of_map + let _: Option = None.or_else(|| Some(3)); + let _ = deep.0.or_else(|| Some(3)); + let _ = opt.or_else(|| Some(3)); + + // Should lint - Result + let res: Result = Err(5); + let res2: Result = Err(SomeStruct { some_field: 5 }); + + let _ = res2.unwrap_or(2); + let _ = res2.unwrap_or(astronomers_pi); + let _ = res2.unwrap_or(ext_str.some_field); + + // Should not lint - Result + let _ = res.unwrap_or_else(|err| err); + let _ = res2.unwrap_or_else(|err| err.some_field); + let _ = res2.unwrap_or_else(|err| err.return_some_field()); + let _ = res2.unwrap_or_else(|_| ext_str.return_some_field()); + + let _: Result = res.and_then(|x| Ok(x)); + let _: Result = res.and_then(|x| Err(x)); + + let _: Result = res.or_else(|err| Ok(err)); + let _: Result = res.or_else(|err| Err(err)); + + // These are handled by bind_instead_of_map + let _: Result = res.and_then(|_| Ok(2)); + let _: Result = res.and_then(|_| Ok(astronomers_pi)); + let _: Result = res.and_then(|_| Ok(ext_str.some_field)); + + let _: Result = res.and_then(|_| Err(2)); + let _: Result = res.and_then(|_| Err(astronomers_pi)); + let _: Result = res.and_then(|_| Err(ext_str.some_field)); + + let _: Result = res.or_else(|_| Ok(2)); + let _: Result = res.or_else(|_| Ok(astronomers_pi)); + let _: Result = res.or_else(|_| Ok(ext_str.some_field)); + + let _: Result = res.or_else(|_| Err(2)); + let _: Result = res.or_else(|_| Err(astronomers_pi)); + let _: Result = res.or_else(|_| Err(ext_str.some_field)); +} diff --git a/tests/ui/unnecessary_lazy_eval.rs b/tests/ui/unnecessary_lazy_eval.rs new file mode 100644 index 00000000000..04b3c8ae1e2 --- /dev/null +++ b/tests/ui/unnecessary_lazy_eval.rs @@ -0,0 +1,105 @@ +// run-rustfix +#![warn(clippy::unnecessary_lazy_eval)] +#![allow(clippy::redundant_closure)] +#![allow(clippy::bind_instead_of_map)] + +struct Deep(Option); + +#[derive(Copy, Clone)] +struct SomeStruct { + some_field: u32, +} + +impl SomeStruct { + fn return_some_field(&self) -> u32 { + self.some_field + } +} + +fn some_call() -> T { + T::default() +} + +fn main() { + let astronomers_pi = 10; + let ext_str = SomeStruct { some_field: 10 }; + + // Should lint - Option + let mut opt = Some(42); + let ext_opt = Some(42); + let _ = opt.unwrap_or_else(|| 2); + let _ = opt.unwrap_or_else(|| astronomers_pi); + let _ = opt.unwrap_or_else(|| ext_str.some_field); + let _ = opt.and_then(|_| ext_opt); + let _ = opt.or_else(|| ext_opt); + let _ = opt.or_else(|| None); + let _ = opt.get_or_insert_with(|| 2); + let _ = opt.ok_or_else(|| 2); + + // Cases when unwrap is not called on a simple variable + let _ = Some(10).unwrap_or_else(|| 2); + let _ = Some(10).and_then(|_| ext_opt); + let _: Option = None.or_else(|| ext_opt); + let _ = None.get_or_insert_with(|| 2); + let _: Result = None.ok_or_else(|| 2); + let _: Option = None.or_else(|| None); + + let mut deep = Deep(Some(42)); + let _ = deep.0.unwrap_or_else(|| 2); + let _ = deep.0.and_then(|_| ext_opt); + let _ = deep.0.or_else(|| None); + let _ = deep.0.get_or_insert_with(|| 2); + let _ = deep.0.ok_or_else(|| 2); + + // Should not lint - Option + let _ = opt.unwrap_or_else(|| ext_str.return_some_field()); + let _ = opt.or_else(some_call); + let _ = opt.or_else(|| some_call()); + let _: Result = opt.ok_or_else(|| some_call()); + let _: Result = opt.ok_or_else(some_call); + let _ = deep.0.get_or_insert_with(|| some_call()); + let _ = deep.0.or_else(some_call); + let _ = deep.0.or_else(|| some_call()); + + // These are handled by bind_instead_of_map + let _: Option = None.or_else(|| Some(3)); + let _ = deep.0.or_else(|| Some(3)); + let _ = opt.or_else(|| Some(3)); + + // Should lint - Result + let res: Result = Err(5); + let res2: Result = Err(SomeStruct { some_field: 5 }); + + let _ = res2.unwrap_or_else(|_| 2); + let _ = res2.unwrap_or_else(|_| astronomers_pi); + let _ = res2.unwrap_or_else(|_| ext_str.some_field); + + // Should not lint - Result + let _ = res.unwrap_or_else(|err| err); + let _ = res2.unwrap_or_else(|err| err.some_field); + let _ = res2.unwrap_or_else(|err| err.return_some_field()); + let _ = res2.unwrap_or_else(|_| ext_str.return_some_field()); + + let _: Result = res.and_then(|x| Ok(x)); + let _: Result = res.and_then(|x| Err(x)); + + let _: Result = res.or_else(|err| Ok(err)); + let _: Result = res.or_else(|err| Err(err)); + + // These are handled by bind_instead_of_map + let _: Result = res.and_then(|_| Ok(2)); + let _: Result = res.and_then(|_| Ok(astronomers_pi)); + let _: Result = res.and_then(|_| Ok(ext_str.some_field)); + + let _: Result = res.and_then(|_| Err(2)); + let _: Result = res.and_then(|_| Err(astronomers_pi)); + let _: Result = res.and_then(|_| Err(ext_str.some_field)); + + let _: Result = res.or_else(|_| Ok(2)); + let _: Result = res.or_else(|_| Ok(astronomers_pi)); + let _: Result = res.or_else(|_| Ok(ext_str.some_field)); + + let _: Result = res.or_else(|_| Err(2)); + let _: Result = res.or_else(|_| Err(astronomers_pi)); + let _: Result = res.or_else(|_| Err(ext_str.some_field)); +} diff --git a/tests/ui/unnecessary_lazy_eval.stderr b/tests/ui/unnecessary_lazy_eval.stderr new file mode 100644 index 00000000000..b941bf84246 --- /dev/null +++ b/tests/ui/unnecessary_lazy_eval.stderr @@ -0,0 +1,144 @@ +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:30:13 + | +LL | let _ = opt.unwrap_or_else(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(2)` + | + = note: `-D clippy::unnecessary-lazy-evaluation` implied by `-D warnings` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:31:13 + | +LL | let _ = opt.unwrap_or_else(|| astronomers_pi); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(astronomers_pi)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:32:13 + | +LL | let _ = opt.unwrap_or_else(|| ext_str.some_field); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(ext_str.some_field)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:33:13 + | +LL | let _ = opt.and_then(|_| ext_opt); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `opt.and(ext_opt)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:34:13 + | +LL | let _ = opt.or_else(|| ext_opt); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(ext_opt)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:35:13 + | +LL | let _ = opt.or_else(|| None); + | ^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(None)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:36:13 + | +LL | let _ = opt.get_or_insert_with(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `opt.get_or_insert(2)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:37:13 + | +LL | let _ = opt.ok_or_else(|| 2); + | ^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `opt.ok_or(2)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:40:13 + | +LL | let _ = Some(10).unwrap_or_else(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `Some(10).unwrap_or(2)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:41:13 + | +LL | let _ = Some(10).and_then(|_| ext_opt); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `Some(10).and(ext_opt)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:42:26 + | +LL | let _: Option = None.or_else(|| ext_opt); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(ext_opt)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:43:13 + | +LL | let _ = None.get_or_insert_with(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `None.get_or_insert(2)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:44:31 + | +LL | let _: Result = None.ok_or_else(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `None.ok_or(2)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:45:26 + | +LL | let _: Option = None.or_else(|| None); + | ^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(None)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:48:13 + | +LL | let _ = deep.0.unwrap_or_else(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `deep.0.unwrap_or(2)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:49:13 + | +LL | let _ = deep.0.and_then(|_| ext_opt); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `deep.0.and(ext_opt)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:50:13 + | +LL | let _ = deep.0.or_else(|| None); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `deep.0.or(None)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:51:13 + | +LL | let _ = deep.0.get_or_insert_with(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `deep.0.get_or_insert(2)` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:52:13 + | +LL | let _ = deep.0.ok_or_else(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `deep.0.ok_or(2)` + +error: unnecessary closure used to substitute value for `Result::Err` + --> $DIR/unnecessary_lazy_eval.rs:73:13 + | +LL | let _ = res2.unwrap_or_else(|_| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(2)` + +error: unnecessary closure used to substitute value for `Result::Err` + --> $DIR/unnecessary_lazy_eval.rs:74:13 + | +LL | let _ = res2.unwrap_or_else(|_| astronomers_pi); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(astronomers_pi)` + +error: unnecessary closure used to substitute value for `Result::Err` + --> $DIR/unnecessary_lazy_eval.rs:75:13 + | +LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(ext_str.some_field)` + +error: unknown clippy lint: clippy::unnecessary_lazy_eval + --> $DIR/unnecessary_lazy_eval.rs:2:9 + | +LL | #![warn(clippy::unnecessary_lazy_eval)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::unnecessary_lazy_evaluation` + | + = note: `-D clippy::unknown-clippy-lints` implied by `-D warnings` + +error: aborting due to 23 previous errors + -- cgit 1.4.1-3-g733a5 From a7cc5d40683b9351c35a627b05886f43fdec684f Mon Sep 17 00:00:00 2001 From: Dániel Buga Date: Mon, 15 Jun 2020 15:26:02 +0200 Subject: Also simplify if the closure body is an index expression --- clippy_lints/src/methods/mod.rs | 10 ++++++ tests/ui/unnecessary_lazy_eval.fixed | 60 +++++++++++++++++-------------- tests/ui/unnecessary_lazy_eval.rs | 60 +++++++++++++++++-------------- tests/ui/unnecessary_lazy_eval.stderr | 68 ++++++++++++++++++++--------------- 4 files changed, 116 insertions(+), 82 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index c672ca41dec..ed8eaba75d1 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2753,6 +2753,16 @@ fn lint_lazy_eval<'a, 'tcx>( // Closures returning literals can be unconditionally simplified hir::ExprKind::Lit(_) => true, + hir::ExprKind::Index(ref object, ref index) => { + // arguments are not being indexed into + if !expr_uses_argument(object, params) { + // arguments are not used as index + !expr_uses_argument(index, params) + } else { + false + } + }, + // Reading fields can be simplified if the object is not an argument of the closure hir::ExprKind::Field(ref object, _) => !expr_uses_argument(object, params), diff --git a/tests/ui/unnecessary_lazy_eval.fixed b/tests/ui/unnecessary_lazy_eval.fixed index fcfa6dfe12d..c806cf8dce4 100644 --- a/tests/ui/unnecessary_lazy_eval.fixed +++ b/tests/ui/unnecessary_lazy_eval.fixed @@ -3,15 +3,15 @@ #![allow(clippy::redundant_closure)] #![allow(clippy::bind_instead_of_map)] -struct Deep(Option); +struct Deep(Option); #[derive(Copy, Clone)] struct SomeStruct { - some_field: u32, + some_field: usize, } impl SomeStruct { - fn return_some_field(&self) -> u32 { + fn return_some_field(&self) -> usize { self.some_field } } @@ -22,6 +22,7 @@ fn some_call() -> T { fn main() { let astronomers_pi = 10; + let ext_arr: [usize; 1] = [2]; let ext_str = SomeStruct { some_field: 10 }; // Should lint - Option @@ -30,19 +31,21 @@ fn main() { let _ = opt.unwrap_or(2); let _ = opt.unwrap_or(astronomers_pi); let _ = opt.unwrap_or(ext_str.some_field); + let _ = opt.unwrap_or(ext_arr[0]); let _ = opt.and(ext_opt); let _ = opt.or(ext_opt); let _ = opt.or(None); let _ = opt.get_or_insert(2); let _ = opt.ok_or(2); + let _ = opt.ok_or(ext_arr[0]); // Cases when unwrap is not called on a simple variable let _ = Some(10).unwrap_or(2); let _ = Some(10).and(ext_opt); - let _: Option = None.or(ext_opt); + let _: Option = None.or(ext_opt); let _ = None.get_or_insert(2); - let _: Result = None.ok_or(2); - let _: Option = None.or(None); + let _: Result = None.ok_or(2); + let _: Option = None.or(None); let mut deep = Deep(Some(42)); let _ = deep.0.unwrap_or(2); @@ -55,20 +58,22 @@ fn main() { let _ = opt.unwrap_or_else(|| ext_str.return_some_field()); let _ = opt.or_else(some_call); let _ = opt.or_else(|| some_call()); - let _: Result = opt.ok_or_else(|| some_call()); - let _: Result = opt.ok_or_else(some_call); + let _: Result = opt.ok_or_else(|| some_call()); + let _: Result = opt.ok_or_else(some_call); let _ = deep.0.get_or_insert_with(|| some_call()); let _ = deep.0.or_else(some_call); let _ = deep.0.or_else(|| some_call()); // These are handled by bind_instead_of_map - let _: Option = None.or_else(|| Some(3)); + let _ = Some(10).and_then(|idx| Some(ext_arr[idx])); + let _ = Some(10).and_then(|idx| Some(idx)); + let _: Option = None.or_else(|| Some(3)); let _ = deep.0.or_else(|| Some(3)); let _ = opt.or_else(|| Some(3)); // Should lint - Result - let res: Result = Err(5); - let res2: Result = Err(SomeStruct { some_field: 5 }); + let res: Result = Err(5); + let res2: Result = Err(SomeStruct { some_field: 5 }); let _ = res2.unwrap_or(2); let _ = res2.unwrap_or(astronomers_pi); @@ -76,30 +81,31 @@ fn main() { // Should not lint - Result let _ = res.unwrap_or_else(|err| err); + let _ = res.unwrap_or_else(|err| ext_arr[err]); let _ = res2.unwrap_or_else(|err| err.some_field); let _ = res2.unwrap_or_else(|err| err.return_some_field()); let _ = res2.unwrap_or_else(|_| ext_str.return_some_field()); - let _: Result = res.and_then(|x| Ok(x)); - let _: Result = res.and_then(|x| Err(x)); + let _: Result = res.and_then(|x| Ok(x)); + let _: Result = res.and_then(|x| Err(x)); - let _: Result = res.or_else(|err| Ok(err)); - let _: Result = res.or_else(|err| Err(err)); + let _: Result = res.or_else(|err| Ok(err)); + let _: Result = res.or_else(|err| Err(err)); // These are handled by bind_instead_of_map - let _: Result = res.and_then(|_| Ok(2)); - let _: Result = res.and_then(|_| Ok(astronomers_pi)); - let _: Result = res.and_then(|_| Ok(ext_str.some_field)); + let _: Result = res.and_then(|_| Ok(2)); + let _: Result = res.and_then(|_| Ok(astronomers_pi)); + let _: Result = res.and_then(|_| Ok(ext_str.some_field)); - let _: Result = res.and_then(|_| Err(2)); - let _: Result = res.and_then(|_| Err(astronomers_pi)); - let _: Result = res.and_then(|_| Err(ext_str.some_field)); + let _: Result = res.and_then(|_| Err(2)); + let _: Result = res.and_then(|_| Err(astronomers_pi)); + let _: Result = res.and_then(|_| Err(ext_str.some_field)); - let _: Result = res.or_else(|_| Ok(2)); - let _: Result = res.or_else(|_| Ok(astronomers_pi)); - let _: Result = res.or_else(|_| Ok(ext_str.some_field)); + let _: Result = res.or_else(|_| Ok(2)); + let _: Result = res.or_else(|_| Ok(astronomers_pi)); + let _: Result = res.or_else(|_| Ok(ext_str.some_field)); - let _: Result = res.or_else(|_| Err(2)); - let _: Result = res.or_else(|_| Err(astronomers_pi)); - let _: Result = res.or_else(|_| Err(ext_str.some_field)); + let _: Result = res.or_else(|_| Err(2)); + let _: Result = res.or_else(|_| Err(astronomers_pi)); + let _: Result = res.or_else(|_| Err(ext_str.some_field)); } diff --git a/tests/ui/unnecessary_lazy_eval.rs b/tests/ui/unnecessary_lazy_eval.rs index 04b3c8ae1e2..dfc6d3ba573 100644 --- a/tests/ui/unnecessary_lazy_eval.rs +++ b/tests/ui/unnecessary_lazy_eval.rs @@ -3,15 +3,15 @@ #![allow(clippy::redundant_closure)] #![allow(clippy::bind_instead_of_map)] -struct Deep(Option); +struct Deep(Option); #[derive(Copy, Clone)] struct SomeStruct { - some_field: u32, + some_field: usize, } impl SomeStruct { - fn return_some_field(&self) -> u32 { + fn return_some_field(&self) -> usize { self.some_field } } @@ -22,6 +22,7 @@ fn some_call() -> T { fn main() { let astronomers_pi = 10; + let ext_arr: [usize; 1] = [2]; let ext_str = SomeStruct { some_field: 10 }; // Should lint - Option @@ -30,19 +31,21 @@ fn main() { let _ = opt.unwrap_or_else(|| 2); let _ = opt.unwrap_or_else(|| astronomers_pi); let _ = opt.unwrap_or_else(|| ext_str.some_field); + let _ = opt.unwrap_or_else(|| ext_arr[0]); let _ = opt.and_then(|_| ext_opt); let _ = opt.or_else(|| ext_opt); let _ = opt.or_else(|| None); let _ = opt.get_or_insert_with(|| 2); let _ = opt.ok_or_else(|| 2); + let _ = opt.ok_or_else(|| ext_arr[0]); // Cases when unwrap is not called on a simple variable let _ = Some(10).unwrap_or_else(|| 2); let _ = Some(10).and_then(|_| ext_opt); - let _: Option = None.or_else(|| ext_opt); + let _: Option = None.or_else(|| ext_opt); let _ = None.get_or_insert_with(|| 2); - let _: Result = None.ok_or_else(|| 2); - let _: Option = None.or_else(|| None); + let _: Result = None.ok_or_else(|| 2); + let _: Option = None.or_else(|| None); let mut deep = Deep(Some(42)); let _ = deep.0.unwrap_or_else(|| 2); @@ -55,20 +58,22 @@ fn main() { let _ = opt.unwrap_or_else(|| ext_str.return_some_field()); let _ = opt.or_else(some_call); let _ = opt.or_else(|| some_call()); - let _: Result = opt.ok_or_else(|| some_call()); - let _: Result = opt.ok_or_else(some_call); + let _: Result = opt.ok_or_else(|| some_call()); + let _: Result = opt.ok_or_else(some_call); let _ = deep.0.get_or_insert_with(|| some_call()); let _ = deep.0.or_else(some_call); let _ = deep.0.or_else(|| some_call()); // These are handled by bind_instead_of_map - let _: Option = None.or_else(|| Some(3)); + let _ = Some(10).and_then(|idx| Some(ext_arr[idx])); + let _ = Some(10).and_then(|idx| Some(idx)); + let _: Option = None.or_else(|| Some(3)); let _ = deep.0.or_else(|| Some(3)); let _ = opt.or_else(|| Some(3)); // Should lint - Result - let res: Result = Err(5); - let res2: Result = Err(SomeStruct { some_field: 5 }); + let res: Result = Err(5); + let res2: Result = Err(SomeStruct { some_field: 5 }); let _ = res2.unwrap_or_else(|_| 2); let _ = res2.unwrap_or_else(|_| astronomers_pi); @@ -76,30 +81,31 @@ fn main() { // Should not lint - Result let _ = res.unwrap_or_else(|err| err); + let _ = res.unwrap_or_else(|err| ext_arr[err]); let _ = res2.unwrap_or_else(|err| err.some_field); let _ = res2.unwrap_or_else(|err| err.return_some_field()); let _ = res2.unwrap_or_else(|_| ext_str.return_some_field()); - let _: Result = res.and_then(|x| Ok(x)); - let _: Result = res.and_then(|x| Err(x)); + let _: Result = res.and_then(|x| Ok(x)); + let _: Result = res.and_then(|x| Err(x)); - let _: Result = res.or_else(|err| Ok(err)); - let _: Result = res.or_else(|err| Err(err)); + let _: Result = res.or_else(|err| Ok(err)); + let _: Result = res.or_else(|err| Err(err)); // These are handled by bind_instead_of_map - let _: Result = res.and_then(|_| Ok(2)); - let _: Result = res.and_then(|_| Ok(astronomers_pi)); - let _: Result = res.and_then(|_| Ok(ext_str.some_field)); + let _: Result = res.and_then(|_| Ok(2)); + let _: Result = res.and_then(|_| Ok(astronomers_pi)); + let _: Result = res.and_then(|_| Ok(ext_str.some_field)); - let _: Result = res.and_then(|_| Err(2)); - let _: Result = res.and_then(|_| Err(astronomers_pi)); - let _: Result = res.and_then(|_| Err(ext_str.some_field)); + let _: Result = res.and_then(|_| Err(2)); + let _: Result = res.and_then(|_| Err(astronomers_pi)); + let _: Result = res.and_then(|_| Err(ext_str.some_field)); - let _: Result = res.or_else(|_| Ok(2)); - let _: Result = res.or_else(|_| Ok(astronomers_pi)); - let _: Result = res.or_else(|_| Ok(ext_str.some_field)); + let _: Result = res.or_else(|_| Ok(2)); + let _: Result = res.or_else(|_| Ok(astronomers_pi)); + let _: Result = res.or_else(|_| Ok(ext_str.some_field)); - let _: Result = res.or_else(|_| Err(2)); - let _: Result = res.or_else(|_| Err(astronomers_pi)); - let _: Result = res.or_else(|_| Err(ext_str.some_field)); + let _: Result = res.or_else(|_| Err(2)); + let _: Result = res.or_else(|_| Err(astronomers_pi)); + let _: Result = res.or_else(|_| Err(ext_str.some_field)); } diff --git a/tests/ui/unnecessary_lazy_eval.stderr b/tests/ui/unnecessary_lazy_eval.stderr index b941bf84246..15591817540 100644 --- a/tests/ui/unnecessary_lazy_eval.stderr +++ b/tests/ui/unnecessary_lazy_eval.stderr @@ -1,5 +1,5 @@ error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:30:13 + --> $DIR/unnecessary_lazy_eval.rs:31:13 | LL | let _ = opt.unwrap_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(2)` @@ -7,43 +7,49 @@ LL | let _ = opt.unwrap_or_else(|| 2); = note: `-D clippy::unnecessary-lazy-evaluation` implied by `-D warnings` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:31:13 + --> $DIR/unnecessary_lazy_eval.rs:32:13 | LL | let _ = opt.unwrap_or_else(|| astronomers_pi); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(astronomers_pi)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:32:13 + --> $DIR/unnecessary_lazy_eval.rs:33:13 | LL | let _ = opt.unwrap_or_else(|| ext_str.some_field); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(ext_str.some_field)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:33:13 + --> $DIR/unnecessary_lazy_eval.rs:34:13 + | +LL | let _ = opt.unwrap_or_else(|| ext_arr[0]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(ext_arr[0])` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:35:13 | LL | let _ = opt.and_then(|_| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `opt.and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:34:13 + --> $DIR/unnecessary_lazy_eval.rs:36:13 | LL | let _ = opt.or_else(|| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:35:13 + --> $DIR/unnecessary_lazy_eval.rs:37:13 | LL | let _ = opt.or_else(|| None); | ^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:36:13 + --> $DIR/unnecessary_lazy_eval.rs:38:13 | LL | let _ = opt.get_or_insert_with(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `opt.get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:37:13 + --> $DIR/unnecessary_lazy_eval.rs:39:13 | LL | let _ = opt.ok_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `opt.ok_or(2)` @@ -51,83 +57,89 @@ LL | let _ = opt.ok_or_else(|| 2); error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:40:13 | +LL | let _ = opt.ok_or_else(|| ext_arr[0]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `opt.ok_or(ext_arr[0])` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:43:13 + | LL | let _ = Some(10).unwrap_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `Some(10).unwrap_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:41:13 + --> $DIR/unnecessary_lazy_eval.rs:44:13 | LL | let _ = Some(10).and_then(|_| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `Some(10).and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:42:26 + --> $DIR/unnecessary_lazy_eval.rs:45:28 | -LL | let _: Option = None.or_else(|| ext_opt); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(ext_opt)` +LL | let _: Option = None.or_else(|| ext_opt); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:43:13 + --> $DIR/unnecessary_lazy_eval.rs:46:13 | LL | let _ = None.get_or_insert_with(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `None.get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:44:31 + --> $DIR/unnecessary_lazy_eval.rs:47:35 | -LL | let _: Result = None.ok_or_else(|| 2); - | ^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `None.ok_or(2)` +LL | let _: Result = None.ok_or_else(|| 2); + | ^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `None.ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:45:26 + --> $DIR/unnecessary_lazy_eval.rs:48:28 | -LL | let _: Option = None.or_else(|| None); - | ^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(None)` +LL | let _: Option = None.or_else(|| None); + | ^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:48:13 + --> $DIR/unnecessary_lazy_eval.rs:51:13 | LL | let _ = deep.0.unwrap_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `deep.0.unwrap_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:49:13 + --> $DIR/unnecessary_lazy_eval.rs:52:13 | LL | let _ = deep.0.and_then(|_| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `deep.0.and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:50:13 + --> $DIR/unnecessary_lazy_eval.rs:53:13 | LL | let _ = deep.0.or_else(|| None); | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `deep.0.or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:51:13 + --> $DIR/unnecessary_lazy_eval.rs:54:13 | LL | let _ = deep.0.get_or_insert_with(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `deep.0.get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:52:13 + --> $DIR/unnecessary_lazy_eval.rs:55:13 | LL | let _ = deep.0.ok_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `deep.0.ok_or(2)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:73:13 + --> $DIR/unnecessary_lazy_eval.rs:78:13 | LL | let _ = res2.unwrap_or_else(|_| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(2)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:74:13 + --> $DIR/unnecessary_lazy_eval.rs:79:13 | LL | let _ = res2.unwrap_or_else(|_| astronomers_pi); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(astronomers_pi)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:75:13 + --> $DIR/unnecessary_lazy_eval.rs:80:13 | LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(ext_str.some_field)` @@ -140,5 +152,5 @@ LL | #![warn(clippy::unnecessary_lazy_eval)] | = note: `-D clippy::unknown-clippy-lints` implied by `-D warnings` -error: aborting due to 23 previous errors +error: aborting due to 25 previous errors -- cgit 1.4.1-3-g733a5 From 75637c1edac6db37b3c8aa17ef6b5a91db699a00 Mon Sep 17 00:00:00 2001 From: Dániel Buga Date: Mon, 15 Jun 2020 20:01:18 +0200 Subject: Catch function calls in argument lists, add tests that tuples don't get linted --- clippy_lints/src/methods/mod.rs | 25 ++++++++++-------- tests/ui/unnecessary_lazy_eval.fixed | 8 +++++- tests/ui/unnecessary_lazy_eval.rs | 8 +++++- tests/ui/unnecessary_lazy_eval.stderr | 48 +++++++++++++++++------------------ 4 files changed, 52 insertions(+), 37 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index ed8eaba75d1..463ef48f62c 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2744,12 +2744,8 @@ fn lint_lazy_eval<'a, 'tcx>( paths.iter().any(|candidate| match_qpath(path, candidate)) } - if let hir::ExprKind::Closure(_, _, eid, _, _) = args[1].kind { - let body = cx.tcx.hir().body(eid); - let ex = &body.value; - let params = &body.params; - - let simplify = match ex.kind { + fn can_simplify(expr: &hir::Expr<'_>, params: &[hir::Param<'_>], variant_calls: bool) -> bool { + match expr.kind { // Closures returning literals can be unconditionally simplified hir::ExprKind::Lit(_) => true, @@ -2767,15 +2763,16 @@ fn lint_lazy_eval<'a, 'tcx>( hir::ExprKind::Field(ref object, _) => !expr_uses_argument(object, params), // Paths can be simplified if the root is not the argument, this also covers None - hir::ExprKind::Path(_) => !expr_uses_argument(ex, params), + hir::ExprKind::Path(_) => !expr_uses_argument(expr, params), // Calls to Some, Ok, Err can be considered literals if they don't derive an argument hir::ExprKind::Call(ref func, ref args) => if_chain! { - if allow_variant_calls; // Disable lint when rules conflict with bind_instead_of_map + if variant_calls; // Disable lint when rules conflict with bind_instead_of_map if let hir::ExprKind::Path(ref path) = func.kind; if match_any_qpath(path, &[&["Some"], &["Ok"], &["Err"]]); then { - !args.iter().any(|arg| expr_uses_argument(arg, params)) + // Recursively check all arguments + args.iter().all(|arg| can_simplify(arg, params, variant_calls)) } else { false } @@ -2784,9 +2781,15 @@ fn lint_lazy_eval<'a, 'tcx>( // For anything more complex than the above, a closure is probably the right solution, // or the case is handled by an other lint _ => false, - }; + } + } + + if let hir::ExprKind::Closure(_, _, eid, _, _) = args[1].kind { + let body = cx.tcx.hir().body(eid); + let ex = &body.value; + let params = &body.params; - if simplify { + if can_simplify(ex, params, allow_variant_calls) { let msg = if is_option { "unnecessary closure used to substitute value for `Option::None`" } else { diff --git a/tests/ui/unnecessary_lazy_eval.fixed b/tests/ui/unnecessary_lazy_eval.fixed index c806cf8dce4..7f9d90a8569 100644 --- a/tests/ui/unnecessary_lazy_eval.fixed +++ b/tests/ui/unnecessary_lazy_eval.fixed @@ -25,9 +25,12 @@ fn main() { let ext_arr: [usize; 1] = [2]; let ext_str = SomeStruct { some_field: 10 }; - // Should lint - Option let mut opt = Some(42); let ext_opt = Some(42); + let nested_opt = Some(Some(42)); + let nested_tuple_opt = Some(Some((42, 43))); + + // Should lint - Option let _ = opt.unwrap_or(2); let _ = opt.unwrap_or(astronomers_pi); let _ = opt.unwrap_or(ext_str.some_field); @@ -56,6 +59,9 @@ fn main() { // Should not lint - Option let _ = opt.unwrap_or_else(|| ext_str.return_some_field()); + let _ = nested_opt.unwrap_or_else(|| Some(some_call())); + let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); + let _ = nested_tuple_opt.unwrap_or_else(|| Some((some_call(), some_call()))); let _ = opt.or_else(some_call); let _ = opt.or_else(|| some_call()); let _: Result = opt.ok_or_else(|| some_call()); diff --git a/tests/ui/unnecessary_lazy_eval.rs b/tests/ui/unnecessary_lazy_eval.rs index dfc6d3ba573..ca8238d6dcf 100644 --- a/tests/ui/unnecessary_lazy_eval.rs +++ b/tests/ui/unnecessary_lazy_eval.rs @@ -25,9 +25,12 @@ fn main() { let ext_arr: [usize; 1] = [2]; let ext_str = SomeStruct { some_field: 10 }; - // Should lint - Option let mut opt = Some(42); let ext_opt = Some(42); + let nested_opt = Some(Some(42)); + let nested_tuple_opt = Some(Some((42, 43))); + + // Should lint - Option let _ = opt.unwrap_or_else(|| 2); let _ = opt.unwrap_or_else(|| astronomers_pi); let _ = opt.unwrap_or_else(|| ext_str.some_field); @@ -56,6 +59,9 @@ fn main() { // Should not lint - Option let _ = opt.unwrap_or_else(|| ext_str.return_some_field()); + let _ = nested_opt.unwrap_or_else(|| Some(some_call())); + let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); + let _ = nested_tuple_opt.unwrap_or_else(|| Some((some_call(), some_call()))); let _ = opt.or_else(some_call); let _ = opt.or_else(|| some_call()); let _: Result = opt.ok_or_else(|| some_call()); diff --git a/tests/ui/unnecessary_lazy_eval.stderr b/tests/ui/unnecessary_lazy_eval.stderr index 15591817540..b8ec654e5c7 100644 --- a/tests/ui/unnecessary_lazy_eval.stderr +++ b/tests/ui/unnecessary_lazy_eval.stderr @@ -1,5 +1,5 @@ error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:31:13 + --> $DIR/unnecessary_lazy_eval.rs:34:13 | LL | let _ = opt.unwrap_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(2)` @@ -7,139 +7,139 @@ LL | let _ = opt.unwrap_or_else(|| 2); = note: `-D clippy::unnecessary-lazy-evaluation` implied by `-D warnings` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:32:13 + --> $DIR/unnecessary_lazy_eval.rs:35:13 | LL | let _ = opt.unwrap_or_else(|| astronomers_pi); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(astronomers_pi)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:33:13 + --> $DIR/unnecessary_lazy_eval.rs:36:13 | LL | let _ = opt.unwrap_or_else(|| ext_str.some_field); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(ext_str.some_field)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:34:13 + --> $DIR/unnecessary_lazy_eval.rs:37:13 | LL | let _ = opt.unwrap_or_else(|| ext_arr[0]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(ext_arr[0])` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:35:13 + --> $DIR/unnecessary_lazy_eval.rs:38:13 | LL | let _ = opt.and_then(|_| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `opt.and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:36:13 + --> $DIR/unnecessary_lazy_eval.rs:39:13 | LL | let _ = opt.or_else(|| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:37:13 + --> $DIR/unnecessary_lazy_eval.rs:40:13 | LL | let _ = opt.or_else(|| None); | ^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:38:13 + --> $DIR/unnecessary_lazy_eval.rs:41:13 | LL | let _ = opt.get_or_insert_with(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `opt.get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:39:13 + --> $DIR/unnecessary_lazy_eval.rs:42:13 | LL | let _ = opt.ok_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `opt.ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:40:13 + --> $DIR/unnecessary_lazy_eval.rs:43:13 | LL | let _ = opt.ok_or_else(|| ext_arr[0]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `opt.ok_or(ext_arr[0])` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:43:13 + --> $DIR/unnecessary_lazy_eval.rs:46:13 | LL | let _ = Some(10).unwrap_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `Some(10).unwrap_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:44:13 + --> $DIR/unnecessary_lazy_eval.rs:47:13 | LL | let _ = Some(10).and_then(|_| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `Some(10).and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:45:28 + --> $DIR/unnecessary_lazy_eval.rs:48:28 | LL | let _: Option = None.or_else(|| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:46:13 + --> $DIR/unnecessary_lazy_eval.rs:49:13 | LL | let _ = None.get_or_insert_with(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `None.get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:47:35 + --> $DIR/unnecessary_lazy_eval.rs:50:35 | LL | let _: Result = None.ok_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `None.ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:48:28 + --> $DIR/unnecessary_lazy_eval.rs:51:28 | LL | let _: Option = None.or_else(|| None); | ^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:51:13 + --> $DIR/unnecessary_lazy_eval.rs:54:13 | LL | let _ = deep.0.unwrap_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `deep.0.unwrap_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:52:13 + --> $DIR/unnecessary_lazy_eval.rs:55:13 | LL | let _ = deep.0.and_then(|_| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `deep.0.and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:53:13 + --> $DIR/unnecessary_lazy_eval.rs:56:13 | LL | let _ = deep.0.or_else(|| None); | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `deep.0.or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:54:13 + --> $DIR/unnecessary_lazy_eval.rs:57:13 | LL | let _ = deep.0.get_or_insert_with(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `deep.0.get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:55:13 + --> $DIR/unnecessary_lazy_eval.rs:58:13 | LL | let _ = deep.0.ok_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `deep.0.ok_or(2)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:78:13 + --> $DIR/unnecessary_lazy_eval.rs:84:13 | LL | let _ = res2.unwrap_or_else(|_| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(2)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:79:13 + --> $DIR/unnecessary_lazy_eval.rs:85:13 | LL | let _ = res2.unwrap_or_else(|_| astronomers_pi); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(astronomers_pi)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:80:13 + --> $DIR/unnecessary_lazy_eval.rs:86:13 | LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(ext_str.some_field)` -- cgit 1.4.1-3-g733a5 From b175642a85f5d1507b35b8ef269ecbdaef9aa27d Mon Sep 17 00:00:00 2001 From: Dániel Buga Date: Sun, 16 Aug 2020 21:33:29 +0200 Subject: Fix missed rename --- tests/ui/unnecessary_lazy_eval.rs | 2 +- tests/ui/unnecessary_lazy_eval.stderr | 10 +--------- 2 files changed, 2 insertions(+), 10 deletions(-) (limited to 'tests') diff --git a/tests/ui/unnecessary_lazy_eval.rs b/tests/ui/unnecessary_lazy_eval.rs index ca8238d6dcf..fd8f8ed0329 100644 --- a/tests/ui/unnecessary_lazy_eval.rs +++ b/tests/ui/unnecessary_lazy_eval.rs @@ -1,5 +1,5 @@ // run-rustfix -#![warn(clippy::unnecessary_lazy_eval)] +#![warn(clippy::unnecessary_lazy_evaluation)] #![allow(clippy::redundant_closure)] #![allow(clippy::bind_instead_of_map)] diff --git a/tests/ui/unnecessary_lazy_eval.stderr b/tests/ui/unnecessary_lazy_eval.stderr index b8ec654e5c7..e86b7ed6253 100644 --- a/tests/ui/unnecessary_lazy_eval.stderr +++ b/tests/ui/unnecessary_lazy_eval.stderr @@ -144,13 +144,5 @@ error: unnecessary closure used to substitute value for `Result::Err` LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(ext_str.some_field)` -error: unknown clippy lint: clippy::unnecessary_lazy_eval - --> $DIR/unnecessary_lazy_eval.rs:2:9 - | -LL | #![warn(clippy::unnecessary_lazy_eval)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::unnecessary_lazy_evaluation` - | - = note: `-D clippy::unknown-clippy-lints` implied by `-D warnings` - -error: aborting due to 25 previous errors +error: aborting due to 24 previous errors -- cgit 1.4.1-3-g733a5 From fc1e07e0c1803edb3ade2db2f46034cf227642c9 Mon Sep 17 00:00:00 2001 From: Dániel Buga Date: Sun, 16 Aug 2020 22:16:39 +0200 Subject: Rename lint to use plural form --- CHANGELOG.md | 2 +- clippy_lints/src/lib.rs | 6 +++--- clippy_lints/src/methods/mod.rs | 4 ++-- clippy_lints/src/methods/unnecessary_lazy_eval.rs | 4 ++-- src/lintlist/mod.rs | 2 +- tests/ui/unnecessary_lazy_eval.fixed | 2 +- tests/ui/unnecessary_lazy_eval.rs | 2 +- tests/ui/unnecessary_lazy_eval.stderr | 2 +- 8 files changed, 12 insertions(+), 12 deletions(-) (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 675dfd420a7..f662de122f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1754,7 +1754,7 @@ Released 2018-09-13 [`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast [`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map [`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold -[`unnecessary_lazy_evaluation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluation +[`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations [`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation [`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index ae7045884f7..17501e8e6da 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -685,7 +685,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::UNINIT_ASSUMED_INIT, &methods::UNNECESSARY_FILTER_MAP, &methods::UNNECESSARY_FOLD, - &methods::UNNECESSARY_LAZY_EVALUATION, + &methods::UNNECESSARY_LAZY_EVALUATIONS, &methods::UNWRAP_USED, &methods::USELESS_ASREF, &methods::WRONG_PUB_SELF_CONVENTION, @@ -1361,7 +1361,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::UNINIT_ASSUMED_INIT), LintId::of(&methods::UNNECESSARY_FILTER_MAP), LintId::of(&methods::UNNECESSARY_FOLD), - LintId::of(&methods::UNNECESSARY_LAZY_EVALUATION), + LintId::of(&methods::UNNECESSARY_LAZY_EVALUATIONS), LintId::of(&methods::USELESS_ASREF), LintId::of(&methods::WRONG_SELF_CONVENTION), LintId::of(&methods::ZST_OFFSET), @@ -1542,7 +1542,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::SINGLE_CHAR_PUSH_STR), LintId::of(&methods::STRING_EXTEND_CHARS), LintId::of(&methods::UNNECESSARY_FOLD), - LintId::of(&methods::UNNECESSARY_LAZY_EVALUATION), + LintId::of(&methods::UNNECESSARY_LAZY_EVALUATIONS), LintId::of(&methods::WRONG_SELF_CONVENTION), LintId::of(&misc::TOPLEVEL_REF_ARG), LintId::of(&misc::ZERO_PTR), diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index c2b2cb98012..0f50a4c813a 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1361,7 +1361,7 @@ declare_clippy_lint! { /// /// opt.unwrap_or(42); /// ``` - pub UNNECESSARY_LAZY_EVALUATION, + pub UNNECESSARY_LAZY_EVALUATIONS, style, "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation" } @@ -1415,7 +1415,7 @@ declare_lint_pass!(Methods => [ ZST_OFFSET, FILETYPE_IS_FILE, OPTION_AS_REF_DEREF, - UNNECESSARY_LAZY_EVALUATION, + UNNECESSARY_LAZY_EVALUATIONS, ]); impl<'tcx> LateLintPass<'tcx> for Methods { diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs index a3e7e9971f8..31517659c34 100644 --- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -4,7 +4,7 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use super::UNNECESSARY_LAZY_EVALUATION; +use super::UNNECESSARY_LAZY_EVALUATIONS; // Return true if the expression is an accessor of any of the arguments fn expr_uses_argument(expr: &hir::Expr<'_>, params: &[hir::Param<'_>]) -> bool { @@ -93,7 +93,7 @@ pub(super) fn lint<'tcx>( span_lint_and_sugg( cx, - UNNECESSARY_LAZY_EVALUATION, + UNNECESSARY_LAZY_EVALUATIONS, expr.span, msg, &format!("Use `{}` instead", simplify_using), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 2a66d3495ed..3229c8da507 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2384,7 +2384,7 @@ pub static ref ALL_LINTS: Vec = vec![ module: "methods", }, Lint { - name: "unnecessary_lazy_evaluation", + name: "unnecessary_lazy_evaluations", group: "style", desc: "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation", deprecation: None, diff --git a/tests/ui/unnecessary_lazy_eval.fixed b/tests/ui/unnecessary_lazy_eval.fixed index 7f9d90a8569..fa66e68794e 100644 --- a/tests/ui/unnecessary_lazy_eval.fixed +++ b/tests/ui/unnecessary_lazy_eval.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![warn(clippy::unnecessary_lazy_evaluation)] +#![warn(clippy::unnecessary_lazy_evaluations)] #![allow(clippy::redundant_closure)] #![allow(clippy::bind_instead_of_map)] diff --git a/tests/ui/unnecessary_lazy_eval.rs b/tests/ui/unnecessary_lazy_eval.rs index fd8f8ed0329..04f47d1aa29 100644 --- a/tests/ui/unnecessary_lazy_eval.rs +++ b/tests/ui/unnecessary_lazy_eval.rs @@ -1,5 +1,5 @@ // run-rustfix -#![warn(clippy::unnecessary_lazy_evaluation)] +#![warn(clippy::unnecessary_lazy_evaluations)] #![allow(clippy::redundant_closure)] #![allow(clippy::bind_instead_of_map)] diff --git a/tests/ui/unnecessary_lazy_eval.stderr b/tests/ui/unnecessary_lazy_eval.stderr index e86b7ed6253..5c1b2eb1f14 100644 --- a/tests/ui/unnecessary_lazy_eval.stderr +++ b/tests/ui/unnecessary_lazy_eval.stderr @@ -4,7 +4,7 @@ error: unnecessary closure used to substitute value for `Option::None` LL | let _ = opt.unwrap_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(2)` | - = note: `-D clippy::unnecessary-lazy-evaluation` implied by `-D warnings` + = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:35:13 -- cgit 1.4.1-3-g733a5 From 4f4abf4e0640edbb1614f3dcb8ff62e8afc54801 Mon Sep 17 00:00:00 2001 From: Tomasz Miąsko Date: Sun, 16 Aug 2020 00:00:00 +0000 Subject: Warn about explicit self-assignment Warn about assignments where left-hand side place expression is the same as right-hand side value expression. For example, warn about assignment in: ```rust pub struct Event { id: usize, x: i32, y: i32, } pub fn copy_position(a: &mut Event, b: &Event) { a.x = b.x; a.y = a.y; } ``` --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 +++ clippy_lints/src/self_assignment.rs | 51 +++++++++++++++++++++++++++ src/lintlist/mod.rs | 7 ++++ tests/ui/self_assignment.rs | 67 +++++++++++++++++++++++++++++++++++ tests/ui/self_assignment.stderr | 70 +++++++++++++++++++++++++++++++++++++ 6 files changed, 201 insertions(+) create mode 100644 clippy_lints/src/self_assignment.rs create mode 100644 tests/ui/self_assignment.rs create mode 100644 tests/ui/self_assignment.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index f662de122f9..5ce63c0a157 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1690,6 +1690,7 @@ Released 2018-09-13 [`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition [`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push [`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some +[`self_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_assignment [`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse [`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse [`shadow_same`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_same diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 17501e8e6da..87c297e72eb 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -284,6 +284,7 @@ mod reference; mod regex; mod repeat_once; mod returns; +mod self_assignment; mod serde_api; mod shadow; mod single_component_path_imports; @@ -773,6 +774,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &repeat_once::REPEAT_ONCE, &returns::LET_AND_RETURN, &returns::NEEDLESS_RETURN, + &self_assignment::SELF_ASSIGNMENT, &serde_api::SERDE_API_MISUSE, &shadow::SHADOW_REUSE, &shadow::SHADOW_SAME, @@ -1090,6 +1092,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch); store.register_late_pass(|| box stable_sort_primitive::StableSortPrimitive); store.register_late_pass(|| box repeat_once::RepeatOnce); + store.register_late_pass(|| box self_assignment::SelfAssignment); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1421,6 +1424,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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(&serde_api::SERDE_API_MISUSE), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), @@ -1714,6 +1718,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ptr::MUT_FROM_REF), LintId::of(&ranges::REVERSED_EMPTY_RANGES), LintId::of(®ex::INVALID_REGEX), + LintId::of(&self_assignment::SELF_ASSIGNMENT), LintId::of(&serde_api::SERDE_API_MISUSE), LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), diff --git a/clippy_lints/src/self_assignment.rs b/clippy_lints/src/self_assignment.rs new file mode 100644 index 00000000000..e096c9aebc1 --- /dev/null +++ b/clippy_lints/src/self_assignment.rs @@ -0,0 +1,51 @@ +use crate::utils::{eq_expr_value, snippet, span_lint}; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for explicit self-assignments. + /// + /// **Why is this bad?** Self-assignments are redundant and unlikely to be + /// intentional. + /// + /// **Known problems:** If expression contains any deref coercions or + /// indexing operations they are assumed not to have any side effects. + /// + /// **Example:** + /// + /// ```rust + /// struct Event { + /// id: usize, + /// x: i32, + /// y: i32, + /// } + /// + /// fn copy_position(a: &mut Event, b: &Event) { + /// a.x = b.x; + /// a.y = a.y; + /// } + /// ``` + pub SELF_ASSIGNMENT, + correctness, + "explicit self-assignment" +} + +declare_lint_pass!(SelfAssignment => [SELF_ASSIGNMENT]); + +impl<'tcx> LateLintPass<'tcx> for SelfAssignment { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::Assign(lhs, rhs, _) = &expr.kind { + if eq_expr_value(cx, lhs, rhs) { + let lhs = snippet(cx, lhs.span, ""); + let rhs = snippet(cx, rhs.span, ""); + span_lint( + cx, + SELF_ASSIGNMENT, + expr.span, + &format!("self-assignment of `{}` to `{}`", rhs, lhs), + ); + } + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 3229c8da507..bf58c117aaa 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1956,6 +1956,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "methods", }, + Lint { + name: "self_assignment", + group: "correctness", + desc: "explicit self-assignment", + deprecation: None, + module: "self_assignment", + }, Lint { name: "serde_api_misuse", group: "correctness", diff --git a/tests/ui/self_assignment.rs b/tests/ui/self_assignment.rs new file mode 100644 index 00000000000..a7cbb9cd78b --- /dev/null +++ b/tests/ui/self_assignment.rs @@ -0,0 +1,67 @@ +#![warn(clippy::self_assignment)] + +pub struct S<'a> { + a: i32, + b: [i32; 10], + c: Vec>, + e: &'a mut i32, + f: &'a mut i32, +} + +pub fn positives(mut a: usize, b: &mut u32, mut s: S) { + a = a; + *b = *b; + s = s; + s.a = s.a; + s.b[10] = s.b[5 + 5]; + s.c[0][1] = s.c[0][1]; + s.b[a] = s.b[a]; + *s.e = *s.e; + s.b[a + 10] = s.b[10 + a]; + + let mut t = (0, 1); + t.1 = t.1; + t.0 = (t.0); +} + +pub fn negatives_not_equal(mut a: usize, b: &mut usize, mut s: S) { + dbg!(&a); + a = *b; + dbg!(&a); + s.b[1] += s.b[1]; + s.b[1] = s.b[2]; + s.c[1][0] = s.c[0][1]; + s.b[a] = s.b[*b]; + s.b[a + 10] = s.b[a + 11]; + *s.e = *s.f; + + let mut t = (0, 1); + t.0 = t.1; +} + +#[allow(clippy::eval_order_dependence)] +pub fn negatives_side_effects() { + let mut v = vec![1, 2, 3, 4, 5]; + let mut i = 0; + v[{ + i += 1; + i + }] = v[{ + i += 1; + i + }]; + + fn next(n: &mut usize) -> usize { + let v = *n; + *n += 1; + v + } + + let mut w = vec![1, 2, 3, 4, 5]; + let mut i = 0; + let i = &mut i; + w[next(i)] = w[next(i)]; + w[next(i)] = w[next(i)]; +} + +fn main() {} diff --git a/tests/ui/self_assignment.stderr b/tests/ui/self_assignment.stderr new file mode 100644 index 00000000000..826e0d0ba88 --- /dev/null +++ b/tests/ui/self_assignment.stderr @@ -0,0 +1,70 @@ +error: self-assignment of `a` to `a` + --> $DIR/self_assignment.rs:12:5 + | +LL | a = a; + | ^^^^^ + | + = note: `-D clippy::self-assignment` implied by `-D warnings` + +error: self-assignment of `*b` to `*b` + --> $DIR/self_assignment.rs:13:5 + | +LL | *b = *b; + | ^^^^^^^ + +error: self-assignment of `s` to `s` + --> $DIR/self_assignment.rs:14:5 + | +LL | s = s; + | ^^^^^ + +error: self-assignment of `s.a` to `s.a` + --> $DIR/self_assignment.rs:15:5 + | +LL | s.a = s.a; + | ^^^^^^^^^ + +error: self-assignment of `s.b[5 + 5]` to `s.b[10]` + --> $DIR/self_assignment.rs:16:5 + | +LL | s.b[10] = s.b[5 + 5]; + | ^^^^^^^^^^^^^^^^^^^^ + +error: self-assignment of `s.c[0][1]` to `s.c[0][1]` + --> $DIR/self_assignment.rs:17:5 + | +LL | s.c[0][1] = s.c[0][1]; + | ^^^^^^^^^^^^^^^^^^^^^ + +error: self-assignment of `s.b[a]` to `s.b[a]` + --> $DIR/self_assignment.rs:18:5 + | +LL | s.b[a] = s.b[a]; + | ^^^^^^^^^^^^^^^ + +error: self-assignment of `*s.e` to `*s.e` + --> $DIR/self_assignment.rs:19:5 + | +LL | *s.e = *s.e; + | ^^^^^^^^^^^ + +error: self-assignment of `s.b[10 + a]` to `s.b[a + 10]` + --> $DIR/self_assignment.rs:20:5 + | +LL | s.b[a + 10] = s.b[10 + a]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: self-assignment of `t.1` to `t.1` + --> $DIR/self_assignment.rs:23:5 + | +LL | t.1 = t.1; + | ^^^^^^^^^ + +error: self-assignment of `(t.0)` to `t.0` + --> $DIR/self_assignment.rs:24:5 + | +LL | t.0 = (t.0); + | ^^^^^^^^^^^ + +error: aborting due to 11 previous errors + -- cgit 1.4.1-3-g733a5 From df4d42fc2debf7a7d43226c79480c71037be42b8 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Tue, 18 Aug 2020 01:00:11 +0200 Subject: transmute: avoid suggesting from/to bits in const --- clippy_lints/src/transmute.rs | 10 +++++-- tests/ui/transmute.rs | 15 ++++++++-- tests/ui/transmute.stderr | 52 +++++++++++++++++----------------- tests/ui/transmute_float_to_int.rs | 11 ++++++- tests/ui/transmute_float_to_int.stderr | 12 ++++---- 5 files changed, 61 insertions(+), 39 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/transmute.rs b/clippy_lints/src/transmute.rs index 28fd55f6ff0..1d238b242b0 100644 --- a/clippy_lints/src/transmute.rs +++ b/clippy_lints/src/transmute.rs @@ -1,5 +1,5 @@ use crate::utils::{ - is_normalizable, last_path_segment, match_def_path, paths, snippet, span_lint, span_lint_and_sugg, + in_constant, is_normalizable, last_path_segment, match_def_path, paths, snippet, span_lint, span_lint_and_sugg, span_lint_and_then, sugg, }; use if_chain::if_chain; @@ -331,6 +331,10 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id(); if match_def_path(cx, def_id, &paths::TRANSMUTE); then { + // Avoid suggesting f32::(from|to)_bits in const contexts. + // See https://github.com/rust-lang/rust/issues/73736 for progress on making them `const fn`. + let const_context = in_constant(cx, e.hir_id); + let from_ty = cx.typeck_results().expr_ty(&args[0]); let to_ty = cx.typeck_results().expr_ty(e); @@ -544,7 +548,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { }, ) }, - (ty::Int(_) | ty::Uint(_), ty::Float(_)) => span_lint_and_then( + (ty::Int(_) | ty::Uint(_), ty::Float(_)) if !const_context => span_lint_and_then( cx, TRANSMUTE_INT_TO_FLOAT, e.span, @@ -567,7 +571,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { ); }, ), - (ty::Float(float_ty), ty::Int(_) | ty::Uint(_)) => span_lint_and_then( + (ty::Float(float_ty), ty::Int(_) | ty::Uint(_)) if !const_context => span_lint_and_then( cx, TRANSMUTE_FLOAT_TO_INT, e.span, diff --git a/tests/ui/transmute.rs b/tests/ui/transmute.rs index bb853d23704..7caad34edb3 100644 --- a/tests/ui/transmute.rs +++ b/tests/ui/transmute.rs @@ -1,3 +1,4 @@ +#![feature(const_fn_transmute)] #![allow(dead_code)] extern crate core; @@ -81,9 +82,17 @@ fn int_to_bool() { } #[warn(clippy::transmute_int_to_float)] -fn int_to_float() { - let _: f32 = unsafe { std::mem::transmute(0_u32) }; - let _: f32 = unsafe { std::mem::transmute(0_i32) }; +mod int_to_float { + fn test() { + let _: f32 = unsafe { std::mem::transmute(0_u32) }; + let _: f32 = unsafe { std::mem::transmute(0_i32) }; + } + + // See issue #5747 + const VALUE: f32 = unsafe { std::mem::transmute(0_u32) }; + const fn from_bits(v: u32) -> f32 { + unsafe { std::mem::transmute(v) } + } } fn bytes_to_str(b: &[u8], mb: &mut [u8]) { diff --git a/tests/ui/transmute.stderr b/tests/ui/transmute.stderr index 8582080498f..d817c08b52f 100644 --- a/tests/ui/transmute.stderr +++ b/tests/ui/transmute.stderr @@ -1,5 +1,5 @@ error: transmute from a type (`&T`) to itself - --> $DIR/transmute.rs:19:20 + --> $DIR/transmute.rs:20:20 | LL | let _: &'a T = core::intrinsics::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,67 +7,67 @@ LL | let _: &'a T = core::intrinsics::transmute(t); = note: `-D clippy::useless-transmute` implied by `-D warnings` error: transmute from a reference to a pointer - --> $DIR/transmute.rs:23:23 + --> $DIR/transmute.rs:24:23 | LL | let _: *const T = core::intrinsics::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T` error: transmute from a reference to a pointer - --> $DIR/transmute.rs:25:21 + --> $DIR/transmute.rs:26:21 | LL | let _: *mut T = core::intrinsics::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *mut T` error: transmute from a reference to a pointer - --> $DIR/transmute.rs:27:23 + --> $DIR/transmute.rs:28:23 | LL | let _: *const U = core::intrinsics::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *const U` error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:33:27 + --> $DIR/transmute.rs:34:27 | LL | let _: Vec = core::intrinsics::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:35:27 + --> $DIR/transmute.rs:36:27 | LL | let _: Vec = core::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:37:27 + --> $DIR/transmute.rs:38:27 | LL | let _: Vec = std::intrinsics::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:39:27 + --> $DIR/transmute.rs:40:27 | LL | let _: Vec = std::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:41:27 + --> $DIR/transmute.rs:42:27 | LL | let _: Vec = my_transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^ error: transmute from an integer to a pointer - --> $DIR/transmute.rs:43:31 + --> $DIR/transmute.rs:44:31 | LL | let _: *const usize = std::mem::transmute(5_isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `5_isize as *const usize` error: transmute from an integer to a pointer - --> $DIR/transmute.rs:47:31 + --> $DIR/transmute.rs:48:31 | LL | let _: *const usize = std::mem::transmute(1 + 1usize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(1 + 1usize) as *const usize` error: transmute from a type (`*const Usize`) to the type that it points to (`Usize`) - --> $DIR/transmute.rs:62:24 + --> $DIR/transmute.rs:63:24 | LL | let _: Usize = core::intrinsics::transmute(int_const_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -75,25 +75,25 @@ LL | let _: Usize = core::intrinsics::transmute(int_const_ptr); = note: `-D clippy::crosspointer-transmute` implied by `-D warnings` error: transmute from a type (`*mut Usize`) to the type that it points to (`Usize`) - --> $DIR/transmute.rs:64:24 + --> $DIR/transmute.rs:65:24 | LL | let _: Usize = core::intrinsics::transmute(int_mut_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`Usize`) to a pointer to that type (`*const Usize`) - --> $DIR/transmute.rs:66:31 + --> $DIR/transmute.rs:67:31 | LL | let _: *const Usize = core::intrinsics::transmute(my_int()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`Usize`) to a pointer to that type (`*mut Usize`) - --> $DIR/transmute.rs:68:29 + --> $DIR/transmute.rs:69:29 | LL | let _: *mut Usize = core::intrinsics::transmute(my_int()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a `u32` to a `char` - --> $DIR/transmute.rs:74:28 + --> $DIR/transmute.rs:75:28 | LL | let _: char = unsafe { std::mem::transmute(0_u32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_u32).unwrap()` @@ -101,13 +101,13 @@ LL | let _: char = unsafe { std::mem::transmute(0_u32) }; = note: `-D clippy::transmute-int-to-char` implied by `-D warnings` error: transmute from a `i32` to a `char` - --> $DIR/transmute.rs:75:28 + --> $DIR/transmute.rs:76:28 | LL | let _: char = unsafe { std::mem::transmute(0_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_i32 as u32).unwrap()` error: transmute from a `u8` to a `bool` - --> $DIR/transmute.rs:80:28 + --> $DIR/transmute.rs:81:28 | LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `0_u8 != 0` @@ -115,21 +115,21 @@ LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; = note: `-D clippy::transmute-int-to-bool` implied by `-D warnings` error: transmute from a `u32` to a `f32` - --> $DIR/transmute.rs:85:27 + --> $DIR/transmute.rs:87:31 | -LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)` +LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)` | = note: `-D clippy::transmute-int-to-float` implied by `-D warnings` error: transmute from a `i32` to a `f32` - --> $DIR/transmute.rs:86:27 + --> $DIR/transmute.rs:88:31 | -LL | let _: f32 = unsafe { std::mem::transmute(0_i32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)` +LL | let _: f32 = unsafe { std::mem::transmute(0_i32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)` error: transmute from a `&[u8]` to a `&str` - --> $DIR/transmute.rs:90:28 + --> $DIR/transmute.rs:99:28 | LL | let _: &str = unsafe { std::mem::transmute(b) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(b).unwrap()` @@ -137,7 +137,7 @@ 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:91:32 + --> $DIR/transmute.rs:100:32 | LL | let _: &mut str = unsafe { std::mem::transmute(mb) }; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()` diff --git a/tests/ui/transmute_float_to_int.rs b/tests/ui/transmute_float_to_int.rs index ce942751ada..8173944d959 100644 --- a/tests/ui/transmute_float_to_int.rs +++ b/tests/ui/transmute_float_to_int.rs @@ -1,4 +1,5 @@ -#[warn(clippy::transmute_float_to_int)] +#![feature(const_fn_transmute)] +#![warn(clippy::transmute_float_to_int)] fn float_to_int() { let _: u32 = unsafe { std::mem::transmute(1f32) }; @@ -9,4 +10,12 @@ fn float_to_int() { let _: u64 = unsafe { std::mem::transmute(-1.0) }; } +mod issue_5747 { + const VALUE: u32 = unsafe { std::mem::transmute(1f32) }; + + const fn to_bits(v: f32) -> u32 { + unsafe { std::mem::transmute(v) } + } +} + fn main() {} diff --git a/tests/ui/transmute_float_to_int.stderr b/tests/ui/transmute_float_to_int.stderr index eb786bb39f9..5a40cf381d6 100644 --- a/tests/ui/transmute_float_to_int.stderr +++ b/tests/ui/transmute_float_to_int.stderr @@ -1,5 +1,5 @@ error: transmute from a `f32` to a `u32` - --> $DIR/transmute_float_to_int.rs:4:27 + --> $DIR/transmute_float_to_int.rs:5:27 | LL | let _: u32 = unsafe { std::mem::transmute(1f32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f32.to_bits()` @@ -7,31 +7,31 @@ LL | let _: u32 = unsafe { std::mem::transmute(1f32) }; = note: `-D clippy::transmute-float-to-int` implied by `-D warnings` error: transmute from a `f32` to a `i32` - --> $DIR/transmute_float_to_int.rs:5:27 + --> $DIR/transmute_float_to_int.rs:6:27 | LL | let _: i32 = unsafe { std::mem::transmute(1f32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f32.to_bits() as i32` error: transmute from a `f64` to a `u64` - --> $DIR/transmute_float_to_int.rs:6:27 + --> $DIR/transmute_float_to_int.rs:7:27 | LL | let _: u64 = unsafe { std::mem::transmute(1f64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f64.to_bits()` error: transmute from a `f64` to a `i64` - --> $DIR/transmute_float_to_int.rs:7:27 + --> $DIR/transmute_float_to_int.rs:8:27 | LL | let _: i64 = unsafe { std::mem::transmute(1f64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f64.to_bits() as i64` error: transmute from a `f64` to a `u64` - --> $DIR/transmute_float_to_int.rs:8:27 + --> $DIR/transmute_float_to_int.rs:9:27 | LL | let _: u64 = unsafe { std::mem::transmute(1.0) }; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1.0f64.to_bits()` error: transmute from a `f64` to a `u64` - --> $DIR/transmute_float_to_int.rs:9:27 + --> $DIR/transmute_float_to_int.rs:10:27 | LL | let _: u64 = unsafe { std::mem::transmute(-1.0) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-1.0f64).to_bits()` -- cgit 1.4.1-3-g733a5 From 6a12bae1941bdef7c2716ffefed5594e5d7e9f8e Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Tue, 18 Aug 2020 22:19:30 +0200 Subject: no from/to bits in const: add tests cases for f64 --- clippy_lints/src/transmute.rs | 2 +- tests/ui/transmute.rs | 17 +++++++++++++---- tests/ui/transmute.stderr | 18 +++++++++++++++--- tests/ui/transmute_float_to_int.rs | 9 +++++++-- 4 files changed, 36 insertions(+), 10 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/transmute.rs b/clippy_lints/src/transmute.rs index 1d238b242b0..50d9c93f9d4 100644 --- a/clippy_lints/src/transmute.rs +++ b/clippy_lints/src/transmute.rs @@ -331,7 +331,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id(); if match_def_path(cx, def_id, &paths::TRANSMUTE); then { - // Avoid suggesting f32::(from|to)_bits in const contexts. + // Avoid suggesting from/to bits in const contexts. // See https://github.com/rust-lang/rust/issues/73736 for progress on making them `const fn`. let const_context = in_constant(cx, e.hir_id); diff --git a/tests/ui/transmute.rs b/tests/ui/transmute.rs index 7caad34edb3..9f1948359e7 100644 --- a/tests/ui/transmute.rs +++ b/tests/ui/transmute.rs @@ -86,12 +86,21 @@ mod int_to_float { fn test() { let _: f32 = unsafe { std::mem::transmute(0_u32) }; let _: f32 = unsafe { std::mem::transmute(0_i32) }; + let _: f64 = unsafe { std::mem::transmute(0_u64) }; + let _: f64 = unsafe { std::mem::transmute(0_i64) }; } - // See issue #5747 - const VALUE: f32 = unsafe { std::mem::transmute(0_u32) }; - const fn from_bits(v: u32) -> f32 { - unsafe { std::mem::transmute(v) } + mod issue_5747 { + const VALUE32: f32 = unsafe { std::mem::transmute(0_u32) }; + const VALUE64: f64 = unsafe { std::mem::transmute(0_i64) }; + + const fn from_bits_32(v: i32) -> f32 { + unsafe { std::mem::transmute(v) } + } + + const fn from_bits_64(v: u64) -> f64 { + unsafe { std::mem::transmute(v) } + } } } diff --git a/tests/ui/transmute.stderr b/tests/ui/transmute.stderr index d817c08b52f..ad9953d12bc 100644 --- a/tests/ui/transmute.stderr +++ b/tests/ui/transmute.stderr @@ -128,8 +128,20 @@ error: transmute from a `i32` to a `f32` LL | let _: f32 = unsafe { std::mem::transmute(0_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)` +error: transmute from a `u64` to a `f64` + --> $DIR/transmute.rs:89:31 + | +LL | let _: f64 = unsafe { std::mem::transmute(0_u64) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_u64)` + +error: transmute from a `i64` to a `f64` + --> $DIR/transmute.rs:90:31 + | +LL | let _: f64 = unsafe { std::mem::transmute(0_i64) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_i64 as u64)` + error: transmute from a `&[u8]` to a `&str` - --> $DIR/transmute.rs:99:28 + --> $DIR/transmute.rs:108:28 | LL | let _: &str = unsafe { std::mem::transmute(b) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(b).unwrap()` @@ -137,10 +149,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:100:32 + --> $DIR/transmute.rs:109:32 | LL | let _: &mut str = unsafe { std::mem::transmute(mb) }; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()` -error: aborting due to 22 previous errors +error: aborting due to 24 previous errors diff --git a/tests/ui/transmute_float_to_int.rs b/tests/ui/transmute_float_to_int.rs index 8173944d959..1040fee4b34 100644 --- a/tests/ui/transmute_float_to_int.rs +++ b/tests/ui/transmute_float_to_int.rs @@ -11,9 +11,14 @@ fn float_to_int() { } mod issue_5747 { - const VALUE: u32 = unsafe { std::mem::transmute(1f32) }; + const VALUE32: i32 = unsafe { std::mem::transmute(1f32) }; + const VALUE64: u64 = unsafe { std::mem::transmute(1f64) }; - const fn to_bits(v: f32) -> u32 { + const fn to_bits_32(v: f32) -> u32 { + unsafe { std::mem::transmute(v) } + } + + const fn to_bits_64(v: f64) -> i64 { unsafe { std::mem::transmute(v) } } } -- cgit 1.4.1-3-g733a5 From 902b28275ef6e06826f269bdf2459c4ba4562145 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Wed, 19 Aug 2020 22:31:34 +0900 Subject: Improve lint message in `to_string_in_display` --- clippy_lints/src/to_string_in_display.rs | 4 ++-- src/lintlist/mod.rs | 2 +- tests/ui/to_string_in_display.stderr | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/to_string_in_display.rs b/clippy_lints/src/to_string_in_display.rs index 11bdd27d9b1..4b6a0a6a0c9 100644 --- a/clippy_lints/src/to_string_in_display.rs +++ b/clippy_lints/src/to_string_in_display.rs @@ -39,7 +39,7 @@ declare_clippy_lint! { /// ``` pub TO_STRING_IN_DISPLAY, correctness, - "to_string method used while implementing Display trait" + "`to_string` method used while implementing `Display` trait" } #[derive(Default)] @@ -80,7 +80,7 @@ impl LateLintPass<'_> for ToStringInDisplay { cx, TO_STRING_IN_DISPLAY, expr.span, - "Using to_string in fmt::Display implementation might lead to infinite recursion", + "using `to_string` in `fmt::Display` implementation might lead to infinite recursion", ); } } diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index bf58c117aaa..c50c6b900ae 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2183,7 +2183,7 @@ pub static ref ALL_LINTS: Vec = vec![ Lint { name: "to_string_in_display", group: "correctness", - desc: "to_string method used while implementing Display trait", + desc: "`to_string` method used while implementing `Display` trait", deprecation: None, module: "to_string_in_display", }, diff --git a/tests/ui/to_string_in_display.stderr b/tests/ui/to_string_in_display.stderr index cbc0a41036b..5f26ef413e2 100644 --- a/tests/ui/to_string_in_display.stderr +++ b/tests/ui/to_string_in_display.stderr @@ -1,4 +1,4 @@ -error: Using to_string in fmt::Display implementation might lead to infinite recursion +error: using `to_string` in `fmt::Display` implementation might lead to infinite recursion --> $DIR/to_string_in_display.rs:25:25 | LL | write!(f, "{}", self.to_string()) -- cgit 1.4.1-3-g733a5 From c236c0fb5694948353b5fcbe46010cf04d5a7107 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Thu, 20 Aug 2020 06:34:48 +0200 Subject: Fix false positive in `PRECEDENCE` lint Extend the lint to handle chains of methods combined with unary negation. Closes #5924 --- clippy_lints/src/precedence.rs | 59 +++++++++++++++++++++--------------------- tests/ui/precedence.fixed | 8 ++++++ tests/ui/precedence.rs | 8 ++++++ tests/ui/precedence.stderr | 20 +++++++++++++- 4 files changed, 65 insertions(+), 30 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/precedence.rs b/clippy_lints/src/precedence.rs index 4797771e7bd..c9d18c3cb72 100644 --- a/clippy_lints/src/precedence.rs +++ b/clippy_lints/src/precedence.rs @@ -1,4 +1,5 @@ use crate::utils::{snippet_with_applicability, span_lint_and_sugg}; +use if_chain::if_chain; use rustc_ast::ast::{BinOpKind, Expr, ExprKind, LitKind, UnOp}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -102,36 +103,36 @@ impl EarlyLintPass for Precedence { } } - if let ExprKind::Unary(UnOp::Neg, ref rhs) = expr.kind { - if let ExprKind::MethodCall(ref path_segment, ref args, _) = rhs.kind { + if let ExprKind::Unary(UnOp::Neg, operand) = &expr.kind { + let mut arg = operand; + + let mut all_odd = true; + while let ExprKind::MethodCall(path_segment, args, _) = &arg.kind { let path_segment_str = path_segment.ident.name.as_str(); - if let Some(slf) = args.first() { - if let ExprKind::Lit(ref lit) = slf.kind { - match lit.kind { - LitKind::Int(..) | LitKind::Float(..) => { - if ALLOWED_ODD_FUNCTIONS - .iter() - .any(|odd_function| **odd_function == *path_segment_str) - { - return; - } - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - PRECEDENCE, - expr.span, - "unary minus has lower precedence than method call", - "consider adding parentheses to clarify your intent", - format!( - "-({})", - snippet_with_applicability(cx, rhs.span, "..", &mut applicability) - ), - applicability, - ); - }, - _ => (), - } - } + all_odd &= ALLOWED_ODD_FUNCTIONS + .iter() + .any(|odd_function| **odd_function == *path_segment_str); + arg = args.first().expect("A method always has a receiver."); + } + + if_chain! { + if !all_odd; + if let ExprKind::Lit(lit) = &arg.kind; + if let LitKind::Int(..) | LitKind::Float(..) = &lit.kind; + then { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + PRECEDENCE, + expr.span, + "unary minus has lower precedence than method call", + "consider adding parentheses to clarify your intent", + format!( + "-({})", + snippet_with_applicability(cx, operand.span, "..", &mut applicability) + ), + applicability, + ); } } } diff --git a/tests/ui/precedence.fixed b/tests/ui/precedence.fixed index 4d284ae1319..163bd044c17 100644 --- a/tests/ui/precedence.fixed +++ b/tests/ui/precedence.fixed @@ -48,6 +48,14 @@ fn main() { let _ = -1f64.to_degrees(); let _ = -1f64.to_radians(); + // Chains containing any non-odd function should trigger (issue #5924) + let _ = -(1.0_f64.cos().cos()); + let _ = -(1.0_f64.cos().sin()); + let _ = -(1.0_f64.sin().cos()); + + // Chains of odd functions shouldn't trigger + let _ = -1f64.sin().sin(); + let b = 3; trip!(b * 8); } diff --git a/tests/ui/precedence.rs b/tests/ui/precedence.rs index 2d08e82f349..8c849e3209b 100644 --- a/tests/ui/precedence.rs +++ b/tests/ui/precedence.rs @@ -48,6 +48,14 @@ fn main() { let _ = -1f64.to_degrees(); let _ = -1f64.to_radians(); + // Chains containing any non-odd function should trigger (issue #5924) + let _ = -1.0_f64.cos().cos(); + let _ = -1.0_f64.cos().sin(); + let _ = -1.0_f64.sin().cos(); + + // Chains of odd functions shouldn't trigger + let _ = -1f64.sin().sin(); + let b = 3; trip!(b * 8); } diff --git a/tests/ui/precedence.stderr b/tests/ui/precedence.stderr index a2ed5392bfc..03d585b3975 100644 --- a/tests/ui/precedence.stderr +++ b/tests/ui/precedence.stderr @@ -54,5 +54,23 @@ error: unary minus has lower precedence than method call LL | -1f32.abs(); | ^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1f32.abs())` -error: aborting due to 9 previous errors +error: unary minus has lower precedence than method call + --> $DIR/precedence.rs:52:13 + | +LL | let _ = -1.0_f64.cos().cos(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1.0_f64.cos().cos())` + +error: unary minus has lower precedence than method call + --> $DIR/precedence.rs:53:13 + | +LL | let _ = -1.0_f64.cos().sin(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1.0_f64.cos().sin())` + +error: unary minus has lower precedence than method call + --> $DIR/precedence.rs:54:13 + | +LL | let _ = -1.0_f64.sin().cos(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider adding parentheses to clarify your intent: `-(1.0_f64.sin().cos())` + +error: aborting due to 12 previous errors -- cgit 1.4.1-3-g733a5 From 2ecc2ac864739cff6aed2609021e2467dedb117a Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Fri, 21 Aug 2020 00:07:56 +0200 Subject: unit-arg - improve suggestion --- clippy_lints/src/types.rs | 88 +++++++++++++++------------ tests/ui/unit_arg.rs | 7 ++- tests/ui/unit_arg.stderr | 111 ++++++++++++++++------------------ tests/ui/unit_arg_empty_blocks.stderr | 21 ++----- 4 files changed, 115 insertions(+), 112 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 7e9190bef5e..3f5b3a5bcd5 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -11,8 +11,8 @@ use rustc_hir as hir; use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::{ BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericParamKind, HirId, ImplItem, - ImplItemKind, Item, ItemKind, Lifetime, Local, MatchSource, MutTy, Mutability, QPath, Stmt, StmtKind, TraitFn, - TraitItem, TraitItemKind, TyKind, UnOp, + ImplItemKind, Item, ItemKind, Lifetime, Local, MatchSource, MutTy, Mutability, Node, QPath, Stmt, StmtKind, + TraitFn, TraitItem, TraitItemKind, TyKind, UnOp, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; @@ -29,10 +29,10 @@ use rustc_typeck::hir_ty_to_ty; use crate::consts::{constant, Constant}; use crate::utils::paths; use crate::utils::{ - clip, comparisons, differing_macro_contexts, higher, in_constant, indent_of, int_bits, is_type_diagnostic_item, + clip, comparisons, differing_macro_contexts, higher, in_constant, int_bits, is_type_diagnostic_item, last_path_segment, match_def_path, match_path, method_chain_args, multispan_sugg, numeric_literal::NumericLiteral, - qpath_res, sext, snippet, snippet_block_with_applicability, snippet_opt, snippet_with_applicability, - snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext, + qpath_res, sext, snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite, span_lint, + span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext, }; declare_clippy_lint! { @@ -844,43 +844,54 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp Applicability::MaybeIncorrect, ); or = "or "; + applicability = Applicability::MaybeIncorrect; }); - let sugg = args_to_recover + + let arg_snippets: Vec = args_to_recover + .iter() + .filter_map(|arg| snippet_opt(cx, arg.span)) + .collect(); + let arg_snippets_without_empty_blocks: Vec = args_to_recover .iter() .filter(|arg| !is_empty_block(arg)) - .enumerate() - .map(|(i, arg)| { - let indent = if i == 0 { - 0 - } else { - indent_of(cx, expr.span).unwrap_or(0) - }; - format!( - "{}{};", - " ".repeat(indent), - snippet_block_with_applicability(cx, arg.span, "..", Some(expr.span), &mut applicability) - ) - }) - .collect::>(); - let mut and = ""; - if !sugg.is_empty() { - let plural = if sugg.len() > 1 { "s" } else { "" }; - db.span_suggestion( - expr.span.with_hi(expr.span.lo()), - &format!("{}move the expression{} in front of the call...", or, plural), - format!("{}\n", sugg.join("\n")), - applicability, - ); - and = "...and " + .filter_map(|arg| snippet_opt(cx, arg.span)) + .collect(); + + if let Some(mut sugg) = snippet_opt(cx, expr.span) { + arg_snippets.iter().for_each(|arg| { + sugg = sugg.replacen(arg, "()", 1); + }); + sugg = format!("{}{}{}", arg_snippets_without_empty_blocks.join("; "), "; ", sugg); + let parent_node = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(expr.hir_id)); + if !matches!(parent_node, Some(Node::Block(_))) && !matches!(parent_node, Some(Node::Stmt(_))) { + // expr is not in a block statement or result expression position, wrap in a block + sugg = format!("{{ {} }}", sugg); + } + + if arg_snippets_without_empty_blocks.is_empty() { + db.multipart_suggestion( + &format!("use {}unit literal{} instead", singular, plural), + args_to_recover + .iter() + .map(|arg| (arg.span, "()".to_string())) + .collect::>(), + applicability, + ); + } else { + let plural = arg_snippets_without_empty_blocks.len() > 1; + let empty_or_s = if plural { "s" } else { "" }; + let it_or_them = if plural { "them" } else { "it" }; + db.span_suggestion( + expr.span, + &format!( + "{}move the expression{} in front of the call and replace {} with the unit literal `()`", + or, empty_or_s, it_or_them + ), + sugg, + applicability, + ); + } } - db.multipart_suggestion( - &format!("{}use {}unit literal{} instead", and, singular, plural), - args_to_recover - .iter() - .map(|arg| (arg.span, "()".to_string())) - .collect::>(), - applicability, - ); }, ); } @@ -2055,6 +2066,7 @@ impl PartialOrd for FullInt { }) } } + impl Ord for FullInt { #[must_use] fn cmp(&self, other: &Self) -> Ordering { diff --git a/tests/ui/unit_arg.rs b/tests/ui/unit_arg.rs index 2992abae775..2e2bd054e42 100644 --- a/tests/ui/unit_arg.rs +++ b/tests/ui/unit_arg.rs @@ -1,5 +1,5 @@ #![warn(clippy::unit_arg)] -#![allow(clippy::no_effect, unused_must_use, unused_variables)] +#![allow(clippy::no_effect, unused_must_use, unused_variables, clippy::unused_unit)] use std::fmt::Debug; @@ -47,6 +47,11 @@ fn bad() { foo(3); }, ); + // here Some(foo(2)) isn't the top level statement expression, wrap the suggestion in a block + None.or(Some(foo(2))); + // in this case, the suggestion can be inlined, no need for a surrounding block + // foo(()); foo(()) instead of { foo(()); foo(()) } + foo(foo(())) } fn ok() { diff --git a/tests/ui/unit_arg.stderr b/tests/ui/unit_arg.stderr index 56f6a855dfa..2a0cc1f18e2 100644 --- a/tests/ui/unit_arg.stderr +++ b/tests/ui/unit_arg.stderr @@ -11,16 +11,12 @@ help: remove the semicolon from the last statement in the block | LL | 1 | -help: or move the expression in front of the call... +help: or move the expression in front of the call and replace it with the unit literal `()` | LL | { LL | 1; -LL | }; +LL | }; foo(()); | -help: ...and use a unit literal instead - | -LL | foo(()); - | ^^ error: passing a unit value to a function --> $DIR/unit_arg.rs:26:5 @@ -28,14 +24,10 @@ error: passing a unit value to a function LL | foo(foo(1)); | ^^^^^^^^^^^ | -help: move the expression in front of the call... - | -LL | foo(1); +help: move the expression in front of the call and replace it with the unit literal `()` | -help: ...and use a unit literal instead - | -LL | foo(()); - | ^^ +LL | foo(1); foo(()); + | ^^^^^^^^^^^^^^^ error: passing a unit value to a function --> $DIR/unit_arg.rs:27:5 @@ -50,17 +42,13 @@ help: remove the semicolon from the last statement in the block | LL | foo(2) | -help: or move the expression in front of the call... +help: or move the expression in front of the call and replace it with the unit literal `()` | LL | { LL | foo(1); LL | foo(2); -LL | }; - | -help: ...and use a unit literal instead +LL | }; foo(()); | -LL | foo(()); - | ^^ error: passing a unit value to a function --> $DIR/unit_arg.rs:32:5 @@ -74,16 +62,12 @@ help: remove the semicolon from the last statement in the block | LL | 1 | -help: or move the expression in front of the call... +help: or move the expression in front of the call and replace it with the unit literal `()` | LL | { LL | 1; -LL | }; +LL | }; b.bar(()); | -help: ...and use a unit literal instead - | -LL | b.bar(()); - | ^^ error: passing unit values to a function --> $DIR/unit_arg.rs:35:5 @@ -91,15 +75,10 @@ error: passing unit values to a function LL | taking_multiple_units(foo(0), foo(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: move the expressions in front of the call... - | -LL | foo(0); -LL | foo(1); - | -help: ...and use unit literals instead +help: move the expressions in front of the call and replace them with the unit literal `()` | -LL | taking_multiple_units((), ()); - | ^^ ^^ +LL | foo(0); foo(1); taking_multiple_units((), ()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: passing unit values to a function --> $DIR/unit_arg.rs:36:5 @@ -114,18 +93,13 @@ help: remove the semicolon from the last statement in the block | LL | foo(2) | -help: or move the expressions in front of the call... +help: or move the expressions in front of the call and replace them with the unit literal `()` | -LL | foo(0); -LL | { +LL | foo(0); { LL | foo(1); LL | foo(2); -LL | }; - | -help: ...and use unit literals instead +LL | }; taking_multiple_units((), ()); | -LL | taking_multiple_units((), ()); - | ^^ ^^ error: passing unit values to a function --> $DIR/unit_arg.rs:40:5 @@ -147,35 +121,56 @@ help: remove the semicolon from the last statement in the block | LL | foo(3) | -help: or move the expressions in front of the call... +help: or move the expressions in front of the call and replace them with the unit literal `()` | LL | { -LL | foo(0); -LL | foo(1); -LL | }; -LL | { -LL | foo(2); +LL | foo(0); +LL | foo(1); +LL | }; { +LL | foo(2); +LL | foo(3); ... -help: ...and use unit literals instead + +error: use of `or` followed by a function call + --> $DIR/unit_arg.rs:51:10 | -LL | (), -LL | (), +LL | None.or(Some(foo(2))); + | ^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some(foo(2)))` | + = note: `-D clippy::or-fun-call` implied by `-D warnings` error: passing a unit value to a function - --> $DIR/unit_arg.rs:82:5 + --> $DIR/unit_arg.rs:51:13 | -LL | Some(foo(1)) +LL | None.or(Some(foo(2))); + | ^^^^^^^^^^^^ + | +help: move the expression in front of the call and replace it with the unit literal `()` + | +LL | None.or({ foo(2); Some(()) }); + | ^^^^^^^^^^^^^^^^^^^^ + +error: passing a unit value to a function + --> $DIR/unit_arg.rs:54:5 + | +LL | foo(foo(())) | ^^^^^^^^^^^^ | -help: move the expression in front of the call... +help: move the expression in front of the call and replace it with the unit literal `()` + | +LL | foo(()); foo(()) + | + +error: passing a unit value to a function + --> $DIR/unit_arg.rs:87:5 + | +LL | Some(foo(1)) + | ^^^^^^^^^^^^ | -LL | foo(1); +help: move the expression in front of the call and replace it with the unit literal `()` | -help: ...and use a unit literal instead +LL | foo(1); Some(()) | -LL | Some(()) - | ^^ -error: aborting due to 8 previous errors +error: aborting due to 11 previous errors diff --git a/tests/ui/unit_arg_empty_blocks.stderr b/tests/ui/unit_arg_empty_blocks.stderr index bb58483584b..4cbbc8b8cd4 100644 --- a/tests/ui/unit_arg_empty_blocks.stderr +++ b/tests/ui/unit_arg_empty_blocks.stderr @@ -22,14 +22,10 @@ error: passing unit values to a function LL | taking_two_units({}, foo(0)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: move the expression in front of the call... +help: move the expression in front of the call and replace it with the unit literal `()` | -LL | foo(0); - | -help: ...and use unit literals instead - | -LL | taking_two_units((), ()); - | ^^ ^^ +LL | foo(0); taking_two_units((), ()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: passing unit values to a function --> $DIR/unit_arg_empty_blocks.rs:18:5 @@ -37,15 +33,10 @@ error: passing unit values to a function LL | taking_three_units({}, foo(0), foo(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: move the expressions in front of the call... - | -LL | foo(0); -LL | foo(1); - | -help: ...and use unit literals instead +help: move the expressions in front of the call and replace them with the unit literal `()` | -LL | taking_three_units((), (), ()); - | ^^ ^^ ^^ +LL | foo(0); foo(1); taking_three_units((), (), ()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From 11efd75aeb8637bd5444104a57f1051b86c0d62b Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Fri, 21 Aug 2020 07:23:04 +0200 Subject: Fix false negative in `option_as_ref_deref` --- clippy_lints/src/methods/mod.rs | 7 ++++++- tests/ui/option_as_ref_deref.fixed | 3 +++ tests/ui/option_as_ref_deref.rs | 3 +++ tests/ui/option_as_ref_deref.stderr | 8 +++++++- 4 files changed, 19 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 2498c48f067..22942d9fb0c 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3421,7 +3421,12 @@ fn lint_option_as_ref_deref<'tcx>( ]; let is_deref = match map_args[1].kind { - hir::ExprKind::Path(ref expr_qpath) => deref_aliases.iter().any(|path| match_qpath(expr_qpath, path)), + hir::ExprKind::Path(ref expr_qpath) => cx + .qpath_res(expr_qpath, map_args[1].hir_id) + .opt_def_id() + .map_or(false, |fun_def_id| { + deref_aliases.iter().any(|path| match_def_path(cx, fun_def_id, path)) + }), hir::ExprKind::Closure(_, _, body_id, _, _) => { let closure_body = cx.tcx.hir().body(body_id); let closure_expr = remove_blocks(&closure_body.value); diff --git a/tests/ui/option_as_ref_deref.fixed b/tests/ui/option_as_ref_deref.fixed index 076692e6445..07d7f0b45b0 100644 --- a/tests/ui/option_as_ref_deref.fixed +++ b/tests/ui/option_as_ref_deref.fixed @@ -38,4 +38,7 @@ fn main() { let _ = opt.as_deref(); let _ = opt.as_deref_mut(); + + // Issue #5927 + let _ = opt.as_deref(); } diff --git a/tests/ui/option_as_ref_deref.rs b/tests/ui/option_as_ref_deref.rs index 3bf5f715f83..6ae059c9425 100644 --- a/tests/ui/option_as_ref_deref.rs +++ b/tests/ui/option_as_ref_deref.rs @@ -41,4 +41,7 @@ fn main() { let _ = opt.as_ref().map(|x| &**x); let _ = opt.as_mut().map(|x| &mut **x); + + // Issue #5927 + let _ = opt.as_ref().map(std::ops::Deref::deref); } diff --git a/tests/ui/option_as_ref_deref.stderr b/tests/ui/option_as_ref_deref.stderr index a106582a633..62f28232475 100644 --- a/tests/ui/option_as_ref_deref.stderr +++ b/tests/ui/option_as_ref_deref.stderr @@ -100,5 +100,11 @@ error: called `.as_mut().map(|x| &mut **x)` on an Option value. This can be done LL | let _ = opt.as_mut().map(|x| &mut **x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref_mut instead: `opt.as_deref_mut()` -error: aborting due to 16 previous errors +error: called `.as_ref().map(std::ops::Deref::deref)` on an Option value. This can be done more directly by calling `opt.as_deref()` instead + --> $DIR/option_as_ref_deref.rs:46:13 + | +LL | let _ = opt.as_ref().map(std::ops::Deref::deref); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()` + +error: aborting due to 17 previous errors -- cgit 1.4.1-3-g733a5 From 03bc7aed441240190030c9dfdcce6405014178dd Mon Sep 17 00:00:00 2001 From: ThibsG Date: Fri, 21 Aug 2020 08:18:05 +0200 Subject: Add async test case for `wrong_self_convention` lint --- tests/ui/wrong_self_convention.rs | 13 +++++++++++++ tests/ui/wrong_self_convention.stderr | 24 ++++++++++++------------ 2 files changed, 25 insertions(+), 12 deletions(-) (limited to 'tests') diff --git a/tests/ui/wrong_self_convention.rs b/tests/ui/wrong_self_convention.rs index 99652ca4470..f44305d7e48 100644 --- a/tests/ui/wrong_self_convention.rs +++ b/tests/ui/wrong_self_convention.rs @@ -1,3 +1,4 @@ +// edition:2018 #![warn(clippy::wrong_self_convention)] #![warn(clippy::wrong_pub_self_convention)] #![allow(dead_code)] @@ -75,3 +76,15 @@ mod issue4293 { fn into_t3(self: Arc) {} } } + +// False positive for async (see #4037) +mod issue4037 { + pub struct Foo; + pub struct Bar; + + impl Foo { + pub async fn into_bar(self) -> Bar { + Bar + } + } +} diff --git a/tests/ui/wrong_self_convention.stderr b/tests/ui/wrong_self_convention.stderr index 0d0eb19cd07..ef3ad73ebc7 100644 --- a/tests/ui/wrong_self_convention.stderr +++ b/tests/ui/wrong_self_convention.stderr @@ -1,5 +1,5 @@ error: methods called `from_*` usually take no self; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:17:17 + --> $DIR/wrong_self_convention.rs:18:17 | LL | fn from_i32(self) {} | ^^^^ @@ -7,67 +7,67 @@ LL | fn from_i32(self) {} = note: `-D clippy::wrong-self-convention` implied by `-D warnings` error: methods called `from_*` usually take no self; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:23:21 + --> $DIR/wrong_self_convention.rs:24:21 | LL | pub fn from_i64(self) {} | ^^^^ error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:35:15 + --> $DIR/wrong_self_convention.rs:36:15 | LL | fn as_i32(self) {} | ^^^^ error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:37:17 + --> $DIR/wrong_self_convention.rs:38:17 | LL | fn into_i32(&self) {} | ^^^^^ error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:39:15 + --> $DIR/wrong_self_convention.rs:40:15 | LL | fn is_i32(self) {} | ^^^^ error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:41:15 + --> $DIR/wrong_self_convention.rs:42:15 | LL | fn to_i32(self) {} | ^^^^ error: methods called `from_*` usually take no self; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:43:17 + --> $DIR/wrong_self_convention.rs:44:17 | LL | fn from_i32(self) {} | ^^^^ error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:45:19 + --> $DIR/wrong_self_convention.rs:46:19 | LL | pub fn as_i64(self) {} | ^^^^ error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:46:21 + --> $DIR/wrong_self_convention.rs:47:21 | LL | pub fn into_i64(&self) {} | ^^^^^ error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:47:19 + --> $DIR/wrong_self_convention.rs:48:19 | LL | pub fn is_i64(self) {} | ^^^^ error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:48:19 + --> $DIR/wrong_self_convention.rs:49:19 | LL | pub fn to_i64(self) {} | ^^^^ error: methods called `from_*` usually take no self; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:49:21 + --> $DIR/wrong_self_convention.rs:50:21 | LL | pub fn from_i64(self) {} | ^^^^ -- cgit 1.4.1-3-g733a5 From 191b6c798fa40334ec8898d07b4d28fd76743120 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sat, 22 Aug 2020 10:35:18 +0200 Subject: Don't lint if it has always inline attribute --- clippy_lints/src/trivially_copy_pass_by_ref.rs | 9 +++++++-- tests/ui/trivially_copy_pass_by_ref.rs | 18 ++++++++++++++++++ tests/ui/trivially_copy_pass_by_ref.stderr | 14 +++++++++++++- 3 files changed, 38 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/trivially_copy_pass_by_ref.rs b/clippy_lints/src/trivially_copy_pass_by_ref.rs index 7948d99162b..92f42168a1e 100644 --- a/clippy_lints/src/trivially_copy_pass_by_ref.rs +++ b/clippy_lints/src/trivially_copy_pass_by_ref.rs @@ -2,6 +2,7 @@ use std::cmp; use crate::utils::{is_copy, is_self_ty, snippet, span_lint_and_sugg}; use if_chain::if_chain; +use rustc_ast::attr; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; @@ -155,8 +156,12 @@ impl<'tcx> LateLintPass<'tcx> for TriviallyCopyPassByRef { return; } for a in attrs { - if a.meta_item_list().is_some() && a.has_name(sym!(proc_macro_derive)) { - return; + if let Some(meta_items) = a.meta_item_list() { + if a.has_name(sym!(proc_macro_derive)) + || (a.has_name(sym!(inline)) && attr::list_contains_name(&meta_items, sym!(always))) + { + return; + } } } }, diff --git a/tests/ui/trivially_copy_pass_by_ref.rs b/tests/ui/trivially_copy_pass_by_ref.rs index 316426f1cf1..e7e0a31febc 100644 --- a/tests/ui/trivially_copy_pass_by_ref.rs +++ b/tests/ui/trivially_copy_pass_by_ref.rs @@ -97,6 +97,24 @@ mod issue3992 { pub fn c(d: &u16) {} } +mod issue5876 { + // Don't lint here as it is always inlined + #[inline(always)] + fn foo_always(x: &i32) { + println!("{}", x); + } + + #[inline(never)] + fn foo_never(x: &i32) { + println!("{}", x); + } + + #[inline] + fn foo(x: &i32) { + println!("{}", x); + } +} + fn main() { let (mut foo, bar) = (Foo(0), Bar([0; 24])); let (mut a, b, c, x, y, z) = (0, 0, Bar([0; 24]), 0, Foo(0), 0); diff --git a/tests/ui/trivially_copy_pass_by_ref.stderr b/tests/ui/trivially_copy_pass_by_ref.stderr index be0914e4a79..ccc3cdb2b74 100644 --- a/tests/ui/trivially_copy_pass_by_ref.stderr +++ b/tests/ui/trivially_copy_pass_by_ref.stderr @@ -94,5 +94,17 @@ error: this argument (N byte) is passed by reference, but would be more efficien LL | fn trait_method2(&self, _color: &Color); | ^^^^^^ help: consider passing by value instead: `Color` -error: aborting due to 15 previous errors +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/trivially_copy_pass_by_ref.rs:108:21 + | +LL | fn foo_never(x: &i32) { + | ^^^^ help: consider passing by value instead: `i32` + +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> $DIR/trivially_copy_pass_by_ref.rs:113:15 + | +LL | fn foo(x: &i32) { + | ^^^^ help: consider passing by value instead: `i32` + +error: aborting due to 17 previous errors -- cgit 1.4.1-3-g733a5 From 5b07b9ed61d1979320e9e63219b5d0d6f4b350f7 Mon Sep 17 00:00:00 2001 From: Christian Stefanescu Date: Thu, 20 Aug 2020 22:49:39 +0200 Subject: Widen understanding of prelude import Prelude imports are exempt from wildcard import warnings. Until now only imports of the form ``` use ...::prelude::*; ``` were considered. This change makes it so that the segment `prelude` can show up anywhere, for instance: ``` use ...::prelude::v1::*; ``` Fixes #5917 --- clippy_lints/src/wildcard_imports.rs | 7 ++----- tests/ui/auxiliary/wildcard_imports_helper.rs | 6 ++++++ tests/ui/wildcard_imports.rs | 2 ++ 3 files changed, 10 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index e7eb7c2e980..717741129a8 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -195,13 +195,10 @@ impl WildcardImports { } } -// Allow "...prelude::*" imports. +// Allow "...prelude::..::*" imports. // Many crates have a prelude, and it is imported as a glob by design. fn is_prelude_import(segments: &[PathSegment<'_>]) -> bool { - segments - .iter() - .last() - .map_or(false, |ps| ps.ident.as_str() == "prelude") + segments.iter().filter(|ps| ps.ident.as_str() == "prelude").count() > 0 } // Allow "super::*" imports in tests. diff --git a/tests/ui/auxiliary/wildcard_imports_helper.rs b/tests/ui/auxiliary/wildcard_imports_helper.rs index 414477aedd7..d75cdd625f9 100644 --- a/tests/ui/auxiliary/wildcard_imports_helper.rs +++ b/tests/ui/auxiliary/wildcard_imports_helper.rs @@ -19,3 +19,9 @@ mod extern_exports { A, } } + +pub mod prelude { + pub mod v1 { + pub struct PreludeModAnywhere; + } +} diff --git a/tests/ui/wildcard_imports.rs b/tests/ui/wildcard_imports.rs index 3ad1a29aeba..1f261159f4a 100644 --- a/tests/ui/wildcard_imports.rs +++ b/tests/ui/wildcard_imports.rs @@ -20,6 +20,7 @@ use wildcard_imports_helper::inner::inner_for_self_import::*; use wildcard_imports_helper::*; use std::io::prelude::*; +use wildcard_imports_helper::prelude::v1::*; struct ReadFoo; @@ -75,6 +76,7 @@ fn main() { let _ = A; let _ = inner_struct_mod::C; let _ = ExternA; + let _ = PreludeModAnywhere; double_struct_import_test!(); double_struct_import_test!(); -- cgit 1.4.1-3-g733a5 From 53508aeb65963c389233c956105202466128a900 Mon Sep 17 00:00:00 2001 From: Christian Stefanescu Date: Thu, 20 Aug 2020 22:54:46 +0200 Subject: Run sh tests/ui/update-all-references.sh --- tests/ui/wildcard_imports.fixed | 2 ++ tests/ui/wildcard_imports.stderr | 28 ++++++++++++++-------------- 2 files changed, 16 insertions(+), 14 deletions(-) (limited to 'tests') diff --git a/tests/ui/wildcard_imports.fixed b/tests/ui/wildcard_imports.fixed index 67423e6ec1d..287f8935327 100644 --- a/tests/ui/wildcard_imports.fixed +++ b/tests/ui/wildcard_imports.fixed @@ -20,6 +20,7 @@ use wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar; use wildcard_imports_helper::{ExternA, extern_foo}; use std::io::prelude::*; +use wildcard_imports_helper::prelude::v1::*; struct ReadFoo; @@ -75,6 +76,7 @@ fn main() { let _ = A; let _ = inner_struct_mod::C; let _ = ExternA; + let _ = PreludeModAnywhere; double_struct_import_test!(); double_struct_import_test!(); diff --git a/tests/ui/wildcard_imports.stderr b/tests/ui/wildcard_imports.stderr index fab43b738eb..351988f31ea 100644 --- a/tests/ui/wildcard_imports.stderr +++ b/tests/ui/wildcard_imports.stderr @@ -37,55 +37,55 @@ LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:89:13 + --> $DIR/wildcard_imports.rs:91:13 | LL | use crate::fn_mod::*; | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:95:75 + --> $DIR/wildcard_imports.rs:97:75 | LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; | ^ help: try: `inner_extern_foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:96:13 + --> $DIR/wildcard_imports.rs:98:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:107:20 + --> $DIR/wildcard_imports.rs:109:20 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^ help: try: `inner::inner_foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:107:30 + --> $DIR/wildcard_imports.rs:109:30 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^^ help: try: `inner2::inner_bar` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:114:13 + --> $DIR/wildcard_imports.rs:116:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:143:9 + --> $DIR/wildcard_imports.rs:145:9 | LL | use crate::in_fn_test::*; | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:152:9 + --> $DIR/wildcard_imports.rs:154:9 | LL | use crate:: in_fn_test:: * ; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:153:9 + --> $DIR/wildcard_imports.rs:155:9 | LL | use crate:: fn_mod:: | _________^ @@ -93,31 +93,31 @@ LL | | *; | |_________^ help: try: `crate:: fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:164:13 + --> $DIR/wildcard_imports.rs:166:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:199:17 + --> $DIR/wildcard_imports.rs:201:17 | LL | use super::*; | ^^^^^^^^ help: try: `super::insidefoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:207:13 + --> $DIR/wildcard_imports.rs:209:13 | LL | use super_imports::*; | ^^^^^^^^^^^^^^^^ help: try: `super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:216:17 + --> $DIR/wildcard_imports.rs:218:17 | LL | use super::super::*; | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:225:13 + --> $DIR/wildcard_imports.rs:227:13 | LL | use super::super::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` -- cgit 1.4.1-3-g733a5 From e8d33d73dc3f9d0daf9b3affe65a2431f5a3e13a Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Sun, 23 Aug 2020 07:50:59 +0200 Subject: Fix `let_and_return` bad suggestion Add a cast to the suggestion when the return expression has adjustments. These adjustments are lost when the suggestion is applied. This is similar to the problem in issue #4437. Closes #5729 --- clippy_lints/src/returns.rs | 5 ++++- tests/ui/let_and_return.rs | 21 +++++++++++++++++++++ tests/ui/let_and_return.stderr | 16 +++++++++++++++- 3 files changed, 40 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 3c5541e64b4..a6e4252a0c8 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -99,7 +99,10 @@ impl<'tcx> LateLintPass<'tcx> for Return { |err| { err.span_label(local.span, "unnecessary `let` binding"); - if let Some(snippet) = snippet_opt(cx, initexpr.span) { + if let Some(mut snippet) = snippet_opt(cx, initexpr.span) { + if !cx.typeck_results().expr_adjustments(&retexpr).is_empty() { + snippet.push_str(" as _"); + } err.multipart_suggestion( "return the expression directly", vec![ diff --git a/tests/ui/let_and_return.rs b/tests/ui/let_and_return.rs index 09614b8c1ad..73e550b3df8 100644 --- a/tests/ui/let_and_return.rs +++ b/tests/ui/let_and_return.rs @@ -135,4 +135,25 @@ mod no_lint_if_stmt_borrows { } } +mod issue_5729 { + use std::sync::Arc; + + trait Foo {} + + trait FooStorage { + fn foo_cloned(&self) -> Arc; + } + + struct FooStorageImpl { + foo: Arc, + } + + impl FooStorage for FooStorageImpl { + fn foo_cloned(&self) -> Arc { + let clone = Arc::clone(&self.foo); + clone + } + } +} + fn main() {} diff --git a/tests/ui/let_and_return.stderr b/tests/ui/let_and_return.stderr index eacf948b392..fe878e5f206 100644 --- a/tests/ui/let_and_return.stderr +++ b/tests/ui/let_and_return.stderr @@ -27,5 +27,19 @@ LL | LL | 5 | -error: aborting due to 2 previous errors +error: returning the result of a `let` binding from a block + --> $DIR/let_and_return.rs:154:13 + | +LL | let clone = Arc::clone(&self.foo); + | ---------------------------------- unnecessary `let` binding +LL | clone + | ^^^^^ + | +help: return the expression directly + | +LL | +LL | Arc::clone(&self.foo) as _ + | + +error: aborting due to 3 previous errors -- cgit 1.4.1-3-g733a5 From 8776db9f6d5163ca40232336dc51ad77eeb2b5e5 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sun, 23 Aug 2020 12:05:34 +0200 Subject: Fix ICE in `repeat_once` lint --- clippy_lints/src/repeat_once.rs | 14 +++++++------- tests/ui/crashes/ice-5944.rs | 13 +++++++++++++ 2 files changed, 20 insertions(+), 7 deletions(-) create mode 100644 tests/ui/crashes/ice-5944.rs (limited to 'tests') diff --git a/clippy_lints/src/repeat_once.rs b/clippy_lints/src/repeat_once.rs index 77c206002ea..c0890018d46 100644 --- a/clippy_lints/src/repeat_once.rs +++ b/clippy_lints/src/repeat_once.rs @@ -39,12 +39,12 @@ declare_lint_pass!(RepeatOnce => [REPEAT_ONCE]); impl<'tcx> LateLintPass<'tcx> for RepeatOnce { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) { if_chain! { - if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind; + 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(&args[1]); - if !in_macro(args[0].span); + if let Some(Constant::Int(1)) = constant_context(cx, cx.typeck_results()).expr(&count); + if !in_macro(receiver.span); then { - let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&args[0])); + let ty = walk_ptrs_ty(cx.typeck_results().expr_ty(&receiver)); if ty.is_str() { span_lint_and_sugg( cx, @@ -52,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for RepeatOnce { expr.span, "calling `repeat(1)` on str", "consider using `.to_string()` instead", - format!("{}.to_string()", snippet(cx, args[0].span, r#""...""#)), + format!("{}.to_string()", snippet(cx, receiver.span, r#""...""#)), Applicability::MachineApplicable, ); } else if ty.builtin_index().is_some() { @@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for RepeatOnce { expr.span, "calling `repeat(1)` on slice", "consider using `.to_vec()` instead", - format!("{}.to_vec()", snippet(cx, args[0].span, r#""...""#)), + format!("{}.to_vec()", snippet(cx, receiver.span, r#""...""#)), Applicability::MachineApplicable, ); } else if is_type_diagnostic_item(cx, ty, sym!(string_type)) { @@ -72,7 +72,7 @@ impl<'tcx> LateLintPass<'tcx> for RepeatOnce { expr.span, "calling `repeat(1)` on a string literal", "consider using `.clone()` instead", - format!("{}.clone()", snippet(cx, args[0].span, r#""...""#)), + format!("{}.clone()", snippet(cx, receiver.span, r#""...""#)), Applicability::MachineApplicable, ); } diff --git a/tests/ui/crashes/ice-5944.rs b/tests/ui/crashes/ice-5944.rs new file mode 100644 index 00000000000..5caf29c6197 --- /dev/null +++ b/tests/ui/crashes/ice-5944.rs @@ -0,0 +1,13 @@ +#![warn(clippy::repeat_once)] + +trait Repeat { + fn repeat(&self) {} +} + +impl Repeat for usize { + fn repeat(&self) {} +} + +fn main() { + let _ = 42.repeat(); +} -- cgit 1.4.1-3-g733a5 From 91b200c62b7c464cb890c68230ab2d74605a60d0 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Sun, 23 Aug 2020 22:16:24 +1200 Subject: Fix fp in `borrow_interior_mutable_const` Fix false positive when referencing a field behind a pointer. --- clippy_lints/src/non_copy_const.rs | 15 +++++++++++- tests/ui/borrow_interior_mutable_const.rs | 35 ++++++++++++++++++++++++++- tests/ui/borrow_interior_mutable_const.stderr | 32 ++++++++++++------------ 3 files changed, 64 insertions(+), 18 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 031d69e86a1..f1df634701d 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -211,8 +211,21 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { needs_check_adjustment = false; }, ExprKind::Field(..) => { - dereferenced_expr = parent_expr; needs_check_adjustment = true; + + // Check whether implicit dereferences happened; + // if so, no need to go further up + // because of the same reason as the `ExprKind::Unary` case. + if cx + .typeck_results() + .expr_adjustments(dereferenced_expr) + .iter() + .any(|adj| matches!(adj.kind, Adjust::Deref(_))) + { + break; + } + + dereferenced_expr = parent_expr; }, ExprKind::Index(e, _) if ptr::eq(&**e, cur_expr) => { // `e[i]` => desugared to `*Index::index(&e, i)`, diff --git a/tests/ui/borrow_interior_mutable_const.rs b/tests/ui/borrow_interior_mutable_const.rs index fef9f4f39f8..310769cd002 100644 --- a/tests/ui/borrow_interior_mutable_const.rs +++ b/tests/ui/borrow_interior_mutable_const.rs @@ -2,7 +2,7 @@ #![allow(clippy::declare_interior_mutable_const, clippy::ref_in_deref)] use std::borrow::Cow; -use std::cell::Cell; +use std::cell::{Cell, UnsafeCell}; use std::fmt::Display; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Once; @@ -30,6 +30,37 @@ impl Trait for u64 { const ATOMIC: AtomicUsize = AtomicUsize::new(9); } +// This is just a pointer that can be safely dereferended, +// it's semantically the same as `&'static T`; +// but it isn't allowed make a static reference from an arbitrary integer value at the moment. +// For more information, please see the issue #5918. +pub struct StaticRef { + ptr: *const T, +} + +impl StaticRef { + /// Create a new `StaticRef` from a raw pointer + /// + /// ## Safety + /// + /// Callers must pass in a reference to statically allocated memory which + /// does not overlap with other values. + pub const unsafe fn new(ptr: *const T) -> StaticRef { + StaticRef { ptr } + } +} + +impl std::ops::Deref for StaticRef { + type Target = T; + + fn deref(&self) -> &'static T { + unsafe { &*self.ptr } + } +} + +// use a tuple to make sure referencing a field behind a pointer isn't linted. +const CELL_REF: StaticRef<(UnsafeCell,)> = unsafe { StaticRef::new(std::ptr::null()) }; + fn main() { ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability @@ -82,4 +113,6 @@ fn main() { assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability assert_eq!(NO_ANN.to_string(), "70"); // should never lint this. + + let _ = &CELL_REF.0; } diff --git a/tests/ui/borrow_interior_mutable_const.stderr b/tests/ui/borrow_interior_mutable_const.stderr index dc738064a17..1e0b3e4d20a 100644 --- a/tests/ui/borrow_interior_mutable_const.stderr +++ b/tests/ui/borrow_interior_mutable_const.stderr @@ -1,5 +1,5 @@ error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:34:5 + --> $DIR/borrow_interior_mutable_const.rs:65:5 | LL | ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability | ^^^^^^ @@ -8,7 +8,7 @@ LL | ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:35:16 + --> $DIR/borrow_interior_mutable_const.rs:66:16 | LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability | ^^^^^^ @@ -16,7 +16,7 @@ LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutabi = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:38:22 + --> $DIR/borrow_interior_mutable_const.rs:69:22 | LL | let _once_ref = &ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -24,7 +24,7 @@ LL | let _once_ref = &ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:39:25 + --> $DIR/borrow_interior_mutable_const.rs:70:25 | LL | let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -32,7 +32,7 @@ LL | let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:40:27 + --> $DIR/borrow_interior_mutable_const.rs:71:27 | LL | let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -40,7 +40,7 @@ LL | let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:41:26 + --> $DIR/borrow_interior_mutable_const.rs:72:26 | LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -48,7 +48,7 @@ LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:52:14 + --> $DIR/borrow_interior_mutable_const.rs:83:14 | LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -56,7 +56,7 @@ LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:53:14 + --> $DIR/borrow_interior_mutable_const.rs:84:14 | LL | let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:54:19 + --> $DIR/borrow_interior_mutable_const.rs:85:19 | LL | let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:55:14 + --> $DIR/borrow_interior_mutable_const.rs:86:14 | LL | let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -80,7 +80,7 @@ LL | let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:56:13 + --> $DIR/borrow_interior_mutable_const.rs:87:13 | LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -88,7 +88,7 @@ LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mu = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:62:13 + --> $DIR/borrow_interior_mutable_const.rs:93:13 | LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -96,7 +96,7 @@ LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:67:5 + --> $DIR/borrow_interior_mutable_const.rs:98:5 | LL | CELL.set(2); //~ ERROR interior mutability | ^^^^ @@ -104,7 +104,7 @@ LL | CELL.set(2); //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:68:16 + --> $DIR/borrow_interior_mutable_const.rs:99:16 | LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability | ^^^^ @@ -112,7 +112,7 @@ LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:81:5 + --> $DIR/borrow_interior_mutable_const.rs:112:5 | LL | u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability | ^^^^^^^^^^^ @@ -120,7 +120,7 @@ LL | u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:82:16 + --> $DIR/borrow_interior_mutable_const.rs:113:16 | LL | assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability | ^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From b2c226679251bb795d786d7cdd11d2e2c9d0e685 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 18 Aug 2020 23:00:21 +0900 Subject: Fix FP for `redundant_closure_call` Visit the nested things like function body when checking closure call. --- clippy_lints/src/redundant_closure_call.rs | 17 +++++++++++------ tests/ui/redundant_closure_call_late.rs | 12 ++++++++++++ 2 files changed, 23 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index 8aa478ea2d6..d8fad217e5a 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -95,12 +95,17 @@ impl EarlyLintPass for RedundantClosureCall { impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { - fn count_closure_usage<'tcx>(block: &'tcx hir::Block<'_>, path: &'tcx hir::Path<'tcx>) -> usize { - struct ClosureUsageCount<'tcx> { + fn count_closure_usage<'a, 'tcx>( + cx: &'a LateContext<'tcx>, + block: &'tcx hir::Block<'_>, + path: &'tcx hir::Path<'tcx>, + ) -> usize { + struct ClosureUsageCount<'a, 'tcx> { + cx: &'a LateContext<'tcx>, path: &'tcx hir::Path<'tcx>, count: usize, }; - impl<'tcx> hir_visit::Visitor<'tcx> for ClosureUsageCount<'tcx> { + impl<'a, 'tcx> hir_visit::Visitor<'tcx> for ClosureUsageCount<'a, 'tcx> { type Map = Map<'tcx>; fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { @@ -117,10 +122,10 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { } fn nested_visit_map(&mut self) -> hir_visit::NestedVisitorMap { - hir_visit::NestedVisitorMap::None + hir_visit::NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) } }; - let mut closure_usage_count = ClosureUsageCount { path, count: 0 }; + let mut closure_usage_count = ClosureUsageCount { cx, path, count: 0 }; closure_usage_count.visit_block(block); closure_usage_count.count } @@ -136,7 +141,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { if let hir::ExprKind::Call(ref closure, _) = call.kind; if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = closure.kind; if ident == path.segments[0].ident; - if count_closure_usage(block, path) == 1; + if count_closure_usage(cx, block, path) == 1; then { span_lint( cx, diff --git a/tests/ui/redundant_closure_call_late.rs b/tests/ui/redundant_closure_call_late.rs index e29a1dce0c7..1f4864b7289 100644 --- a/tests/ui/redundant_closure_call_late.rs +++ b/tests/ui/redundant_closure_call_late.rs @@ -24,4 +24,16 @@ fn main() { let shadowed_closure = || 2; i = shadowed_closure(); i = shadowed_closure(); + + // Fix FP in #5916 + let mut x; + let create = || 2 * 2; + x = create(); + fun(move || { + x = create(); + }) +} + +fn fun(mut f: T) { + f(); } -- cgit 1.4.1-3-g733a5 From 9fe0ac36a53dfba14f5f33f5bab2fd243fb2c18e Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Mon, 24 Aug 2020 10:11:53 +0900 Subject: Avoid period in lint message according to convention --- clippy_lints/src/redundant_closure_call.rs | 2 +- tests/ui/redundant_closure_call_early.stderr | 4 ++-- tests/ui/redundant_closure_call_fixable.stderr | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index d8fad217e5a..49cb2ffc4e3 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -77,7 +77,7 @@ impl EarlyLintPass for RedundantClosureCall { cx, REDUNDANT_CLOSURE_CALL, expr.span, - "try not to call a closure in the expression where it is declared.", + "try not to call a closure in the expression where it is declared", |diag| { if decl.inputs.is_empty() { let mut app = Applicability::MachineApplicable; diff --git a/tests/ui/redundant_closure_call_early.stderr b/tests/ui/redundant_closure_call_early.stderr index 79f27663461..2735e41738f 100644 --- a/tests/ui/redundant_closure_call_early.stderr +++ b/tests/ui/redundant_closure_call_early.stderr @@ -1,4 +1,4 @@ -error: try not to call a closure in the expression where it is declared. +error: try not to call a closure in the expression where it is declared --> $DIR/redundant_closure_call_early.rs:9:17 | LL | let mut k = (|m| m + 1)(i); @@ -6,7 +6,7 @@ LL | let mut k = (|m| m + 1)(i); | = note: `-D clippy::redundant-closure-call` implied by `-D warnings` -error: try not to call a closure in the expression where it is declared. +error: try not to call a closure in the expression where it is declared --> $DIR/redundant_closure_call_early.rs:12:9 | LL | k = (|a, b| a * b)(1, 5); diff --git a/tests/ui/redundant_closure_call_fixable.stderr b/tests/ui/redundant_closure_call_fixable.stderr index 644161d9f5d..afd704ef12a 100644 --- a/tests/ui/redundant_closure_call_fixable.stderr +++ b/tests/ui/redundant_closure_call_fixable.stderr @@ -1,4 +1,4 @@ -error: try not to call a closure in the expression where it is declared. +error: try not to call a closure in the expression where it is declared --> $DIR/redundant_closure_call_fixable.rs:7:13 | LL | let a = (|| 42)(); -- cgit 1.4.1-3-g733a5 From ce8915c85cebaa30e6846322e0ab44a4f4f9cb65 Mon Sep 17 00:00:00 2001 From: rail-rain <12975677+rail-rain@users.noreply.github.com> Date: Mon, 24 Aug 2020 19:08:38 +1200 Subject: typo Co-authored-by: Thibaud --- tests/ui/borrow_interior_mutable_const.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/ui/borrow_interior_mutable_const.rs b/tests/ui/borrow_interior_mutable_const.rs index 310769cd002..a414832bcd3 100644 --- a/tests/ui/borrow_interior_mutable_const.rs +++ b/tests/ui/borrow_interior_mutable_const.rs @@ -32,7 +32,7 @@ impl Trait for u64 { // This is just a pointer that can be safely dereferended, // it's semantically the same as `&'static T`; -// but it isn't allowed make a static reference from an arbitrary integer value at the moment. +// but it isn't allowed to make a static reference from an arbitrary integer value at the moment. // For more information, please see the issue #5918. pub struct StaticRef { ptr: *const T, -- cgit 1.4.1-3-g733a5 From 3d820f71fe820c0bb7a1204e28ce549407a937cc Mon Sep 17 00:00:00 2001 From: ThibsG Date: Mon, 24 Aug 2020 14:01:27 +0200 Subject: Fix incorrect suggestion when `clone_on_ref_ptr` is triggered in macros --- clippy_lints/src/methods/mod.rs | 13 +++++++------ tests/ui/unnecessary_clone.rs | 18 ++++++++++++++++++ tests/ui/unnecessary_clone.stderr | 8 +++++++- 3 files changed, 32 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 22942d9fb0c..4f0a533592c 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2150,18 +2150,19 @@ fn lint_clone_on_ref_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir:: return; }; + let snippet = if in_macro(arg.span) { + snippet_with_macro_callsite(cx, arg.span, "_") + } else { + snippet(cx, arg.span, "_") + }; + span_lint_and_sugg( cx, CLONE_ON_REF_PTR, expr.span, "using `.clone()` on a ref-counted pointer", "try this", - format!( - "{}::<{}>::clone(&{})", - caller_type, - subst.type_at(0), - snippet(cx, arg.span, "_") - ), + format!("{}::<{}>::clone(&{})", caller_type, subst.type_at(0), snippet), Applicability::Unspecified, // Sometimes unnecessary ::<_> after Rc/Arc/Weak ); } diff --git a/tests/ui/unnecessary_clone.rs b/tests/ui/unnecessary_clone.rs index 2c9d4d39e6c..e785ac02feb 100644 --- a/tests/ui/unnecessary_clone.rs +++ b/tests/ui/unnecessary_clone.rs @@ -90,3 +90,21 @@ mod many_derefs { let _ = &encoded.clone(); } } + +mod issue2076 { + use std::rc::Rc; + + macro_rules! try_opt { + ($expr: expr) => { + match $expr { + Some(value) => value, + None => return None, + } + }; + } + + fn func() -> Option> { + let rc = Rc::new(42); + Some(try_opt!(Some(rc)).clone()) + } +} diff --git a/tests/ui/unnecessary_clone.stderr b/tests/ui/unnecessary_clone.stderr index 113fab69009..5ffa6c4fd06 100644 --- a/tests/ui/unnecessary_clone.stderr +++ b/tests/ui/unnecessary_clone.stderr @@ -96,5 +96,11 @@ help: or try being explicit if you are sure, that you want to clone a reference LL | let _ = &<&[u8]>::clone(encoded); | ^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 11 previous errors +error: using `.clone()` on a ref-counted pointer + --> $DIR/unnecessary_clone.rs:108:14 + | +LL | Some(try_opt!(Some(rc)).clone()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Rc::::clone(&try_opt!(Some(rc)))` + +error: aborting due to 12 previous errors -- cgit 1.4.1-3-g733a5 From 680c68153bced747c90947e0265c92076edcb838 Mon Sep 17 00:00:00 2001 From: Bastian Date: Mon, 24 Aug 2020 16:31:51 +0200 Subject: Added a lint which corrects expressions like (a - b) < f32::EPSILON --- CHANGELOG.md | 1 + clippy_lints/src/float_equality_without_abs.rs | 112 +++++++++++++++++++++++++ clippy_lints/src/lib.rs | 5 ++ src/lintlist/mod.rs | 7 ++ tests/ui/float_equality_without_abs.rs | 31 +++++++ tests/ui/float_equality_without_abs.stderr | 70 ++++++++++++++++ 6 files changed, 226 insertions(+) create mode 100644 clippy_lints/src/float_equality_without_abs.rs create mode 100644 tests/ui/float_equality_without_abs.rs create mode 100644 tests/ui/float_equality_without_abs.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ce63c0a157..a5da0f7b767 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1498,6 +1498,7 @@ Released 2018-09-13 [`float_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_arithmetic [`float_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp [`float_cmp_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp_const +[`float_equality_without_abs`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_equality_without_abs [`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 diff --git a/clippy_lints/src/float_equality_without_abs.rs b/clippy_lints/src/float_equality_without_abs.rs new file mode 100644 index 00000000000..c8e53ce6f95 --- /dev/null +++ b/clippy_lints/src/float_equality_without_abs.rs @@ -0,0 +1,112 @@ +use crate::utils::{match_qpath, snippet, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Spanned; + +declare_clippy_lint! { + /// **What it does:** Checks for statements of the form `(a - b) < f32::EPSILON` or + /// `(a - b) < f64::EPSILON`. Notes the missing `.abs()`. + /// + /// **Why is this bad?** The code without `.abs()` is more likely to have a bug. + /// + /// **Known problems:** If the user can ensure that b is larger than a, the `.abs()` is + /// technically unneccessary. However, it will make the code more robust and doesn't have any + /// large performance implications. If the abs call was deliberately left out for performance + /// reasons, it is probably better to state this explicitly in the code, which then can be done + /// with an allow. + /// + /// **Example:** + /// + /// ```rust + /// pub fn is_roughly_equal(a: f32, b: f32) -> bool { + /// (a - b) < f32::EPSILON + /// } + /// ``` + /// Use instead: + /// ```rust + /// pub fn is_roughly_equal(a: f32, b: f32) -> bool { + /// (a - b).abs() < f32::EPSILON + /// } + /// ``` + pub FLOAT_EQUALITY_WITHOUT_ABS, + correctness, + "float equality check without `.abs()`" +} + +declare_lint_pass!(FloatEqualityWithoutAbs => [FLOAT_EQUALITY_WITHOUT_ABS]); + +impl<'tcx> LateLintPass<'tcx> for FloatEqualityWithoutAbs { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + let lhs; + let rhs; + + // check if expr is a binary expression with a lt or gt operator + if let ExprKind::Binary(op, ref left, ref right) = expr.kind { + match op.node { + BinOpKind::Lt => { + lhs = left; + rhs = right; + }, + BinOpKind::Gt => { + lhs = right; + rhs = left; + }, + _ => return, + }; + } else { + return; + } + + if_chain! { + + // left hand side is a substraction + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Sub, + .. + }, + val_l, + val_r, + ) = lhs.kind; + + // right hand side matches either f32::EPSILON or f64::EPSILON + if let ExprKind::Path(ref epsilon_path) = rhs.kind; + if match_qpath(epsilon_path, &["f32", "EPSILON"]) || match_qpath(epsilon_path, &["f64", "EPSILON"]); + + // values of the substractions on the left hand side are of the type float + let t_val_l = cx.typeck_results().expr_ty(val_l); + let t_val_r = cx.typeck_results().expr_ty(val_r); + if let ty::Float(_) = t_val_l.kind; + if let ty::Float(_) = t_val_r.kind; + + then { + // get the snippet string + let lhs_string = snippet( + cx, + lhs.span, + "(...)", + ); + // format the suggestion + let suggestion = if lhs_string.starts_with('(') { + format!("{}.abs()", lhs_string) + } else { + format!("({}).abs()", lhs_string) + }; + // spans the lint + span_lint_and_sugg( + cx, + FLOAT_EQUALITY_WITHOUT_ABS, + expr.span, + "float equality check without `.abs()`", + "add `.abs()`", + suggestion, + Applicability::MaybeIncorrect, + ); + } + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d45394ab8d2..f78de7a175f 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -193,6 +193,7 @@ mod excessive_bools; mod exit; mod explicit_write; mod fallible_impl_from; +mod float_equality_without_abs; mod float_literal; mod floating_point_arithmetic; mod format; @@ -549,6 +550,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &exit::EXIT, &explicit_write::EXPLICIT_WRITE, &fallible_impl_from::FALLIBLE_IMPL_FROM, + &float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS, &float_literal::EXCESSIVE_PRECISION, &float_literal::LOSSY_FLOAT_LITERAL, &floating_point_arithmetic::IMPRECISE_FLOPS, @@ -1093,6 +1095,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box stable_sort_primitive::StableSortPrimitive); store.register_late_pass(|| box repeat_once::RepeatOnce); store.register_late_pass(|| box self_assignment::SelfAssignment); + store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1268,6 +1271,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), @@ -1686,6 +1690,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT), LintId::of(&eq_op::EQ_OP), LintId::of(&erasing_op::ERASING_OP), + LintId::of(&float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS), LintId::of(&formatting::POSSIBLE_MISSING_COMMA), LintId::of(&functions::NOT_UNSAFE_PTR_ARG_DEREF), LintId::of(&if_let_mutex::IF_LET_MUTEX), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index c50c6b900ae..eadd2621a40 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -661,6 +661,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "misc", }, + Lint { + name: "float_equality_without_abs", + group: "correctness", + desc: "float equality check without `.abs()`", + deprecation: None, + module: "float_equality_without_abs", + }, Lint { name: "fn_address_comparisons", group: "correctness", diff --git a/tests/ui/float_equality_without_abs.rs b/tests/ui/float_equality_without_abs.rs new file mode 100644 index 00000000000..d40fa00c315 --- /dev/null +++ b/tests/ui/float_equality_without_abs.rs @@ -0,0 +1,31 @@ +#![warn(clippy::float_equality_without_abs)] + +pub fn is_roughly_equal(a: f32, b: f32) -> bool { + (a - b) < f32::EPSILON +} + +pub fn main() { + // all errors + is_roughly_equal(1.0, 2.0); + let a = 0.05; + let b = 0.0500001; + + let _ = (a - b) < f32::EPSILON; + let _ = a - b < f32::EPSILON; + let _ = a - b.abs() < f32::EPSILON; + let _ = (a as f64 - b as f64) < f64::EPSILON; + let _ = 1.0 - 2.0 < f32::EPSILON; + + let _ = f32::EPSILON > (a - b); + let _ = f32::EPSILON > a - b; + let _ = f32::EPSILON > a - b.abs(); + let _ = f64::EPSILON > (a as f64 - b as f64); + let _ = f32::EPSILON > 1.0 - 2.0; + + // those are correct + let _ = (a - b).abs() < f32::EPSILON; + let _ = (a as f64 - b as f64).abs() < f64::EPSILON; + + let _ = f32::EPSILON > (a - b).abs(); + let _ = f64::EPSILON > (a as f64 - b as f64).abs(); +} diff --git a/tests/ui/float_equality_without_abs.stderr b/tests/ui/float_equality_without_abs.stderr new file mode 100644 index 00000000000..74b9078afe8 --- /dev/null +++ b/tests/ui/float_equality_without_abs.stderr @@ -0,0 +1,70 @@ +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:4:5 + | +LL | (a - b) < f32::EPSILON + | ^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b).abs()` + | + = note: `-D clippy::float-equality-without-abs` implied by `-D warnings` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:13:13 + | +LL | let _ = (a - b) < f32::EPSILON; + | ^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:14:13 + | +LL | let _ = a - b < f32::EPSILON; + | ^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:15:13 + | +LL | let _ = a - b.abs() < f32::EPSILON; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b.abs()).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:16:13 + | +LL | let _ = (a as f64 - b as f64) < f64::EPSILON; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a as f64 - b as f64).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:17:13 + | +LL | let _ = 1.0 - 2.0 < f32::EPSILON; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(1.0 - 2.0).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:19:13 + | +LL | let _ = f32::EPSILON > (a - b); + | ^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:20:13 + | +LL | let _ = f32::EPSILON > a - b; + | ^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:21:13 + | +LL | let _ = f32::EPSILON > a - b.abs(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b.abs()).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:22:13 + | +LL | let _ = f64::EPSILON > (a as f64 - b as f64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a as f64 - b as f64).abs()` + +error: float equality check without `.abs()` + --> $DIR/float_equality_without_abs.rs:23:13 + | +LL | let _ = f32::EPSILON > 1.0 - 2.0; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(1.0 - 2.0).abs()` + +error: aborting due to 11 previous errors + -- cgit 1.4.1-3-g733a5 From e9964f2e8a1903d46e576f67122e234f719a90f0 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Fri, 21 Aug 2020 02:03:57 +0200 Subject: stable_sort_primitive: print the type that is being sorted in the lint message --- clippy_lints/src/stable_sort_primitive.rs | 10 ++++++---- clippy_lints/src/utils/mod.rs | 27 +++++++++++++++++++++++---- tests/ui/stable_sort_primitive.stderr | 14 +++++++------- 3 files changed, 36 insertions(+), 15 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/stable_sort_primitive.rs b/clippy_lints/src/stable_sort_primitive.rs index 22c49a20451..99e4b293ac6 100644 --- a/clippy_lints/src/stable_sort_primitive.rs +++ b/clippy_lints/src/stable_sort_primitive.rs @@ -86,6 +86,7 @@ struct LintDetection { slice_name: String, method: SortingKind, method_args: String, + slice_type: String, } fn detect_stable_sort_primitive(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { @@ -93,10 +94,10 @@ fn detect_stable_sort_primitive(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option if let ExprKind::MethodCall(method_name, _, args, _) = &expr.kind; if let Some(slice) = &args.get(0); if let Some(method) = SortingKind::from_stable_name(&method_name.ident.name.as_str()); - if is_slice_of_primitives(cx, slice); + if let Some(slice_type) = is_slice_of_primitives(cx, slice); then { let args_str = args.iter().skip(1).map(|arg| Sugg::hir(cx, arg, "..").to_string()).collect::>().join(", "); - Some(LintDetection { slice_name: Sugg::hir(cx, slice, "..").to_string(), method, method_args: args_str }) + Some(LintDetection { slice_name: Sugg::hir(cx, slice, "..").to_string(), method, method_args: args_str, slice_type }) } else { None } @@ -111,9 +112,10 @@ impl LateLintPass<'_> for StableSortPrimitive { STABLE_SORT_PRIMITIVE, expr.span, format!( - "used {} instead of {}", + "used {} instead of {} to sort primitive type `{}`", detection.method.stable_name(), - detection.method.unstable_name() + detection.method.unstable_name(), + detection.slice_type, ) .as_str(), "try", diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 2aef995cec4..25301d6dede 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -1409,11 +1409,13 @@ pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool { } } -/// Returns true iff the given expression is a slice of primitives (as defined in the -/// `is_recursively_primitive_type` function). -pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { +/// Returns Option where String is a textual representation of the type encapsulated in the +/// slice iff the given expression is a slice of primitives (as defined in the +/// `is_recursively_primitive_type` function) and None otherwise. +pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { let expr_type = cx.typeck_results().expr_ty_adjusted(expr); - match expr_type.kind { + let expr_kind = &expr_type.kind; + let is_primitive = match expr_kind { ty::Slice(ref element_type) | ty::Ref( _, @@ -1424,7 +1426,24 @@ pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { _, ) => is_recursively_primitive_type(element_type), _ => false, + }; + + if is_primitive { + // if we have wrappers like Array, Slice or Tuple, print these + // and get the type enclosed in the slice ref + match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind { + ty::Slice(..) => return Some("slice".into()), + ty::Array(..) => return Some("array".into()), + ty::Tuple(..) => return Some("tuple".into()), + _ => { + // is_recursively_primitive_type() should have taken care + // of the rest and we can rely on the type that is found + let refs_peeled = expr_type.peel_refs(); + return Some(refs_peeled.walk().last().unwrap().to_string()); + }, + } } + None } #[macro_export] diff --git a/tests/ui/stable_sort_primitive.stderr b/tests/ui/stable_sort_primitive.stderr index b73012a4691..780389f32bc 100644 --- a/tests/ui/stable_sort_primitive.stderr +++ b/tests/ui/stable_sort_primitive.stderr @@ -1,4 +1,4 @@ -error: used sort instead of sort_unstable +error: used sort instead of sort_unstable to sort primitive type `i32` --> $DIR/stable_sort_primitive.rs:7:5 | LL | vec.sort(); @@ -6,37 +6,37 @@ LL | vec.sort(); | = note: `-D clippy::stable-sort-primitive` implied by `-D warnings` -error: used sort instead of sort_unstable +error: used sort instead of sort_unstable to sort primitive type `bool` --> $DIR/stable_sort_primitive.rs:9:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` -error: used sort instead of sort_unstable +error: used sort instead of sort_unstable to sort primitive type `char` --> $DIR/stable_sort_primitive.rs:11:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` -error: used sort instead of sort_unstable +error: used sort instead of sort_unstable to sort primitive type `str` --> $DIR/stable_sort_primitive.rs:13:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` -error: used sort instead of sort_unstable +error: used sort instead of sort_unstable to sort primitive type `tuple` --> $DIR/stable_sort_primitive.rs:15:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` -error: used sort instead of sort_unstable +error: used sort instead of sort_unstable to sort primitive type `array` --> $DIR/stable_sort_primitive.rs:17:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` -error: used sort instead of sort_unstable +error: used sort instead of sort_unstable to sort primitive type `i32` --> $DIR/stable_sort_primitive.rs:19:5 | LL | arr.sort(); -- cgit 1.4.1-3-g733a5 From 91028326927266af5051b5d172dc3162d147a4d9 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Tue, 25 Aug 2020 05:53:28 +0200 Subject: Fix incorrect comment --- tests/fmt.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/fmt.rs b/tests/fmt.rs index 3aff8741f60..ee3ce26ee40 100644 --- a/tests/fmt.rs +++ b/tests/fmt.rs @@ -7,7 +7,7 @@ fn fmt() { return; } - // Skip this test if rustup nightly is unavailable + // Skip this test if rustfmt nightly is unavailable let rustup_output = Command::new("rustup") .args(&["component", "list", "--toolchain", "nightly"]) .output() -- cgit 1.4.1-3-g733a5 From 48d473691359f7c59d7348937f71de0e5aa4ae12 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Tue, 25 Aug 2020 05:53:28 +0200 Subject: Simplify fmt test by using the clippy dev alias --- tests/fmt.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/tests/fmt.rs b/tests/fmt.rs index ee3ce26ee40..d529952bc69 100644 --- a/tests/fmt.rs +++ b/tests/fmt.rs @@ -19,12 +19,9 @@ fn fmt() { } let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - let dev_dir = root_dir.join("clippy_dev"); - let target_dir = root_dir.join("target"); - let target_dir = target_dir.to_str().unwrap(); let output = Command::new("cargo") - .current_dir(dev_dir) - .args(&["+nightly", "run", "--target-dir", target_dir, "--", "fmt", "--check"]) + .current_dir(root_dir) + .args(&["dev", "fmt", "--check"]) .output() .unwrap(); -- cgit 1.4.1-3-g733a5 From acc6b6ce07a5816cbfdaba7798edfc5b22ac976b Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Tue, 25 Aug 2020 18:01:08 +0200 Subject: Fix typo --- tests/fmt.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/fmt.rs b/tests/fmt.rs index d529952bc69..7616d8001e8 100644 --- a/tests/fmt.rs +++ b/tests/fmt.rs @@ -7,7 +7,7 @@ fn fmt() { return; } - // Skip this test if rustfmt nightly is unavailable + // Skip this test if nightly rustfmt is unavailable let rustup_output = Command::new("rustup") .args(&["component", "list", "--toolchain", "nightly"]) .output() -- cgit 1.4.1-3-g733a5 From 3b1e5d6ff79f93e0215c6fb4c802167834ea4e15 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Tue, 25 Aug 2020 12:05:02 -0700 Subject: Re-enable len_zero for ranges now that `is_empty` is stable on them --- clippy_lints/src/len_zero.rs | 17 +---------------- tests/ui/len_zero.fixed | 8 -------- tests/ui/len_zero.rs | 8 -------- tests/ui/len_zero_ranges.fixed | 10 ++++++---- tests/ui/len_zero_ranges.rs | 10 ++++++---- tests/ui/len_zero_ranges.stderr | 10 ++++++++-- 6 files changed, 21 insertions(+), 42 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index e5daa30f8ca..b691d363d2f 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -1,4 +1,4 @@ -use crate::utils::{get_item_name, higher, snippet_with_applicability, span_lint, span_lint_and_sugg, walk_ptrs_ty}; +use crate::utils::{get_item_name, snippet_with_applicability, span_lint, span_lint_and_sugg, walk_ptrs_ty}; use rustc_ast::ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -260,17 +260,6 @@ fn check_len( /// Checks if this type has an `is_empty` method. fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - /// Special case ranges until `range_is_empty` is stabilized. See issue 3807. - fn should_skip_range(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - higher::range(expr).map_or(false, |_| { - !cx.tcx - .features() - .declared_lib_features - .iter() - .any(|(name, _)| name.as_str() == "range_is_empty") - }) - } - /// 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 { @@ -296,10 +285,6 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { }) } - if should_skip_range(cx, expr) { - return false; - } - let ty = &walk_ptrs_ty(cx.typeck_results().expr_ty(expr)); match ty.kind { ty::Dynamic(ref tt, ..) => tt.principal().map_or(false, |principal| { diff --git a/tests/ui/len_zero.fixed b/tests/ui/len_zero.fixed index d81676a3d9f..1f3b8ac99b1 100644 --- a/tests/ui/len_zero.fixed +++ b/tests/ui/len_zero.fixed @@ -141,11 +141,3 @@ fn main() { fn test_slice(b: &[u8]) { if !b.is_empty() {} } - -mod issue_3807 { - // Avoid suggesting changes to ranges if the user did not enable `range_is_empty`. - // See https://github.com/rust-lang/rust/issues/48111#issuecomment-445132965 - fn no_suggestion() { - let _ = (0..42).len() == 0; - } -} diff --git a/tests/ui/len_zero.rs b/tests/ui/len_zero.rs index ecdba921a5d..dc21de0001b 100644 --- a/tests/ui/len_zero.rs +++ b/tests/ui/len_zero.rs @@ -141,11 +141,3 @@ fn main() { fn test_slice(b: &[u8]) { if b.len() != 0 {} } - -mod issue_3807 { - // Avoid suggesting changes to ranges if the user did not enable `range_is_empty`. - // See https://github.com/rust-lang/rust/issues/48111#issuecomment-445132965 - fn no_suggestion() { - let _ = (0..42).len() == 0; - } -} diff --git a/tests/ui/len_zero_ranges.fixed b/tests/ui/len_zero_ranges.fixed index eee3db9b7d4..79781766242 100644 --- a/tests/ui/len_zero_ranges.fixed +++ b/tests/ui/len_zero_ranges.fixed @@ -1,15 +1,17 @@ // run-rustfix -#![feature(range_is_empty)] #![warn(clippy::len_zero)] #![allow(unused)] -#![allow(stable_features)] // TODO: https://github.com/rust-lang/rust-clippy/issues/5956 +// Now that `Range(Inclusive)::is_empty` is stable (1.47), we can always suggest this mod issue_3807 { - // With the feature enabled, `is_empty` should be suggested - fn suggestion_is_fine() { + fn suggestion_is_fine_range() { let _ = (0..42).is_empty(); } + + fn suggestion_is_fine_range_inclusive() { + let _ = (0_u8..=42).is_empty(); + } } fn main() {} diff --git a/tests/ui/len_zero_ranges.rs b/tests/ui/len_zero_ranges.rs index be2e0f38fd1..a0eb51cc976 100644 --- a/tests/ui/len_zero_ranges.rs +++ b/tests/ui/len_zero_ranges.rs @@ -1,15 +1,17 @@ // run-rustfix -#![feature(range_is_empty)] #![warn(clippy::len_zero)] #![allow(unused)] -#![allow(stable_features)] // TODO: https://github.com/rust-lang/rust-clippy/issues/5956 +// Now that `Range(Inclusive)::is_empty` is stable (1.47), we can always suggest this mod issue_3807 { - // With the feature enabled, `is_empty` should be suggested - fn suggestion_is_fine() { + fn suggestion_is_fine_range() { let _ = (0..42).len() == 0; } + + fn suggestion_is_fine_range_inclusive() { + let _ = (0_u8..=42).len() == 0; + } } fn main() {} diff --git a/tests/ui/len_zero_ranges.stderr b/tests/ui/len_zero_ranges.stderr index acb85f7100a..d0defb5a79e 100644 --- a/tests/ui/len_zero_ranges.stderr +++ b/tests/ui/len_zero_ranges.stderr @@ -1,10 +1,16 @@ error: length comparison to zero - --> $DIR/len_zero_ranges.rs:11:17 + --> $DIR/len_zero_ranges.rs:9:17 | LL | let _ = (0..42).len() == 0; | ^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(0..42).is_empty()` | = note: `-D clippy::len-zero` implied by `-D warnings` -error: aborting due to previous error +error: length comparison to zero + --> $DIR/len_zero_ranges.rs:13:17 + | +LL | let _ = (0_u8..=42).len() == 0; + | ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(0_u8..=42).is_empty()` + +error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From 2d853148d72d49956052cf1cdb5f3be18c85a9fc Mon Sep 17 00:00:00 2001 From: Bastian Date: Wed, 26 Aug 2020 16:39:30 +0200 Subject: Changed the location of the suggestion as well as the way the suggestion is assembled --- clippy_lints/src/float_equality_without_abs.rs | 30 ++++++++---------- tests/ui/float_equality_without_abs.stderr | 44 +++++++++++++++++++------- 2 files changed, 47 insertions(+), 27 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/float_equality_without_abs.rs b/clippy_lints/src/float_equality_without_abs.rs index dc1c3bfc9ff..9ac5a45eb45 100644 --- a/clippy_lints/src/float_equality_without_abs.rs +++ b/clippy_lints/src/float_equality_without_abs.rs @@ -1,5 +1,6 @@ -use crate::utils::{match_qpath, paths, snippet, span_lint_and_sugg}; +use crate::utils::{match_qpath, paths, span_lint_and_then, sugg}; use if_chain::if_chain; +use rustc_ast::util::parser::AssocOp; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -84,27 +85,24 @@ impl<'tcx> LateLintPass<'tcx> for FloatEqualityWithoutAbs { if let ty::Float(_) = t_val_r.kind; then { - // get the snippet string - let lhs_string = snippet( - cx, - lhs.span, - "(...)", - ); + let sug_l = sugg::Sugg::hir(cx, &val_l, ".."); + let sug_r = sugg::Sugg::hir(cx, &val_r, ".."); // format the suggestion - let suggestion = if lhs_string.starts_with('(') { - format!("{}.abs()", lhs_string) - } else { - format!("({}).abs()", lhs_string) - }; + let suggestion = format!("{}.abs()", sugg::make_assoc(AssocOp::Subtract, &sug_l, &sug_r).maybe_par()); // spans the lint - span_lint_and_sugg( + span_lint_and_then( cx, FLOAT_EQUALITY_WITHOUT_ABS, expr.span, "float equality check without `.abs()`", - "add `.abs()`", - suggestion, - Applicability::MaybeIncorrect, + | diag | { + diag.span_suggestion( + lhs.span, + "add `.abs()`", + suggestion, + Applicability::MaybeIncorrect, + ); + } ); } } diff --git a/tests/ui/float_equality_without_abs.stderr b/tests/ui/float_equality_without_abs.stderr index 74b9078afe8..b34c8159da0 100644 --- a/tests/ui/float_equality_without_abs.stderr +++ b/tests/ui/float_equality_without_abs.stderr @@ -2,7 +2,9 @@ error: float equality check without `.abs()` --> $DIR/float_equality_without_abs.rs:4:5 | LL | (a - b) < f32::EPSILON - | ^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b).abs()` + | -------^^^^^^^^^^^^^^^ + | | + | help: add `.abs()`: `(a - b).abs()` | = note: `-D clippy::float-equality-without-abs` implied by `-D warnings` @@ -10,61 +12,81 @@ error: float equality check without `.abs()` --> $DIR/float_equality_without_abs.rs:13:13 | LL | let _ = (a - b) < f32::EPSILON; - | ^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b).abs()` + | -------^^^^^^^^^^^^^^^ + | | + | help: add `.abs()`: `(a - b).abs()` error: float equality check without `.abs()` --> $DIR/float_equality_without_abs.rs:14:13 | LL | let _ = a - b < f32::EPSILON; - | ^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b).abs()` + | -----^^^^^^^^^^^^^^^ + | | + | help: add `.abs()`: `(a - b).abs()` error: float equality check without `.abs()` --> $DIR/float_equality_without_abs.rs:15:13 | LL | let _ = a - b.abs() < f32::EPSILON; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b.abs()).abs()` + | -----------^^^^^^^^^^^^^^^ + | | + | help: add `.abs()`: `(a - b.abs()).abs()` error: float equality check without `.abs()` --> $DIR/float_equality_without_abs.rs:16:13 | LL | let _ = (a as f64 - b as f64) < f64::EPSILON; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a as f64 - b as f64).abs()` + | ---------------------^^^^^^^^^^^^^^^ + | | + | help: add `.abs()`: `(a as f64 - b as f64).abs()` error: float equality check without `.abs()` --> $DIR/float_equality_without_abs.rs:17:13 | LL | let _ = 1.0 - 2.0 < f32::EPSILON; - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(1.0 - 2.0).abs()` + | ---------^^^^^^^^^^^^^^^ + | | + | help: add `.abs()`: `(1.0 - 2.0).abs()` error: float equality check without `.abs()` --> $DIR/float_equality_without_abs.rs:19:13 | LL | let _ = f32::EPSILON > (a - b); - | ^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b).abs()` + | ^^^^^^^^^^^^^^^------- + | | + | help: add `.abs()`: `(a - b).abs()` error: float equality check without `.abs()` --> $DIR/float_equality_without_abs.rs:20:13 | LL | let _ = f32::EPSILON > a - b; - | ^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b).abs()` + | ^^^^^^^^^^^^^^^----- + | | + | help: add `.abs()`: `(a - b).abs()` error: float equality check without `.abs()` --> $DIR/float_equality_without_abs.rs:21:13 | LL | let _ = f32::EPSILON > a - b.abs(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a - b.abs()).abs()` + | ^^^^^^^^^^^^^^^----------- + | | + | help: add `.abs()`: `(a - b.abs()).abs()` error: float equality check without `.abs()` --> $DIR/float_equality_without_abs.rs:22:13 | LL | let _ = f64::EPSILON > (a as f64 - b as f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(a as f64 - b as f64).abs()` + | ^^^^^^^^^^^^^^^--------------------- + | | + | help: add `.abs()`: `(a as f64 - b as f64).abs()` error: float equality check without `.abs()` --> $DIR/float_equality_without_abs.rs:23:13 | LL | let _ = f32::EPSILON > 1.0 - 2.0; - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: add `.abs()`: `(1.0 - 2.0).abs()` + | ^^^^^^^^^^^^^^^--------- + | | + | help: add `.abs()`: `(1.0 - 2.0).abs()` error: aborting due to 11 previous errors -- cgit 1.4.1-3-g733a5 From 91024f1fdee2d2c2febfc7c76127d68d2b6e629e Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Wed, 26 Aug 2020 16:31:49 -0700 Subject: Add new lint to prevent usage of unwrap in fns that return result --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 4 + clippy_lints/src/unwrap_in_result.rs | 140 +++++++++++++++++++++++++++++++++++ src/lintlist/mod.rs | 7 ++ tests/ui/unwrap_in_result.rs | 44 +++++++++++ tests/ui/unwrap_in_result.stderr | 41 ++++++++++ 6 files changed, 237 insertions(+) create mode 100644 clippy_lints/src/unwrap_in_result.rs create mode 100644 tests/ui/unwrap_in_result.rs create mode 100644 tests/ui/unwrap_in_result.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index a5da0f7b767..137b561028a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1778,6 +1778,7 @@ Released 2018-09-13 [`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label [`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self [`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit +[`unwrap_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_in_result [`unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used [`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug [`use_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_self diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f78de7a175f..577ce6523b4 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -314,6 +314,7 @@ mod unused_io_amount; mod unused_self; mod unused_unit; mod unwrap; +mod unwrap_in_result; mod use_self; mod useless_conversion; mod vec; @@ -850,6 +851,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &unused_unit::UNUSED_UNIT, &unwrap::PANICKING_UNWRAP, &unwrap::UNNECESSARY_UNWRAP, + &unwrap_in_result::UNWRAP_IN_RESULT, &use_self::USE_SELF, &useless_conversion::USELESS_CONVERSION, &utils::internal_lints::CLIPPY_LINTS_INTERNAL, @@ -1094,6 +1096,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch); store.register_late_pass(|| box stable_sort_primitive::StableSortPrimitive); store.register_late_pass(|| box repeat_once::RepeatOnce); + store.register_late_pass(|| box unwrap_in_result::UnwrapInResult); store.register_late_pass(|| box self_assignment::SelfAssignment); store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); @@ -1133,6 +1136,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&shadow::SHADOW_REUSE), LintId::of(&shadow::SHADOW_SAME), LintId::of(&strings::STRING_ADD), + LintId::of(&unwrap_in_result::UNWRAP_IN_RESULT), LintId::of(&verbose_file_reads::VERBOSE_FILE_READS), LintId::of(&write::PRINT_STDOUT), LintId::of(&write::USE_DEBUG), diff --git a/clippy_lints/src/unwrap_in_result.rs b/clippy_lints/src/unwrap_in_result.rs new file mode 100644 index 00000000000..1c7e62ecd3d --- /dev/null +++ b/clippy_lints/src/unwrap_in_result.rs @@ -0,0 +1,140 @@ +use crate::utils::{is_type_diagnostic_item, method_chain_args, return_ty, span_lint_and_then, walk_ptrs_ty}; +use if_chain::if_chain; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +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 functions of type Result that contain `expect()` or `unwrap()` + /// + /// **Why is this bad?** These functions promote recoverable errors to non-recoverable errors which may be undesirable in code bases which wish to avoid panics. + /// + /// **Known problems:** This can cause false positives in functions that handle both recoverable and non recoverable errors. + /// + /// **Example:** + /// Before: + /// ```rust + /// fn divisible_by_3(i_str: String) -> Result<(), String> { + /// let i = i_str + /// .parse::() + /// .expect("cannot divide the input by three"); + /// + /// if i % 3 != 0 { + /// Err("Number is not divisible by 3")? + /// } + /// + /// Ok(()) + /// } + /// ``` + /// + /// After: + /// ```rust + /// fn divisible_by_3(i_str: String) -> Result<(), String> { + /// let i = i_str + /// .parse::() + /// .map_err(|e| format!("cannot divide the input by three: {}", e))?; + /// + /// if i % 3 != 0 { + /// Err("Number is not divisible by 3")? + /// } + /// + /// Ok(()) + /// } + /// ``` + pub UNWRAP_IN_RESULT, + restriction, + "functions of type `Result<..>` or `Option`<...> that contain `expect()` or `unwrap()`" +} + +declare_lint_pass!(UnwrapInResult=> [UNWRAP_IN_RESULT]); + +impl<'tcx> LateLintPass<'tcx> for UnwrapInResult { + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { + if_chain! { + // first check if it's a method or function + if let hir::ImplItemKind::Fn(ref _signature, _) = impl_item.kind; + // checking if its return type is `result` or `option` + if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(result_type)) + || is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(option_type)); + then { + lint_impl_body(cx, impl_item.span, impl_item); + } + } + } +} + +use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::{Expr, ImplItemKind}; + +struct FindExpectUnwrap<'a, 'tcx> { + lcx: &'a LateContext<'tcx>, + typeck_results: &'tcx ty::TypeckResults<'tcx>, + result: Vec, +} + +impl<'a, 'tcx> Visitor<'tcx> for FindExpectUnwrap<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + // check for `expect` + if let Some(arglists) = method_chain_args(expr, &["expect"]) { + let reciever_ty = walk_ptrs_ty(self.typeck_results.expr_ty(&arglists[0][0])); + if is_type_diagnostic_item(self.lcx, reciever_ty, sym!(option_type)) + || is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type)) + { + self.result.push(expr.span); + } + } + + // check for `unwrap` + if let Some(arglists) = method_chain_args(expr, &["unwrap"]) { + let reciever_ty = walk_ptrs_ty(self.typeck_results.expr_ty(&arglists[0][0])); + if is_type_diagnostic_item(self.lcx, reciever_ty, sym!(option_type)) + || is_type_diagnostic_item(self.lcx, reciever_ty, sym!(result_type)) + { + self.result.push(expr.span); + } + } + + // and check sub-expressions + intravisit::walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tcx hir::ImplItem<'_>) { + if_chain! { + + if let ImplItemKind::Fn(_, body_id) = impl_item.kind; + then { + let body = cx.tcx.hir().body(body_id); + let impl_item_def_id = cx.tcx.hir().local_def_id(impl_item.hir_id); + let mut fpu = FindExpectUnwrap { + lcx: cx, + typeck_results: cx.tcx.typeck(impl_item_def_id), + result: Vec::new(), + }; + fpu.visit_expr(&body.value); + + // if we've found one, lint + if !fpu.result.is_empty() { + span_lint_and_then( + cx, + UNWRAP_IN_RESULT, + impl_span, + "used unwrap or expect in a function that returns result or option", + move |diag| { + diag.help( + "unwrap and expect should not be used in a function that returns result or option" ); + diag.span_note(fpu.result, "potential non-recoverable error(s)"); + }); + } + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index eadd2621a40..687fac7baa8 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2516,6 +2516,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "unused_unit", }, + Lint { + name: "unwrap_in_result", + group: "restriction", + desc: "functions of type `Result<..>` or `Option`<...> that contain `expect()` or `unwrap()`", + deprecation: None, + module: "unwrap_in_result", + }, Lint { name: "unwrap_used", group: "restriction", diff --git a/tests/ui/unwrap_in_result.rs b/tests/ui/unwrap_in_result.rs new file mode 100644 index 00000000000..2aa842adc85 --- /dev/null +++ b/tests/ui/unwrap_in_result.rs @@ -0,0 +1,44 @@ +#![warn(clippy::unwrap_in_result)] + +struct A; + +impl A { + // should not be detected + fn good_divisible_by_3(i_str: String) -> Result { + // checks whether a string represents a number divisible by 3 + let i_result = i_str.parse::(); + match i_result { + Err(_e) => Err("Not a number".to_string()), + Ok(i) => { + if i % 3 == 0 { + return Ok(true); + } + Err("Number is not divisible by 3".to_string()) + }, + } + } + + // should be detected + fn bad_divisible_by_3(i_str: String) -> Result { + // checks whether a string represents a number divisible by 3 + let i = i_str.parse::().unwrap(); + if i % 3 == 0 { + Ok(true) + } else { + Err("Number is not divisible by 3".to_string()) + } + } + + fn example_option_expect(i_str: String) -> Option { + let i = i_str.parse::().expect("not a number"); + if i % 3 == 0 { + return Some(true); + } + None + } +} + +fn main() { + A::bad_divisible_by_3("3".to_string()); + A::good_divisible_by_3("3".to_string()); +} diff --git a/tests/ui/unwrap_in_result.stderr b/tests/ui/unwrap_in_result.stderr new file mode 100644 index 00000000000..56bc2f2d1c0 --- /dev/null +++ b/tests/ui/unwrap_in_result.stderr @@ -0,0 +1,41 @@ +error: used unwrap or expect in a function that returns result or option + --> $DIR/unwrap_in_result.rs:22:5 + | +LL | / fn bad_divisible_by_3(i_str: String) -> Result { +LL | | // checks whether a string represents a number divisible by 3 +LL | | let i = i_str.parse::().unwrap(); +LL | | if i % 3 == 0 { +... | +LL | | } +LL | | } + | |_____^ + | + = note: `-D clippy::unwrap-in-result` implied by `-D warnings` + = help: unwrap and expect should not be used in a function that returns result or option +note: potential non-recoverable error(s) + --> $DIR/unwrap_in_result.rs:24:17 + | +LL | let i = i_str.parse::().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: used unwrap or expect in a function that returns result or option + --> $DIR/unwrap_in_result.rs:32:5 + | +LL | / fn example_option_expect(i_str: String) -> Option { +LL | | let i = i_str.parse::().expect("not a number"); +LL | | if i % 3 == 0 { +LL | | return Some(true); +LL | | } +LL | | None +LL | | } + | |_____^ + | + = help: unwrap and expect should not be used in a function that returns result or option +note: potential non-recoverable error(s) + --> $DIR/unwrap_in_result.rs:33:17 + | +LL | let i = i_str.parse::().expect("not a number"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From 04bff17668be1305d9efe235665a32727ff3e0b5 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Thu, 27 Aug 2020 23:37:47 +0900 Subject: Fix FP in `to_string_in_display` Don't emit a lint when `.to_string()` on anything that is not `self` --- clippy_lints/src/to_string_in_display.rs | 32 +++++++++++++++++++++++++++----- tests/ui/to_string_in_display.rs | 14 ++++++++++++++ 2 files changed, 41 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/to_string_in_display.rs b/clippy_lints/src/to_string_in_display.rs index 4b6a0a6a0c9..006d7a3a12d 100644 --- a/clippy_lints/src/to_string_in_display.rs +++ b/clippy_lints/src/to_string_in_display.rs @@ -1,6 +1,7 @@ -use crate::utils::{match_def_path, match_trait_method, paths, span_lint}; +use crate::utils::{match_def_path, match_trait_method, paths, qpath_res, span_lint}; use if_chain::if_chain; -use rustc_hir::{Expr, ExprKind, Item, ItemKind}; +use rustc_hir::def::Res; +use rustc_hir::{Expr, ExprKind, HirId, ImplItem, ImplItemKind, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -45,11 +46,15 @@ declare_clippy_lint! { #[derive(Default)] pub struct ToStringInDisplay { in_display_impl: bool, + self_hir_id: Option, } impl ToStringInDisplay { pub fn new() -> Self { - Self { in_display_impl: false } + Self { + in_display_impl: false, + self_hir_id: None, + } } } @@ -65,16 +70,33 @@ impl LateLintPass<'_> for ToStringInDisplay { fn check_item_post(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { if is_display_impl(cx, item) { self.in_display_impl = false; + self.self_hir_id = None; + } + } + + fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &ImplItem<'_>) { + if_chain! { + if self.in_display_impl; + if let ImplItemKind::Fn(.., body_id) = &impl_item.kind; + let body = cx.tcx.hir().body(*body_id); + if !body.params.is_empty(); + then { + let self_param = &body.params[0]; + self.self_hir_id = Some(self_param.pat.hir_id); + } } } fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { - if let ExprKind::MethodCall(ref path, _, _, _) = expr.kind; + if let ExprKind::MethodCall(ref path, _, args, _) = expr.kind; if path.ident.name == sym!(to_string); if match_trait_method(cx, expr, &paths::TO_STRING); if self.in_display_impl; - + if let ExprKind::Path(ref qpath) = args[0].kind; + if let Res::Local(hir_id) = qpath_res(cx, qpath, args[0].hir_id); + if let Some(self_hir_id) = self.self_hir_id; + if hir_id == self_hir_id; then { span_lint( cx, diff --git a/tests/ui/to_string_in_display.rs b/tests/ui/to_string_in_display.rs index 3b46324704e..eb8105c6b6d 100644 --- a/tests/ui/to_string_in_display.rs +++ b/tests/ui/to_string_in_display.rs @@ -44,6 +44,20 @@ impl fmt::Display for C { } } +enum D { + E(String), + F, +} + +impl std::fmt::Display for D { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self { + Self::E(string) => write!(f, "E {}", string.to_string()), + Self::F => write!(f, "F"), + } + } +} + fn main() { let a = A; a.to_string(); -- cgit 1.4.1-3-g733a5 From 2a3ee5fa854b49530008582900c6ea7fac120d1c Mon Sep 17 00:00:00 2001 From: ThibsG Date: Tue, 7 Jul 2020 22:47:32 +0200 Subject: Fix FP in `new_ret_no_self`: trigger in trait def instead of impl block --- clippy_lints/src/methods/mod.rs | 58 ++++++++++++++++-- tests/ui/new_ret_no_self.rs | 130 ++++++++++++++++++++++++++++++++++++++++ tests/ui/new_ret_no_self.stderr | 30 +++++++++- 3 files changed, 212 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 1ef54d285f6..8e91cbb3cdf 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -15,6 +15,7 @@ use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::{FnRetTy, FnSig, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass, Lint, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; @@ -28,11 +29,11 @@ use crate::consts::{constant, Constant}; use crate::utils::usage::mutated_variables; use crate::utils::{ get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro, is_copy, - is_ctor_or_promotable_const_function, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, - match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls, method_chain_args, paths, - remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, - span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty, - walk_ptrs_ty_depth, SpanlessEq, + is_ctor_or_promotable_const_function, is_expn_of, is_self_ty, is_type_diagnostic_item, iter_input_pats, + last_path_segment, match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls, + method_chain_args, paths, remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability, + snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, + span_lint_and_then, sugg, walk_ptrs_ty, walk_ptrs_ty_depth, SpanlessEq, }; declare_clippy_lint! { @@ -1631,6 +1632,11 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } } + // if this impl block implements a trait, lint in trait definition instead + if let hir::ItemKind::Impl { of_trait: Some(_), .. } = item.kind { + return; + } + if let hir::ImplItemKind::Fn(_, _) = impl_item.kind { let ret_ty = return_ty(cx, impl_item.hir_id); @@ -1670,6 +1676,48 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } } } + + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { + if_chain! { + if !in_external_macro(cx.tcx.sess, item.span); + if !item.span.from_expansion(); + if item.ident.name == sym!(new); + if let TraitItemKind::Fn(FnSig { decl, .. }, _) = &item.kind; + if let FnRetTy::Return(ret_ty) = &decl.output; + + then { + let mut visitor = HasSelfVisitor { has_self_ty: false }; + visitor.visit_ty(ret_ty); + if !visitor.has_self_ty { + span_lint( + cx, + NEW_RET_NO_SELF, + item.span, + "methods called `new` usually return `Self`", + ); + } + } + } + } +} + +struct HasSelfVisitor { + pub has_self_ty: bool, +} + +impl<'tcx> intravisit::Visitor<'tcx> for HasSelfVisitor { + type Map = Map<'tcx>; + + fn visit_ty(&mut self, ty: &'tcx hir::Ty<'_>) { + if is_self_ty(ty) { + self.has_self_ty = true; + } else { + intravisit::walk_ty(self, ty); + } + } + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::None + } } /// Checks for the `OR_FUN_CALL` lint. diff --git a/tests/ui/new_ret_no_self.rs b/tests/ui/new_ret_no_self.rs index 2c2d1e27589..e98360ea691 100644 --- a/tests/ui/new_ret_no_self.rs +++ b/tests/ui/new_ret_no_self.rs @@ -210,3 +210,133 @@ impl<'a> WithLifetime<'a> { unimplemented!(); } } + +mod issue5435 { + struct V; + + pub trait TraitRetSelf { + // should not trigger lint + fn new() -> Self; + } + + pub trait TraitRet { + // should trigger lint as we are in trait definition + fn new() -> String; + } + pub struct StructRet; + impl TraitRet for StructRet { + // should not trigger lint as we are in the impl block + fn new() -> String { + unimplemented!(); + } + } + + pub trait TraitRet2 { + // should trigger lint + fn new(_: String) -> String; + } + + trait TupleReturnerOk { + // should not trigger lint + fn new() -> (Self, u32) + where + Self: Sized, + { + unimplemented!(); + } + } + + trait TupleReturnerOk2 { + // should not trigger lint (it doesn't matter which element in the tuple is Self) + fn new() -> (u32, Self) + where + Self: Sized, + { + unimplemented!(); + } + } + + trait TupleReturnerOk3 { + // should not trigger lint (tuple can contain multiple Self) + fn new() -> (Self, Self) + where + Self: Sized, + { + unimplemented!(); + } + } + + trait TupleReturnerBad { + // should trigger lint + fn new() -> (u32, u32) { + unimplemented!(); + } + } + + trait MutPointerReturnerOk { + // should not trigger lint + fn new() -> *mut Self + where + Self: Sized, + { + unimplemented!(); + } + } + + trait MutPointerReturnerOk2 { + // should not trigger lint + fn new() -> *const Self + where + Self: Sized, + { + unimplemented!(); + } + } + + trait MutPointerReturnerBad { + // should trigger lint + fn new() -> *mut V { + unimplemented!(); + } + } + + trait GenericReturnerOk { + // should not trigger lint + fn new() -> Option + where + Self: Sized, + { + unimplemented!(); + } + } + + trait NestedReturnerOk { + // should not trigger lint + fn new() -> (Option, u32) + where + Self: Sized, + { + unimplemented!(); + } + } + + trait NestedReturnerOk2 { + // should not trigger lint + fn new() -> ((Self, u32), u32) + where + Self: Sized, + { + unimplemented!(); + } + } + + trait NestedReturnerOk3 { + // should not trigger lint + fn new() -> Option<(Self, u32)> + where + Self: Sized, + { + unimplemented!(); + } + } +} diff --git a/tests/ui/new_ret_no_self.stderr b/tests/ui/new_ret_no_self.stderr index dd5a24bcbe7..8217bc6187f 100644 --- a/tests/ui/new_ret_no_self.stderr +++ b/tests/ui/new_ret_no_self.stderr @@ -48,5 +48,33 @@ LL | | unimplemented!(); LL | | } | |_____^ -error: aborting due to 6 previous errors +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:224:9 + | +LL | fn new() -> String; + | ^^^^^^^^^^^^^^^^^^^ + +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:236:9 + | +LL | fn new(_: String) -> String; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:271:9 + | +LL | / fn new() -> (u32, u32) { +LL | | unimplemented!(); +LL | | } + | |_________^ + +error: methods called `new` usually return `Self` + --> $DIR/new_ret_no_self.rs:298:9 + | +LL | / fn new() -> *mut V { +LL | | unimplemented!(); +LL | | } + | |_________^ + +error: aborting due to 10 previous errors -- cgit 1.4.1-3-g733a5 From 3cb75c2e5cdd4f450f2974c5e052d569674d95fd Mon Sep 17 00:00:00 2001 From: ThibsG Date: Tue, 25 Aug 2020 09:16:08 +0200 Subject: Remove expansion restriction + fix doc and tests naming --- clippy_lints/src/methods/mod.rs | 34 ++++++++++++++++++++++++---------- tests/ui/new_ret_no_self.rs | 6 +++--- 2 files changed, 27 insertions(+), 13 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 8e91cbb3cdf..2388310628f 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -725,6 +725,7 @@ declare_clippy_lint! { /// **Known problems:** None. /// /// **Example:** + /// In an impl block: /// ```rust /// # struct Foo; /// # struct NotAFoo; @@ -737,25 +738,40 @@ declare_clippy_lint! { /// /// ```rust /// # struct Foo; - /// # struct FooError; + /// struct Bar(Foo); /// impl Foo { - /// // Good. Return type contains `Self` - /// fn new() -> Result { - /// # Ok(Foo) + /// // Bad. The type name must contain `Self` + /// fn new() -> Bar { + /// # Bar(Foo) /// } /// } /// ``` /// /// ```rust /// # struct Foo; - /// struct Bar(Foo); + /// # struct FooError; /// impl Foo { - /// // Bad. The type name must contain `Self`. - /// fn new() -> Bar { - /// # Bar(Foo) + /// // Good. Return type contains `Self` + /// fn new() -> Result { + /// # Ok(Foo) /// } /// } /// ``` + /// + /// Or in a trait definition: + /// ```rust + /// pub trait Trait { + /// // Bad. The type name must contain `Self` + /// fn new(); + /// } + /// ``` + /// + /// ```rust + /// pub trait Trait { + /// // Good. Return type contains `Self` + /// fn new() -> Self; + /// } + /// ``` pub NEW_RET_NO_SELF, style, "not returning type containing `Self` in a `new` method" @@ -1679,8 +1695,6 @@ impl<'tcx> LateLintPass<'tcx> for Methods { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { if_chain! { - if !in_external_macro(cx.tcx.sess, item.span); - if !item.span.from_expansion(); if item.ident.name == sym!(new); if let TraitItemKind::Fn(FnSig { decl, .. }, _) = &item.kind; if let FnRetTy::Return(ret_ty) = &decl.output; diff --git a/tests/ui/new_ret_no_self.rs b/tests/ui/new_ret_no_self.rs index e98360ea691..e82873629a5 100644 --- a/tests/ui/new_ret_no_self.rs +++ b/tests/ui/new_ret_no_self.rs @@ -137,9 +137,9 @@ impl MutPointerReturnerOk { } } -struct MutPointerReturnerOk2; +struct ConstPointerReturnerOk2; -impl MutPointerReturnerOk2 { +impl ConstPointerReturnerOk2 { // should not trigger lint pub fn new() -> *const Self { unimplemented!(); @@ -283,7 +283,7 @@ mod issue5435 { } } - trait MutPointerReturnerOk2 { + trait ConstPointerReturnerOk2 { // should not trigger lint fn new() -> *const Self where -- cgit 1.4.1-3-g733a5 From f3ccbef2af24d5d83f82f1fb50bd97a9b75e609f Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Thu, 27 Aug 2020 01:40:02 +0200 Subject: unit-arg - pr comments --- clippy_lints/src/types.rs | 41 +++++++++++++----- clippy_lints/src/utils/mod.rs | 2 +- tests/ui/unit_arg.rs | 8 +++- tests/ui/unit_arg.stderr | 79 +++++++++++++++++++---------------- tests/ui/unit_arg_empty_blocks.stderr | 11 +++-- 5 files changed, 88 insertions(+), 53 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 3f5b3a5bcd5..16e48d91916 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -29,10 +29,10 @@ use rustc_typeck::hir_ty_to_ty; use crate::consts::{constant, Constant}; use crate::utils::paths; use crate::utils::{ - clip, comparisons, differing_macro_contexts, higher, in_constant, int_bits, is_type_diagnostic_item, + clip, comparisons, differing_macro_contexts, higher, in_constant, indent_of, int_bits, is_type_diagnostic_item, last_path_segment, match_def_path, match_path, method_chain_args, multispan_sugg, numeric_literal::NumericLiteral, qpath_res, sext, snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite, span_lint, - span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext, + span_lint_and_help, span_lint_and_sugg, span_lint_and_then, trim_multiline, unsext, }; declare_clippy_lint! { @@ -802,6 +802,7 @@ impl<'tcx> LateLintPass<'tcx> for UnitArg { } } +#[allow(clippy::too_many_lines)] fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Expr<'_>]) { let mut applicability = Applicability::MachineApplicable; let (singular, plural) = if args_to_recover.len() > 1 { @@ -856,18 +857,38 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp .filter(|arg| !is_empty_block(arg)) .filter_map(|arg| snippet_opt(cx, arg.span)) .collect(); + let indent = indent_of(cx, expr.span).unwrap_or(0); - if let Some(mut sugg) = snippet_opt(cx, expr.span) { - arg_snippets.iter().for_each(|arg| { - sugg = sugg.replacen(arg, "()", 1); - }); - sugg = format!("{}{}{}", arg_snippets_without_empty_blocks.join("; "), "; ", sugg); + if let Some(expr_str) = snippet_opt(cx, expr.span) { + let expr_with_replacements = arg_snippets + .iter() + .fold(expr_str, |acc, arg| acc.replacen(arg, "()", 1)); + + // expr is not in a block statement or result expression position, wrap in a block let parent_node = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(expr.hir_id)); - if !matches!(parent_node, Some(Node::Block(_))) && !matches!(parent_node, Some(Node::Stmt(_))) { - // expr is not in a block statement or result expression position, wrap in a block - sugg = format!("{{ {} }}", sugg); + let wrap_in_block = + !matches!(parent_node, Some(Node::Block(_))) && !matches!(parent_node, Some(Node::Stmt(_))); + + let stmts_indent = if wrap_in_block { indent + 4 } else { indent }; + let mut stmts_and_call = arg_snippets_without_empty_blocks.clone(); + stmts_and_call.push(expr_with_replacements); + let mut stmts_and_call_str = stmts_and_call + .into_iter() + .enumerate() + .map(|(i, v)| { + let with_indent_prefix = if i > 0 { " ".repeat(stmts_indent) + &v } else { v }; + trim_multiline(with_indent_prefix.into(), true, Some(stmts_indent)).into_owned() + }) + .collect::>() + .join(";\n"); + + if wrap_in_block { + stmts_and_call_str = " ".repeat(stmts_indent) + &stmts_and_call_str; + stmts_and_call_str = format!("{{\n{}\n{}}}", &stmts_and_call_str, " ".repeat(indent)); } + let sugg = stmts_and_call_str; + if arg_snippets_without_empty_blocks.is_empty() { db.multipart_suggestion( &format!("use {}unit literal{} instead", singular, plural), diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 2aef995cec4..d20b33c4a1d 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -662,7 +662,7 @@ pub fn expr_block<'a, T: LintContext>( /// Trim indentation from a multiline string with possibility of ignoring the /// first line. -fn trim_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option) -> Cow<'_, str> { +pub fn trim_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option) -> Cow<'_, str> { let s_space = trim_multiline_inner(s, ignore_first, indent, ' '); let s_tab = trim_multiline_inner(s_space, ignore_first, indent, '\t'); trim_multiline_inner(s_tab, ignore_first, indent, ' ') diff --git a/tests/ui/unit_arg.rs b/tests/ui/unit_arg.rs index 2e2bd054e42..fec115ff29d 100644 --- a/tests/ui/unit_arg.rs +++ b/tests/ui/unit_arg.rs @@ -1,5 +1,11 @@ #![warn(clippy::unit_arg)] -#![allow(clippy::no_effect, unused_must_use, unused_variables, clippy::unused_unit)] +#![allow( + clippy::no_effect, + unused_must_use, + unused_variables, + clippy::unused_unit, + clippy::or_fun_call +)] use std::fmt::Debug; diff --git a/tests/ui/unit_arg.stderr b/tests/ui/unit_arg.stderr index 2a0cc1f18e2..90fee3aab23 100644 --- a/tests/ui/unit_arg.stderr +++ b/tests/ui/unit_arg.stderr @@ -1,5 +1,5 @@ error: passing a unit value to a function - --> $DIR/unit_arg.rs:23:5 + --> $DIR/unit_arg.rs:29:5 | LL | / foo({ LL | | 1; @@ -15,22 +15,24 @@ help: or move the expression in front of the call and replace it with the unit l | LL | { LL | 1; -LL | }; foo(()); +LL | }; +LL | foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:26:5 + --> $DIR/unit_arg.rs:32:5 | LL | foo(foo(1)); | ^^^^^^^^^^^ | help: move the expression in front of the call and replace it with the unit literal `()` | -LL | foo(1); foo(()); - | ^^^^^^^^^^^^^^^ +LL | foo(1); +LL | foo(()); + | error: passing a unit value to a function - --> $DIR/unit_arg.rs:27:5 + --> $DIR/unit_arg.rs:33:5 | LL | / foo({ LL | | foo(1); @@ -47,11 +49,12 @@ help: or move the expression in front of the call and replace it with the unit l LL | { LL | foo(1); LL | foo(2); -LL | }; foo(()); +LL | }; +LL | foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:32:5 + --> $DIR/unit_arg.rs:38:5 | LL | / b.bar({ LL | | 1; @@ -66,22 +69,25 @@ help: or move the expression in front of the call and replace it with the unit l | LL | { LL | 1; -LL | }; b.bar(()); +LL | }; +LL | b.bar(()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:35:5 + --> $DIR/unit_arg.rs:41:5 | LL | taking_multiple_units(foo(0), foo(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: move the expressions in front of the call and replace them with the unit literal `()` | -LL | foo(0); foo(1); taking_multiple_units((), ()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | foo(0); +LL | foo(1); +LL | taking_multiple_units((), ()); + | error: passing unit values to a function - --> $DIR/unit_arg.rs:36:5 + --> $DIR/unit_arg.rs:42:5 | LL | / taking_multiple_units(foo(0), { LL | | foo(1); @@ -95,14 +101,16 @@ LL | foo(2) | help: or move the expressions in front of the call and replace them with the unit literal `()` | -LL | foo(0); { +LL | foo(0); +LL | { LL | foo(1); LL | foo(2); -LL | }; taking_multiple_units((), ()); +LL | }; +LL | taking_multiple_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:40:5 + --> $DIR/unit_arg.rs:46:5 | LL | / taking_multiple_units( LL | | { @@ -124,53 +132,50 @@ LL | foo(3) help: or move the expressions in front of the call and replace them with the unit literal `()` | LL | { -LL | foo(0); -LL | foo(1); -LL | }; { -LL | foo(2); -LL | foo(3); +LL | foo(0); +LL | foo(1); +LL | }; +LL | { +LL | foo(2); ... -error: use of `or` followed by a function call - --> $DIR/unit_arg.rs:51:10 - | -LL | None.or(Some(foo(2))); - | ^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some(foo(2)))` - | - = note: `-D clippy::or-fun-call` implied by `-D warnings` - error: passing a unit value to a function - --> $DIR/unit_arg.rs:51:13 + --> $DIR/unit_arg.rs:57:13 | LL | None.or(Some(foo(2))); | ^^^^^^^^^^^^ | help: move the expression in front of the call and replace it with the unit literal `()` | -LL | None.or({ foo(2); Some(()) }); - | ^^^^^^^^^^^^^^^^^^^^ +LL | None.or({ +LL | foo(2); +LL | Some(()) +LL | }); + | error: passing a unit value to a function - --> $DIR/unit_arg.rs:54:5 + --> $DIR/unit_arg.rs:60:5 | LL | foo(foo(())) | ^^^^^^^^^^^^ | help: move the expression in front of the call and replace it with the unit literal `()` | -LL | foo(()); foo(()) +LL | foo(()); +LL | foo(()) | error: passing a unit value to a function - --> $DIR/unit_arg.rs:87:5 + --> $DIR/unit_arg.rs:93:5 | LL | Some(foo(1)) | ^^^^^^^^^^^^ | help: move the expression in front of the call and replace it with the unit literal `()` | -LL | foo(1); Some(()) +LL | foo(1); +LL | Some(()) | -error: aborting due to 11 previous errors +error: aborting due to 10 previous errors diff --git a/tests/ui/unit_arg_empty_blocks.stderr b/tests/ui/unit_arg_empty_blocks.stderr index 4cbbc8b8cd4..456b12a2c6b 100644 --- a/tests/ui/unit_arg_empty_blocks.stderr +++ b/tests/ui/unit_arg_empty_blocks.stderr @@ -24,8 +24,9 @@ LL | taking_two_units({}, foo(0)); | help: move the expression in front of the call and replace it with the unit literal `()` | -LL | foo(0); taking_two_units((), ()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | foo(0); +LL | taking_two_units((), ()); + | error: passing unit values to a function --> $DIR/unit_arg_empty_blocks.rs:18:5 @@ -35,8 +36,10 @@ LL | taking_three_units({}, foo(0), foo(1)); | help: move the expressions in front of the call and replace them with the unit literal `()` | -LL | foo(0); foo(1); taking_three_units((), (), ()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | foo(0); +LL | foo(1); +LL | taking_three_units((), (), ()); + | error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From 459969f88ff95c94b7b34043a7f0e13de91de4f8 Mon Sep 17 00:00:00 2001 From: Vali Schneider Date: Thu, 27 Aug 2020 16:18:05 -0700 Subject: added restriction lint that prohibits the usage of unimplemented, unreachable or panic in a function of type result or option --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 ++ clippy_lints/src/panic_in_result.rs | 100 ++++++++++++++++++++++++++++++++++ src/lintlist/mod.rs | 7 +++ tests/ui/panic_in_result.rs | 62 +++++++++++++++++++++ tests/ui/panic_in_result.stderr | 105 ++++++++++++++++++++++++++++++++++++ 6 files changed, 280 insertions(+) create mode 100644 clippy_lints/src/panic_in_result.rs create mode 100644 tests/ui/panic_in_result.rs create mode 100644 tests/ui/panic_in_result.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 137b561028a..7af3b666cca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1651,6 +1651,7 @@ Released 2018-09-13 [`out_of_bounds_indexing`]: https://rust-lang.github.io/rust-clippy/master/index.html#out_of_bounds_indexing [`overflow_check_conditional`]: https://rust-lang.github.io/rust-clippy/master/index.html#overflow_check_conditional [`panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic +[`panic_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_in_result [`panic_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_params [`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap [`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 577ce6523b4..b70d126af5b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -267,6 +267,7 @@ mod open_options; mod option_env_unwrap; mod option_if_let_else; mod overflow_check_conditional; +mod panic_in_result; mod panic_unimplemented; mod partialeq_ne_impl; mod path_buf_push_overwrite; @@ -747,6 +748,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &option_env_unwrap::OPTION_ENV_UNWRAP, &option_if_let_else::OPTION_IF_LET_ELSE, &overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL, + &panic_in_result::PANIC_IN_RESULT, &panic_unimplemented::PANIC, &panic_unimplemented::PANIC_PARAMS, &panic_unimplemented::TODO, @@ -1086,6 +1088,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box manual_async_fn::ManualAsyncFn); store.register_early_pass(|| box redundant_field_names::RedundantFieldNames); store.register_late_pass(|| box vec_resize_to_zero::VecResizeToZero); + store.register_late_pass(|| box panic_in_result::PanicInResult); + let single_char_binding_names_threshold = conf.single_char_binding_names_threshold; store.register_early_pass(move || box non_expressive_names::NonExpressiveNames { single_char_binding_names_threshold, @@ -1128,6 +1132,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS), LintId::of(&missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS), LintId::of(&modulo_arithmetic::MODULO_ARITHMETIC), + LintId::of(&panic_in_result::PANIC_IN_RESULT), LintId::of(&panic_unimplemented::PANIC), LintId::of(&panic_unimplemented::TODO), LintId::of(&panic_unimplemented::UNIMPLEMENTED), diff --git a/clippy_lints/src/panic_in_result.rs b/clippy_lints/src/panic_in_result.rs new file mode 100644 index 00000000000..3a71a0db6fe --- /dev/null +++ b/clippy_lints/src/panic_in_result.rs @@ -0,0 +1,100 @@ +use crate::utils::{is_expn_of, is_type_diagnostic_item, return_ty, span_lint_and_then}; +use if_chain::if_chain; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `panic!`, `unimplemented!` or `unreachable!` in a function of type result/option. + /// + /// **Why is this bad?** For some codebases, + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// fn option_with_panic() -> Option // should emit lint + /// { + /// panic!("error"); + /// } + /// ``` + + pub PANIC_IN_RESULT, + restriction, + "functions of type `Result<..>` / `Option`<...> that contain `panic!()` or `unreachable()` or `unimplemented()` " +} + +declare_lint_pass!(PanicInResult => [PANIC_IN_RESULT]); + +impl<'tcx> LateLintPass<'tcx> for PanicInResult { + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { + if_chain! { + // first check if it's a method or function + if let hir::ImplItemKind::Fn(ref _signature, _) = impl_item.kind; + // checking if its return type is `result` or `option` + if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(result_type)) + || is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(option_type)); + then { + lint_impl_body(cx, impl_item.span, impl_item); + } + } + } +} + +use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::{Expr, ImplItemKind}; + +struct FindPanicUnimplementedUnreachable { + result: Vec, +} + +impl<'tcx> Visitor<'tcx> for FindPanicUnimplementedUnreachable { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if is_expn_of(expr.span, "unimplemented").is_some() { + self.result.push(expr.span); + } else if is_expn_of(expr.span, "unreachable").is_some() { + self.result.push(expr.span); + } else if is_expn_of(expr.span, "panic").is_some() { + self.result.push(expr.span); + } + + // and check sub-expressions + intravisit::walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tcx hir::ImplItem<'_>) { + if_chain! { + if let ImplItemKind::Fn(_, body_id) = impl_item.kind; + then { + let body = cx.tcx.hir().body(body_id); + let mut fpu = FindPanicUnimplementedUnreachable { + result: Vec::new(), + }; + fpu.visit_expr(&body.value); + + // if we've found one, lint + if !fpu.result.is_empty() { + span_lint_and_then( + cx, + PANIC_IN_RESULT, + impl_span, + "used unimplemented, unreachable or panic in a function that returns result or option", + move |diag| { + diag.help( + "unimplemented, unreachable or panic should not be used in a function that returns result or option" ); + diag.span_note(fpu.result, "will cause the application to crash."); + }); + } + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 687fac7baa8..ad57146048e 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1704,6 +1704,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "panic_unimplemented", }, + Lint { + name: "panic_in_result", + group: "restriction", + desc: "default lint description", + deprecation: None, + module: "panic_in_result", + }, Lint { name: "panic_params", group: "style", diff --git a/tests/ui/panic_in_result.rs b/tests/ui/panic_in_result.rs new file mode 100644 index 00000000000..21e9efca87b --- /dev/null +++ b/tests/ui/panic_in_result.rs @@ -0,0 +1,62 @@ +#![warn(clippy::panic_in_result)] + +struct A; + +impl A { + fn result_with_panic() -> Result // should emit lint + { + panic!("error"); + } + + fn result_with_unimplemented() -> Result // should emit lint + { + unimplemented!(); + } + + fn result_with_unreachable() -> Result // should emit lint + { + unreachable!(); + } + + fn option_with_unreachable() -> Option // should emit lint + { + unreachable!(); + } + + fn option_with_unimplemented() -> Option // should emit lint + { + unimplemented!(); + } + + fn option_with_panic() -> Option // should emit lint + { + panic!("error"); + } + + fn other_with_panic() // should not emit lint + { + panic!(""); + } + + fn other_with_unreachable() // should not emit lint + { + unreachable!(); + } + + fn other_with_unimplemented() // should not emit lint + { + unimplemented!(); + } + + fn result_without_banned_functions() -> Result // should not emit lint + { + Ok(true) + } + + fn option_without_banned_functions() -> Option // should not emit lint + { + Some(true) + } +} + +fn main() {} diff --git a/tests/ui/panic_in_result.stderr b/tests/ui/panic_in_result.stderr new file mode 100644 index 00000000000..74273bd9abb --- /dev/null +++ b/tests/ui/panic_in_result.stderr @@ -0,0 +1,105 @@ +error: used unimplemented, unreachable or panic in a function that returns result or option + --> $DIR/panic_in_result.rs:6:5 + | +LL | / fn result_with_panic() -> Result // should emit lint +LL | | { +LL | | panic!("error"); +LL | | } + | |_____^ + | + = note: `-D clippy::panic-in-result` implied by `-D warnings` + = help: unimplemented, unreachable or panic should not be used in a function that returns result or option +note: will cause the application to crash. + --> $DIR/panic_in_result.rs:8:9 + | +LL | panic!("error"); + | ^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: used unimplemented, unreachable or panic in a function that returns result or option + --> $DIR/panic_in_result.rs:11:5 + | +LL | / fn result_with_unimplemented() -> Result // should emit lint +LL | | { +LL | | unimplemented!(); +LL | | } + | |_____^ + | + = help: unimplemented, unreachable or panic should not be used in a function that returns result or option +note: will cause the application to crash. + --> $DIR/panic_in_result.rs:13:9 + | +LL | unimplemented!(); + | ^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: used unimplemented, unreachable or panic in a function that returns result or option + --> $DIR/panic_in_result.rs:16:5 + | +LL | / fn result_with_unreachable() -> Result // should emit lint +LL | | { +LL | | unreachable!(); +LL | | } + | |_____^ + | + = help: unimplemented, unreachable or panic should not be used in a function that returns result or option +note: will cause the application to crash. + --> $DIR/panic_in_result.rs:18:9 + | +LL | unreachable!(); + | ^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: used unimplemented, unreachable or panic in a function that returns result or option + --> $DIR/panic_in_result.rs:21:5 + | +LL | / fn option_with_unreachable() -> Option // should emit lint +LL | | { +LL | | unreachable!(); +LL | | } + | |_____^ + | + = help: unimplemented, unreachable or panic should not be used in a function that returns result or option +note: will cause the application to crash. + --> $DIR/panic_in_result.rs:23:9 + | +LL | unreachable!(); + | ^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: used unimplemented, unreachable or panic in a function that returns result or option + --> $DIR/panic_in_result.rs:26:5 + | +LL | / fn option_with_unimplemented() -> Option // should emit lint +LL | | { +LL | | unimplemented!(); +LL | | } + | |_____^ + | + = help: unimplemented, unreachable or panic should not be used in a function that returns result or option +note: will cause the application to crash. + --> $DIR/panic_in_result.rs:28:9 + | +LL | unimplemented!(); + | ^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: used unimplemented, unreachable or panic in a function that returns result or option + --> $DIR/panic_in_result.rs:31:5 + | +LL | / fn option_with_panic() -> Option // should emit lint +LL | | { +LL | | panic!("error"); +LL | | } + | |_____^ + | + = help: unimplemented, unreachable or panic should not be used in a function that returns result or option +note: will cause the application to crash. + --> $DIR/panic_in_result.rs:33:9 + | +LL | panic!("error"); + | ^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 6 previous errors + -- cgit 1.4.1-3-g733a5 From b006522393a3c3c2656e1ccdfbb0076ff1bd7e99 Mon Sep 17 00:00:00 2001 From: Vali Schneider Date: Thu, 27 Aug 2020 16:55:23 -0700 Subject: added lint for todo and removed option --- clippy_lints/src/panic_in_result.rs | 26 ++++++++-------- src/lintlist/mod.rs | 2 +- tests/ui/panic_in_result.rs | 22 ++++---------- tests/ui/panic_in_result.stderr | 60 ++++++++----------------------------- 4 files changed, 32 insertions(+), 78 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/panic_in_result.rs b/clippy_lints/src/panic_in_result.rs index 239b4bdbdb1..2901f393fc6 100644 --- a/clippy_lints/src/panic_in_result.rs +++ b/clippy_lints/src/panic_in_result.rs @@ -7,16 +7,16 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; declare_clippy_lint! { - /// **What it does:** Checks for usage of `panic!`, `unimplemented!` or `unreachable!` in a function of type result/option. + /// **What it does:** Checks for usage of `panic!`, `unimplemented!`, `todo!` or `unreachable!` in a function of type result. /// - /// **Why is this bad?** For some codebases, it is desirable for functions of type option/result to return an error instead of crashing. Hence unimplemented, panic and unreachable should be avoided. + /// **Why is this bad?** For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence unimplemented, panic and unreachable should be avoided. /// /// **Known problems:** None. /// /// **Example:** /// /// ```rust - /// fn option_with_panic() -> Option + /// fn result_with_panic() -> Result /// { /// panic!("error"); /// } @@ -24,7 +24,7 @@ declare_clippy_lint! { pub PANIC_IN_RESULT, restriction, - "functions of type `Result<..>` / `Option`<...> that contain `panic!()` or `unreachable()` or `unimplemented()` " + "functions of type `Result<..>` that contain `panic!()`, `todo!()` or `unreachable()` or `unimplemented()` " } declare_lint_pass!(PanicInResult => [PANIC_IN_RESULT]); @@ -35,8 +35,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResult { // first check if it's a method or function if let hir::ImplItemKind::Fn(ref _signature, _) = impl_item.kind; // checking if its return type is `result` or `option` - if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(result_type)) - || is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(option_type)); + if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(result_type)); then { lint_impl_body(cx, impl_item.span, impl_item); } @@ -55,14 +54,13 @@ impl<'tcx> Visitor<'tcx> for FindPanicUnimplementedUnreachable { type Map = Map<'tcx>; fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if is_expn_of(expr.span, "unimplemented").is_some() { - self.result.push(expr.span); - } else if is_expn_of(expr.span, "unreachable").is_some() { - self.result.push(expr.span); - } else if is_expn_of(expr.span, "panic").is_some() { + if is_expn_of(expr.span, "unimplemented").is_some() + || is_expn_of(expr.span, "unreachable").is_some() + || is_expn_of(expr.span, "panic").is_some() + || is_expn_of(expr.span, "todo").is_some() + { self.result.push(expr.span); } - // and check sub-expressions intravisit::walk_expr(self, expr); } @@ -88,10 +86,10 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tc cx, PANIC_IN_RESULT, impl_span, - "used unimplemented, unreachable or panic in a function that returns result or option", + "used unimplemented, unreachable, todo or panic in a function that returns result", move |diag| { diag.help( - "unimplemented, unreachable or panic should not be used in a function that returns result or option" ); + "unimplemented, unreachable, todo or panic should not be used in a function that returns result" ); diag.span_note(fpu.result, "will cause the application to crash."); }); } diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index b4f20ca7f14..1f56c56f081 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1707,7 +1707,7 @@ pub static ref ALL_LINTS: Vec = vec![ Lint { name: "panic_in_result", group: "restriction", - desc: "functions of type `Result<..>` / `Option`<...> that contain `panic!()` or `unreachable()` or `unimplemented()` ", + desc: "functions of type `Result<..>` that contain `panic!()`, `todo!()` or `unreachable()` or `unimplemented()` ", deprecation: None, module: "panic_in_result", }, diff --git a/tests/ui/panic_in_result.rs b/tests/ui/panic_in_result.rs index 21e9efca87b..056778995a4 100644 --- a/tests/ui/panic_in_result.rs +++ b/tests/ui/panic_in_result.rs @@ -18,19 +18,9 @@ impl A { unreachable!(); } - fn option_with_unreachable() -> Option // should emit lint + fn result_with_todo() -> Result // should emit lint { - unreachable!(); - } - - fn option_with_unimplemented() -> Option // should emit lint - { - unimplemented!(); - } - - fn option_with_panic() -> Option // should emit lint - { - panic!("error"); + todo!("Finish this"); } fn other_with_panic() // should not emit lint @@ -48,14 +38,14 @@ impl A { unimplemented!(); } - fn result_without_banned_functions() -> Result // should not emit lint + fn other_with_todo() // should not emit lint { - Ok(true) + todo!("finish this") } - fn option_without_banned_functions() -> Option // should not emit lint + fn result_without_banned_functions() -> Result // should not emit lint { - Some(true) + Ok(true) } } diff --git a/tests/ui/panic_in_result.stderr b/tests/ui/panic_in_result.stderr index 74273bd9abb..3b9ac69f20d 100644 --- a/tests/ui/panic_in_result.stderr +++ b/tests/ui/panic_in_result.stderr @@ -1,4 +1,4 @@ -error: used unimplemented, unreachable or panic in a function that returns result or option +error: used unimplemented, unreachable, todo or panic in a function that returns result --> $DIR/panic_in_result.rs:6:5 | LL | / fn result_with_panic() -> Result // should emit lint @@ -8,7 +8,7 @@ LL | | } | |_____^ | = note: `-D clippy::panic-in-result` implied by `-D warnings` - = help: unimplemented, unreachable or panic should not be used in a function that returns result or option + = help: unimplemented, unreachable, todo or panic should not be used in a function that returns result note: will cause the application to crash. --> $DIR/panic_in_result.rs:8:9 | @@ -16,7 +16,7 @@ LL | panic!("error"); | ^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: used unimplemented, unreachable or panic in a function that returns result or option +error: used unimplemented, unreachable, todo or panic in a function that returns result --> $DIR/panic_in_result.rs:11:5 | LL | / fn result_with_unimplemented() -> Result // should emit lint @@ -25,7 +25,7 @@ LL | | unimplemented!(); LL | | } | |_____^ | - = help: unimplemented, unreachable or panic should not be used in a function that returns result or option + = help: unimplemented, unreachable, todo or panic should not be used in a function that returns result note: will cause the application to crash. --> $DIR/panic_in_result.rs:13:9 | @@ -33,7 +33,7 @@ LL | unimplemented!(); | ^^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: used unimplemented, unreachable or panic in a function that returns result or option +error: used unimplemented, unreachable, todo or panic in a function that returns result --> $DIR/panic_in_result.rs:16:5 | LL | / fn result_with_unreachable() -> Result // should emit lint @@ -42,7 +42,7 @@ LL | | unreachable!(); LL | | } | |_____^ | - = help: unimplemented, unreachable or panic should not be used in a function that returns result or option + = help: unimplemented, unreachable, todo or panic should not be used in a function that returns result note: will cause the application to crash. --> $DIR/panic_in_result.rs:18:9 | @@ -50,56 +50,22 @@ LL | unreachable!(); | ^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: used unimplemented, unreachable or panic in a function that returns result or option +error: used unimplemented, unreachable, todo or panic in a function that returns result --> $DIR/panic_in_result.rs:21:5 | -LL | / fn option_with_unreachable() -> Option // should emit lint +LL | / fn result_with_todo() -> Result // should emit lint LL | | { -LL | | unreachable!(); +LL | | todo!("Finish this"); LL | | } | |_____^ | - = help: unimplemented, unreachable or panic should not be used in a function that returns result or option + = help: unimplemented, unreachable, todo or panic should not be used in a function that returns result note: will cause the application to crash. --> $DIR/panic_in_result.rs:23:9 | -LL | unreachable!(); - | ^^^^^^^^^^^^^^^ - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: used unimplemented, unreachable or panic in a function that returns result or option - --> $DIR/panic_in_result.rs:26:5 - | -LL | / fn option_with_unimplemented() -> Option // should emit lint -LL | | { -LL | | unimplemented!(); -LL | | } - | |_____^ - | - = help: unimplemented, unreachable or panic should not be used in a function that returns result or option -note: will cause the application to crash. - --> $DIR/panic_in_result.rs:28:9 - | -LL | unimplemented!(); - | ^^^^^^^^^^^^^^^^^ - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: used unimplemented, unreachable or panic in a function that returns result or option - --> $DIR/panic_in_result.rs:31:5 - | -LL | / fn option_with_panic() -> Option // should emit lint -LL | | { -LL | | panic!("error"); -LL | | } - | |_____^ - | - = help: unimplemented, unreachable or panic should not be used in a function that returns result or option -note: will cause the application to crash. - --> $DIR/panic_in_result.rs:33:9 - | -LL | panic!("error"); - | ^^^^^^^^^^^^^^^^ +LL | todo!("Finish this"); + | ^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 6 previous errors +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From 5574182b4d2d08c848a88a1ac5633fc194e0465e Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Fri, 28 Aug 2020 18:40:22 +0900 Subject: Add a new lint to prevent `create_dir` from being used --- CHANGELOG.md | 1 + clippy_lints/src/create_dir.rs | 50 ++++++++++++++++++++++++++++++++++++++++++ clippy_lints/src/lib.rs | 4 ++++ src/lintlist/mod.rs | 7 ++++++ tests/ui/create_dir.fixed | 13 +++++++++++ tests/ui/create_dir.rs | 13 +++++++++++ tests/ui/create_dir.stderr | 16 ++++++++++++++ 7 files changed, 104 insertions(+) create mode 100644 clippy_lints/src/create_dir.rs create mode 100644 tests/ui/create_dir.fixed create mode 100644 tests/ui/create_dir.rs create mode 100644 tests/ui/create_dir.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 137b561028a..b37273af44d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1444,6 +1444,7 @@ Released 2018-09-13 [`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if [`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain [`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator +[`create_dir`]: https://rust-lang.github.io/rust-clippy/master/index.html#create_dir [`crosspointer_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#crosspointer_transmute [`dbg_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro [`debug_assert_with_mut_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#debug_assert_with_mut_call diff --git a/clippy_lints/src/create_dir.rs b/clippy_lints/src/create_dir.rs new file mode 100644 index 00000000000..229536bd673 --- /dev/null +++ b/clippy_lints/src/create_dir.rs @@ -0,0 +1,50 @@ +use crate::utils::{match_qpath, snippet, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::*; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks usage of `std::fs::create_dir` and suggest using `std::fs::create_dir_all` instead. + /// + /// **Why is this bad?** Sometimes `std::fs::crate_dir` is mistakenly chosen over `std::fs::create_dir_all`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// std::fs::create_dir("foo") + /// ``` + /// Use instead: + /// ```rust + /// std::fs::create_dir_all("foo") + /// ``` + pub CREATE_DIR, + restriction, + "calling `std::fs::create_dir` instead of `std::fs::create_dir_all`" +} + +declare_lint_pass!(CreateDir => [CREATE_DIR]); + +impl LateLintPass<'_> for CreateDir { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if_chain! { + if let ExprKind::Call(ref func, ref args) = expr.kind; + if let ExprKind::Path(ref path) = func.kind; + if match_qpath(path, &["std", "fs", "create_dir"]); + then { + span_lint_and_sugg( + cx, + CREATE_DIR, + expr.span, + "calling `std::fs::create_dir` where there may be a better way", + "consider calling `std::fs::create_dir_all` instead", + format!("std::fs::create_dir_all({})", snippet(cx, args[0].span, "..")), + Applicability::MachineApplicable, + ) + } + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 577ce6523b4..7943be34c62 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -169,6 +169,7 @@ mod collapsible_if; mod comparison_chain; mod copies; mod copy_iterator; +mod create_dir; mod dbg_macro; mod default_trait_access; mod dereference; @@ -511,6 +512,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &copies::MATCH_SAME_ARMS, &copies::SAME_FUNCTIONS_IN_IF_CONDITION, ©_iterator::COPY_ITERATOR, + &create_dir::CREATE_DIR, &dbg_macro::DBG_MACRO, &default_trait_access::DEFAULT_TRAIT_ACCESS, &dereference::EXPLICIT_DEREF_METHODS, @@ -1042,6 +1044,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box items_after_statements::ItemsAfterStatements); store.register_early_pass(|| box precedence::Precedence); store.register_early_pass(|| box needless_continue::NeedlessContinue); + store.register_late_pass(|| box create_dir::CreateDir); store.register_early_pass(|| box needless_arbitrary_self_type::NeedlessArbitrarySelfType); store.register_early_pass(|| box redundant_static_lifetimes::RedundantStaticLifetimes); store.register_late_pass(|| box cargo_common_metadata::CargoCommonMetadata); @@ -1104,6 +1107,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&arithmetic::FLOAT_ARITHMETIC), LintId::of(&arithmetic::INTEGER_ARITHMETIC), LintId::of(&as_conversions::AS_CONVERSIONS), + LintId::of(&create_dir::CREATE_DIR), LintId::of(&dbg_macro::DBG_MACRO), LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE), LintId::of(&exit::EXIT), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 687fac7baa8..e5134510922 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -290,6 +290,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "copy_iterator", }, + Lint { + name: "create_dir", + group: "restriction", + desc: "calling `std::fs::create_dir` instead of `std::fs::create_dir_all`", + deprecation: None, + module: "create_dir", + }, Lint { name: "crosspointer_transmute", group: "complexity", diff --git a/tests/ui/create_dir.fixed b/tests/ui/create_dir.fixed new file mode 100644 index 00000000000..50f31f0c9c5 --- /dev/null +++ b/tests/ui/create_dir.fixed @@ -0,0 +1,13 @@ +// run-rustfix +#![allow(unused_must_use)] +#![warn(clippy::create_dir)] + +fn not_create_dir() {} + +fn main() { + std::fs::create_dir_all("foo"); + std::fs::create_dir_all("bar").unwrap(); + + not_create_dir(); + std::fs::create_dir_all("foobar"); +} diff --git a/tests/ui/create_dir.rs b/tests/ui/create_dir.rs new file mode 100644 index 00000000000..00ef37f413f --- /dev/null +++ b/tests/ui/create_dir.rs @@ -0,0 +1,13 @@ +// run-rustfix +#![allow(unused_must_use)] +#![warn(clippy::create_dir)] + +fn not_create_dir() {} + +fn main() { + std::fs::create_dir("foo"); + std::fs::create_dir("bar").unwrap(); + + not_create_dir(); + std::fs::create_dir_all("foobar"); +} diff --git a/tests/ui/create_dir.stderr b/tests/ui/create_dir.stderr new file mode 100644 index 00000000000..3ae4680d929 --- /dev/null +++ b/tests/ui/create_dir.stderr @@ -0,0 +1,16 @@ +error: calling `std::fs::create_dir` where there may be a better way + --> $DIR/create_dir.rs:8:5 + | +LL | std::fs::create_dir("foo"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `std::fs::create_dir_all("foo")` + | + = note: `-D clippy::create-dir` implied by `-D warnings` + +error: calling `std::fs::create_dir` where there may be a better way + --> $DIR/create_dir.rs:9:5 + | +LL | std::fs::create_dir("bar").unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `std::fs::create_dir_all("bar")` + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From 7a66e6502dc3c7085b3f4078c01d4957d96175ed Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sat, 29 Aug 2020 01:18:42 +0200 Subject: or_fn_call: ignore nullary associated const fns --- clippy_lints/src/utils/mod.rs | 2 +- tests/ui/or_fun_call.fixed | 17 +++++++++++------ tests/ui/or_fun_call.rs | 17 +++++++++++------ tests/ui/or_fun_call.stderr | 20 ++++---------------- 4 files changed, 27 insertions(+), 29 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 82005257115..fe2ee093157 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -899,7 +899,7 @@ pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_ return match res { def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true, // FIXME: check the constness of the arguments, see https://github.com/rust-lang/rust-clippy/pull/5682#issuecomment-638681210 - def::Res::Def(DefKind::Fn, def_id) if has_no_arguments(cx, def_id) => { + def::Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) if has_no_arguments(cx, def_id) => { const_eval::is_const_fn(cx.tcx, def_id) }, def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id), diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 67faa8bd4a0..5fb568672d3 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -58,12 +58,6 @@ fn or_fun_call() { let without_default = Some(Foo); without_default.unwrap_or_else(Foo::new); - let mut map = HashMap::::new(); - map.entry(42).or_insert_with(String::new); - - let mut btree = BTreeMap::::new(); - btree.entry(42).or_insert_with(String::new); - let stringy = Some(String::from("")); let _ = stringy.unwrap_or_else(|| "".to_owned()); @@ -122,6 +116,17 @@ pub fn skip_const_fn_with_no_args() { Some(42) } let _ = None.or(foo()); + + // See issue #5693. + let mut map = std::collections::HashMap::new(); + map.insert(1, vec![1]); + map.entry(1).or_insert(vec![]); + + let mut map = HashMap::::new(); + map.entry(42).or_insert(String::new()); + + let mut btree = BTreeMap::::new(); + btree.entry(42).or_insert(String::new()); } fn main() {} diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 9867e2eedcf..737b0f7e55b 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -58,12 +58,6 @@ fn or_fun_call() { let without_default = Some(Foo); without_default.unwrap_or(Foo::new()); - let mut map = HashMap::::new(); - map.entry(42).or_insert(String::new()); - - let mut btree = BTreeMap::::new(); - btree.entry(42).or_insert(String::new()); - let stringy = Some(String::from("")); let _ = stringy.unwrap_or("".to_owned()); @@ -122,6 +116,17 @@ pub fn skip_const_fn_with_no_args() { Some(42) } let _ = None.or(foo()); + + // See issue #5693. + let mut map = std::collections::HashMap::new(); + map.insert(1, vec![1]); + map.entry(1).or_insert(vec![]); + + let mut map = HashMap::::new(); + map.entry(42).or_insert(String::new()); + + let mut btree = BTreeMap::::new(); + btree.entry(42).or_insert(String::new()); } fn main() {} diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index bc5978b538f..b8a436993f3 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -60,35 +60,23 @@ error: use of `unwrap_or` followed by a function call LL | without_default.unwrap_or(Foo::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(Foo::new)` -error: use of `or_insert` followed by a function call - --> $DIR/or_fun_call.rs:62:19 - | -LL | map.entry(42).or_insert(String::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)` - -error: use of `or_insert` followed by a function call - --> $DIR/or_fun_call.rs:65:21 - | -LL | btree.entry(42).or_insert(String::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)` - error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:68:21 + --> $DIR/or_fun_call.rs:62:21 | LL | let _ = stringy.unwrap_or("".to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "".to_owned())` error: use of `or` followed by a function call - --> $DIR/or_fun_call.rs:93:35 + --> $DIR/or_fun_call.rs:87:35 | LL | let _ = Some("a".to_string()).or(Some("b".to_string())); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some("b".to_string()))` error: use of `or` followed by a function call - --> $DIR/or_fun_call.rs:97:10 + --> $DIR/or_fun_call.rs:91:10 | LL | .or(Some(Bar(b, Duration::from_secs(2)))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some(Bar(b, Duration::from_secs(2))))` -error: aborting due to 15 previous errors +error: aborting due to 13 previous errors -- cgit 1.4.1-3-g733a5 From 4972989b616cbf96c015cd9fdf1f4b4464ecaace Mon Sep 17 00:00:00 2001 From: Kyle Huey Date: Fri, 14 Aug 2020 17:30:48 -0700 Subject: Add a lint for an async block/closure that yields a type that is itself awaitable. This catches bugs of the form tokio::spawn(async move { let f = some_async_thing(); f // Oh no I forgot to await f so that work will never complete. }); --- CHANGELOG.md | 1 + clippy_lints/src/async_yields_async.rs | 88 +++++++++++++++++++++++++++++++ clippy_lints/src/lib.rs | 5 ++ src/lintlist/mod.rs | 7 +++ tests/ui/async_yields_async.fixed | 61 +++++++++++++++++++++ tests/ui/async_yields_async.rs | 61 +++++++++++++++++++++ tests/ui/async_yields_async.stderr | 96 ++++++++++++++++++++++++++++++++++ 7 files changed, 319 insertions(+) create mode 100644 clippy_lints/src/async_yields_async.rs create mode 100644 tests/ui/async_yields_async.fixed create mode 100644 tests/ui/async_yields_async.rs create mode 100644 tests/ui/async_yields_async.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 34d48821023..99a8b1a6293 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1512,6 +1512,7 @@ Released 2018-09-13 [`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants [`assign_op_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_pattern [`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops +[`async_yields_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#async_yields_async [`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock [`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask [`bind_instead_of_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#bind_instead_of_map diff --git a/clippy_lints/src/async_yields_async.rs b/clippy_lints/src/async_yields_async.rs new file mode 100644 index 00000000000..ae347fcd3e8 --- /dev/null +++ b/clippy_lints/src/async_yields_async.rs @@ -0,0 +1,88 @@ +use crate::utils::{implements_trait, snippet, span_lint_and_then}; +use rustc_errors::Applicability; +use rustc_hir::{AsyncGeneratorKind, Body, BodyId, ExprKind, GeneratorKind, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** + /// Checks for async blocks that yield values of types that can themselves + /// be awaited. + /// + /// **Why is this bad?** + /// An await is likely missing. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// async fn foo() {} + /// + /// fn bar() { + /// let x = async { + /// foo() + /// }; + /// } + /// ``` + /// Use instead: + /// ```rust + /// async fn foo() {} + /// + /// fn bar() { + /// let x = async { + /// foo().await + /// }; + /// } + /// ``` + pub ASYNC_YIELDS_ASYNC, + correctness, + "async blocks that return a type that can be awaited" +} + +declare_lint_pass!(AsyncYieldsAsync => [ASYNC_YIELDS_ASYNC]); + +impl<'tcx> LateLintPass<'tcx> for AsyncYieldsAsync { + fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) { + use AsyncGeneratorKind::{Block, Closure}; + // For functions, with explicitly defined types, don't warn. + // XXXkhuey maybe we should? + if let Some(GeneratorKind::Async(Block | Closure)) = body.generator_kind { + if let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait() { + let body_id = BodyId { + hir_id: body.value.hir_id, + }; + let def_id = cx.tcx.hir().body_owner_def_id(body_id); + let typeck_results = cx.tcx.typeck(def_id); + let expr_ty = typeck_results.expr_ty(&body.value); + + if implements_trait(cx, expr_ty, future_trait_def_id, &[]) { + let return_expr_span = match &body.value.kind { + // XXXkhuey there has to be a better way. + ExprKind::Block(block, _) => block.expr.map(|e| e.span), + ExprKind::Path(QPath::Resolved(_, path)) => Some(path.span), + _ => None, + }; + if let Some(return_expr_span) = return_expr_span { + span_lint_and_then( + cx, + ASYNC_YIELDS_ASYNC, + return_expr_span, + "an async construct yields a type which is itself awaitable", + |db| { + db.span_label(body.value.span, "outer async construct"); + db.span_label(return_expr_span, "awaitable value not awaited"); + db.span_suggestion( + return_expr_span, + "consider awaiting this value", + format!("{}.await", snippet(cx, return_expr_span, "..")), + Applicability::MaybeIncorrect, + ); + }, + ); + } + } + } + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 577ce6523b4..0eb1d331366 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -154,6 +154,7 @@ mod arithmetic; mod as_conversions; mod assertions_on_constants; mod assign_ops; +mod async_yields_async; mod atomic_ordering; mod attrs; mod await_holding_lock; @@ -483,6 +484,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &assertions_on_constants::ASSERTIONS_ON_CONSTANTS, &assign_ops::ASSIGN_OP_PATTERN, &assign_ops::MISREFACTORED_ASSIGN_OP, + &async_yields_async::ASYNC_YIELDS_ASYNC, &atomic_ordering::INVALID_ATOMIC_ORDERING, &attrs::BLANKET_CLIPPY_RESTRICTION_LINTS, &attrs::DEPRECATED_CFG_ATTR, @@ -1099,6 +1101,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box unwrap_in_result::UnwrapInResult); store.register_late_pass(|| box self_assignment::SelfAssignment); store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); + store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1232,6 +1235,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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(&atomic_ordering::INVALID_ATOMIC_ORDERING), LintId::of(&attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), LintId::of(&attrs::DEPRECATED_CFG_ATTR), @@ -1675,6 +1679,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_group(true, "clippy::correctness", Some("clippy_correctness"), vec![ LintId::of(&approx_const::APPROX_CONSTANT), + LintId::of(&async_yields_async::ASYNC_YIELDS_ASYNC), LintId::of(&atomic_ordering::INVALID_ATOMIC_ORDERING), LintId::of(&attrs::DEPRECATED_SEMVER), LintId::of(&attrs::MISMATCHED_TARGET_OS), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 687fac7baa8..dff19ef440f 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -52,6 +52,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "assign_ops", }, + Lint { + name: "async_yields_async", + group: "correctness", + desc: "async blocks that return a type that can be awaited", + deprecation: None, + module: "async_yields_async", + }, Lint { name: "await_holding_lock", group: "pedantic", diff --git a/tests/ui/async_yields_async.fixed b/tests/ui/async_yields_async.fixed new file mode 100644 index 00000000000..cadc6494c76 --- /dev/null +++ b/tests/ui/async_yields_async.fixed @@ -0,0 +1,61 @@ +// run-rustfix +// edition:2018 + +#![feature(async_closure)] +#![warn(clippy::async_yields_async)] + +use core::future::Future; +use core::pin::Pin; +use core::task::{Context, Poll}; + +struct CustomFutureType; + +impl Future for CustomFutureType { + type Output = u8; + + fn poll(self: Pin<&mut Self>, _: &mut Context) -> Poll { + Poll::Ready(3) + } +} + +fn custom_future_type_ctor() -> CustomFutureType { + CustomFutureType +} + +#[rustfmt::skip] +fn main() { + let _f = { + 3 + }; + let _g = async { + 3 + }; + let _h = async { + async { + 3 + }.await + }; + let _i = async { + CustomFutureType.await + }; + let _i = async || { + 3 + }; + let _j = async || { + async { + 3 + }.await + }; + let _k = async || { + CustomFutureType.await + }; + let _l = async || CustomFutureType.await; + let _m = async || { + println!("I'm bored"); + // Some more stuff + + // Finally something to await + CustomFutureType.await + }; + let _n = async || custom_future_type_ctor(); +} diff --git a/tests/ui/async_yields_async.rs b/tests/ui/async_yields_async.rs new file mode 100644 index 00000000000..898fe1a9561 --- /dev/null +++ b/tests/ui/async_yields_async.rs @@ -0,0 +1,61 @@ +// run-rustfix +// edition:2018 + +#![feature(async_closure)] +#![warn(clippy::async_yields_async)] + +use core::future::Future; +use core::pin::Pin; +use core::task::{Context, Poll}; + +struct CustomFutureType; + +impl Future for CustomFutureType { + type Output = u8; + + fn poll(self: Pin<&mut Self>, _: &mut Context) -> Poll { + Poll::Ready(3) + } +} + +fn custom_future_type_ctor() -> CustomFutureType { + CustomFutureType +} + +#[rustfmt::skip] +fn main() { + let _f = { + 3 + }; + let _g = async { + 3 + }; + let _h = async { + async { + 3 + } + }; + let _i = async { + CustomFutureType + }; + let _i = async || { + 3 + }; + let _j = async || { + async { + 3 + } + }; + let _k = async || { + CustomFutureType + }; + let _l = async || CustomFutureType; + let _m = async || { + println!("I'm bored"); + // Some more stuff + + // Finally something to await + CustomFutureType + }; + let _n = async || custom_future_type_ctor(); +} diff --git a/tests/ui/async_yields_async.stderr b/tests/ui/async_yields_async.stderr new file mode 100644 index 00000000000..112984cdccb --- /dev/null +++ b/tests/ui/async_yields_async.stderr @@ -0,0 +1,96 @@ +error: an async construct yields a type which is itself awaitable + --> $DIR/async_yields_async.rs:34:9 + | +LL | let _h = async { + | ____________________- +LL | | async { + | |_________^ +LL | || 3 +LL | || } + | ||_________^ awaitable value not awaited +LL | | }; + | |_____- outer async construct + | + = note: `-D clippy::async-yields-async` implied by `-D warnings` +help: consider awaiting this value + | +LL | async { +LL | 3 +LL | }.await + | + +error: an async construct yields a type which is itself awaitable + --> $DIR/async_yields_async.rs:39:9 + | +LL | let _i = async { + | ____________________- +LL | | CustomFutureType + | | ^^^^^^^^^^^^^^^^ + | | | + | | awaitable value not awaited + | | help: consider awaiting this value: `CustomFutureType.await` +LL | | }; + | |_____- outer async construct + +error: an async construct yields a type which is itself awaitable + --> $DIR/async_yields_async.rs:45:9 + | +LL | let _j = async || { + | _______________________- +LL | | async { + | |_________^ +LL | || 3 +LL | || } + | ||_________^ awaitable value not awaited +LL | | }; + | |_____- outer async construct + | +help: consider awaiting this value + | +LL | async { +LL | 3 +LL | }.await + | + +error: an async construct yields a type which is itself awaitable + --> $DIR/async_yields_async.rs:50:9 + | +LL | let _k = async || { + | _______________________- +LL | | CustomFutureType + | | ^^^^^^^^^^^^^^^^ + | | | + | | awaitable value not awaited + | | help: consider awaiting this value: `CustomFutureType.await` +LL | | }; + | |_____- outer async construct + +error: an async construct yields a type which is itself awaitable + --> $DIR/async_yields_async.rs:52:23 + | +LL | let _l = async || CustomFutureType; + | ^^^^^^^^^^^^^^^^ + | | + | outer async construct + | awaitable value not awaited + | help: consider awaiting this value: `CustomFutureType.await` + +error: an async construct yields a type which is itself awaitable + --> $DIR/async_yields_async.rs:58:9 + | +LL | let _m = async || { + | _______________________- +LL | | println!("I'm bored"); +LL | | // Some more stuff +LL | | +LL | | // Finally something to await +LL | | CustomFutureType + | | ^^^^^^^^^^^^^^^^ + | | | + | | awaitable value not awaited + | | help: consider awaiting this value: `CustomFutureType.await` +LL | | }; + | |_____- outer async construct + +error: aborting due to 6 previous errors + -- cgit 1.4.1-3-g733a5 From c1d2b9376a6bb4fc06f845e12b9c2a93079bb2ee Mon Sep 17 00:00:00 2001 From: Kyle Huey Date: Sat, 22 Aug 2020 21:36:39 -0700 Subject: Add a test for an async function. --- tests/ui/async_yields_async.fixed | 7 +++++++ tests/ui/async_yields_async.rs | 7 +++++++ tests/ui/async_yields_async.stderr | 12 ++++++------ 3 files changed, 20 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/tests/ui/async_yields_async.fixed b/tests/ui/async_yields_async.fixed index cadc6494c76..9b1a7ac3ba9 100644 --- a/tests/ui/async_yields_async.fixed +++ b/tests/ui/async_yields_async.fixed @@ -22,6 +22,12 @@ fn custom_future_type_ctor() -> CustomFutureType { CustomFutureType } +async fn f() -> CustomFutureType { + // Don't warn for functions since you have to explicitly declare their + // return types. + CustomFutureType +} + #[rustfmt::skip] fn main() { let _f = { @@ -58,4 +64,5 @@ fn main() { CustomFutureType.await }; let _n = async || custom_future_type_ctor(); + let _o = async || f(); } diff --git a/tests/ui/async_yields_async.rs b/tests/ui/async_yields_async.rs index 898fe1a9561..731c094edb4 100644 --- a/tests/ui/async_yields_async.rs +++ b/tests/ui/async_yields_async.rs @@ -22,6 +22,12 @@ fn custom_future_type_ctor() -> CustomFutureType { CustomFutureType } +async fn f() -> CustomFutureType { + // Don't warn for functions since you have to explicitly declare their + // return types. + CustomFutureType +} + #[rustfmt::skip] fn main() { let _f = { @@ -58,4 +64,5 @@ fn main() { CustomFutureType }; let _n = async || custom_future_type_ctor(); + let _o = async || f(); } diff --git a/tests/ui/async_yields_async.stderr b/tests/ui/async_yields_async.stderr index 112984cdccb..17d0c375106 100644 --- a/tests/ui/async_yields_async.stderr +++ b/tests/ui/async_yields_async.stderr @@ -1,5 +1,5 @@ error: an async construct yields a type which is itself awaitable - --> $DIR/async_yields_async.rs:34:9 + --> $DIR/async_yields_async.rs:40:9 | LL | let _h = async { | ____________________- @@ -20,7 +20,7 @@ LL | }.await | error: an async construct yields a type which is itself awaitable - --> $DIR/async_yields_async.rs:39:9 + --> $DIR/async_yields_async.rs:45:9 | LL | let _i = async { | ____________________- @@ -33,7 +33,7 @@ LL | | }; | |_____- outer async construct error: an async construct yields a type which is itself awaitable - --> $DIR/async_yields_async.rs:45:9 + --> $DIR/async_yields_async.rs:51:9 | LL | let _j = async || { | _______________________- @@ -53,7 +53,7 @@ LL | }.await | error: an async construct yields a type which is itself awaitable - --> $DIR/async_yields_async.rs:50:9 + --> $DIR/async_yields_async.rs:56:9 | LL | let _k = async || { | _______________________- @@ -66,7 +66,7 @@ LL | | }; | |_____- outer async construct error: an async construct yields a type which is itself awaitable - --> $DIR/async_yields_async.rs:52:23 + --> $DIR/async_yields_async.rs:58:23 | LL | let _l = async || CustomFutureType; | ^^^^^^^^^^^^^^^^ @@ -76,7 +76,7 @@ LL | let _l = async || CustomFutureType; | help: consider awaiting this value: `CustomFutureType.await` error: an async construct yields a type which is itself awaitable - --> $DIR/async_yields_async.rs:58:9 + --> $DIR/async_yields_async.rs:64:9 | LL | let _m = async || { | _______________________- -- cgit 1.4.1-3-g733a5 From a424a2c1676a29c147252873037e8943d54941d3 Mon Sep 17 00:00:00 2001 From: Vali Schneider Date: Sat, 29 Aug 2020 16:17:53 -0700 Subject: changed check_impl_item to check_fn and added a few more test cases --- clippy_lints/src/panic_in_result.rs | 69 ++++++++++++++++++------------------- tests/ui/panic_in_result.rs | 20 ++++++++++- tests/ui/panic_in_result.stderr | 60 +++++++++++++++++++++++++------- 3 files changed, 99 insertions(+), 50 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/panic_in_result.rs b/clippy_lints/src/panic_in_result.rs index 2901f393fc6..11fefc12316 100644 --- a/clippy_lints/src/panic_in_result.rs +++ b/clippy_lints/src/panic_in_result.rs @@ -1,6 +1,8 @@ use crate::utils::{is_expn_of, is_type_diagnostic_item, return_ty, span_lint_and_then}; use if_chain::if_chain; use rustc_hir as hir; +use rustc_hir::intravisit::{self, FnKind, NestedVisitorMap, Visitor}; +use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -21,7 +23,6 @@ declare_clippy_lint! { /// panic!("error"); /// } /// ``` - pub PANIC_IN_RESULT, restriction, "functions of type `Result<..>` that contain `panic!()`, `todo!()` or `unreachable()` or `unimplemented()` " @@ -30,22 +31,26 @@ declare_clippy_lint! { declare_lint_pass!(PanicInResult => [PANIC_IN_RESULT]); impl<'tcx> LateLintPass<'tcx> for PanicInResult { - fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { + /* + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + _: FnKind<'tcx>, + _: &'tcx hir::FnDecl<'tcx>, + body: &'tcx hir::Body<'tcx>, + span: Span, + hir_id: hir::HirId, + ) { if_chain! { - // first check if it's a method or function - if let hir::ImplItemKind::Fn(ref _signature, _) = impl_item.kind; - // checking if its return type is `result` or `option` - if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(result_type)); - then { - lint_impl_body(cx, impl_item.span, impl_item); + if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)); + then + { + lint_impl_body(cx, span, body); } } - } + }*/ } -use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; -use rustc_hir::{Expr, ImplItemKind}; - struct FindPanicUnimplementedUnreachable { result: Vec, } @@ -70,29 +75,21 @@ impl<'tcx> Visitor<'tcx> for FindPanicUnimplementedUnreachable { } } -fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tcx hir::ImplItem<'_>) { - if_chain! { - if let ImplItemKind::Fn(_, body_id) = impl_item.kind; - then { - let body = cx.tcx.hir().body(body_id); - let mut fpu = FindPanicUnimplementedUnreachable { - result: Vec::new(), - }; - fpu.visit_expr(&body.value); - - // if we've found one, lint - if !fpu.result.is_empty() { - span_lint_and_then( - cx, - PANIC_IN_RESULT, - impl_span, - "used unimplemented, unreachable, todo or panic in a function that returns result", - move |diag| { - diag.help( - "unimplemented, unreachable, todo or panic should not be used in a function that returns result" ); - diag.span_note(fpu.result, "will cause the application to crash."); - }); - } - } +fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) { + let mut panics = FindPanicUnimplementedUnreachable { result: Vec::new() }; + panics.visit_expr(&body.value); + if !panics.result.is_empty() { + span_lint_and_then( + cx, + PANIC_IN_RESULT, + impl_span, + "used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`", + move |diag| { + diag.help( + "`unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing", + ); + diag.span_note(panics.result, "return Err() instead of panicking"); + }, + ); } } diff --git a/tests/ui/panic_in_result.rs b/tests/ui/panic_in_result.rs index 056778995a4..f6fb2f1ab61 100644 --- a/tests/ui/panic_in_result.rs +++ b/tests/ui/panic_in_result.rs @@ -49,4 +49,22 @@ impl A { } } -fn main() {} +fn function_result_with_panic() -> Result // should emit lint +{ + panic!("error"); +} + +fn todo() { + println!("something"); +} + +fn function_result_with_custom_todo() -> Result // should not emit lint +{ + todo(); + Ok(true) +} + +fn main() -> Result<(), String> { + todo!("finish main method"); + Ok(()) +} diff --git a/tests/ui/panic_in_result.stderr b/tests/ui/panic_in_result.stderr index 3b9ac69f20d..9faedf82986 100644 --- a/tests/ui/panic_in_result.stderr +++ b/tests/ui/panic_in_result.stderr @@ -1,4 +1,4 @@ -error: used unimplemented, unreachable, todo or panic in a function that returns result +error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` --> $DIR/panic_in_result.rs:6:5 | LL | / fn result_with_panic() -> Result // should emit lint @@ -8,15 +8,15 @@ LL | | } | |_____^ | = note: `-D clippy::panic-in-result` implied by `-D warnings` - = help: unimplemented, unreachable, todo or panic should not be used in a function that returns result -note: will cause the application to crash. + = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking --> $DIR/panic_in_result.rs:8:9 | LL | panic!("error"); | ^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: used unimplemented, unreachable, todo or panic in a function that returns result +error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` --> $DIR/panic_in_result.rs:11:5 | LL | / fn result_with_unimplemented() -> Result // should emit lint @@ -25,15 +25,15 @@ LL | | unimplemented!(); LL | | } | |_____^ | - = help: unimplemented, unreachable, todo or panic should not be used in a function that returns result -note: will cause the application to crash. + = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking --> $DIR/panic_in_result.rs:13:9 | LL | unimplemented!(); | ^^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: used unimplemented, unreachable, todo or panic in a function that returns result +error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` --> $DIR/panic_in_result.rs:16:5 | LL | / fn result_with_unreachable() -> Result // should emit lint @@ -42,15 +42,15 @@ LL | | unreachable!(); LL | | } | |_____^ | - = help: unimplemented, unreachable, todo or panic should not be used in a function that returns result -note: will cause the application to crash. + = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking --> $DIR/panic_in_result.rs:18:9 | LL | unreachable!(); | ^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: used unimplemented, unreachable, todo or panic in a function that returns result +error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` --> $DIR/panic_in_result.rs:21:5 | LL | / fn result_with_todo() -> Result // should emit lint @@ -59,13 +59,47 @@ LL | | todo!("Finish this"); LL | | } | |_____^ | - = help: unimplemented, unreachable, todo or panic should not be used in a function that returns result -note: will cause the application to crash. + = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking --> $DIR/panic_in_result.rs:23:9 | LL | todo!("Finish this"); | ^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 4 previous errors +error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` + --> $DIR/panic_in_result.rs:52:1 + | +LL | / fn function_result_with_panic() -> Result // should emit lint +LL | | { +LL | | panic!("error"); +LL | | } + | |_^ + | + = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking + --> $DIR/panic_in_result.rs:54:5 + | +LL | panic!("error"); + | ^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` + --> $DIR/panic_in_result.rs:67:1 + | +LL | / fn main() -> Result<(), String> { +LL | | todo!("finish main method"); +LL | | Ok(()) +LL | | } + | |_^ + | + = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking + --> $DIR/panic_in_result.rs:68:5 + | +LL | todo!("finish main method"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 6 previous errors -- cgit 1.4.1-3-g733a5 From 451ef7880392f3f06088ff7a7b957e3485f4bc6c Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Mon, 31 Aug 2020 22:40:47 +0900 Subject: Use `match_def_path` instead of `match_qpath` --- clippy_lints/src/create_dir.rs | 5 +++-- tests/ui/create_dir.fixed | 6 ++++-- tests/ui/create_dir.rs | 6 ++++-- tests/ui/create_dir.stderr | 4 ++-- 4 files changed, 13 insertions(+), 8 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/create_dir.rs b/clippy_lints/src/create_dir.rs index 1042eb45524..f80a0efa7a5 100644 --- a/clippy_lints/src/create_dir.rs +++ b/clippy_lints/src/create_dir.rs @@ -1,4 +1,4 @@ -use crate::utils::{match_qpath, paths, snippet, span_lint_and_sugg}; +use crate::utils::{match_def_path, paths, snippet, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -33,7 +33,8 @@ impl LateLintPass<'_> for CreateDir { if_chain! { if let ExprKind::Call(ref func, ref args) = expr.kind; if let ExprKind::Path(ref path) = func.kind; - if match_qpath(path, &paths::STD_FS_CREATE_DIR); + if let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id(); + if match_def_path(cx, def_id, &paths::STD_FS_CREATE_DIR); then { span_lint_and_sugg( cx, diff --git a/tests/ui/create_dir.fixed b/tests/ui/create_dir.fixed index 50f31f0c9c5..0e28f87e33d 100644 --- a/tests/ui/create_dir.fixed +++ b/tests/ui/create_dir.fixed @@ -2,12 +2,14 @@ #![allow(unused_must_use)] #![warn(clippy::create_dir)] -fn not_create_dir() {} +fn create_dir() {} fn main() { + // Should be warned std::fs::create_dir_all("foo"); std::fs::create_dir_all("bar").unwrap(); - not_create_dir(); + // Shouldn't be warned + create_dir(); std::fs::create_dir_all("foobar"); } diff --git a/tests/ui/create_dir.rs b/tests/ui/create_dir.rs index 00ef37f413f..1f226298c0d 100644 --- a/tests/ui/create_dir.rs +++ b/tests/ui/create_dir.rs @@ -2,12 +2,14 @@ #![allow(unused_must_use)] #![warn(clippy::create_dir)] -fn not_create_dir() {} +fn create_dir() {} fn main() { + // Should be warned std::fs::create_dir("foo"); std::fs::create_dir("bar").unwrap(); - not_create_dir(); + // Shouldn't be warned + create_dir(); std::fs::create_dir_all("foobar"); } diff --git a/tests/ui/create_dir.stderr b/tests/ui/create_dir.stderr index 3ae4680d929..0c97bdd0f7a 100644 --- a/tests/ui/create_dir.stderr +++ b/tests/ui/create_dir.stderr @@ -1,5 +1,5 @@ error: calling `std::fs::create_dir` where there may be a better way - --> $DIR/create_dir.rs:8:5 + --> $DIR/create_dir.rs:9:5 | LL | std::fs::create_dir("foo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `std::fs::create_dir_all("foo")` @@ -7,7 +7,7 @@ LL | std::fs::create_dir("foo"); = note: `-D clippy::create-dir` implied by `-D warnings` error: calling `std::fs::create_dir` where there may be a better way - --> $DIR/create_dir.rs:9:5 + --> $DIR/create_dir.rs:10:5 | LL | std::fs::create_dir("bar").unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `std::fs::create_dir_all("bar")` -- cgit 1.4.1-3-g733a5 From 001f9e45f24c5617d816e7d9dfbca4dc1a694dd9 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 1 Sep 2020 00:05:53 +0900 Subject: Fix the wrong suggestion when using macro in `collapsible_if` --- clippy_lints/src/utils/sugg.rs | 6 +++++- tests/ui/collapsible_if.fixed | 3 +++ tests/ui/collapsible_if.rs | 5 +++++ tests/ui/collapsible_if.stderr | 10 +++++++++- 4 files changed, 22 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/utils/sugg.rs b/clippy_lints/src/utils/sugg.rs index 2955f8d8e59..811fde388d1 100644 --- a/clippy_lints/src/utils/sugg.rs +++ b/clippy_lints/src/utils/sugg.rs @@ -132,7 +132,11 @@ impl<'a> Sugg<'a> { pub fn ast(cx: &EarlyContext<'_>, expr: &ast::Expr, default: &'a str) -> Self { use rustc_ast::ast::RangeLimits; - let snippet = snippet(cx, expr.span, default); + let snippet = if expr.span.from_expansion() { + snippet_with_macro_callsite(cx, expr.span, default) + } else { + snippet(cx, expr.span, default) + }; match expr.kind { ast::ExprKind::AddrOf(..) diff --git a/tests/ui/collapsible_if.fixed b/tests/ui/collapsible_if.fixed index 561283fc8e7..efd4187947b 100644 --- a/tests/ui/collapsible_if.fixed +++ b/tests/ui/collapsible_if.fixed @@ -135,4 +135,7 @@ fn main() { if truth() {} } } + + // Fix #5962 + if matches!(true, true) && matches!(true, true) {} } diff --git a/tests/ui/collapsible_if.rs b/tests/ui/collapsible_if.rs index dc9d9b451c0..657f32d38a3 100644 --- a/tests/ui/collapsible_if.rs +++ b/tests/ui/collapsible_if.rs @@ -149,4 +149,9 @@ fn main() { if truth() {} } } + + // Fix #5962 + if matches!(true, true) { + if matches!(true, true) {} + } } diff --git a/tests/ui/collapsible_if.stderr b/tests/ui/collapsible_if.stderr index f56dd65b9dd..acd1ec3f2ca 100644 --- a/tests/ui/collapsible_if.stderr +++ b/tests/ui/collapsible_if.stderr @@ -118,5 +118,13 @@ LL | println!("Hello world!"); LL | } | -error: aborting due to 7 previous errors +error: this `if` statement can be collapsed + --> $DIR/collapsible_if.rs:154:5 + | +LL | / if matches!(true, true) { +LL | | if matches!(true, true) {} +LL | | } + | |_____^ help: collapse nested if block: `if matches!(true, true) && matches!(true, true) {}` + +error: aborting due to 8 previous errors -- cgit 1.4.1-3-g733a5 From 8b0aa6a00b19b8e47a72157ec8e8f9e9060cb2fb Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 1 Sep 2020 00:31:53 +0900 Subject: default_trait_access: Fix wrong suggestion --- clippy_lints/src/default_trait_access.rs | 4 +- tests/ui/default_trait_access.fixed | 106 +++++++++++++++++++++++++++++++ tests/ui/default_trait_access.rs | 5 +- tests/ui/default_trait_access.stderr | 26 ++++---- 4 files changed, 127 insertions(+), 14 deletions(-) create mode 100644 tests/ui/default_trait_access.fixed (limited to 'tests') diff --git a/clippy_lints/src/default_trait_access.rs b/clippy_lints/src/default_trait_access.rs index 067ea903bdd..0b0a1307876 100644 --- a/clippy_lints/src/default_trait_access.rs +++ b/clippy_lints/src/default_trait_access.rs @@ -55,8 +55,8 @@ impl<'tcx> LateLintPass<'tcx> for DefaultTraitAccess { // TODO: Work out a way to put "whatever the imported way of referencing // this type in this file" rather than a fully-qualified type. let expr_ty = cx.typeck_results().expr_ty(expr); - if let ty::Adt(..) = expr_ty.kind { - let replacement = format!("{}::default()", expr_ty); + if let ty::Adt(def, ..) = expr_ty.kind { + let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did)); span_lint_and_sugg( cx, DEFAULT_TRAIT_ACCESS, diff --git a/tests/ui/default_trait_access.fixed b/tests/ui/default_trait_access.fixed new file mode 100644 index 00000000000..d05567a3f82 --- /dev/null +++ b/tests/ui/default_trait_access.fixed @@ -0,0 +1,106 @@ +// run-rustfix + +#![allow(unused_imports)] +#![deny(clippy::default_trait_access)] + +use std::default; +use std::default::Default as D2; +use std::string; + +fn main() { + let s1: String = std::string::String::default(); + + let s2 = String::default(); + + let s3: String = std::string::String::default(); + + let s4: String = std::string::String::default(); + + let s5 = string::String::default(); + + let s6: String = std::string::String::default(); + + let s7 = std::string::String::default(); + + let s8: String = DefaultFactory::make_t_badly(); + + let s9: String = DefaultFactory::make_t_nicely(); + + let s10 = DerivedDefault::default(); + + let s11: GenericDerivedDefault = GenericDerivedDefault::default(); + + let s12 = GenericDerivedDefault::::default(); + + let s13 = TupleDerivedDefault::default(); + + let s14: TupleDerivedDefault = TupleDerivedDefault::default(); + + let s15: ArrayDerivedDefault = ArrayDerivedDefault::default(); + + let s16 = ArrayDerivedDefault::default(); + + let s17: TupleStructDerivedDefault = TupleStructDerivedDefault::default(); + + let s18 = TupleStructDerivedDefault::default(); + + let s19 = ::default(); + + println!( + "[{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}], [{:?}]", + s1, + s2, + s3, + s4, + s5, + s6, + s7, + s8, + s9, + s10, + s11, + s12, + s13, + s14, + s15, + s16, + s17, + s18, + s19, + ); +} + +struct DefaultFactory; + +impl DefaultFactory { + pub fn make_t_badly() -> T { + Default::default() + } + + pub fn make_t_nicely() -> T { + T::default() + } +} + +#[derive(Debug, Default)] +struct DerivedDefault { + pub s: String, +} + +#[derive(Debug, Default)] +struct GenericDerivedDefault { + pub s: T, +} + +#[derive(Debug, Default)] +struct TupleDerivedDefault { + pub s: (String, String), +} + +#[derive(Debug, Default)] +struct ArrayDerivedDefault { + pub s: [String; 10], +} + +#[derive(Debug, Default)] +struct TupleStructDerivedDefault(String); diff --git a/tests/ui/default_trait_access.rs b/tests/ui/default_trait_access.rs index 2f1490a7036..447e70c0bbb 100644 --- a/tests/ui/default_trait_access.rs +++ b/tests/ui/default_trait_access.rs @@ -1,4 +1,7 @@ -#![warn(clippy::default_trait_access)] +// run-rustfix + +#![allow(unused_imports)] +#![deny(clippy::default_trait_access)] use std::default; use std::default::Default as D2; diff --git a/tests/ui/default_trait_access.stderr b/tests/ui/default_trait_access.stderr index 26b2057548b..df8a5b94ddc 100644 --- a/tests/ui/default_trait_access.stderr +++ b/tests/ui/default_trait_access.stderr @@ -1,49 +1,53 @@ error: calling `std::string::String::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:8:22 + --> $DIR/default_trait_access.rs:11:22 | LL | let s1: String = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()` | - = note: `-D clippy::default-trait-access` implied by `-D warnings` +note: the lint level is defined here + --> $DIR/default_trait_access.rs:4:9 + | +LL | #![deny(clippy::default_trait_access)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: calling `std::string::String::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:12:22 + --> $DIR/default_trait_access.rs:15:22 | LL | let s3: String = D2::default(); | ^^^^^^^^^^^^^ help: try: `std::string::String::default()` error: calling `std::string::String::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:14:22 + --> $DIR/default_trait_access.rs:17:22 | LL | let s4: String = std::default::Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()` error: calling `std::string::String::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:18:22 + --> $DIR/default_trait_access.rs:21:22 | LL | let s6: String = default::Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()` -error: calling `GenericDerivedDefault::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:28:46 +error: calling `GenericDerivedDefault::default()` is more clear than this expression + --> $DIR/default_trait_access.rs:31:46 | LL | let s11: GenericDerivedDefault = Default::default(); - | ^^^^^^^^^^^^^^^^^^ help: try: `GenericDerivedDefault::default()` + | ^^^^^^^^^^^^^^^^^^ help: try: `GenericDerivedDefault::default()` error: calling `TupleDerivedDefault::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:34:36 + --> $DIR/default_trait_access.rs:37:36 | LL | let s14: TupleDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `TupleDerivedDefault::default()` error: calling `ArrayDerivedDefault::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:36:36 + --> $DIR/default_trait_access.rs:39:36 | LL | let s15: ArrayDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `ArrayDerivedDefault::default()` error: calling `TupleStructDerivedDefault::default()` is more clear than this expression - --> $DIR/default_trait_access.rs:40:42 + --> $DIR/default_trait_access.rs:43:42 | LL | let s17: TupleStructDerivedDefault = Default::default(); | ^^^^^^^^^^^^^^^^^^ help: try: `TupleStructDerivedDefault::default()` -- cgit 1.4.1-3-g733a5 From afeb917fca7eaa5b44acd29a60f687024c0ebeac Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Tue, 1 Sep 2020 11:21:48 +1200 Subject: Fix a fp in `transmute_ptr_to_ptr` Avoid firing the lint when `transmute` in const contexts as dereferencing raw pointers in consts is unstable. cc #5959 --- clippy_lints/src/transmute.rs | 6 ++++-- tests/ui/transmute_ptr_to_ptr.rs | 8 ++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/transmute.rs b/clippy_lints/src/transmute.rs index 50d9c93f9d4..789d124eae2 100644 --- a/clippy_lints/src/transmute.rs +++ b/clippy_lints/src/transmute.rs @@ -331,8 +331,9 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id(); if match_def_path(cx, def_id, &paths::TRANSMUTE); then { - // Avoid suggesting from/to bits in const contexts. + // Avoid suggesting from/to bits and dereferencing raw pointers in const contexts. // See https://github.com/rust-lang/rust/issues/73736 for progress on making them `const fn`. + // And see https://github.com/rust-lang/rust/issues/51911 for dereferencing raw pointers. let const_context = in_constant(cx, e.hir_id); let from_ty = cx.typeck_results().expr_ty(&args[0]); @@ -486,7 +487,8 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { Applicability::Unspecified, ); } else { - if cx.tcx.erase_regions(&from_ty) != cx.tcx.erase_regions(&to_ty) { + if (cx.tcx.erase_regions(&from_ty) != cx.tcx.erase_regions(&to_ty)) + && !const_context { span_lint_and_then( cx, TRANSMUTE_PTR_TO_PTR, diff --git a/tests/ui/transmute_ptr_to_ptr.rs b/tests/ui/transmute_ptr_to_ptr.rs index 0d8a322f2b2..26b03bdc740 100644 --- a/tests/ui/transmute_ptr_to_ptr.rs +++ b/tests/ui/transmute_ptr_to_ptr.rs @@ -51,4 +51,12 @@ fn transmute_ptr_to_ptr() { let _: &GenericParam<&LifetimeParam<'static>> = unsafe { std::mem::transmute(&GenericParam { t: &lp }) }; } +// dereferencing raw pointers in const contexts, should not lint as it's unstable (issue 5959) +const _: &() = { + struct ZST; + let zst = &ZST; + + unsafe { std::mem::transmute::<&'static ZST, &'static ()>(zst) } +}; + fn main() {} -- cgit 1.4.1-3-g733a5 From 2e4b4cebbbb37efa5dc69dd2616f3b7a288b92aa Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 1 Sep 2020 12:09:32 +0900 Subject: useless_attribute: Permit wildcard_imports and enum_glob_use --- clippy_lints/src/attrs.rs | 38 ++++++++++++++++++++++---------------- tests/ui/useless_attribute.fixed | 8 ++++++++ tests/ui/useless_attribute.rs | 8 ++++++++ tests/ui/useless_attribute.stderr | 2 +- 4 files changed, 39 insertions(+), 17 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index cfcc1b3c5f3..c8f153e7201 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -71,8 +71,9 @@ declare_clippy_lint! { /// **What it does:** Checks for `extern crate` and `use` items annotated with /// lint attributes. /// - /// This lint permits `#[allow(unused_imports)]`, `#[allow(deprecated)]` and - /// `#[allow(unreachable_pub)]` on `use` items and `#[allow(unused_imports)]` on + /// This lint permits `#[allow(unused_imports)]`, `#[allow(deprecated)]`, + /// `#[allow(unreachable_pub)]`, `#[allow(clippy::wildcard_imports)]` and + /// `#[allow(clippy::enum_glob_use)]` on `use` items and `#[allow(unused_imports)]` on /// `extern crate` items with a `#[macro_use]` attribute. /// /// **Why is this bad?** Lint attributes have no effect on crate imports. Most @@ -318,7 +319,8 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { if let Some(ident) = attr.ident() { match &*ident.as_str() { "allow" | "warn" | "deny" | "forbid" => { - // permit `unused_imports`, `deprecated` and `unreachable_pub` for `use` items + // permit `unused_imports`, `deprecated`, `unreachable_pub`, + // `clippy::wildcard_imports`, and `clippy::enum_glob_use` for `use` items // and `unused_imports` for `extern crate` items with `macro_use` for lint in lint_list { match item.kind { @@ -327,6 +329,9 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { || is_word(lint, sym!(deprecated)) || is_word(lint, sym!(unreachable_pub)) || is_word(lint, sym!(unused)) + || extract_clippy_lint(lint) + .map_or(false, |s| s == "wildcard_imports") + || extract_clippy_lint(lint).map_or(false, |s| s == "enum_glob_use") { return; } @@ -387,24 +392,25 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { } } -fn check_clippy_lint_names(cx: &LateContext<'_>, ident: &str, items: &[NestedMetaItem]) { - fn extract_name(lint: &NestedMetaItem) -> Option { - if_chain! { - if let Some(meta_item) = lint.meta_item(); - if meta_item.path.segments.len() > 1; - if let tool_name = meta_item.path.segments[0].ident; - if tool_name.as_str() == "clippy"; - let lint_name = meta_item.path.segments.last().unwrap().ident.name; - then { - return Some(lint_name.as_str()); - } +/// Returns the lint name if it is clippy lint. +fn extract_clippy_lint(lint: &NestedMetaItem) -> Option { + if_chain! { + if let Some(meta_item) = lint.meta_item(); + if meta_item.path.segments.len() > 1; + if let tool_name = meta_item.path.segments[0].ident; + if tool_name.as_str() == "clippy"; + let lint_name = meta_item.path.segments.last().unwrap().ident.name; + then { + return Some(lint_name.as_str()); } - None } + None +} +fn check_clippy_lint_names(cx: &LateContext<'_>, ident: &str, items: &[NestedMetaItem]) { let lint_store = cx.lints(); for lint in items { - if let Some(lint_name) = extract_name(lint) { + if let Some(lint_name) = extract_clippy_lint(lint) { if let CheckLintNameResult::Tool(Err((None, _))) = lint_store.check_lint_name(&lint_name, Some(sym!(clippy))) { diff --git a/tests/ui/useless_attribute.fixed b/tests/ui/useless_attribute.fixed index b222e2f7976..a5fcde768f1 100644 --- a/tests/ui/useless_attribute.fixed +++ b/tests/ui/useless_attribute.fixed @@ -49,6 +49,14 @@ mod a { pub use self::b::C; } +// don't lint on clippy::wildcard_imports for `use` items +#[allow(clippy::wildcard_imports)] +pub use std::io::prelude::*; + +// don't lint on clippy::enum_glob_use for `use` items +#[allow(clippy::enum_glob_use)] +pub use std::cmp::Ordering::*; + fn test_indented_attr() { #![allow(clippy::almost_swapped)] use std::collections::HashSet; diff --git a/tests/ui/useless_attribute.rs b/tests/ui/useless_attribute.rs index 3422eace4ab..0396d39e3d5 100644 --- a/tests/ui/useless_attribute.rs +++ b/tests/ui/useless_attribute.rs @@ -49,6 +49,14 @@ mod a { pub use self::b::C; } +// don't lint on clippy::wildcard_imports for `use` items +#[allow(clippy::wildcard_imports)] +pub use std::io::prelude::*; + +// don't lint on clippy::enum_glob_use for `use` items +#[allow(clippy::enum_glob_use)] +pub use std::cmp::Ordering::*; + fn test_indented_attr() { #[allow(clippy::almost_swapped)] use std::collections::HashSet; diff --git a/tests/ui/useless_attribute.stderr b/tests/ui/useless_attribute.stderr index 57ba976730c..d0194e4bbbe 100644 --- a/tests/ui/useless_attribute.stderr +++ b/tests/ui/useless_attribute.stderr @@ -13,7 +13,7 @@ LL | #[cfg_attr(feature = "cargo-clippy", allow(dead_code))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![cfg_attr(feature = "cargo-clippy", allow(dead_code)` error: useless lint attribute - --> $DIR/useless_attribute.rs:53:5 + --> $DIR/useless_attribute.rs:61:5 | LL | #[allow(clippy::almost_swapped)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![allow(clippy::almost_swapped)]` -- cgit 1.4.1-3-g733a5 From aa7ffa5257667edb284de16b529df3d4111d70ab Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 1 Sep 2020 22:39:09 +0900 Subject: Fix FP in `same_item_push` Don't emit a lint when the pushed item doesn't have Clone trait --- clippy_lints/src/loops.rs | 78 +++++++++++++++++++++++++--------------------- tests/ui/same_item_push.rs | 6 ++++ 2 files changed, 49 insertions(+), 35 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index c95e43a9430..25345f8fa31 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1140,43 +1140,51 @@ fn detect_same_item_push<'tcx>( walk_expr(&mut same_item_push_visitor, body); if same_item_push_visitor.should_lint { if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push { - // Make sure that the push does not involve possibly mutating values - if let PatKind::Wild = pat.kind { - let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); - let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); - if let ExprKind::Path(ref qpath) = pushed_item.kind { - if_chain! { - if let Res::Local(hir_id) = qpath_res(cx, qpath, pushed_item.hir_id); - let node = cx.tcx.hir().get(hir_id); - if let Node::Binding(pat) = node; - if let PatKind::Binding(bind_ann, ..) = pat.kind; - if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable); - then { - span_lint_and_help( - cx, - SAME_ITEM_PUSH, - vec.span, - "it looks like the same item is being pushed into this Vec", - None, - &format!( - "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", - item_str, vec_str, item_str - ), - ) + let ty = cx.typeck_results().expr_ty(pushed_item); + if cx + .tcx + .lang_items() + .clone_trait() + .map_or(false, |id| implements_trait(cx, ty, id, &[])) + { + // Make sure that the push does not involve possibly mutating values + if let PatKind::Wild = pat.kind { + let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); + let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); + if let ExprKind::Path(ref qpath) = pushed_item.kind { + if_chain! { + if let Res::Local(hir_id) = qpath_res(cx, qpath, pushed_item.hir_id); + let node = cx.tcx.hir().get(hir_id); + if let Node::Binding(pat) = node; + if let PatKind::Binding(bind_ann, ..) = pat.kind; + if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable); + then { + span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ) + } } + } else if mutated_variables(pushed_item, cx).map_or(false, |mutvars| mutvars.is_empty()) { + span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ) } - } else if mutated_variables(pushed_item, cx).map_or(false, |mutvars| mutvars.is_empty()) { - span_lint_and_help( - cx, - SAME_ITEM_PUSH, - vec.span, - "it looks like the same item is being pushed into this Vec", - None, - &format!( - "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", - item_str, vec_str, item_str - ), - ) } } } diff --git a/tests/ui/same_item_push.rs b/tests/ui/same_item_push.rs index bfe27e02044..0903a873826 100644 --- a/tests/ui/same_item_push.rs +++ b/tests/ui/same_item_push.rs @@ -94,4 +94,10 @@ fn main() { vec13.push(item); item += 10; } + + // Fix #5979 + let mut vec14: Vec = Vec::new(); + for _ in 0..10 { + vec14.push(std::fs::File::open("foobar").unwrap()); + } } -- cgit 1.4.1-3-g733a5 From 202a80c927412a548180eca5990ee764d76ed643 Mon Sep 17 00:00:00 2001 From: Ricky Date: Tue, 1 Sep 2020 16:59:37 -0400 Subject: Added tests for map_err, ignored map_err lint on drop_ref tests --- clippy_lints/src/map_err_ignore.rs | 49 ++++++++++++++++++++++---------------- tests/ui/drop_ref.rs | 1 + tests/ui/drop_ref.stderr | 36 ++++++++++++++-------------- tests/ui/map_err.rs | 24 +++++++++++++++++++ tests/ui/map_err.stderr | 10 ++++++++ 5 files changed, 82 insertions(+), 38 deletions(-) create mode 100644 tests/ui/map_err.rs create mode 100644 tests/ui/map_err.stderr (limited to 'tests') diff --git a/clippy_lints/src/map_err_ignore.rs b/clippy_lints/src/map_err_ignore.rs index c63c201a9f3..43bfcf0b8f1 100644 --- a/clippy_lints/src/map_err_ignore.rs +++ b/clippy_lints/src/map_err_ignore.rs @@ -1,6 +1,6 @@ use crate::utils::span_lint_and_sugg; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, CaptureBy, PatKind, QPath}; +use rustc_hir::{CaptureBy, Expr, ExprKind, PatKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -18,7 +18,7 @@ declare_clippy_lint! { /// Ignore ///} ///fn main() -> Result<(), Errors> { - /// + /// /// let x = u32::try_from(-123_i32); /// /// println!("{:?}", x.map_err(|_| Errors::Ignore)); @@ -32,7 +32,7 @@ declare_clippy_lint! { /// WithContext(TryFromIntError) ///} ///fn main() -> Result<(), Errors> { - /// + /// /// let x = u32::try_from(-123_i32); /// /// println!("{:?}", x.map_err(|e| Errors::WithContext(e))); @@ -48,7 +48,7 @@ declare_clippy_lint! { declare_lint_pass!(MapErrIgnore => [MAP_ERR_IGNORE]); impl<'tcx> LateLintPass<'tcx> for MapErrIgnore { - // do not try to lint if this is from a macro or desugaring + // do not try to lint if this is from a macro or desugaring fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) { if e.span.from_expansion() { return; @@ -56,26 +56,34 @@ impl<'tcx> LateLintPass<'tcx> for MapErrIgnore { // check if this is a method call (e.g. x.foo()) if let ExprKind::MethodCall(ref method, _t_span, ref args, _) = e.kind { - // only work if the method name is `map_err` and there are only 2 arguments (e.g. x.map_err(|_|[1] Enum::Variant[2])) + // only work if the method name is `map_err` and there are only 2 arguments (e.g. x.map_err(|_|[1] + // Enum::Variant[2])) if method.ident.as_str() == "map_err" && args.len() == 2 { // make sure the first argument is a closure, and grab the CaptureRef, body_id, and body_span fields - if let ExprKind::Closure(capture, _, body_id, body_span, _) = args[1].kind { + if let ExprKind::Closure(capture, _, body_id, body_span, _) = args[1].kind { // check if this is by Reference (meaning there's no move statement) - if capture == CaptureBy::Ref { - // Get the closure body to check the parameters and values + if capture == CaptureBy::Ref { + // Get the closure body to check the parameters and values let closure_body = cx.tcx.hir().body(body_id); // make sure there's only one parameter (`|_|`) - if closure_body.params.len() == 1 { - // make sure that parameter is the wild token (`_`) + if closure_body.params.len() == 1 { + // make sure that parameter is the wild token (`_`) if let PatKind::Wild = closure_body.params[0].pat.kind { - // Check the value of the closure to see if we can build the enum we are throwing away the error for - // make sure this is a Path + // Check the value of the closure to see if we can build the enum we are throwing away + // the error for make sure this is a Path if let ExprKind::Path(q_path) = &closure_body.value.kind { // this should be a resolved path, only keep the path field if let QPath::Resolved(_, path) = q_path { - // finally get the idents for each path segment collect them as a string and join them with the path separator ("::"") - let closure_fold: String = path.segments.iter().map(|x| x.ident.as_str().to_string()).collect::>().join("::"); - //Span the body of the closure (the |...| bit) and suggest the fix by taking the error and encapsulating it in the enum + // finally get the idents for each path segment collect them as a string and + // join them with the path separator ("::"") + let closure_fold: String = path + .segments + .iter() + .map(|x| x.ident.as_str().to_string()) + .collect::>() + .join("::"); + //Span the body of the closure (the |...| bit) and suggest the fix by taking + // the error and encapsulating it in the enum span_lint_and_sugg( cx, MAP_ERR_IGNORE, @@ -84,10 +92,11 @@ impl<'tcx> LateLintPass<'tcx> for MapErrIgnore { "Allow the error enum to encapsulate the original error", format!("|e| {}(e)", closure_fold), Applicability::HasPlaceholders, - ); + ); } } else { - //If we cannot build the enum in a human readable way just suggest not throwing way the error + //If we cannot build the enum in a human readable way just suggest not throwing way + // the error span_lint_and_sugg( cx, MAP_ERR_IGNORE, @@ -96,13 +105,13 @@ impl<'tcx> LateLintPass<'tcx> for MapErrIgnore { "Allow the error enum to encapsulate the original error", "|e|".to_string(), Applicability::HasPlaceholders, - ); + ); } } } - } + } } } } } -} \ No newline at end of file +} diff --git a/tests/ui/drop_ref.rs b/tests/ui/drop_ref.rs index 9181d789d4f..6b5bcdaa78e 100644 --- a/tests/ui/drop_ref.rs +++ b/tests/ui/drop_ref.rs @@ -1,5 +1,6 @@ #![warn(clippy::drop_ref)] #![allow(clippy::toplevel_ref_arg)] +#![allow(clippy::map_err_ignore)] use std::mem::drop; diff --git a/tests/ui/drop_ref.stderr b/tests/ui/drop_ref.stderr index 35ae88b78a4..7974bf56d44 100644 --- a/tests/ui/drop_ref.stderr +++ b/tests/ui/drop_ref.stderr @@ -1,108 +1,108 @@ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:9:5 + --> $DIR/drop_ref.rs:10:5 | LL | drop(&SomeStruct); | ^^^^^^^^^^^^^^^^^ | = note: `-D clippy::drop-ref` implied by `-D warnings` note: argument has type `&SomeStruct` - --> $DIR/drop_ref.rs:9:10 + --> $DIR/drop_ref.rs:10:10 | LL | drop(&SomeStruct); | ^^^^^^^^^^^ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:12:5 + --> $DIR/drop_ref.rs:13:5 | LL | drop(&owned1); | ^^^^^^^^^^^^^ | note: argument has type `&SomeStruct` - --> $DIR/drop_ref.rs:12:10 + --> $DIR/drop_ref.rs:13:10 | LL | drop(&owned1); | ^^^^^^^ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:13:5 + --> $DIR/drop_ref.rs:14:5 | LL | drop(&&owned1); | ^^^^^^^^^^^^^^ | note: argument has type `&&SomeStruct` - --> $DIR/drop_ref.rs:13:10 + --> $DIR/drop_ref.rs:14:10 | LL | drop(&&owned1); | ^^^^^^^^ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:14:5 + --> $DIR/drop_ref.rs:15:5 | LL | drop(&mut owned1); | ^^^^^^^^^^^^^^^^^ | note: argument has type `&mut SomeStruct` - --> $DIR/drop_ref.rs:14:10 + --> $DIR/drop_ref.rs:15:10 | LL | drop(&mut owned1); | ^^^^^^^^^^^ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:18:5 + --> $DIR/drop_ref.rs:19:5 | LL | drop(reference1); | ^^^^^^^^^^^^^^^^ | note: argument has type `&SomeStruct` - --> $DIR/drop_ref.rs:18:10 + --> $DIR/drop_ref.rs:19:10 | LL | drop(reference1); | ^^^^^^^^^^ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:21:5 + --> $DIR/drop_ref.rs:22:5 | LL | drop(reference2); | ^^^^^^^^^^^^^^^^ | note: argument has type `&mut SomeStruct` - --> $DIR/drop_ref.rs:21:10 + --> $DIR/drop_ref.rs:22:10 | LL | drop(reference2); | ^^^^^^^^^^ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:24:5 + --> $DIR/drop_ref.rs:25:5 | LL | drop(reference3); | ^^^^^^^^^^^^^^^^ | note: argument has type `&SomeStruct` - --> $DIR/drop_ref.rs:24:10 + --> $DIR/drop_ref.rs:25:10 | LL | drop(reference3); | ^^^^^^^^^^ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:29:5 + --> $DIR/drop_ref.rs:30:5 | LL | drop(&val); | ^^^^^^^^^^ | note: argument has type `&T` - --> $DIR/drop_ref.rs:29:10 + --> $DIR/drop_ref.rs:30:10 | LL | drop(&val); | ^^^^ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:37:5 + --> $DIR/drop_ref.rs:38:5 | LL | std::mem::drop(&SomeStruct); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: argument has type `&SomeStruct` - --> $DIR/drop_ref.rs:37:20 + --> $DIR/drop_ref.rs:38:20 | LL | std::mem::drop(&SomeStruct); | ^^^^^^^^^^^ diff --git a/tests/ui/map_err.rs b/tests/ui/map_err.rs new file mode 100644 index 00000000000..f3a74ad95cd --- /dev/null +++ b/tests/ui/map_err.rs @@ -0,0 +1,24 @@ +use std::convert::TryFrom; +use std::error::Error; +use std::fmt; + +#[derive(Debug)] +enum Errors { + Ignored, +} + +impl Error for Errors {} + +impl fmt::Display for Errors { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Error") + } +} + +fn main() -> Result<(), Errors> { + let x = u32::try_from(-123_i32); + + println!("{:?}", x.map_err(|_| Errors::Ignored)); + + Ok(()) +} diff --git a/tests/ui/map_err.stderr b/tests/ui/map_err.stderr new file mode 100644 index 00000000000..8cd37d8c025 --- /dev/null +++ b/tests/ui/map_err.stderr @@ -0,0 +1,10 @@ +error: `map_err` has thrown away the original error + --> $DIR/map_err.rs:21:32 + | +LL | println!("{:?}", x.map_err(|_| Errors::Ignored)); + | ^^^ help: Allow the error enum to encapsulate the original error: `|e| Errors::Ignored(e)` + | + = note: `-D clippy::map-err-ignore` implied by `-D warnings` + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From 22c994061359aa9e0e08e44cd698dbfc6553834c Mon Sep 17 00:00:00 2001 From: Koxiaet <38139193+Koxiaet@users.noreply.github.com> Date: Wed, 2 Sep 2020 12:51:44 +0100 Subject: Add tests for allowed non-backticked identifiers in doc --- tests/ui/doc.rs | 13 ++++++++++--- tests/ui/doc.stderr | 50 ++++++++++++++++++++++++++++---------------------- 2 files changed, 38 insertions(+), 25 deletions(-) (limited to 'tests') diff --git a/tests/ui/doc.rs b/tests/ui/doc.rs index 77620c857e6..68c5d32846f 100644 --- a/tests/ui/doc.rs +++ b/tests/ui/doc.rs @@ -49,6 +49,16 @@ fn test_emphasis() { fn test_units() { } +/// This tests allowed identifiers. +/// DirectX +/// ECMAScript +/// OAuth GraphQL +/// TeX LaTeX BibTeX BibLaTeX +/// CamelCase (see also #2395) +/// be_sure_we_got_to_the_end_of_it +fn test_allowed() { +} + /// This test has [a link_with_underscores][chunked-example] inside it. See #823. /// See also [the issue tracker](https://github.com/rust-lang/rust-clippy/search?q=clippy::doc_markdown&type=Issues) /// on GitHub (which is a camel-cased word, but is OK). And here is another [inline link][inline_link]. @@ -168,9 +178,6 @@ fn issue_1920() {} /// Not ok: http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels fn issue_1832() {} -/// Ok: CamelCase (It should not be surrounded by backticks) -fn issue_2395() {} - /// An iterator over mycrate::Collection's values. /// It should not lint a `'static` lifetime in ticks. fn issue_2210() {} diff --git a/tests/ui/doc.stderr b/tests/ui/doc.stderr index ae9bb394cb9..23fca43590b 100644 --- a/tests/ui/doc.stderr +++ b/tests/ui/doc.stderr @@ -54,131 +54,137 @@ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the doc LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation + --> $DIR/doc.rs:58:5 + | +LL | /// be_sure_we_got_to_the_end_of_it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: you should put `link_with_underscores` between ticks in the documentation - --> $DIR/doc.rs:52:22 + --> $DIR/doc.rs:62:22 | LL | /// This test has [a link_with_underscores][chunked-example] inside it. See #823. | ^^^^^^^^^^^^^^^^^^^^^ error: you should put `inline_link2` between ticks in the documentation - --> $DIR/doc.rs:55:21 + --> $DIR/doc.rs:65:21 | LL | /// It can also be [inline_link2]. | ^^^^^^^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:65:5 + --> $DIR/doc.rs:75:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `CamelCaseThing` between ticks in the documentation - --> $DIR/doc.rs:73:8 + --> $DIR/doc.rs:83:8 | LL | /// ## CamelCaseThing | ^^^^^^^^^^^^^^ error: you should put `CamelCaseThing` between ticks in the documentation - --> $DIR/doc.rs:76:7 + --> $DIR/doc.rs:86:7 | LL | /// # CamelCaseThing | ^^^^^^^^^^^^^^ error: you should put `CamelCaseThing` between ticks in the documentation - --> $DIR/doc.rs:78:22 + --> $DIR/doc.rs:88:22 | LL | /// Not a title #897 CamelCaseThing | ^^^^^^^^^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:79:5 + --> $DIR/doc.rs:89:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:86:5 + --> $DIR/doc.rs:96:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:99:5 + --> $DIR/doc.rs:109:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `FooBar` between ticks in the documentation - --> $DIR/doc.rs:110:43 + --> $DIR/doc.rs:120:43 | LL | /** E.g., serialization of an empty list: FooBar | ^^^^^^ error: you should put `BarQuz` between ticks in the documentation - --> $DIR/doc.rs:115:5 + --> $DIR/doc.rs:125:5 | LL | And BarQuz too. | ^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:116:1 + --> $DIR/doc.rs:126:1 | LL | be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `FooBar` between ticks in the documentation - --> $DIR/doc.rs:121:43 + --> $DIR/doc.rs:131:43 | LL | /** E.g., serialization of an empty list: FooBar | ^^^^^^ error: you should put `BarQuz` between ticks in the documentation - --> $DIR/doc.rs:126:5 + --> $DIR/doc.rs:136:5 | LL | And BarQuz too. | ^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:127:1 + --> $DIR/doc.rs:137:1 | LL | be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:138:5 + --> $DIR/doc.rs:148:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> $DIR/doc.rs:165:13 + --> $DIR/doc.rs:175:13 | LL | /// Not ok: http://www.unicode.org | ^^^^^^^^^^^^^^^^^^^^^^ error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> $DIR/doc.rs:166:13 + --> $DIR/doc.rs:176:13 | LL | /// Not ok: https://www.unicode.org | ^^^^^^^^^^^^^^^^^^^^^^^ error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> $DIR/doc.rs:167:13 + --> $DIR/doc.rs:177:13 | LL | /// Not ok: http://www.unicode.org/ | ^^^^^^^^^^^^^^^^^^^^^^ error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> $DIR/doc.rs:168:13 + --> $DIR/doc.rs:178:13 | LL | /// Not ok: http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `mycrate::Collection` between ticks in the documentation - --> $DIR/doc.rs:174:22 + --> $DIR/doc.rs:181:22 | LL | /// An iterator over mycrate::Collection's values. | ^^^^^^^^^^^^^^^^^^^ -error: aborting due to 30 previous errors +error: aborting due to 31 previous errors -- cgit 1.4.1-3-g733a5 From 2387f68e437bf2ff5f117f63936257ce64052cfa Mon Sep 17 00:00:00 2001 From: Ricky Date: Wed, 2 Sep 2020 19:21:34 -0400 Subject: Removed map_err suggestion in lint, and updated lint documentation example --- clippy_lints/src/map_err_ignore.rs | 117 ++++++++++++++++++------------------- tests/ui/map_err.stderr | 5 +- 2 files changed, 60 insertions(+), 62 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/map_err_ignore.rs b/clippy_lints/src/map_err_ignore.rs index 43bfcf0b8f1..9211113ed04 100644 --- a/clippy_lints/src/map_err_ignore.rs +++ b/clippy_lints/src/map_err_ignore.rs @@ -1,6 +1,6 @@ -use crate::utils::span_lint_and_sugg; -use rustc_errors::Applicability; -use rustc_hir::{CaptureBy, Expr, ExprKind, PatKind, QPath}; +use crate::utils::span_lint_and_help; + +use rustc_hir::{CaptureBy, Expr, ExprKind, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -12,33 +12,58 @@ declare_clippy_lint! { /// **Known problems:** None. /// /// **Example:** - /// + /// Before: /// ```rust + /// use std::convert::TryFrom; + /// + /// #[derive(Debug)] /// enum Errors { - /// Ignore - ///} - ///fn main() -> Result<(), Errors> { + /// Ignored + /// } /// - /// let x = u32::try_from(-123_i32); + /// fn divisible_by_3(inp: i32) -> Result { + /// let i = u32::try_from(inp).map_err(|_| Errors::Ignored)?; /// - /// println!("{:?}", x.map_err(|_| Errors::Ignore)); + /// Ok(i) + /// } + /// ``` /// - /// Ok(()) - ///} - /// ``` - /// Use instead: - /// ```rust - /// enum Errors { - /// WithContext(TryFromIntError) - ///} - ///fn main() -> Result<(), Errors> { + /// After: + /// ```rust + /// use std::convert::TryFrom; + /// use std::num::TryFromIntError; + /// use std::fmt; + /// use std::error::Error; + /// + /// #[derive(Debug)] + /// enum ParseError { + /// Indivisible { + /// source: TryFromIntError, + /// input: String, + /// } + /// } + /// + /// impl fmt::Display for ParseError { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// match &self { + /// ParseError::Indivisible{source: _, input} => write!(f, "Error: {}", input) + /// } + /// } + /// } + /// + /// impl Error for ParseError {} /// - /// let x = u32::try_from(-123_i32); + /// impl ParseError { + /// fn new(source: TryFromIntError, input: String) -> ParseError { + /// ParseError::Indivisible{source, input} + /// } + /// } /// - /// println!("{:?}", x.map_err(|e| Errors::WithContext(e))); + /// fn divisible_by_3(inp: i32) -> Result { + /// let i = u32::try_from(inp).map_err(|e| ParseError::new(e, e.to_string()))?; /// - /// Ok(()) - ///} + /// Ok(i) + /// } /// ``` pub MAP_ERR_IGNORE, style, @@ -69,44 +94,16 @@ impl<'tcx> LateLintPass<'tcx> for MapErrIgnore { if closure_body.params.len() == 1 { // make sure that parameter is the wild token (`_`) if let PatKind::Wild = closure_body.params[0].pat.kind { - // Check the value of the closure to see if we can build the enum we are throwing away - // the error for make sure this is a Path - if let ExprKind::Path(q_path) = &closure_body.value.kind { - // this should be a resolved path, only keep the path field - if let QPath::Resolved(_, path) = q_path { - // finally get the idents for each path segment collect them as a string and - // join them with the path separator ("::"") - let closure_fold: String = path - .segments - .iter() - .map(|x| x.ident.as_str().to_string()) - .collect::>() - .join("::"); - //Span the body of the closure (the |...| bit) and suggest the fix by taking - // the error and encapsulating it in the enum - span_lint_and_sugg( - cx, - MAP_ERR_IGNORE, - body_span, - "`map_err` has thrown away the original error", - "Allow the error enum to encapsulate the original error", - format!("|e| {}(e)", closure_fold), - Applicability::HasPlaceholders, - ); - } - } else { - //If we cannot build the enum in a human readable way just suggest not throwing way - // the error - span_lint_and_sugg( - cx, - MAP_ERR_IGNORE, - body_span, - "`map_err` has thrown away the original error", - "Allow the error enum to encapsulate the original error", - "|e|".to_string(), - Applicability::HasPlaceholders, - ); - } + // span the area of the closure capture and warn that the + // original error will be thrown away + span_lint_and_help( + cx, + MAP_ERR_IGNORE, + body_span, + "`map_else(|_|...` ignores the original error", + None, + "Consider wrapping the error in an enum variant", + ); } } } diff --git a/tests/ui/map_err.stderr b/tests/ui/map_err.stderr index 8cd37d8c025..7a269ab95ab 100644 --- a/tests/ui/map_err.stderr +++ b/tests/ui/map_err.stderr @@ -1,10 +1,11 @@ -error: `map_err` has thrown away the original error +error: `map_else(|_|...` ignores the original error --> $DIR/map_err.rs:21:32 | LL | println!("{:?}", x.map_err(|_| Errors::Ignored)); - | ^^^ help: Allow the error enum to encapsulate the original error: `|e| Errors::Ignored(e)` + | ^^^ | = note: `-D clippy::map-err-ignore` implied by `-D warnings` + = help: Consider wrapping the error in an enum variant error: aborting due to previous error -- cgit 1.4.1-3-g733a5 From 93ce686b5df94f52a040a05c9434b4341efffec5 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Thu, 3 Sep 2020 04:58:14 +0200 Subject: Update ui stderr with improved rustc output Related rust pull request: rust-lang/rust#76160 --- tests/ui/issue-3145.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/ui/issue-3145.stderr b/tests/ui/issue-3145.stderr index cb0d95f5e26..8f2922b022a 100644 --- a/tests/ui/issue-3145.stderr +++ b/tests/ui/issue-3145.stderr @@ -1,4 +1,4 @@ -error: expected token: `,` +error: expected `,`, found `a` --> $DIR/issue-3145.rs:2:19 | LL | println!("{}" a); //~ERROR expected token: `,` -- cgit 1.4.1-3-g733a5 From db16fa26cef82fff84751dcad7940fd9b819a169 Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Fri, 4 Sep 2020 20:16:02 -0600 Subject: run tests/ui/update-all-references.sh --- tests/ui/functions_maxlines.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/ui/functions_maxlines.stderr b/tests/ui/functions_maxlines.stderr index c640c82d6d7..dc6c8ba2f15 100644 --- a/tests/ui/functions_maxlines.stderr +++ b/tests/ui/functions_maxlines.stderr @@ -1,4 +1,4 @@ -error: this function has a large number of lines +error: this function has too many lines (102/100) --> $DIR/functions_maxlines.rs:58:1 | LL | / fn bad_lines() { -- cgit 1.4.1-3-g733a5 From 9e7ce9d3851426f59ec98eabee7f113e4fd18198 Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Fri, 4 Sep 2020 21:12:16 -0600 Subject: run the specific script suggested by the error message ``` ./tests/ui-toml/update-references.sh './target/debug/test_build_base' 'functions_maxlines/test.rs' ``` --- tests/ui-toml/functions_maxlines/test.stderr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/ui-toml/functions_maxlines/test.stderr b/tests/ui-toml/functions_maxlines/test.stderr index fb12257021a..a27ce945ca5 100644 --- a/tests/ui-toml/functions_maxlines/test.stderr +++ b/tests/ui-toml/functions_maxlines/test.stderr @@ -1,4 +1,4 @@ -error: this function has a large number of lines +error: this function has too many lines (2/1) --> $DIR/test.rs:18:1 | LL | / fn too_many_lines() { @@ -9,7 +9,7 @@ LL | | } | = note: `-D clippy::too-many-lines` implied by `-D warnings` -error: this function has a large number of lines +error: this function has too many lines (2/1) --> $DIR/test.rs:38:1 | LL | / fn comment_before_code() { -- cgit 1.4.1-3-g733a5 From 96b31a5b36ed06b6781804d3384a3a84a52b8ce6 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Sun, 6 Sep 2020 00:02:35 +0900 Subject: Fix FP when coercion kicks in --- clippy_lints/src/loops.rs | 3 ++- tests/ui/same_item_push.rs | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 25345f8fa31..b65ae15edcd 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1140,7 +1140,8 @@ fn detect_same_item_push<'tcx>( walk_expr(&mut same_item_push_visitor, body); if same_item_push_visitor.should_lint { if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push { - let ty = cx.typeck_results().expr_ty(pushed_item); + let vec_ty = cx.typeck_results().expr_ty(vec); + let ty = vec_ty.walk().nth(1).unwrap().expect_ty(); if cx .tcx .lang_items() diff --git a/tests/ui/same_item_push.rs b/tests/ui/same_item_push.rs index 0903a873826..0928820892b 100644 --- a/tests/ui/same_item_push.rs +++ b/tests/ui/same_item_push.rs @@ -100,4 +100,15 @@ fn main() { for _ in 0..10 { vec14.push(std::fs::File::open("foobar").unwrap()); } + // Fix #5979 + #[derive(Clone)] + struct S {} + + trait T {} + impl T for S {} + + let mut vec15: Vec> = Vec::new(); + for _ in 0..10 { + vec15.push(Box::new(S {})); + } } -- cgit 1.4.1-3-g733a5 From 390a13b06c79d4177b829097b06453e38188081f Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Thu, 27 Aug 2020 23:22:32 +0200 Subject: needless-lifetime - fix nested elision site FPs --- clippy_lints/src/lifetimes.rs | 69 +++++++++++++++++++++++++++++++++++--- clippy_lints/src/utils/paths.rs | 3 ++ tests/ui/needless_lifetimes.rs | 32 ++++++++++++++++++ tests/ui/needless_lifetimes.stderr | 8 +---- 4 files changed, 101 insertions(+), 11 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 4df6827d77f..ab6e34d201c 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -1,13 +1,14 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{ - walk_fn_decl, walk_generic_param, walk_generics, walk_param_bound, walk_ty, NestedVisitorMap, Visitor, + walk_fn_decl, walk_generic_param, walk_generics, walk_param_bound, walk_trait_ref, walk_ty, NestedVisitorMap, + Visitor, }; use rustc_hir::FnRetTy::Return; use rustc_hir::{ BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, ImplItemKind, Item, - ItemKind, Lifetime, LifetimeName, ParamName, QPath, TraitBoundModifier, TraitFn, TraitItem, TraitItemKind, Ty, - TyKind, WhereClause, WherePredicate, + ItemKind, Lifetime, LifetimeName, ParamName, QPath, TraitBoundModifier, TraitFn, TraitItem, TraitItemKind, + TraitRef, Ty, TyKind, WhereClause, WherePredicate, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; @@ -15,7 +16,8 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::symbol::{kw, Symbol}; -use crate::utils::{in_macro, last_path_segment, span_lint, trait_ref_of_method}; +use crate::utils::paths; +use crate::utils::{get_trait_def_id, in_macro, last_path_segment, span_lint, trait_ref_of_method}; declare_clippy_lint! { /// **What it does:** Checks for lifetime annotations which can be removed by @@ -127,6 +129,14 @@ fn check_fn_inner<'tcx>( return; } + // fn pointers and closure trait bounds are also lifetime elision sites. This lint does not + // support nested elision sites in a fn item. + if FnPointerOrClosureTraitBoundFinder::find_in_generics(cx, generics) + || FnPointerOrClosureTraitBoundFinder::find_in_fn_decl(cx, decl) + { + return; + } + let mut bounds_lts = Vec::new(); let types = generics .params @@ -523,3 +533,54 @@ impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker { NestedVisitorMap::None } } + +const CLOSURE_TRAIT_BOUNDS: [&[&str]; 3] = [&paths::FN, &paths::FN_MUT, &paths::FN_ONCE]; + +struct FnPointerOrClosureTraitBoundFinder<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + found: bool, +} + +impl<'a, 'tcx> FnPointerOrClosureTraitBoundFinder<'a, 'tcx> { + fn find_in_generics(cx: &'a LateContext<'tcx>, generics: &'tcx Generics<'tcx>) -> bool { + let mut finder = Self { cx, found: false }; + finder.visit_generics(generics); + finder.found + } + + fn find_in_fn_decl(cx: &'a LateContext<'tcx>, generics: &'tcx FnDecl<'tcx>) -> bool { + let mut finder = Self { cx, found: false }; + finder.visit_fn_decl(generics); + finder.found + } +} + +impl<'a, 'tcx> Visitor<'tcx> for FnPointerOrClosureTraitBoundFinder<'a, 'tcx> { + type Map = Map<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + + fn visit_trait_ref(&mut self, tref: &'tcx TraitRef<'tcx>) { + if CLOSURE_TRAIT_BOUNDS + .iter() + .any(|trait_path| tref.trait_def_id() == get_trait_def_id(self.cx, trait_path)) + { + self.found = true; + } + walk_trait_ref(self, tref); + } + + fn visit_ty(&mut self, ty: &'tcx Ty<'tcx>) { + match ty.kind { + TyKind::BareFn(..) => self.found = true, + TyKind::OpaqueDef(item_id, _) => { + let map = self.cx.tcx.hir(); + let item = map.expect_item(item_id.id); + self.visit_item(item); + }, + _ => (), + } + walk_ty(self, ty); + } +} diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index d44854aefe9..9837759fd5e 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -42,6 +42,9 @@ pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"]; pub const FMT_ARGUMENTS_NEW_V1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"]; pub const FMT_ARGUMENTS_NEW_V1_FORMATTED: [&str; 4] = ["core", "fmt", "Arguments", "new_v1_formatted"]; pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"]; +pub const FN: [&str; 3] = ["core", "ops", "Fn"]; +pub const FN_MUT: [&str; 3] = ["core", "ops", "FnMut"]; +pub const FN_ONCE: [&str; 3] = ["core", "ops", "FnOnce"]; pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"]; pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"]; pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; diff --git a/tests/ui/needless_lifetimes.rs b/tests/ui/needless_lifetimes.rs index 913cd004f19..bc725a645ac 100644 --- a/tests/ui/needless_lifetimes.rs +++ b/tests/ui/needless_lifetimes.rs @@ -259,4 +259,36 @@ mod issue4291 { } } +mod nested_elision_sites { + // Don't lint these cases, they cause FPs. + // The lint does not support nested elision sites. + + fn nested_fn_trait_bound<'a>(i: &'a i32) -> impl Fn() -> &'a i32 { + move || i + } + + fn nested_fn_mut_trait_bound<'a>(i: &'a i32) -> impl FnMut() -> &'a i32 { + move || i + } + + fn nested_fn_once_trait_bound<'a>(i: &'a i32) -> impl FnOnce() -> &'a i32 { + move || i + } + + fn nested_generic_fn_trait_bound<'a, T: Fn() -> &'a i32>(f: T) -> &'a i32 { + f() + } + + fn nested_where_clause_fn_trait_bound<'a, T>(f: T) -> &'a i32 + where + T: Fn() -> &'a i32, + { + f() + } + + fn nested_pointer_fn<'a>(_: &'a i32) -> fn(&'a i32) -> &'a i32 { + |i| i + } +} + fn main() {} diff --git a/tests/ui/needless_lifetimes.stderr b/tests/ui/needless_lifetimes.stderr index d3a360ed8b5..b1943bf9d70 100644 --- a/tests/ui/needless_lifetimes.stderr +++ b/tests/ui/needless_lifetimes.stderr @@ -36,12 +36,6 @@ error: explicit lifetimes given in parameter types where they could be elided (o LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:86:1 - | -LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) --> $DIR/needless_lifetimes.rs:120:5 | @@ -102,5 +96,5 @@ error: explicit lifetimes given in parameter types where they could be elided (o LL | fn needless_lt<'a>(_x: &'a u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 17 previous errors +error: aborting due to 16 previous errors -- cgit 1.4.1-3-g733a5 From 9fa9208bd5c3725f98dd88f761956c01ce5fd527 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 4 Sep 2020 11:56:54 +0200 Subject: Restrict unnecessary_sort_by to non-ref copy types --- clippy_lints/src/unnecessary_sort_by.rs | 13 ++++++++-- tests/ui/unnecessary_sort_by.fixed | 42 +++++++++++++++++++++++++++++---- tests/ui/unnecessary_sort_by.rs | 42 +++++++++++++++++++++++++++++---- 3 files changed, 87 insertions(+), 10 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/unnecessary_sort_by.rs b/clippy_lints/src/unnecessary_sort_by.rs index 8b00d29acb5..9b6a9075a29 100644 --- a/clippy_lints/src/unnecessary_sort_by.rs +++ b/clippy_lints/src/unnecessary_sort_by.rs @@ -1,5 +1,4 @@ use crate::utils; -use crate::utils::paths; use crate::utils::sugg::Sugg; use if_chain::if_chain; use rustc_errors::Applicability; @@ -171,12 +170,22 @@ fn mirrored_exprs( } fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { + // NOTE: Vectors of references are not supported. In order to avoid hitting https://github.com/rust-lang/rust/issues/34162, + // (different unnamed lifetimes for closure arg and return type) we need to make sure the suggested + // closure parameter is not a reference in case we suggest `Reverse`. Trying to destructure more + // than one level of references would add some extra complexity as we would have to compensate + // in the closure body. + if_chain! { if let ExprKind::MethodCall(name_ident, _, args, _) = &expr.kind; if let name = name_ident.ident.name.to_ident_string(); if name == "sort_by" || name == "sort_unstable_by"; if let [vec, Expr { kind: ExprKind::Closure(_, _, closure_body_id, _, _), .. }] = args; - if utils::match_type(cx, &cx.typeck_results().expr_ty(vec), &paths::VEC); + let vec_ty = cx.typeck_results().expr_ty(vec); + if utils::is_type_diagnostic_item(cx, vec_ty, sym!(vec_type)); + let ty = vec_ty.walk().nth(1).unwrap().expect_ty(); // T in Vec + if !matches!(&ty.kind(), ty::Ref(..)); + if utils::is_copy(cx, ty); if let closure_body = cx.tcx.hir().body(*closure_body_id); if let &[ Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..}, diff --git a/tests/ui/unnecessary_sort_by.fixed b/tests/ui/unnecessary_sort_by.fixed index 31c2ba0f9c5..ad0d0387db0 100644 --- a/tests/ui/unnecessary_sort_by.fixed +++ b/tests/ui/unnecessary_sort_by.fixed @@ -25,17 +25,25 @@ fn unnecessary_sort_by() { vec.sort_by(|_, b| b.cmp(&5)); vec.sort_by(|_, b| b.cmp(c)); vec.sort_unstable_by(|a, _| a.cmp(c)); + + // Ignore vectors of references + let mut vec: Vec<&&&isize> = vec![&&&3, &&&6, &&&1, &&&2, &&&5]; + vec.sort_by(|a, b| (***a).abs().cmp(&(***b).abs())); + vec.sort_unstable_by(|a, b| (***a).abs().cmp(&(***b).abs())); + vec.sort_by(|a, b| b.cmp(a)); + vec.sort_unstable_by(|a, b| b.cmp(a)); } -// Should not be linted to avoid hitting https://github.com/rust-lang/rust/issues/34162 +// Do not suggest returning a reference to the closure parameter of `Vec::sort_by_key` mod issue_5754 { - struct Test(String); + #[derive(Clone, Copy)] + struct Test(usize); #[derive(PartialOrd, Ord, PartialEq, Eq)] - struct Wrapper<'a>(&'a str); + struct Wrapper<'a>(&'a usize); impl Test { - fn name(&self) -> &str { + fn name(&self) -> &usize { &self.0 } @@ -60,7 +68,33 @@ mod issue_5754 { } } +// `Vec::sort_by_key` closure parameter is `F: FnMut(&T) -> K` +// The suggestion is destructuring T and we know T is not a reference, so test that non-Copy T are +// not linted. +mod issue_6001 { + struct Test(String); + + impl Test { + // Return an owned type so that we don't hit the fix for 5754 + fn name(&self) -> String { + self.0.clone() + } + } + + pub fn test() { + let mut args: Vec = vec![]; + + // Forward + args.sort_by(|a, b| a.name().cmp(&b.name())); + args.sort_unstable_by(|a, b| a.name().cmp(&b.name())); + // Reverse + args.sort_by(|a, b| b.name().cmp(&a.name())); + args.sort_unstable_by(|a, b| b.name().cmp(&a.name())); + } +} + fn main() { unnecessary_sort_by(); issue_5754::test(); + issue_6001::test(); } diff --git a/tests/ui/unnecessary_sort_by.rs b/tests/ui/unnecessary_sort_by.rs index a3c8ae468ed..9746f6e6849 100644 --- a/tests/ui/unnecessary_sort_by.rs +++ b/tests/ui/unnecessary_sort_by.rs @@ -25,17 +25,25 @@ fn unnecessary_sort_by() { vec.sort_by(|_, b| b.cmp(&5)); vec.sort_by(|_, b| b.cmp(c)); vec.sort_unstable_by(|a, _| a.cmp(c)); + + // Ignore vectors of references + let mut vec: Vec<&&&isize> = vec![&&&3, &&&6, &&&1, &&&2, &&&5]; + vec.sort_by(|a, b| (***a).abs().cmp(&(***b).abs())); + vec.sort_unstable_by(|a, b| (***a).abs().cmp(&(***b).abs())); + vec.sort_by(|a, b| b.cmp(a)); + vec.sort_unstable_by(|a, b| b.cmp(a)); } -// Should not be linted to avoid hitting https://github.com/rust-lang/rust/issues/34162 +// Do not suggest returning a reference to the closure parameter of `Vec::sort_by_key` mod issue_5754 { - struct Test(String); + #[derive(Clone, Copy)] + struct Test(usize); #[derive(PartialOrd, Ord, PartialEq, Eq)] - struct Wrapper<'a>(&'a str); + struct Wrapper<'a>(&'a usize); impl Test { - fn name(&self) -> &str { + fn name(&self) -> &usize { &self.0 } @@ -60,7 +68,33 @@ mod issue_5754 { } } +// `Vec::sort_by_key` closure parameter is `F: FnMut(&T) -> K` +// The suggestion is destructuring T and we know T is not a reference, so test that non-Copy T are +// not linted. +mod issue_6001 { + struct Test(String); + + impl Test { + // Return an owned type so that we don't hit the fix for 5754 + fn name(&self) -> String { + self.0.clone() + } + } + + pub fn test() { + let mut args: Vec = vec![]; + + // Forward + args.sort_by(|a, b| a.name().cmp(&b.name())); + args.sort_unstable_by(|a, b| a.name().cmp(&b.name())); + // Reverse + args.sort_by(|a, b| b.name().cmp(&a.name())); + args.sort_unstable_by(|a, b| b.name().cmp(&a.name())); + } +} + fn main() { unnecessary_sort_by(); issue_5754::test(); + issue_6001::test(); } -- cgit 1.4.1-3-g733a5 From 3d30ef7818b9ed562f2c48f3d6d15d90253ae732 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Mon, 7 Sep 2020 22:17:31 +0900 Subject: Restrict `same_item_push` to suppress false positives It emits a lint when the pushed item is a literal, a constant and an immutable binding that are initialized with those. --- clippy_lints/src/loops.rs | 83 ++++++++++++++++++++++++++++++++---------- tests/ui/same_item_push.rs | 13 +++++++ tests/ui/same_item_push.stderr | 34 ++++++++++------- 3 files changed, 97 insertions(+), 33 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index f13e369907b..f417e3a0caf 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1153,27 +1153,70 @@ fn detect_same_item_push<'tcx>( let vec_str = snippet_with_macro_callsite(cx, vec.span, ""); let item_str = snippet_with_macro_callsite(cx, pushed_item.span, ""); if let ExprKind::Path(ref qpath) = pushed_item.kind { - if_chain! { - if let Res::Local(hir_id) = qpath_res(cx, qpath, pushed_item.hir_id); - let node = cx.tcx.hir().get(hir_id); - if let Node::Binding(pat) = node; - if let PatKind::Binding(bind_ann, ..) = pat.kind; - if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable); - then { - span_lint_and_help( - cx, - SAME_ITEM_PUSH, - vec.span, - "it looks like the same item is being pushed into this Vec", - None, - &format!( - "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", - item_str, vec_str, item_str - ), - ) - } + match qpath_res(cx, qpath, pushed_item.hir_id) { + // immutable bindings that are initialized with literal or constant + Res::Local(hir_id) => { + if_chain! { + let node = cx.tcx.hir().get(hir_id); + if let Node::Binding(pat) = node; + if let PatKind::Binding(bind_ann, ..) = pat.kind; + if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable); + let parent_node = cx.tcx.hir().get_parent_node(hir_id); + if let Some(Node::Local(parent_let_expr)) = cx.tcx.hir().find(parent_node); + if let rustc_hir::Local { init: Some(init), .. } = parent_let_expr; + then { + match init.kind { + // immutable bindings that are initialized with literal + ExprKind::Lit(..) => { + span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ) + }, + // immutable bindings that are initialized with constant + ExprKind::Path(ref path) => { + if let Res::Def(DefKind::Const, ..) = qpath_res(cx, path, init.hir_id) { + span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ) + } + } + _ => {}, + } + } + } + }, + // constant + Res::Def(DefKind::Const, ..) => span_lint_and_help( + cx, + SAME_ITEM_PUSH, + vec.span, + "it looks like the same item is being pushed into this Vec", + None, + &format!( + "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})", + item_str, vec_str, item_str + ), + ), + _ => {}, } - } else if mutated_variables(pushed_item, cx).map_or(false, |mutvars| mutvars.is_empty()) { + } else if let ExprKind::Lit(..) = pushed_item.kind { + // literal span_lint_and_help( cx, SAME_ITEM_PUSH, diff --git a/tests/ui/same_item_push.rs b/tests/ui/same_item_push.rs index 0928820892b..bd4792c4a76 100644 --- a/tests/ui/same_item_push.rs +++ b/tests/ui/same_item_push.rs @@ -1,5 +1,7 @@ #![warn(clippy::same_item_push)] +const VALUE: u8 = 7; + fn mutate_increment(x: &mut u8) -> u8 { *x += 1; *x @@ -111,4 +113,15 @@ fn main() { for _ in 0..10 { vec15.push(Box::new(S {})); } + + let mut vec16 = Vec::new(); + for _ in 0..20 { + vec16.push(VALUE); + } + + let mut vec17 = Vec::new(); + let item = VALUE; + for _ in 0..20 { + vec17.push(item); + } } diff --git a/tests/ui/same_item_push.stderr b/tests/ui/same_item_push.stderr index ddc5d48cd41..4896479791a 100644 --- a/tests/ui/same_item_push.stderr +++ b/tests/ui/same_item_push.stderr @@ -1,22 +1,14 @@ error: it looks like the same item is being pushed into this Vec - --> $DIR/same_item_push.rs:16:9 - | -LL | spaces.push(vec![b' ']); - | ^^^^^^ - | - = note: `-D clippy::same-item-push` implied by `-D warnings` - = help: try using vec![vec![b' '];SIZE] or spaces.resize(NEW_SIZE, vec![b' ']) - -error: it looks like the same item is being pushed into this Vec - --> $DIR/same_item_push.rs:22:9 + --> $DIR/same_item_push.rs:24:9 | LL | vec2.push(item); | ^^^^ | + = note: `-D clippy::same-item-push` implied by `-D warnings` = help: try using vec![item;SIZE] or vec2.resize(NEW_SIZE, item) error: it looks like the same item is being pushed into this Vec - --> $DIR/same_item_push.rs:28:9 + --> $DIR/same_item_push.rs:30:9 | LL | vec3.push(item); | ^^^^ @@ -24,12 +16,28 @@ LL | vec3.push(item); = help: try using vec![item;SIZE] or vec3.resize(NEW_SIZE, item) error: it looks like the same item is being pushed into this Vec - --> $DIR/same_item_push.rs:33:9 + --> $DIR/same_item_push.rs:35:9 | LL | vec4.push(13); | ^^^^ | = help: try using vec![13;SIZE] or vec4.resize(NEW_SIZE, 13) -error: aborting due to 4 previous errors +error: it looks like the same item is being pushed into this Vec + --> $DIR/same_item_push.rs:119:9 + | +LL | vec16.push(VALUE); + | ^^^^^ + | + = help: try using vec![VALUE;SIZE] or vec16.resize(NEW_SIZE, VALUE) + +error: it looks like the same item is being pushed into this Vec + --> $DIR/same_item_push.rs:125:9 + | +LL | vec17.push(item); + | ^^^^^ + | + = help: try using vec![item;SIZE] or vec17.resize(NEW_SIZE, item) + +error: aborting due to 5 previous errors -- cgit 1.4.1-3-g733a5 From a60e5de90c7370d4fb3e6561d3cb55495cda2e2a Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Tue, 8 Sep 2020 02:39:39 +0200 Subject: needless-lifetime / nested elision sites / PR remarks --- clippy_lints/src/lifetimes.rs | 222 +++++++-------------- tests/ui/crashes/ice-2774.stderr | 10 + .../crashes/needless_lifetimes_impl_trait.stderr | 14 ++ tests/ui/needless_lifetimes.stderr | 8 +- 4 files changed, 102 insertions(+), 152 deletions(-) create mode 100644 tests/ui/crashes/ice-2774.stderr create mode 100644 tests/ui/crashes/needless_lifetimes_impl_trait.stderr (limited to 'tests') diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index ab6e34d201c..1d17fbd3725 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -1,23 +1,22 @@ +use crate::utils::paths; +use crate::utils::{get_trait_def_id, in_macro, span_lint, trait_ref_of_method}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{ - walk_fn_decl, walk_generic_param, walk_generics, walk_param_bound, walk_trait_ref, walk_ty, NestedVisitorMap, - Visitor, + walk_fn_decl, walk_generic_param, walk_generics, walk_item, walk_param_bound, walk_poly_trait_ref, walk_ty, + NestedVisitorMap, Visitor, }; use rustc_hir::FnRetTy::Return; use rustc_hir::{ - BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, ImplItemKind, Item, - ItemKind, Lifetime, LifetimeName, ParamName, QPath, TraitBoundModifier, TraitFn, TraitItem, TraitItemKind, - TraitRef, Ty, TyKind, WhereClause, WherePredicate, + BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, + ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, ParamName, PolyTraitRef, TraitBoundModifier, TraitFn, + TraitItem, TraitItemKind, Ty, TyKind, WhereClause, WherePredicate, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::symbol::{kw, Symbol}; - -use crate::utils::paths; -use crate::utils::{get_trait_def_id, in_macro, last_path_segment, span_lint, trait_ref_of_method}; +use std::iter::FromIterator; declare_clippy_lint! { /// **What it does:** Checks for lifetime annotations which can be removed by @@ -110,7 +109,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { } /// The lifetime of a &-reference. -#[derive(PartialEq, Eq, Hash, Debug)] +#[derive(PartialEq, Eq, Hash, Debug, Clone)] enum RefLt { Unnamed, Static, @@ -129,15 +128,6 @@ fn check_fn_inner<'tcx>( return; } - // fn pointers and closure trait bounds are also lifetime elision sites. This lint does not - // support nested elision sites in a fn item. - if FnPointerOrClosureTraitBoundFinder::find_in_generics(cx, generics) - || FnPointerOrClosureTraitBoundFinder::find_in_fn_decl(cx, decl) - { - return; - } - - let mut bounds_lts = Vec::new(); let types = generics .params .iter() @@ -166,13 +156,12 @@ fn check_fn_inner<'tcx>( if bound.name != LifetimeName::Static && !bound.is_elided() { return; } - bounds_lts.push(bound); } } } } } - if could_use_elision(cx, decl, body, &generics.params, bounds_lts) { + if could_use_elision(cx, decl, body, &generics.params) { span_lint( cx, NEEDLESS_LIFETIMES, @@ -191,7 +180,6 @@ fn could_use_elision<'tcx>( func: &'tcx FnDecl<'_>, body: Option, named_generics: &'tcx [GenericParam<'_>], - bounds_lts: Vec<&'tcx Lifetime>, ) -> bool { // There are two scenarios where elision works: // * no output references, all input references have different LT @@ -214,15 +202,31 @@ fn could_use_elision<'tcx>( if let Return(ref ty) = func.output { output_visitor.visit_ty(ty); } + for lt in named_generics { + input_visitor.visit_generic_param(lt) + } - let input_lts = match input_visitor.into_vec() { - Some(lts) => lts_from_bounds(lts, bounds_lts.into_iter()), - None => return false, - }; - let output_lts = match output_visitor.into_vec() { - Some(val) => val, - None => return false, - }; + if input_visitor.abort() || output_visitor.abort() { + return false; + } + + if allowed_lts + .intersection(&FxHashSet::from_iter( + input_visitor + .nested_elision_site_lts + .iter() + .chain(output_visitor.nested_elision_site_lts.iter()) + .cloned() + .filter(|v| matches!(v, RefLt::Named(_))), + )) + .next() + .is_some() + { + return false; + } + + let input_lts = input_visitor.lts; + let output_lts = output_visitor.lts; if let Some(body_id) = body { let mut checker = BodyLifetimeChecker { @@ -287,27 +291,20 @@ fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet { allowed_lts } -fn lts_from_bounds<'a, T: Iterator>(mut vec: Vec, bounds_lts: T) -> Vec { - for lt in bounds_lts { - if lt.name != LifetimeName::Static { - vec.push(RefLt::Named(lt.name.ident().name)); - } - } - - vec -} - /// Number of unique lifetimes in the given vector. #[must_use] fn unique_lifetimes(lts: &[RefLt]) -> usize { lts.iter().collect::>().len() } +const CLOSURE_TRAIT_BOUNDS: [&[&str]; 3] = [&paths::FN, &paths::FN_MUT, &paths::FN_ONCE]; + /// A visitor usable for `rustc_front::visit::walk_ty()`. struct RefVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, lts: Vec, - abort: bool, + nested_elision_site_lts: Vec, + unelided_trait_object_lifetime: bool, } impl<'a, 'tcx> RefVisitor<'a, 'tcx> { @@ -315,7 +312,8 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> { Self { cx, lts: Vec::new(), - abort: false, + nested_elision_site_lts: Vec::new(), + unelided_trait_object_lifetime: false, } } @@ -335,40 +333,16 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> { } } - fn into_vec(self) -> Option> { - if self.abort { - None - } else { - Some(self.lts) - } + fn all_lts(&self) -> Vec { + self.lts + .iter() + .chain(self.nested_elision_site_lts.iter()) + .cloned() + .collect::>() } - fn collect_anonymous_lifetimes(&mut self, qpath: &QPath<'_>, ty: &Ty<'_>) { - if let Some(ref last_path_segment) = last_path_segment(qpath).args { - if !last_path_segment.parenthesized - && !last_path_segment - .args - .iter() - .any(|arg| matches!(arg, GenericArg::Lifetime(_))) - { - let hir_id = ty.hir_id; - match self.cx.qpath_res(qpath, hir_id) { - Res::Def(DefKind::TyAlias | DefKind::Struct, def_id) => { - let generics = self.cx.tcx.generics_of(def_id); - for _ in generics.params.as_slice() { - self.record(&None); - } - }, - Res::Def(DefKind::Trait, def_id) => { - let trait_def = self.cx.tcx.trait_def(def_id); - for _ in &self.cx.tcx.generics_of(trait_def.def_id).params { - self.record(&None); - } - }, - _ => (), - } - } - } + fn abort(&self) -> bool { + self.unelided_trait_object_lifetime } } @@ -380,30 +354,36 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { self.record(&Some(*lifetime)); } + fn visit_poly_trait_ref(&mut self, poly_tref: &'tcx PolyTraitRef<'tcx>, tbm: TraitBoundModifier) { + let trait_ref = &poly_tref.trait_ref; + if CLOSURE_TRAIT_BOUNDS + .iter() + .any(|trait_path| trait_ref.trait_def_id() == get_trait_def_id(self.cx, trait_path)) + { + let mut sub_visitor = RefVisitor::new(self.cx); + sub_visitor.visit_trait_ref(trait_ref); + self.nested_elision_site_lts.append(&mut sub_visitor.all_lts()); + } else { + walk_poly_trait_ref(self, poly_tref, tbm); + } + } + fn visit_ty(&mut self, ty: &'tcx Ty<'_>) { match ty.kind { - TyKind::Rptr(ref lt, _) if lt.is_elided() => { - self.record(&None); - }, - TyKind::Path(ref path) => { - self.collect_anonymous_lifetimes(path, ty); - }, TyKind::OpaqueDef(item, _) => { let map = self.cx.tcx.hir(); - if let ItemKind::OpaqueTy(ref exist_ty) = map.expect_item(item.id).kind { - for bound in exist_ty.bounds { - if let GenericBound::Outlives(_) = *bound { - self.record(&None); - } - } - } else { - unreachable!() - } + let item = map.expect_item(item.id); + walk_item(self, item); walk_ty(self, ty); }, + TyKind::BareFn(&BareFnTy { decl, .. }) => { + let mut sub_visitor = RefVisitor::new(self.cx); + sub_visitor.visit_fn_decl(decl); + self.nested_elision_site_lts.append(&mut sub_visitor.all_lts()); + }, TyKind::TraitObject(bounds, ref lt) => { if !lt.is_elided() { - self.abort = true; + self.unelided_trait_object_lifetime = true; } for bound in bounds { self.visit_poly_trait_ref(bound, TraitBoundModifier::None); @@ -440,16 +420,7 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, where_clause: &'tcx WhereCl walk_param_bound(&mut visitor, bound); } // and check that all lifetimes are allowed - match visitor.into_vec() { - None => return false, - Some(lts) => { - for lt in lts { - if !allowed_lts.contains(<) { - return true; - } - } - }, - } + return visitor.all_lts().iter().any(|it| !allowed_lts.contains(it)); }, WherePredicate::EqPredicate(ref pred) => { let mut visitor = RefVisitor::new(cx); @@ -533,54 +504,3 @@ impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker { NestedVisitorMap::None } } - -const CLOSURE_TRAIT_BOUNDS: [&[&str]; 3] = [&paths::FN, &paths::FN_MUT, &paths::FN_ONCE]; - -struct FnPointerOrClosureTraitBoundFinder<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - found: bool, -} - -impl<'a, 'tcx> FnPointerOrClosureTraitBoundFinder<'a, 'tcx> { - fn find_in_generics(cx: &'a LateContext<'tcx>, generics: &'tcx Generics<'tcx>) -> bool { - let mut finder = Self { cx, found: false }; - finder.visit_generics(generics); - finder.found - } - - fn find_in_fn_decl(cx: &'a LateContext<'tcx>, generics: &'tcx FnDecl<'tcx>) -> bool { - let mut finder = Self { cx, found: false }; - finder.visit_fn_decl(generics); - finder.found - } -} - -impl<'a, 'tcx> Visitor<'tcx> for FnPointerOrClosureTraitBoundFinder<'a, 'tcx> { - type Map = Map<'tcx>; - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - - fn visit_trait_ref(&mut self, tref: &'tcx TraitRef<'tcx>) { - if CLOSURE_TRAIT_BOUNDS - .iter() - .any(|trait_path| tref.trait_def_id() == get_trait_def_id(self.cx, trait_path)) - { - self.found = true; - } - walk_trait_ref(self, tref); - } - - fn visit_ty(&mut self, ty: &'tcx Ty<'tcx>) { - match ty.kind { - TyKind::BareFn(..) => self.found = true, - TyKind::OpaqueDef(item_id, _) => { - let map = self.cx.tcx.hir(); - let item = map.expect_item(item_id.id); - self.visit_item(item); - }, - _ => (), - } - walk_ty(self, ty); - } -} diff --git a/tests/ui/crashes/ice-2774.stderr b/tests/ui/crashes/ice-2774.stderr new file mode 100644 index 00000000000..fd502cba73a --- /dev/null +++ b/tests/ui/crashes/ice-2774.stderr @@ -0,0 +1,10 @@ +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/ice-2774.rs:17:1 + | +LL | pub fn add_barfoos_to_foos<'a>(bars: &HashSet<&'a Bar>) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::needless-lifetimes` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/crashes/needless_lifetimes_impl_trait.stderr b/tests/ui/crashes/needless_lifetimes_impl_trait.stderr new file mode 100644 index 00000000000..02b86397ed5 --- /dev/null +++ b/tests/ui/crashes/needless_lifetimes_impl_trait.stderr @@ -0,0 +1,14 @@ +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes_impl_trait.rs:17:5 + | +LL | fn baz<'a>(&'a self) -> impl Foo + 'a { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/needless_lifetimes_impl_trait.rs:3:9 + | +LL | #![deny(clippy::needless_lifetimes)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/tests/ui/needless_lifetimes.stderr b/tests/ui/needless_lifetimes.stderr index b1943bf9d70..d3a360ed8b5 100644 --- a/tests/ui/needless_lifetimes.stderr +++ b/tests/ui/needless_lifetimes.stderr @@ -36,6 +36,12 @@ error: explicit lifetimes given in parameter types where they could be elided (o LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:86:1 + | +LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) --> $DIR/needless_lifetimes.rs:120:5 | @@ -96,5 +102,5 @@ error: explicit lifetimes given in parameter types where they could be elided (o LL | fn needless_lt<'a>(_x: &'a u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 16 previous errors +error: aborting due to 17 previous errors -- cgit 1.4.1-3-g733a5 From 2c9f82e7d22a5cd3f704ed0b932a17fa53f1145b Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 8 Sep 2020 22:45:27 +0900 Subject: Add some tests to `same_item_push` Add tests in which the variable is initialized with a match expression and function call --- tests/ui/same_item_push.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'tests') diff --git a/tests/ui/same_item_push.rs b/tests/ui/same_item_push.rs index bd4792c4a76..7ca829854db 100644 --- a/tests/ui/same_item_push.rs +++ b/tests/ui/same_item_push.rs @@ -11,6 +11,10 @@ fn increment(x: u8) -> u8 { x + 1 } +fn fun() -> usize { + 42 +} + fn main() { // Test for basic case let mut spaces = Vec::with_capacity(10); @@ -124,4 +128,21 @@ fn main() { for _ in 0..20 { vec17.push(item); } + + let mut vec18 = Vec::new(); + let item = 42; + let item = fun(); + for _ in 0..20 { + vec18.push(item); + } + + let mut vec19 = Vec::new(); + let key = 1; + for _ in 0..20 { + let item = match key { + 1 => 10, + _ => 0, + }; + vec19.push(item); + } } -- cgit 1.4.1-3-g733a5 From 952c04674e4225d231f0ba78dff32abf7c06cb8b Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 8 Sep 2020 23:03:15 +0900 Subject: Refactoring: tests in `same_item_push` --- tests/ui/same_item_push.rs | 99 ++++++++++++++++++++++-------------------- tests/ui/same_item_push.stderr | 40 ++++++++--------- 2 files changed, 71 insertions(+), 68 deletions(-) (limited to 'tests') diff --git a/tests/ui/same_item_push.rs b/tests/ui/same_item_push.rs index 7ca829854db..a37c8782ec3 100644 --- a/tests/ui/same_item_push.rs +++ b/tests/ui/same_item_push.rs @@ -16,64 +16,76 @@ fn fun() -> usize { } fn main() { - // Test for basic case - let mut spaces = Vec::with_capacity(10); - for _ in 0..10 { - spaces.push(vec![b' ']); - } - - let mut vec2: Vec = Vec::new(); + // ** linted cases ** + let mut vec: Vec = Vec::new(); let item = 2; for _ in 5..=20 { - vec2.push(item); + vec.push(item); } - let mut vec3: Vec = Vec::new(); + let mut vec: Vec = Vec::new(); for _ in 0..15 { let item = 2; - vec3.push(item); + vec.push(item); } - let mut vec4: Vec = Vec::new(); + let mut vec: Vec = Vec::new(); for _ in 0..15 { - vec4.push(13); + vec.push(13); + } + + let mut vec = Vec::new(); + for _ in 0..20 { + vec.push(VALUE); + } + + let mut vec = Vec::new(); + let item = VALUE; + for _ in 0..20 { + vec.push(item); + } + + // ** non-linted cases ** + let mut spaces = Vec::with_capacity(10); + for _ in 0..10 { + spaces.push(vec![b' ']); } // Suggestion should not be given as pushed variable can mutate - let mut vec5: Vec = Vec::new(); + let mut vec: Vec = Vec::new(); let mut item: u8 = 2; for _ in 0..30 { - vec5.push(mutate_increment(&mut item)); + vec.push(mutate_increment(&mut item)); } - let mut vec6: Vec = Vec::new(); + let mut vec: Vec = Vec::new(); let mut item: u8 = 2; let mut item2 = &mut mutate_increment(&mut item); for _ in 0..30 { - vec6.push(mutate_increment(item2)); + vec.push(mutate_increment(item2)); } - let mut vec7: Vec = Vec::new(); + let mut vec: Vec = Vec::new(); for (a, b) in [0, 1, 4, 9, 16].iter().enumerate() { - vec7.push(a); + vec.push(a); } - let mut vec8: Vec = Vec::new(); + let mut vec: Vec = Vec::new(); for i in 0..30 { - vec8.push(increment(i)); + vec.push(increment(i)); } - let mut vec9: Vec = Vec::new(); + let mut vec: Vec = Vec::new(); for i in 0..30 { - vec9.push(i + i * i); + vec.push(i + i * i); } // Suggestion should not be given as there are multiple pushes that are not the same - let mut vec10: Vec = Vec::new(); + let mut vec: Vec = Vec::new(); let item: u8 = 2; for _ in 0..30 { - vec10.push(item); - vec10.push(item * 2); + vec.push(item); + vec.push(item * 2); } // Suggestion should not be given as Vec is not involved @@ -88,23 +100,23 @@ fn main() { for i in 0..30 { vec_a.push(A { kind: i }); } - let mut vec12: Vec = Vec::new(); + let mut vec: Vec = Vec::new(); for a in vec_a { - vec12.push(2u8.pow(a.kind)); + vec.push(2u8.pow(a.kind)); } // Fix #5902 - let mut vec13: Vec = Vec::new(); + let mut vec: Vec = Vec::new(); let mut item = 0; for _ in 0..10 { - vec13.push(item); + vec.push(item); item += 10; } // Fix #5979 - let mut vec14: Vec = Vec::new(); + let mut vec: Vec = Vec::new(); for _ in 0..10 { - vec14.push(std::fs::File::open("foobar").unwrap()); + vec.push(std::fs::File::open("foobar").unwrap()); } // Fix #5979 #[derive(Clone)] @@ -113,36 +125,27 @@ fn main() { trait T {} impl T for S {} - let mut vec15: Vec> = Vec::new(); + let mut vec: Vec> = Vec::new(); for _ in 0..10 { - vec15.push(Box::new(S {})); - } - - let mut vec16 = Vec::new(); - for _ in 0..20 { - vec16.push(VALUE); - } - - let mut vec17 = Vec::new(); - let item = VALUE; - for _ in 0..20 { - vec17.push(item); + vec.push(Box::new(S {})); } - let mut vec18 = Vec::new(); + // Fix #5985 + let mut vec = Vec::new(); let item = 42; let item = fun(); for _ in 0..20 { - vec18.push(item); + vec.push(item); } - let mut vec19 = Vec::new(); + // Fix #5985 + let mut vec = Vec::new(); let key = 1; for _ in 0..20 { let item = match key { 1 => 10, _ => 0, }; - vec19.push(item); + vec.push(item); } } diff --git a/tests/ui/same_item_push.stderr b/tests/ui/same_item_push.stderr index 4896479791a..d9ffa15780a 100644 --- a/tests/ui/same_item_push.stderr +++ b/tests/ui/same_item_push.stderr @@ -1,43 +1,43 @@ error: it looks like the same item is being pushed into this Vec - --> $DIR/same_item_push.rs:24:9 + --> $DIR/same_item_push.rs:23:9 | -LL | vec2.push(item); - | ^^^^ +LL | vec.push(item); + | ^^^ | = note: `-D clippy::same-item-push` implied by `-D warnings` - = help: try using vec![item;SIZE] or vec2.resize(NEW_SIZE, item) + = help: try using vec![item;SIZE] or vec.resize(NEW_SIZE, item) error: it looks like the same item is being pushed into this Vec - --> $DIR/same_item_push.rs:30:9 + --> $DIR/same_item_push.rs:29:9 | -LL | vec3.push(item); - | ^^^^ +LL | vec.push(item); + | ^^^ | - = help: try using vec![item;SIZE] or vec3.resize(NEW_SIZE, item) + = help: try using vec![item;SIZE] or vec.resize(NEW_SIZE, item) error: it looks like the same item is being pushed into this Vec - --> $DIR/same_item_push.rs:35:9 + --> $DIR/same_item_push.rs:34:9 | -LL | vec4.push(13); - | ^^^^ +LL | vec.push(13); + | ^^^ | - = help: try using vec![13;SIZE] or vec4.resize(NEW_SIZE, 13) + = help: try using vec![13;SIZE] or vec.resize(NEW_SIZE, 13) error: it looks like the same item is being pushed into this Vec - --> $DIR/same_item_push.rs:119:9 + --> $DIR/same_item_push.rs:39:9 | -LL | vec16.push(VALUE); - | ^^^^^ +LL | vec.push(VALUE); + | ^^^ | - = help: try using vec![VALUE;SIZE] or vec16.resize(NEW_SIZE, VALUE) + = help: try using vec![VALUE;SIZE] or vec.resize(NEW_SIZE, VALUE) error: it looks like the same item is being pushed into this Vec - --> $DIR/same_item_push.rs:125:9 + --> $DIR/same_item_push.rs:45:9 | -LL | vec17.push(item); - | ^^^^^ +LL | vec.push(item); + | ^^^ | - = help: try using vec![item;SIZE] or vec17.resize(NEW_SIZE, item) + = help: try using vec![item;SIZE] or vec.resize(NEW_SIZE, item) error: aborting due to 5 previous errors -- cgit 1.4.1-3-g733a5 From a899ad2e125e8dd3a0a339df1f8457ef8cab3cc7 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Tue, 8 Sep 2020 23:35:19 +0900 Subject: Change suggestion to `create_dir_all({})` from `std::fs::create_dir_all({})` --- clippy_lints/src/create_dir.rs | 2 +- tests/ui/create_dir.fixed | 6 ++++-- tests/ui/create_dir.rs | 2 ++ tests/ui/create_dir.stderr | 8 ++++---- 4 files changed, 11 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/create_dir.rs b/clippy_lints/src/create_dir.rs index f80a0efa7a5..4002fb655a5 100644 --- a/clippy_lints/src/create_dir.rs +++ b/clippy_lints/src/create_dir.rs @@ -42,7 +42,7 @@ impl LateLintPass<'_> for CreateDir { expr.span, "calling `std::fs::create_dir` where there may be a better way", "consider calling `std::fs::create_dir_all` instead", - format!("std::fs::create_dir_all({})", snippet(cx, args[0].span, "..")), + format!("create_dir_all({})", snippet(cx, args[0].span, "..")), Applicability::MaybeIncorrect, ) } diff --git a/tests/ui/create_dir.fixed b/tests/ui/create_dir.fixed index 0e28f87e33d..8ed53a56ac0 100644 --- a/tests/ui/create_dir.fixed +++ b/tests/ui/create_dir.fixed @@ -2,12 +2,14 @@ #![allow(unused_must_use)] #![warn(clippy::create_dir)] +use std::fs::create_dir_all; + fn create_dir() {} fn main() { // Should be warned - std::fs::create_dir_all("foo"); - std::fs::create_dir_all("bar").unwrap(); + create_dir_all("foo"); + create_dir_all("bar").unwrap(); // Shouldn't be warned create_dir(); diff --git a/tests/ui/create_dir.rs b/tests/ui/create_dir.rs index 1f226298c0d..19c8fc24ba2 100644 --- a/tests/ui/create_dir.rs +++ b/tests/ui/create_dir.rs @@ -2,6 +2,8 @@ #![allow(unused_must_use)] #![warn(clippy::create_dir)] +use std::fs::create_dir_all; + fn create_dir() {} fn main() { diff --git a/tests/ui/create_dir.stderr b/tests/ui/create_dir.stderr index 0c97bdd0f7a..67298fc4709 100644 --- a/tests/ui/create_dir.stderr +++ b/tests/ui/create_dir.stderr @@ -1,16 +1,16 @@ error: calling `std::fs::create_dir` where there may be a better way - --> $DIR/create_dir.rs:9:5 + --> $DIR/create_dir.rs:11:5 | LL | std::fs::create_dir("foo"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `std::fs::create_dir_all("foo")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `create_dir_all("foo")` | = note: `-D clippy::create-dir` implied by `-D warnings` error: calling `std::fs::create_dir` where there may be a better way - --> $DIR/create_dir.rs:10:5 + --> $DIR/create_dir.rs:12:5 | LL | std::fs::create_dir("bar").unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `std::fs::create_dir_all("bar")` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `create_dir_all("bar")` error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From 4f1c4a99d4d98f1acea3c9c7cc55355aa46119aa Mon Sep 17 00:00:00 2001 From: Ricky Date: Tue, 8 Sep 2020 12:05:18 -0400 Subject: Fixed typo in lint and test --- clippy_lints/src/map_err_ignore.rs | 4 ++-- tests/ui/map_err.stderr | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/map_err_ignore.rs b/clippy_lints/src/map_err_ignore.rs index 649de4133e0..fa59b6ec370 100644 --- a/clippy_lints/src/map_err_ignore.rs +++ b/clippy_lints/src/map_err_ignore.rs @@ -7,7 +7,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// **What it does:** Checks for instances of `map_err(|_| Some::Enum)` /// - /// **Why is this bad?** This map_err throws away the original error rather than allowing the enum to bubble the original error + /// **Why is this bad?** This map_err throws away the original error rather than allowing the enum to contain and report the cause of the error /// /// **Known problems:** None. /// @@ -133,7 +133,7 @@ impl<'tcx> LateLintPass<'tcx> for MapErrIgnore { cx, MAP_ERR_IGNORE, body_span, - "`map_else(|_|...` ignores the original error", + "`map_err(|_|...` ignores the original error", None, "Consider wrapping the error in an enum variant", ); diff --git a/tests/ui/map_err.stderr b/tests/ui/map_err.stderr index 7a269ab95ab..e4e6a289ba0 100644 --- a/tests/ui/map_err.stderr +++ b/tests/ui/map_err.stderr @@ -1,4 +1,4 @@ -error: `map_else(|_|...` ignores the original error +error: `map_err(|_|...` ignores the original error --> $DIR/map_err.rs:21:32 | LL | println!("{:?}", x.map_err(|_| Errors::Ignored)); -- cgit 1.4.1-3-g733a5 From d719b485434eac557e65bf55cca79e63f7b83d5b Mon Sep 17 00:00:00 2001 From: Ricky Date: Tue, 8 Sep 2020 19:37:14 -0400 Subject: Move map_err_ignore from style to pedantic --- clippy_lints/src/lib.rs | 3 +-- clippy_lints/src/map_err_ignore.rs | 2 +- src/lintlist/mod.rs | 2 +- tests/ui/map_err.rs | 1 + tests/ui/map_err.stderr | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3794cae091a..797ab62cb54 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1179,6 +1179,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::EXPLICIT_INTO_ITER_LOOP), LintId::of(&loops::EXPLICIT_ITER_LOOP), LintId::of(¯o_use::MACRO_USE_IMPORTS), + LintId::of(&map_err_ignore::MAP_ERR_IGNORE), LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS), LintId::of(&matches::MATCH_BOOL), LintId::of(&matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS), @@ -1330,7 +1331,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&manual_async_fn::MANUAL_ASYNC_FN), LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(&map_clone::MAP_CLONE), - LintId::of(&map_err_ignore::MAP_ERR_IGNORE), LintId::of(&map_identity::MAP_IDENTITY), LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN), LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN), @@ -1538,7 +1538,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&manual_async_fn::MANUAL_ASYNC_FN), LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(&map_clone::MAP_CLONE), - LintId::of(&map_err_ignore::MAP_ERR_IGNORE), LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH), LintId::of(&matches::MATCH_LIKE_MATCHES_MACRO), LintId::of(&matches::MATCH_OVERLAPPING_ARM), diff --git a/clippy_lints/src/map_err_ignore.rs b/clippy_lints/src/map_err_ignore.rs index fa59b6ec370..5298e16a04d 100644 --- a/clippy_lints/src/map_err_ignore.rs +++ b/clippy_lints/src/map_err_ignore.rs @@ -99,7 +99,7 @@ declare_clippy_lint! { /// } /// ``` pub MAP_ERR_IGNORE, - style, + pedantic, "`map_err` should not ignore the original error" } diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 6725c97f793..5105e953162 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1167,7 +1167,7 @@ pub static ref ALL_LINTS: Vec = vec![ }, Lint { name: "map_err_ignore", - group: "style", + group: "pedantic", desc: "`map_err` should not ignore the original error", deprecation: None, module: "map_err_ignore", diff --git a/tests/ui/map_err.rs b/tests/ui/map_err.rs index f3a74ad95cd..617b6422872 100644 --- a/tests/ui/map_err.rs +++ b/tests/ui/map_err.rs @@ -1,3 +1,4 @@ +#![warn(clippy::map_err_ignore)] use std::convert::TryFrom; use std::error::Error; use std::fmt; diff --git a/tests/ui/map_err.stderr b/tests/ui/map_err.stderr index e4e6a289ba0..7273f460380 100644 --- a/tests/ui/map_err.stderr +++ b/tests/ui/map_err.stderr @@ -1,5 +1,5 @@ error: `map_err(|_|...` ignores the original error - --> $DIR/map_err.rs:21:32 + --> $DIR/map_err.rs:22:32 | LL | println!("{:?}", x.map_err(|_| Errors::Ignored)); | ^^^ -- cgit 1.4.1-3-g733a5 From c8e9e52303da6dff4bc5cc4db3021d608fca6c70 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Tue, 8 Sep 2020 22:22:23 +0200 Subject: needless-lifetime / add test cases for nested elision sites --- tests/ui/needless_lifetimes.rs | 68 +++++++++++++++++++++++++++++++++----- tests/ui/needless_lifetimes.stderr | 32 +++++++++++++++++- 2 files changed, 90 insertions(+), 10 deletions(-) (limited to 'tests') diff --git a/tests/ui/needless_lifetimes.rs b/tests/ui/needless_lifetimes.rs index bc725a645ac..548c5929c61 100644 --- a/tests/ui/needless_lifetimes.rs +++ b/tests/ui/needless_lifetimes.rs @@ -259,36 +259,86 @@ mod issue4291 { } } +mod issue2944 { + trait Foo {} + struct Bar {} + struct Baz<'a> { + bar: &'a Bar, + } + + impl<'a> Foo for Baz<'a> {} + impl Bar { + fn baz<'a>(&'a self) -> impl Foo + 'a { + Baz { bar: self } + } + } +} + mod nested_elision_sites { - // Don't lint these cases, they cause FPs. - // The lint does not support nested elision sites. + // issue #issue2944 - fn nested_fn_trait_bound<'a>(i: &'a i32) -> impl Fn() -> &'a i32 { + // closure trait bounds subject to nested elision + // don't lint because they refer to outer lifetimes + fn trait_fn<'a>(i: &'a i32) -> impl Fn() -> &'a i32 { move || i } - - fn nested_fn_mut_trait_bound<'a>(i: &'a i32) -> impl FnMut() -> &'a i32 { + fn trait_fn_mut<'a>(i: &'a i32) -> impl FnMut() -> &'a i32 { + move || i + } + fn trait_fn_once<'a>(i: &'a i32) -> impl FnOnce() -> &'a i32 { move || i } - fn nested_fn_once_trait_bound<'a>(i: &'a i32) -> impl FnOnce() -> &'a i32 { + // don't lint + fn impl_trait_in_input_position<'a>(f: impl Fn() -> &'a i32) -> &'a i32 { + f() + } + fn impl_trait_in_output_position<'a>(i: &'a i32) -> impl Fn() -> &'a i32 { move || i } + // lint + fn impl_trait_elidable_nested_named_lifetimes<'a>(i: &'a i32, f: impl for<'b> Fn(&'b i32) -> &'b i32) -> &'a i32 { + f(i) + } + fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 { + f(i) + } - fn nested_generic_fn_trait_bound<'a, T: Fn() -> &'a i32>(f: T) -> &'a i32 { + // don't lint + fn generics_not_elidable<'a, T: Fn() -> &'a i32>(f: T) -> &'a i32 { f() } + // lint + fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 { + f(i) + } - fn nested_where_clause_fn_trait_bound<'a, T>(f: T) -> &'a i32 + // don't lint + fn where_clause_not_elidable<'a, T>(f: T) -> &'a i32 where T: Fn() -> &'a i32, { f() } + // lint + fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32 + where + T: Fn(&i32) -> &i32, + { + f(i) + } - fn nested_pointer_fn<'a>(_: &'a i32) -> fn(&'a i32) -> &'a i32 { + // don't lint + fn pointer_fn_in_input_position<'a>(f: fn(&'a i32) -> &'a i32, i: &'a i32) -> &'a i32 { + f(i) + } + fn pointer_fn_in_output_position<'a>(_: &'a i32) -> fn(&'a i32) -> &'a i32 { |i| i } + // lint + fn pointer_fn_elidable<'a>(f: fn(&i32) -> &i32, i: &'a i32) -> &'a i32 { + f(i) + } } fn main() {} diff --git a/tests/ui/needless_lifetimes.stderr b/tests/ui/needless_lifetimes.stderr index d3a360ed8b5..ac38ab8effd 100644 --- a/tests/ui/needless_lifetimes.stderr +++ b/tests/ui/needless_lifetimes.stderr @@ -102,5 +102,35 @@ error: explicit lifetimes given in parameter types where they could be elided (o LL | fn needless_lt<'a>(_x: &'a u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 17 previous errors +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:271:9 + | +LL | fn baz<'a>(&'a self) -> impl Foo + 'a { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:300:5 + | +LL | fn impl_trait_elidable_nested_named_lifetimes<'a>(i: &'a i32, f: impl for<'b> Fn(&'b i32) -> &'b i32) -> &'a i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:303:5 + | +LL | fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:312:5 + | +LL | fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:324:5 + | +LL | fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 22 previous errors -- cgit 1.4.1-3-g733a5 From de195f2d3d3a2039cb8c4141aa37d060780beaa7 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Wed, 9 Sep 2020 17:59:13 +0200 Subject: print the unit type `()` in related lint messages. --- clippy_lints/src/map_unit_fn.rs | 6 ++--- tests/ui/option_map_unit_fn_fixable.stderr | 36 ++++++++++++++-------------- tests/ui/result_map_unit_fn_fixable.stderr | 34 +++++++++++++------------- tests/ui/result_map_unit_fn_unfixable.stderr | 12 +++++----- 4 files changed, 44 insertions(+), 44 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index 1f9ae8c931a..076ef235b8b 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -9,7 +9,7 @@ use rustc_span::source_map::Span; declare_clippy_lint! { /// **What it does:** Checks for usage of `option.map(f)` where f is a function - /// or closure that returns the unit type. + /// or closure that returns the unit type `()`. /// /// **Why is this bad?** Readability, this can be written more clearly with /// an if let statement @@ -51,7 +51,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** Checks for usage of `result.map(f)` where f is a function - /// or closure that returns the unit type. + /// or closure that returns the unit type `()`. /// /// **Why is this bad?** Readability, this can be written more clearly with /// an if let statement @@ -197,7 +197,7 @@ fn let_binding_name(cx: &LateContext<'_>, var_arg: &hir::Expr<'_>) -> String { #[must_use] fn suggestion_msg(function_type: &str, map_type: &str) -> String { format!( - "called `map(f)` on an `{0}` value where `f` is a {1} that returns the unit type", + "called `map(f)` on an `{0}` value where `f` is a {1} that returns the unit type `()`", map_type, function_type ) } diff --git a/tests/ui/option_map_unit_fn_fixable.stderr b/tests/ui/option_map_unit_fn_fixable.stderr index 1312c70b6d5..d7d45ef9b0b 100644 --- a/tests/ui/option_map_unit_fn_fixable.stderr +++ b/tests/ui/option_map_unit_fn_fixable.stderr @@ -1,4 +1,4 @@ -error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:38:5 | LL | x.field.map(do_nothing); @@ -8,7 +8,7 @@ LL | x.field.map(do_nothing); | = note: `-D clippy::option-map-unit-fn` implied by `-D warnings` -error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:40:5 | LL | x.field.map(do_nothing); @@ -16,7 +16,7 @@ LL | x.field.map(do_nothing); | | | help: try this: `if let Some(x_field) = x.field { do_nothing(x_field) }` -error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:42:5 | LL | x.field.map(diverge); @@ -24,7 +24,7 @@ LL | x.field.map(diverge); | | | help: try this: `if let Some(x_field) = x.field { diverge(x_field) }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:48:5 | LL | x.field.map(|value| x.do_option_nothing(value + captured)); @@ -32,7 +32,7 @@ LL | x.field.map(|value| x.do_option_nothing(value + captured)); | | | help: try this: `if let Some(value) = x.field { x.do_option_nothing(value + captured) }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:50:5 | LL | x.field.map(|value| { x.do_option_plus_one(value + captured); }); @@ -40,7 +40,7 @@ LL | x.field.map(|value| { x.do_option_plus_one(value + captured); }); | | | help: try this: `if let Some(value) = x.field { x.do_option_plus_one(value + captured); }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:53:5 | LL | x.field.map(|value| do_nothing(value + captured)); @@ -48,7 +48,7 @@ LL | x.field.map(|value| do_nothing(value + captured)); | | | help: try this: `if let Some(value) = x.field { do_nothing(value + captured) }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:55:5 | LL | x.field.map(|value| { do_nothing(value + captured) }); @@ -56,7 +56,7 @@ LL | x.field.map(|value| { do_nothing(value + captured) }); | | | help: try this: `if let Some(value) = x.field { do_nothing(value + captured) }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:57:5 | LL | x.field.map(|value| { do_nothing(value + captured); }); @@ -64,7 +64,7 @@ LL | x.field.map(|value| { do_nothing(value + captured); }); | | | help: try this: `if let Some(value) = x.field { do_nothing(value + captured); }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:59:5 | LL | x.field.map(|value| { { do_nothing(value + captured); } }); @@ -72,7 +72,7 @@ LL | x.field.map(|value| { { do_nothing(value + captured); } }); | | | help: try this: `if let Some(value) = x.field { do_nothing(value + captured); }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:62:5 | LL | x.field.map(|value| diverge(value + captured)); @@ -80,7 +80,7 @@ LL | x.field.map(|value| diverge(value + captured)); | | | help: try this: `if let Some(value) = x.field { diverge(value + captured) }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:64:5 | LL | x.field.map(|value| { diverge(value + captured) }); @@ -88,7 +88,7 @@ LL | x.field.map(|value| { diverge(value + captured) }); | | | help: try this: `if let Some(value) = x.field { diverge(value + captured) }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:66:5 | LL | x.field.map(|value| { diverge(value + captured); }); @@ -96,7 +96,7 @@ LL | x.field.map(|value| { diverge(value + captured); }); | | | help: try this: `if let Some(value) = x.field { diverge(value + captured); }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:68:5 | LL | x.field.map(|value| { { diverge(value + captured); } }); @@ -104,7 +104,7 @@ LL | x.field.map(|value| { { diverge(value + captured); } }); | | | help: try this: `if let Some(value) = x.field { diverge(value + captured); }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:73:5 | LL | x.field.map(|value| { let y = plus_one(value + captured); }); @@ -112,7 +112,7 @@ LL | x.field.map(|value| { let y = plus_one(value + captured); }); | | | help: try this: `if let Some(value) = x.field { let y = plus_one(value + captured); }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:75:5 | LL | x.field.map(|value| { plus_one(value + captured); }); @@ -120,7 +120,7 @@ LL | x.field.map(|value| { plus_one(value + captured); }); | | | help: try this: `if let Some(value) = x.field { plus_one(value + captured); }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:77:5 | LL | x.field.map(|value| { { plus_one(value + captured); } }); @@ -128,7 +128,7 @@ LL | x.field.map(|value| { { plus_one(value + captured); } }); | | | help: try this: `if let Some(value) = x.field { plus_one(value + captured); }` -error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:80:5 | LL | x.field.map(|ref value| { do_nothing(value + captured) }); @@ -136,7 +136,7 @@ LL | x.field.map(|ref value| { do_nothing(value + captured) }); | | | help: try this: `if let Some(ref value) = x.field { do_nothing(value + captured) }` -error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` --> $DIR/option_map_unit_fn_fixable.rs:82:5 | LL | option().map(do_nothing);} diff --git a/tests/ui/result_map_unit_fn_fixable.stderr b/tests/ui/result_map_unit_fn_fixable.stderr index 467e00263cd..4f3a8c6b792 100644 --- a/tests/ui/result_map_unit_fn_fixable.stderr +++ b/tests/ui/result_map_unit_fn_fixable.stderr @@ -1,4 +1,4 @@ -error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:35:5 | LL | x.field.map(do_nothing); @@ -8,7 +8,7 @@ LL | x.field.map(do_nothing); | = note: `-D clippy::result-map-unit-fn` implied by `-D warnings` -error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:37:5 | LL | x.field.map(do_nothing); @@ -16,7 +16,7 @@ LL | x.field.map(do_nothing); | | | help: try this: `if let Ok(x_field) = x.field { do_nothing(x_field) }` -error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:39:5 | LL | x.field.map(diverge); @@ -24,7 +24,7 @@ LL | x.field.map(diverge); | | | help: try this: `if let Ok(x_field) = x.field { diverge(x_field) }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:45:5 | LL | x.field.map(|value| x.do_result_nothing(value + captured)); @@ -32,7 +32,7 @@ LL | x.field.map(|value| x.do_result_nothing(value + captured)); | | | help: try this: `if let Ok(value) = x.field { x.do_result_nothing(value + captured) }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:47:5 | LL | x.field.map(|value| { x.do_result_plus_one(value + captured); }); @@ -40,7 +40,7 @@ LL | x.field.map(|value| { x.do_result_plus_one(value + captured); }); | | | help: try this: `if let Ok(value) = x.field { x.do_result_plus_one(value + captured); }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:50:5 | LL | x.field.map(|value| do_nothing(value + captured)); @@ -48,7 +48,7 @@ LL | x.field.map(|value| do_nothing(value + captured)); | | | help: try this: `if let Ok(value) = x.field { do_nothing(value + captured) }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:52:5 | LL | x.field.map(|value| { do_nothing(value + captured) }); @@ -56,7 +56,7 @@ LL | x.field.map(|value| { do_nothing(value + captured) }); | | | help: try this: `if let Ok(value) = x.field { do_nothing(value + captured) }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:54:5 | LL | x.field.map(|value| { do_nothing(value + captured); }); @@ -64,7 +64,7 @@ LL | x.field.map(|value| { do_nothing(value + captured); }); | | | help: try this: `if let Ok(value) = x.field { do_nothing(value + captured); }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:56:5 | LL | x.field.map(|value| { { do_nothing(value + captured); } }); @@ -72,7 +72,7 @@ LL | x.field.map(|value| { { do_nothing(value + captured); } }); | | | help: try this: `if let Ok(value) = x.field { do_nothing(value + captured); }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:59:5 | LL | x.field.map(|value| diverge(value + captured)); @@ -80,7 +80,7 @@ LL | x.field.map(|value| diverge(value + captured)); | | | help: try this: `if let Ok(value) = x.field { diverge(value + captured) }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:61:5 | LL | x.field.map(|value| { diverge(value + captured) }); @@ -88,7 +88,7 @@ LL | x.field.map(|value| { diverge(value + captured) }); | | | help: try this: `if let Ok(value) = x.field { diverge(value + captured) }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:63:5 | LL | x.field.map(|value| { diverge(value + captured); }); @@ -96,7 +96,7 @@ LL | x.field.map(|value| { diverge(value + captured); }); | | | help: try this: `if let Ok(value) = x.field { diverge(value + captured); }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:65:5 | LL | x.field.map(|value| { { diverge(value + captured); } }); @@ -104,7 +104,7 @@ LL | x.field.map(|value| { { diverge(value + captured); } }); | | | help: try this: `if let Ok(value) = x.field { diverge(value + captured); }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:70:5 | LL | x.field.map(|value| { let y = plus_one(value + captured); }); @@ -112,7 +112,7 @@ LL | x.field.map(|value| { let y = plus_one(value + captured); }); | | | help: try this: `if let Ok(value) = x.field { let y = plus_one(value + captured); }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:72:5 | LL | x.field.map(|value| { plus_one(value + captured); }); @@ -120,7 +120,7 @@ LL | x.field.map(|value| { plus_one(value + captured); }); | | | help: try this: `if let Ok(value) = x.field { plus_one(value + captured); }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:74:5 | LL | x.field.map(|value| { { plus_one(value + captured); } }); @@ -128,7 +128,7 @@ LL | x.field.map(|value| { { plus_one(value + captured); } }); | | | help: try this: `if let Ok(value) = x.field { plus_one(value + captured); }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_fixable.rs:77:5 | LL | x.field.map(|ref value| { do_nothing(value + captured) }); diff --git a/tests/ui/result_map_unit_fn_unfixable.stderr b/tests/ui/result_map_unit_fn_unfixable.stderr index b23cc608621..88e4efdb0f0 100644 --- a/tests/ui/result_map_unit_fn_unfixable.stderr +++ b/tests/ui/result_map_unit_fn_unfixable.stderr @@ -1,4 +1,4 @@ -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_unfixable.rs:23:5 | LL | x.field.map(|value| { do_nothing(value); do_nothing(value) }); @@ -8,7 +8,7 @@ LL | x.field.map(|value| { do_nothing(value); do_nothing(value) }); | = note: `-D clippy::result-map-unit-fn` implied by `-D warnings` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_unfixable.rs:25:5 | LL | x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) }); @@ -16,7 +16,7 @@ LL | x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) | | | help: try this: `if let Ok(value) = x.field { ... }` -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_unfixable.rs:29:5 | LL | x.field.map(|value| { @@ -30,7 +30,7 @@ LL | || }); | |_______| | -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` --> $DIR/result_map_unit_fn_unfixable.rs:33:5 | LL | x.field.map(|value| { do_nothing(value); do_nothing(value); }); @@ -38,7 +38,7 @@ LL | x.field.map(|value| { do_nothing(value); do_nothing(value); }); | | | help: try this: `if let Ok(value) = x.field { ... }` -error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` --> $DIR/result_map_unit_fn_unfixable.rs:37:5 | LL | "12".parse::().map(diverge); @@ -46,7 +46,7 @@ LL | "12".parse::().map(diverge); | | | help: try this: `if let Ok(a) = "12".parse::() { diverge(a) }` -error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type +error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` --> $DIR/result_map_unit_fn_unfixable.rs:43:5 | LL | y.map(do_nothing); -- cgit 1.4.1-3-g733a5 From 3550568a548091ec4ae738827b50a84e9ddf0b8f Mon Sep 17 00:00:00 2001 From: Vali Schneider Date: Wed, 9 Sep 2020 14:02:34 -0700 Subject: removing if chain and renaming lint --- CHANGELOG.md | 2 +- clippy_lints/src/lib.rs | 8 +-- clippy_lints/src/panic_in_result.rs | 97 ------------------------------ clippy_lints/src/panic_in_result_fn.rs | 90 ++++++++++++++++++++++++++++ src/lintlist/mod.rs | 4 +- tests/ui/panic_in_result.rs | 70 ---------------------- tests/ui/panic_in_result.stderr | 105 --------------------------------- tests/ui/panic_in_result_fn.rs | 70 ++++++++++++++++++++++ tests/ui/panic_in_result_fn.stderr | 105 +++++++++++++++++++++++++++++++++ 9 files changed, 272 insertions(+), 279 deletions(-) delete mode 100644 clippy_lints/src/panic_in_result.rs create mode 100644 clippy_lints/src/panic_in_result_fn.rs delete mode 100644 tests/ui/panic_in_result.rs delete mode 100644 tests/ui/panic_in_result.stderr create mode 100644 tests/ui/panic_in_result_fn.rs create mode 100644 tests/ui/panic_in_result_fn.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 7af3b666cca..d4f0ff4ba78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1651,7 +1651,7 @@ Released 2018-09-13 [`out_of_bounds_indexing`]: https://rust-lang.github.io/rust-clippy/master/index.html#out_of_bounds_indexing [`overflow_check_conditional`]: https://rust-lang.github.io/rust-clippy/master/index.html#overflow_check_conditional [`panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic -[`panic_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_in_result +[`panic_in_result_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_in_result [`panic_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_params [`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap [`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index b70d126af5b..dbe17e6bbfe 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -267,7 +267,7 @@ mod open_options; mod option_env_unwrap; mod option_if_let_else; mod overflow_check_conditional; -mod panic_in_result; +mod panic_in_result_fn; mod panic_unimplemented; mod partialeq_ne_impl; mod path_buf_push_overwrite; @@ -748,7 +748,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &option_env_unwrap::OPTION_ENV_UNWRAP, &option_if_let_else::OPTION_IF_LET_ELSE, &overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL, - &panic_in_result::PANIC_IN_RESULT, + &panic_in_result_fn::PANIC_IN_RESULT_FN, &panic_unimplemented::PANIC, &panic_unimplemented::PANIC_PARAMS, &panic_unimplemented::TODO, @@ -1088,7 +1088,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box manual_async_fn::ManualAsyncFn); store.register_early_pass(|| box redundant_field_names::RedundantFieldNames); store.register_late_pass(|| box vec_resize_to_zero::VecResizeToZero); - store.register_late_pass(|| box panic_in_result::PanicInResult); + store.register_late_pass(|| box panic_in_result_fn::PanicInResultFn); let single_char_binding_names_threshold = conf.single_char_binding_names_threshold; store.register_early_pass(move || box non_expressive_names::NonExpressiveNames { @@ -1132,7 +1132,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS), LintId::of(&missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS), LintId::of(&modulo_arithmetic::MODULO_ARITHMETIC), - LintId::of(&panic_in_result::PANIC_IN_RESULT), + 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), diff --git a/clippy_lints/src/panic_in_result.rs b/clippy_lints/src/panic_in_result.rs deleted file mode 100644 index 8b8e211cb72..00000000000 --- a/clippy_lints/src/panic_in_result.rs +++ /dev/null @@ -1,97 +0,0 @@ -use crate::utils::{is_expn_of, is_type_diagnostic_item, return_ty, span_lint_and_then}; -use if_chain::if_chain; -use rustc_hir as hir; -use rustc_hir::intravisit::{self, FnKind, NestedVisitorMap, Visitor}; -use rustc_hir::Expr; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::hir::map::Map; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::Span; - -declare_clippy_lint! { - /// **What it does:** Checks for usage of `panic!`, `unimplemented!`, `todo!` or `unreachable!` in a function of type result. - /// - /// **Why is this bad?** For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence unimplemented, panic and unreachable should be avoided. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// - /// ```rust - /// fn result_with_panic() -> Result - /// { - /// panic!("error"); - /// } - /// ``` - pub PANIC_IN_RESULT, - restriction, - "functions of type `Result<..>` that contain `panic!()`, `todo!()` or `unreachable()` or `unimplemented()` " -} - -declare_lint_pass!(PanicInResult => [PANIC_IN_RESULT]); - -impl<'tcx> LateLintPass<'tcx> for PanicInResult { - fn check_fn( - &mut self, - cx: &LateContext<'tcx>, - fn_kind: FnKind<'tcx>, - _: &'tcx hir::FnDecl<'tcx>, - body: &'tcx hir::Body<'tcx>, - span: Span, - hir_id: hir::HirId, - ) { - if let FnKind::Closure(_) = fn_kind { - return; - } - if_chain! { - if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)); - then - { - lint_impl_body(cx, span, body); - } - } - } -} - -struct FindPanicUnimplementedUnreachable { - result: Vec, -} - -impl<'tcx> Visitor<'tcx> for FindPanicUnimplementedUnreachable { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if is_expn_of(expr.span, "unimplemented").is_some() - || is_expn_of(expr.span, "unreachable").is_some() - || is_expn_of(expr.span, "panic").is_some() - || is_expn_of(expr.span, "todo").is_some() - { - self.result.push(expr.span); - } - // and check sub-expressions - intravisit::walk_expr(self, expr); - } - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } -} - -fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) { - let mut panics = FindPanicUnimplementedUnreachable { result: Vec::new() }; - panics.visit_expr(&body.value); - if !panics.result.is_empty() { - span_lint_and_then( - cx, - PANIC_IN_RESULT, - impl_span, - "used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`", - move |diag| { - diag.help( - "`unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing", - ); - diag.span_note(panics.result, "return Err() instead of panicking"); - }, - ); - } -} diff --git a/clippy_lints/src/panic_in_result_fn.rs b/clippy_lints/src/panic_in_result_fn.rs new file mode 100644 index 00000000000..4077aba6ef1 --- /dev/null +++ b/clippy_lints/src/panic_in_result_fn.rs @@ -0,0 +1,90 @@ +use crate::utils::{is_expn_of, is_type_diagnostic_item, return_ty, span_lint_and_then}; +use rustc_hir as hir; +use rustc_hir::intravisit::{self, FnKind, NestedVisitorMap, Visitor}; +use rustc_hir::Expr; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `panic!`, `unimplemented!`, `todo!` or `unreachable!` in a function of type result. + /// + /// **Why is this bad?** For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence unimplemented, panic and unreachable should be avoided. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// fn result_with_panic() -> Result + /// { + /// panic!("error"); + /// } + /// ``` + pub PANIC_IN_RESULT_FN, + restriction, + "functions of type `Result<..>` that contain `panic!()`, `todo!()` or `unreachable()` or `unimplemented()` " +} + +declare_lint_pass!(PanicInResultFn => [PANIC_IN_RESULT_FN]); + +impl<'tcx> LateLintPass<'tcx> for PanicInResultFn { + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + fn_kind: FnKind<'tcx>, + _: &'tcx hir::FnDecl<'tcx>, + body: &'tcx hir::Body<'tcx>, + span: Span, + hir_id: hir::HirId, + ) { + if !matches!(fn_kind, FnKind::Closure(_)) + && is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)) + { + lint_impl_body(cx, span, body); + } + } +} + +struct FindPanicUnimplementedUnreachable { + result: Vec, +} + +impl<'tcx> Visitor<'tcx> for FindPanicUnimplementedUnreachable { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if ["unimplemented", "unreachable", "panic", "todo"] + .iter() + .any(|fun| is_expn_of(expr.span, fun).is_some()) + { + self.result.push(expr.span); + } + // and check sub-expressions + intravisit::walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) { + let mut panics = FindPanicUnimplementedUnreachable { result: Vec::new() }; + panics.visit_expr(&body.value); + if !panics.result.is_empty() { + span_lint_and_then( + cx, + PANIC_IN_RESULT_FN, + impl_span, + "used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`", + move |diag| { + diag.help( + "`unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing", + ); + diag.span_note(panics.result, "return Err() instead of panicking"); + }, + ); + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 1f56c56f081..4d6c45761e6 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1705,11 +1705,11 @@ pub static ref ALL_LINTS: Vec = vec![ module: "panic_unimplemented", }, Lint { - name: "panic_in_result", + name: "panic_in_result_fn", group: "restriction", desc: "functions of type `Result<..>` that contain `panic!()`, `todo!()` or `unreachable()` or `unimplemented()` ", deprecation: None, - module: "panic_in_result", + module: "panic_in_result_fn", }, Lint { name: "panic_params", diff --git a/tests/ui/panic_in_result.rs b/tests/ui/panic_in_result.rs deleted file mode 100644 index f6fb2f1ab61..00000000000 --- a/tests/ui/panic_in_result.rs +++ /dev/null @@ -1,70 +0,0 @@ -#![warn(clippy::panic_in_result)] - -struct A; - -impl A { - fn result_with_panic() -> Result // should emit lint - { - panic!("error"); - } - - fn result_with_unimplemented() -> Result // should emit lint - { - unimplemented!(); - } - - fn result_with_unreachable() -> Result // should emit lint - { - unreachable!(); - } - - fn result_with_todo() -> Result // should emit lint - { - todo!("Finish this"); - } - - fn other_with_panic() // should not emit lint - { - panic!(""); - } - - fn other_with_unreachable() // should not emit lint - { - unreachable!(); - } - - fn other_with_unimplemented() // should not emit lint - { - unimplemented!(); - } - - fn other_with_todo() // should not emit lint - { - todo!("finish this") - } - - fn result_without_banned_functions() -> Result // should not emit lint - { - Ok(true) - } -} - -fn function_result_with_panic() -> Result // should emit lint -{ - panic!("error"); -} - -fn todo() { - println!("something"); -} - -fn function_result_with_custom_todo() -> Result // should not emit lint -{ - todo(); - Ok(true) -} - -fn main() -> Result<(), String> { - todo!("finish main method"); - Ok(()) -} diff --git a/tests/ui/panic_in_result.stderr b/tests/ui/panic_in_result.stderr deleted file mode 100644 index 9faedf82986..00000000000 --- a/tests/ui/panic_in_result.stderr +++ /dev/null @@ -1,105 +0,0 @@ -error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` - --> $DIR/panic_in_result.rs:6:5 - | -LL | / fn result_with_panic() -> Result // should emit lint -LL | | { -LL | | panic!("error"); -LL | | } - | |_____^ - | - = note: `-D clippy::panic-in-result` implied by `-D warnings` - = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing -note: return Err() instead of panicking - --> $DIR/panic_in_result.rs:8:9 - | -LL | panic!("error"); - | ^^^^^^^^^^^^^^^^ - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` - --> $DIR/panic_in_result.rs:11:5 - | -LL | / fn result_with_unimplemented() -> Result // should emit lint -LL | | { -LL | | unimplemented!(); -LL | | } - | |_____^ - | - = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing -note: return Err() instead of panicking - --> $DIR/panic_in_result.rs:13:9 - | -LL | unimplemented!(); - | ^^^^^^^^^^^^^^^^^ - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` - --> $DIR/panic_in_result.rs:16:5 - | -LL | / fn result_with_unreachable() -> Result // should emit lint -LL | | { -LL | | unreachable!(); -LL | | } - | |_____^ - | - = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing -note: return Err() instead of panicking - --> $DIR/panic_in_result.rs:18:9 - | -LL | unreachable!(); - | ^^^^^^^^^^^^^^^ - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` - --> $DIR/panic_in_result.rs:21:5 - | -LL | / fn result_with_todo() -> Result // should emit lint -LL | | { -LL | | todo!("Finish this"); -LL | | } - | |_____^ - | - = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing -note: return Err() instead of panicking - --> $DIR/panic_in_result.rs:23:9 - | -LL | todo!("Finish this"); - | ^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` - --> $DIR/panic_in_result.rs:52:1 - | -LL | / fn function_result_with_panic() -> Result // should emit lint -LL | | { -LL | | panic!("error"); -LL | | } - | |_^ - | - = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing -note: return Err() instead of panicking - --> $DIR/panic_in_result.rs:54:5 - | -LL | panic!("error"); - | ^^^^^^^^^^^^^^^^ - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` - --> $DIR/panic_in_result.rs:67:1 - | -LL | / fn main() -> Result<(), String> { -LL | | todo!("finish main method"); -LL | | Ok(()) -LL | | } - | |_^ - | - = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing -note: return Err() instead of panicking - --> $DIR/panic_in_result.rs:68:5 - | -LL | todo!("finish main method"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in a macro (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.rs b/tests/ui/panic_in_result_fn.rs new file mode 100644 index 00000000000..287726f7a2d --- /dev/null +++ b/tests/ui/panic_in_result_fn.rs @@ -0,0 +1,70 @@ +#![warn(clippy::panic_in_result_fn)] + +struct A; + +impl A { + fn result_with_panic() -> Result // should emit lint + { + panic!("error"); + } + + fn result_with_unimplemented() -> Result // should emit lint + { + unimplemented!(); + } + + fn result_with_unreachable() -> Result // should emit lint + { + unreachable!(); + } + + fn result_with_todo() -> Result // should emit lint + { + todo!("Finish this"); + } + + fn other_with_panic() // should not emit lint + { + panic!(""); + } + + fn other_with_unreachable() // should not emit lint + { + unreachable!(); + } + + fn other_with_unimplemented() // should not emit lint + { + unimplemented!(); + } + + fn other_with_todo() // should not emit lint + { + todo!("finish this") + } + + fn result_without_banned_functions() -> Result // should not emit lint + { + Ok(true) + } +} + +fn function_result_with_panic() -> Result // should emit lint +{ + panic!("error"); +} + +fn todo() { + println!("something"); +} + +fn function_result_with_custom_todo() -> Result // should not emit lint +{ + todo(); + Ok(true) +} + +fn main() -> Result<(), String> { + todo!("finish main method"); + Ok(()) +} diff --git a/tests/ui/panic_in_result_fn.stderr b/tests/ui/panic_in_result_fn.stderr new file mode 100644 index 00000000000..c6936fd8692 --- /dev/null +++ b/tests/ui/panic_in_result_fn.stderr @@ -0,0 +1,105 @@ +error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` + --> $DIR/panic_in_result_fn.rs:6:5 + | +LL | / fn result_with_panic() -> Result // should emit lint +LL | | { +LL | | panic!("error"); +LL | | } + | |_____^ + | + = note: `-D clippy::panic-in-result-fn` implied by `-D warnings` + = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking + --> $DIR/panic_in_result_fn.rs:8:9 + | +LL | panic!("error"); + | ^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` + --> $DIR/panic_in_result_fn.rs:11:5 + | +LL | / fn result_with_unimplemented() -> Result // should emit lint +LL | | { +LL | | unimplemented!(); +LL | | } + | |_____^ + | + = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking + --> $DIR/panic_in_result_fn.rs:13:9 + | +LL | unimplemented!(); + | ^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` + --> $DIR/panic_in_result_fn.rs:16:5 + | +LL | / fn result_with_unreachable() -> Result // should emit lint +LL | | { +LL | | unreachable!(); +LL | | } + | |_____^ + | + = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking + --> $DIR/panic_in_result_fn.rs:18:9 + | +LL | unreachable!(); + | ^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` + --> $DIR/panic_in_result_fn.rs:21:5 + | +LL | / fn result_with_todo() -> Result // should emit lint +LL | | { +LL | | todo!("Finish this"); +LL | | } + | |_____^ + | + = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking + --> $DIR/panic_in_result_fn.rs:23:9 + | +LL | todo!("Finish this"); + | ^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` + --> $DIR/panic_in_result_fn.rs:52:1 + | +LL | / fn function_result_with_panic() -> Result // should emit lint +LL | | { +LL | | panic!("error"); +LL | | } + | |_^ + | + = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking + --> $DIR/panic_in_result_fn.rs:54:5 + | +LL | panic!("error"); + | ^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` + --> $DIR/panic_in_result_fn.rs:67:1 + | +LL | / fn main() -> Result<(), String> { +LL | | todo!("finish main method"); +LL | | Ok(()) +LL | | } + | |_^ + | + = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking + --> $DIR/panic_in_result_fn.rs:68:5 + | +LL | todo!("finish main method"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 6 previous errors + -- cgit 1.4.1-3-g733a5 From 6211599ccafee5b2429bfb1bbeb48ead32a48484 Mon Sep 17 00:00:00 2001 From: Thom Chiovoloni Date: Wed, 9 Sep 2020 14:00:31 -0700 Subject: Extend invalid_atomic_ordering to detect misuse of compare_exchange{,_weak} --- clippy_lints/src/atomic_ordering.rs | 82 +++++++++- tests/ui/atomic_ordering_exchange.rs | 84 ++++++++++ tests/ui/atomic_ordering_exchange.stderr | 259 +++++++++++++++++++++++++++++++ 3 files changed, 423 insertions(+), 2 deletions(-) create mode 100644 tests/ui/atomic_ordering_exchange.rs create mode 100644 tests/ui/atomic_ordering_exchange.stderr (limited to 'tests') diff --git a/clippy_lints/src/atomic_ordering.rs b/clippy_lints/src/atomic_ordering.rs index 2d964ac2b9f..748f45f47c2 100644 --- a/clippy_lints/src/atomic_ordering.rs +++ b/clippy_lints/src/atomic_ordering.rs @@ -8,7 +8,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// **What it does:** Checks for usage of invalid atomic - /// ordering in atomic loads/stores and memory fences. + /// ordering in atomic loads/stores/exchanges and memory fences /// /// **Why is this bad?** Using an invalid atomic ordering /// will cause a panic at run-time. @@ -29,10 +29,13 @@ declare_clippy_lint! { /// /// atomic::fence(Ordering::Relaxed); /// atomic::compiler_fence(Ordering::Relaxed); + /// + /// let _ = x.compare_exchange(false, false, Ordering::Relaxed, Ordering::SeqCst); + /// let _ = x.compare_exchange_weak(false, true, Ordering::SeqCst, Ordering::Release); /// ``` pub INVALID_ATOMIC_ORDERING, correctness, - "usage of invalid atomic ordering in atomic loads/stores and memory fences" + "usage of invalid atomic ordering in atomic loads/stores/exchanges ane memory fences" } declare_lint_pass!(AtomicOrdering => [INVALID_ATOMIC_ORDERING]); @@ -127,9 +130,84 @@ fn check_memory_fence(cx: &LateContext<'_>, expr: &Expr<'_>) { } } +fn opt_ordering_defid(cx: &LateContext<'_>, ord_arg: &Expr<'_>) -> Option { + if let ExprKind::Path(ref ord_qpath) = ord_arg.kind { + cx.qpath_res(ord_qpath, ord_arg.hir_id).opt_def_id() + } else { + None + } +} +fn check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>) { + if_chain! { + if let ExprKind::MethodCall(ref method_path, _, args, _) = &expr.kind; + let method = method_path.ident.name.as_str(); + if type_is_atomic(cx, &args[0]); + if method == "compare_exchange" || method == "compare_exchange_weak"; + let failure_order_arg = &args[4]; + if let Some(fail_ordering_def_id) = opt_ordering_defid(cx, failure_order_arg); + then { + // Helper type holding on to some checking and error reporting data. Has + // - (success ordering name, + // - list of failure orderings forbidden by the success order, + // - suggestion message) + type OrdLintInfo = (&'static str, &'static [&'static str], &'static str); + let relaxed: OrdLintInfo = ("Relaxed", &["SeqCst", "Acquire"], "ordering mode `Relaxed`"); + let acquire: OrdLintInfo = ("Acquire", &["SeqCst"], "ordering modes `Acquire` or `Relaxed`"); + let seq_cst: OrdLintInfo = ("SeqCst", &[], "ordering modes `Acquire`, `SeqCst` or `Relaxed`"); + let release = ("Release", relaxed.1, relaxed.2); + let acqrel = ("AcqRel", acquire.1, acquire.2); + let search = [relaxed, acquire, seq_cst, release, acqrel]; + + let success_lint_info = opt_ordering_defid(cx, &args[3]) + .and_then(|success_ord_def_id| -> Option { + search + .iter() + .find(|(ordering, ..)| { + match_def_path(cx, success_ord_def_id, + &["core", "sync", "atomic", "Ordering", ordering]) + }) + .copied() + }); + + if match_ordering_def_path(cx, fail_ordering_def_id, &["Release", "AcqRel"]) { + // If we don't know the success order is, use what we'd suggest + // if it were maximally permissive. + let suggested = success_lint_info.unwrap_or(seq_cst).2; + span_lint_and_help( + cx, + INVALID_ATOMIC_ORDERING, + failure_order_arg.span, + &format!( + "{}'s failure ordering may not be `Release` or `AcqRel`", + method, + ), + None, + &format!("consider using {} instead", suggested), + ); + } else if let Some((success_ord_name, bad_ords_given_success, suggested)) = success_lint_info { + if match_ordering_def_path(cx, fail_ordering_def_id, bad_ords_given_success) { + span_lint_and_help( + cx, + INVALID_ATOMIC_ORDERING, + failure_order_arg.span, + &format!( + "{}'s failure ordering may not stronger than the success ordering of `{}`", + method, + success_ord_name, + ), + None, + &format!("consider using {} instead", suggested), + ); + } + } + } + } +} + impl<'tcx> LateLintPass<'tcx> for AtomicOrdering { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { check_atomic_load_store(cx, expr); check_memory_fence(cx, expr); + check_atomic_compare_exchange(cx, expr); } } diff --git a/tests/ui/atomic_ordering_exchange.rs b/tests/ui/atomic_ordering_exchange.rs new file mode 100644 index 00000000000..2f60a98f037 --- /dev/null +++ b/tests/ui/atomic_ordering_exchange.rs @@ -0,0 +1,84 @@ +#![warn(clippy::invalid_atomic_ordering)] + +use std::sync::atomic::{AtomicUsize, Ordering}; + +fn main() { + // `compare_exchange` (not weak) testing + let x = AtomicUsize::new(0); + + // Allowed ordering combos + let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Relaxed); + let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::Acquire); + let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::Relaxed); + let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Relaxed); + let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::Acquire); + let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::Relaxed); + let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::Relaxed); + let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::Acquire); + let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::SeqCst); + + // AcqRel is always forbidden as a failure ordering + let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::AcqRel); + let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::AcqRel); + let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::AcqRel); + let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::AcqRel); + let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::AcqRel); + + // Release is always forbidden as a failure ordering + let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Release); + let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::Release); + let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Release); + let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::Release); + let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::Release); + + // Release success order forbids failure order of Acquire or SeqCst + let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Acquire); + let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::SeqCst); + + // Relaxed success order also forbids failure order of Acquire or SeqCst + let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::SeqCst); + let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Acquire); + + // Acquire/AcqRel forbids failure order of SeqCst + let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::SeqCst); + let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::SeqCst); + + // compare_exchange_weak tests + + // Allowed ordering combos + let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::Relaxed); + let _ = x.compare_exchange_weak(0, 0, Ordering::Acquire, Ordering::Acquire); + let _ = x.compare_exchange_weak(0, 0, Ordering::Acquire, Ordering::Relaxed); + let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::Relaxed); + let _ = x.compare_exchange_weak(0, 0, Ordering::AcqRel, Ordering::Acquire); + let _ = x.compare_exchange_weak(0, 0, Ordering::AcqRel, Ordering::Relaxed); + let _ = x.compare_exchange_weak(0, 0, Ordering::SeqCst, Ordering::Relaxed); + let _ = x.compare_exchange_weak(0, 0, Ordering::SeqCst, Ordering::Acquire); + let _ = x.compare_exchange_weak(0, 0, Ordering::SeqCst, Ordering::SeqCst); + + // AcqRel is always forbidden as a failure ordering + let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::AcqRel); + let _ = x.compare_exchange_weak(0, 0, Ordering::Acquire, Ordering::AcqRel); + let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::AcqRel); + let _ = x.compare_exchange_weak(0, 0, Ordering::AcqRel, Ordering::AcqRel); + let _ = x.compare_exchange_weak(0, 0, Ordering::SeqCst, Ordering::AcqRel); + + // Release is always forbidden as a failure ordering + let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::Release); + let _ = x.compare_exchange_weak(0, 0, Ordering::Acquire, Ordering::Release); + let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::Release); + let _ = x.compare_exchange_weak(0, 0, Ordering::AcqRel, Ordering::Release); + let _ = x.compare_exchange_weak(0, 0, Ordering::SeqCst, Ordering::Release); + + // Release success order forbids failure order of Acquire or SeqCst + let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::Acquire); + let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::SeqCst); + + // Relaxed success order also forbids failure order of Acquire or SeqCst + let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::SeqCst); + let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::Acquire); + + // Acquire/AcqRel forbids failure order of SeqCst + let _ = x.compare_exchange_weak(0, 0, Ordering::Acquire, Ordering::SeqCst); + let _ = x.compare_exchange_weak(0, 0, Ordering::AcqRel, Ordering::SeqCst); +} diff --git a/tests/ui/atomic_ordering_exchange.stderr b/tests/ui/atomic_ordering_exchange.stderr new file mode 100644 index 00000000000..26ec15be518 --- /dev/null +++ b/tests/ui/atomic_ordering_exchange.stderr @@ -0,0 +1,259 @@ +error: compare_exchange's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:21:57 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings` + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:22:57 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:23:57 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:24:56 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:25:56 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead + +error: compare_exchange's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:28:57 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:29:57 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:30:57 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:31:56 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:32:56 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead + +error: compare_exchange's failure ordering may not stronger than the success ordering of `Release` + --> $DIR/atomic_ordering_exchange.rs:35:57 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Acquire); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange's failure ordering may not stronger than the success ordering of `Release` + --> $DIR/atomic_ordering_exchange.rs:36:57 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::SeqCst); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange's failure ordering may not stronger than the success ordering of `Relaxed` + --> $DIR/atomic_ordering_exchange.rs:39:57 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::SeqCst); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange's failure ordering may not stronger than the success ordering of `Relaxed` + --> $DIR/atomic_ordering_exchange.rs:40:57 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Acquire); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange's failure ordering may not stronger than the success ordering of `Acquire` + --> $DIR/atomic_ordering_exchange.rs:43:57 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::SeqCst); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange's failure ordering may not stronger than the success ordering of `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:44:56 + | +LL | let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::SeqCst); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:60:62 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:61:62 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Acquire, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:62:62 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:63:61 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::AcqRel, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:64:61 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::SeqCst, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:67:62 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:68:62 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Acquire, Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:69:62 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:70:61 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::AcqRel, Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:71:61 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::SeqCst, Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Release` + --> $DIR/atomic_ordering_exchange.rs:74:62 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::Acquire); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Release` + --> $DIR/atomic_ordering_exchange.rs:75:62 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::SeqCst); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Relaxed` + --> $DIR/atomic_ordering_exchange.rs:78:62 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::SeqCst); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Relaxed` + --> $DIR/atomic_ordering_exchange.rs:79:62 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::Acquire); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Acquire` + --> $DIR/atomic_ordering_exchange.rs:82:62 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Acquire, Ordering::SeqCst); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `AcqRel` + --> $DIR/atomic_ordering_exchange.rs:83:61 + | +LL | let _ = x.compare_exchange_weak(0, 0, Ordering::AcqRel, Ordering::SeqCst); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: aborting due to 32 previous errors + -- cgit 1.4.1-3-g733a5 From 61671a2268903e1b5c28fcb3c713c27e84ea3e9b Mon Sep 17 00:00:00 2001 From: Thom Chiovoloni Date: Wed, 9 Sep 2020 14:12:14 -0700 Subject: Detect fetch_update misuse in invalid_atomic_ordering too --- clippy_lints/src/atomic_ordering.rs | 16 +++- src/lintlist/mod.rs | 2 +- tests/ui/atomic_ordering_fetch_update.rs | 45 +++++++++ tests/ui/atomic_ordering_fetch_update.stderr | 131 +++++++++++++++++++++++++++ 4 files changed, 188 insertions(+), 6 deletions(-) create mode 100644 tests/ui/atomic_ordering_fetch_update.rs create mode 100644 tests/ui/atomic_ordering_fetch_update.stderr (limited to 'tests') diff --git a/clippy_lints/src/atomic_ordering.rs b/clippy_lints/src/atomic_ordering.rs index 748f45f47c2..ff2c281ec9d 100644 --- a/clippy_lints/src/atomic_ordering.rs +++ b/clippy_lints/src/atomic_ordering.rs @@ -8,7 +8,8 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// **What it does:** Checks for usage of invalid atomic - /// ordering in atomic loads/stores/exchanges and memory fences + /// ordering in atomic loads/stores/exchanges/updates and + /// memory fences. /// /// **Why is this bad?** Using an invalid atomic ordering /// will cause a panic at run-time. @@ -32,10 +33,11 @@ declare_clippy_lint! { /// /// let _ = x.compare_exchange(false, false, Ordering::Relaxed, Ordering::SeqCst); /// let _ = x.compare_exchange_weak(false, true, Ordering::SeqCst, Ordering::Release); + /// let _ = x.fetch_update(Ordering::AcqRel, Ordering::AcqRel, |val| Some(val ^ val)); /// ``` pub INVALID_ATOMIC_ORDERING, correctness, - "usage of invalid atomic ordering in atomic loads/stores/exchanges ane memory fences" + "usage of invalid atomic ordering in atomic operations and memory fences" } declare_lint_pass!(AtomicOrdering => [INVALID_ATOMIC_ORDERING]); @@ -142,8 +144,12 @@ fn check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::MethodCall(ref method_path, _, args, _) = &expr.kind; let method = method_path.ident.name.as_str(); if type_is_atomic(cx, &args[0]); - if method == "compare_exchange" || method == "compare_exchange_weak"; - let failure_order_arg = &args[4]; + if method == "compare_exchange" || method == "compare_exchange_weak" || method == "fetch_update"; + let (success_order_arg, failure_order_arg) = if method == "fetch_update" { + (&args[1], &args[2]) + } else { + (&args[3], &args[4]) + }; if let Some(fail_ordering_def_id) = opt_ordering_defid(cx, failure_order_arg); then { // Helper type holding on to some checking and error reporting data. Has @@ -158,7 +164,7 @@ fn check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>) { let acqrel = ("AcqRel", acquire.1, acquire.2); let search = [relaxed, acquire, seq_cst, release, acqrel]; - let success_lint_info = opt_ordering_defid(cx, &args[3]) + let success_lint_info = opt_ordering_defid(cx, success_order_arg) .and_then(|success_ord_def_id| -> Option { search .iter() diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index dff19ef440f..5dead1b9b69 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -923,7 +923,7 @@ pub static ref ALL_LINTS: Vec = vec![ Lint { name: "invalid_atomic_ordering", group: "correctness", - desc: "usage of invalid atomic ordering in atomic loads/stores and memory fences", + desc: "usage of invalid atomic ordering in atomic operations and memory fences", deprecation: None, module: "atomic_ordering", }, diff --git a/tests/ui/atomic_ordering_fetch_update.rs b/tests/ui/atomic_ordering_fetch_update.rs new file mode 100644 index 00000000000..550bdb001e4 --- /dev/null +++ b/tests/ui/atomic_ordering_fetch_update.rs @@ -0,0 +1,45 @@ +#![warn(clippy::invalid_atomic_ordering)] + +use std::sync::atomic::{AtomicIsize, Ordering}; + +fn main() { + // `fetch_update` testing + let x = AtomicIsize::new(0); + + // Allowed ordering combos + let _ = x.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::Acquire, Ordering::Acquire, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::Acquire, Ordering::Relaxed, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::Release, Ordering::Relaxed, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::AcqRel, Ordering::Acquire, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::AcqRel, Ordering::Relaxed, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::SeqCst, Ordering::Relaxed, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::SeqCst, Ordering::Acquire, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |old| Some(old + 1)); + + // AcqRel is always forbidden as a failure ordering + let _ = x.fetch_update(Ordering::Relaxed, Ordering::AcqRel, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::Acquire, Ordering::AcqRel, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::Release, Ordering::AcqRel, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::AcqRel, Ordering::AcqRel, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::SeqCst, Ordering::AcqRel, |old| Some(old + 1)); + + // Release is always forbidden as a failure ordering + let _ = x.fetch_update(Ordering::Relaxed, Ordering::Release, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::Acquire, Ordering::Release, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::Release, Ordering::Release, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::AcqRel, Ordering::Release, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::SeqCst, Ordering::Release, |old| Some(old + 1)); + + // Release success order forbids failure order of Acquire or SeqCst + let _ = x.fetch_update(Ordering::Release, Ordering::Acquire, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::Release, Ordering::SeqCst, |old| Some(old + 1)); + + // Relaxed success order also forbids failure order of Acquire or SeqCst + let _ = x.fetch_update(Ordering::Relaxed, Ordering::SeqCst, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::Relaxed, Ordering::Acquire, |old| Some(old + 1)); + + // Acquire/AcqRel forbids failure order of SeqCst + let _ = x.fetch_update(Ordering::Acquire, Ordering::SeqCst, |old| Some(old + 1)); + let _ = x.fetch_update(Ordering::AcqRel, Ordering::SeqCst, |old| Some(old + 1)); +} diff --git a/tests/ui/atomic_ordering_fetch_update.stderr b/tests/ui/atomic_ordering_fetch_update.stderr new file mode 100644 index 00000000000..362e104a244 --- /dev/null +++ b/tests/ui/atomic_ordering_fetch_update.stderr @@ -0,0 +1,131 @@ +error: fetch_update's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_fetch_update.rs:21:47 + | +LL | let _ = x.fetch_update(Ordering::Relaxed, Ordering::AcqRel, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings` + = help: consider using ordering mode `Relaxed` instead + +error: fetch_update's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_fetch_update.rs:22:47 + | +LL | let _ = x.fetch_update(Ordering::Acquire, Ordering::AcqRel, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: fetch_update's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_fetch_update.rs:23:47 + | +LL | let _ = x.fetch_update(Ordering::Release, Ordering::AcqRel, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: fetch_update's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_fetch_update.rs:24:46 + | +LL | let _ = x.fetch_update(Ordering::AcqRel, Ordering::AcqRel, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: fetch_update's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_fetch_update.rs:25:46 + | +LL | let _ = x.fetch_update(Ordering::SeqCst, Ordering::AcqRel, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead + +error: fetch_update's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_fetch_update.rs:28:47 + | +LL | let _ = x.fetch_update(Ordering::Relaxed, Ordering::Release, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: fetch_update's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_fetch_update.rs:29:47 + | +LL | let _ = x.fetch_update(Ordering::Acquire, Ordering::Release, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: fetch_update's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_fetch_update.rs:30:47 + | +LL | let _ = x.fetch_update(Ordering::Release, Ordering::Release, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: fetch_update's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_fetch_update.rs:31:46 + | +LL | let _ = x.fetch_update(Ordering::AcqRel, Ordering::Release, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: fetch_update's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_fetch_update.rs:32:46 + | +LL | let _ = x.fetch_update(Ordering::SeqCst, Ordering::Release, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead + +error: fetch_update's failure ordering may not stronger than the success ordering of `Release` + --> $DIR/atomic_ordering_fetch_update.rs:35:47 + | +LL | let _ = x.fetch_update(Ordering::Release, Ordering::Acquire, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: fetch_update's failure ordering may not stronger than the success ordering of `Release` + --> $DIR/atomic_ordering_fetch_update.rs:36:47 + | +LL | let _ = x.fetch_update(Ordering::Release, Ordering::SeqCst, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: fetch_update's failure ordering may not stronger than the success ordering of `Relaxed` + --> $DIR/atomic_ordering_fetch_update.rs:39:47 + | +LL | let _ = x.fetch_update(Ordering::Relaxed, Ordering::SeqCst, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: fetch_update's failure ordering may not stronger than the success ordering of `Relaxed` + --> $DIR/atomic_ordering_fetch_update.rs:40:47 + | +LL | let _ = x.fetch_update(Ordering::Relaxed, Ordering::Acquire, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: fetch_update's failure ordering may not stronger than the success ordering of `Acquire` + --> $DIR/atomic_ordering_fetch_update.rs:43:47 + | +LL | let _ = x.fetch_update(Ordering::Acquire, Ordering::SeqCst, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: fetch_update's failure ordering may not stronger than the success ordering of `AcqRel` + --> $DIR/atomic_ordering_fetch_update.rs:44:46 + | +LL | let _ = x.fetch_update(Ordering::AcqRel, Ordering::SeqCst, |old| Some(old + 1)); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: aborting due to 16 previous errors + -- cgit 1.4.1-3-g733a5 From 159178e5d4a023f3945b504b49d3742b40453fee Mon Sep 17 00:00:00 2001 From: Thom Chiovoloni Date: Wed, 9 Sep 2020 14:32:38 -0700 Subject: Separate compare_exchange and compare_exchange_weak uitests --- tests/ui/atomic_ordering_exchange.rs | 39 -------- tests/ui/atomic_ordering_exchange.stderr | 130 +------------------------ tests/ui/atomic_ordering_exchange_weak.rs | 47 +++++++++ tests/ui/atomic_ordering_exchange_weak.stderr | 131 ++++++++++++++++++++++++++ 4 files changed, 179 insertions(+), 168 deletions(-) create mode 100644 tests/ui/atomic_ordering_exchange_weak.rs create mode 100644 tests/ui/atomic_ordering_exchange_weak.stderr (limited to 'tests') diff --git a/tests/ui/atomic_ordering_exchange.rs b/tests/ui/atomic_ordering_exchange.rs index 2f60a98f037..1ddc12f9ab2 100644 --- a/tests/ui/atomic_ordering_exchange.rs +++ b/tests/ui/atomic_ordering_exchange.rs @@ -42,43 +42,4 @@ fn main() { // Acquire/AcqRel forbids failure order of SeqCst let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::SeqCst); let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::SeqCst); - - // compare_exchange_weak tests - - // Allowed ordering combos - let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::Relaxed); - let _ = x.compare_exchange_weak(0, 0, Ordering::Acquire, Ordering::Acquire); - let _ = x.compare_exchange_weak(0, 0, Ordering::Acquire, Ordering::Relaxed); - let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::Relaxed); - let _ = x.compare_exchange_weak(0, 0, Ordering::AcqRel, Ordering::Acquire); - let _ = x.compare_exchange_weak(0, 0, Ordering::AcqRel, Ordering::Relaxed); - let _ = x.compare_exchange_weak(0, 0, Ordering::SeqCst, Ordering::Relaxed); - let _ = x.compare_exchange_weak(0, 0, Ordering::SeqCst, Ordering::Acquire); - let _ = x.compare_exchange_weak(0, 0, Ordering::SeqCst, Ordering::SeqCst); - - // AcqRel is always forbidden as a failure ordering - let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::AcqRel); - let _ = x.compare_exchange_weak(0, 0, Ordering::Acquire, Ordering::AcqRel); - let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::AcqRel); - let _ = x.compare_exchange_weak(0, 0, Ordering::AcqRel, Ordering::AcqRel); - let _ = x.compare_exchange_weak(0, 0, Ordering::SeqCst, Ordering::AcqRel); - - // Release is always forbidden as a failure ordering - let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::Release); - let _ = x.compare_exchange_weak(0, 0, Ordering::Acquire, Ordering::Release); - let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::Release); - let _ = x.compare_exchange_weak(0, 0, Ordering::AcqRel, Ordering::Release); - let _ = x.compare_exchange_weak(0, 0, Ordering::SeqCst, Ordering::Release); - - // Release success order forbids failure order of Acquire or SeqCst - let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::Acquire); - let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::SeqCst); - - // Relaxed success order also forbids failure order of Acquire or SeqCst - let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::SeqCst); - let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::Acquire); - - // Acquire/AcqRel forbids failure order of SeqCst - let _ = x.compare_exchange_weak(0, 0, Ordering::Acquire, Ordering::SeqCst); - let _ = x.compare_exchange_weak(0, 0, Ordering::AcqRel, Ordering::SeqCst); } diff --git a/tests/ui/atomic_ordering_exchange.stderr b/tests/ui/atomic_ordering_exchange.stderr index 26ec15be518..c09d2d6ab21 100644 --- a/tests/ui/atomic_ordering_exchange.stderr +++ b/tests/ui/atomic_ordering_exchange.stderr @@ -127,133 +127,5 @@ LL | let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::SeqCst); | = help: consider using ordering modes `Acquire` or `Relaxed` instead -error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_exchange.rs:60:62 - | -LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering mode `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_exchange.rs:61:62 - | -LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Acquire, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire` or `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_exchange.rs:62:62 - | -LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering mode `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_exchange.rs:63:61 - | -LL | let _ = x.compare_exchange_weak(0, 0, Ordering::AcqRel, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire` or `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_exchange.rs:64:61 - | -LL | let _ = x.compare_exchange_weak(0, 0, Ordering::SeqCst, Ordering::AcqRel); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_exchange.rs:67:62 - | -LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::Release); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering mode `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_exchange.rs:68:62 - | -LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Acquire, Ordering::Release); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire` or `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_exchange.rs:69:62 - | -LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::Release); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering mode `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_exchange.rs:70:61 - | -LL | let _ = x.compare_exchange_weak(0, 0, Ordering::AcqRel, Ordering::Release); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire` or `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` - --> $DIR/atomic_ordering_exchange.rs:71:61 - | -LL | let _ = x.compare_exchange_weak(0, 0, Ordering::SeqCst, Ordering::Release); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Release` - --> $DIR/atomic_ordering_exchange.rs:74:62 - | -LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::Acquire); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering mode `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Release` - --> $DIR/atomic_ordering_exchange.rs:75:62 - | -LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Release, Ordering::SeqCst); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering mode `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Relaxed` - --> $DIR/atomic_ordering_exchange.rs:78:62 - | -LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::SeqCst); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering mode `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Relaxed` - --> $DIR/atomic_ordering_exchange.rs:79:62 - | -LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Relaxed, Ordering::Acquire); - | ^^^^^^^^^^^^^^^^^ - | - = help: consider using ordering mode `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Acquire` - --> $DIR/atomic_ordering_exchange.rs:82:62 - | -LL | let _ = x.compare_exchange_weak(0, 0, Ordering::Acquire, Ordering::SeqCst); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire` or `Relaxed` instead - -error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `AcqRel` - --> $DIR/atomic_ordering_exchange.rs:83:61 - | -LL | let _ = x.compare_exchange_weak(0, 0, Ordering::AcqRel, Ordering::SeqCst); - | ^^^^^^^^^^^^^^^^ - | - = help: consider using ordering modes `Acquire` or `Relaxed` instead - -error: aborting due to 32 previous errors +error: aborting due to 16 previous errors diff --git a/tests/ui/atomic_ordering_exchange_weak.rs b/tests/ui/atomic_ordering_exchange_weak.rs new file mode 100644 index 00000000000..59069902507 --- /dev/null +++ b/tests/ui/atomic_ordering_exchange_weak.rs @@ -0,0 +1,47 @@ +#![warn(clippy::invalid_atomic_ordering)] + +use std::sync::atomic::{AtomicPtr, Ordering}; + +fn main() { + let ptr = &mut 5; + let ptr2 = &mut 10; + // `compare_exchange_weak` testing + let x = AtomicPtr::new(ptr); + + // Allowed ordering combos + let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::Relaxed); + let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Acquire, Ordering::Acquire); + let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Acquire, Ordering::Relaxed); + let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Release, Ordering::Relaxed); + let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::AcqRel, Ordering::Acquire); + let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::AcqRel, Ordering::Relaxed); + let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::SeqCst, Ordering::Relaxed); + let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::SeqCst, Ordering::Acquire); + let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::SeqCst, Ordering::SeqCst); + + // AcqRel is always forbidden as a failure ordering + let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Relaxed, Ordering::AcqRel); + let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Acquire, Ordering::AcqRel); + let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::AcqRel); + let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::AcqRel, Ordering::AcqRel); + let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::SeqCst, Ordering::AcqRel); + + // Release is always forbidden as a failure ordering + let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::Release); + let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Acquire, Ordering::Release); + let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Release, Ordering::Release); + let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::AcqRel, Ordering::Release); + let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::SeqCst, Ordering::Release); + + // Release success order forbids failure order of Acquire or SeqCst + let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::Acquire); + let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::SeqCst); + + // Relaxed success order also forbids failure order of Acquire or SeqCst + let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::SeqCst); + let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::Acquire); + + // Acquire/AcqRel forbids failure order of SeqCst + let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Acquire, Ordering::SeqCst); + let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::AcqRel, Ordering::SeqCst); +} diff --git a/tests/ui/atomic_ordering_exchange_weak.stderr b/tests/ui/atomic_ordering_exchange_weak.stderr new file mode 100644 index 00000000000..7210431bd1b --- /dev/null +++ b/tests/ui/atomic_ordering_exchange_weak.stderr @@ -0,0 +1,131 @@ +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange_weak.rs:23:67 + | +LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Relaxed, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings` + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange_weak.rs:24:67 + | +LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Acquire, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange_weak.rs:25:67 + | +LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange_weak.rs:26:66 + | +LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::AcqRel, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange_weak.rs:27:66 + | +LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::SeqCst, Ordering::AcqRel); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange_weak.rs:30:67 + | +LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange_weak.rs:31:67 + | +LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Acquire, Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange_weak.rs:32:67 + | +LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Release, Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange_weak.rs:33:66 + | +LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::AcqRel, Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel` + --> $DIR/atomic_ordering_exchange_weak.rs:34:66 + | +LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::SeqCst, Ordering::Release); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Release` + --> $DIR/atomic_ordering_exchange_weak.rs:37:67 + | +LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::Acquire); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Release` + --> $DIR/atomic_ordering_exchange_weak.rs:38:67 + | +LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::SeqCst); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Relaxed` + --> $DIR/atomic_ordering_exchange_weak.rs:41:67 + | +LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::SeqCst); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Relaxed` + --> $DIR/atomic_ordering_exchange_weak.rs:42:67 + | +LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::Acquire); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider using ordering mode `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Acquire` + --> $DIR/atomic_ordering_exchange_weak.rs:45:67 + | +LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Acquire, Ordering::SeqCst); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `AcqRel` + --> $DIR/atomic_ordering_exchange_weak.rs:46:66 + | +LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::AcqRel, Ordering::SeqCst); + | ^^^^^^^^^^^^^^^^ + | + = help: consider using ordering modes `Acquire` or `Relaxed` instead + +error: aborting due to 16 previous errors + -- cgit 1.4.1-3-g733a5 From 3a072131da2e574e914073af6d72360ead735781 Mon Sep 17 00:00:00 2001 From: Thom Chiovoloni Date: Wed, 9 Sep 2020 23:22:01 -0700 Subject: Ah, right, rerun the scripts --- tests/ui/atomic_ordering_exchange.stderr | 12 ++++++------ tests/ui/atomic_ordering_exchange_weak.stderr | 12 ++++++------ tests/ui/atomic_ordering_fetch_update.stderr | 12 ++++++------ 3 files changed, 18 insertions(+), 18 deletions(-) (limited to 'tests') diff --git a/tests/ui/atomic_ordering_exchange.stderr b/tests/ui/atomic_ordering_exchange.stderr index c09d2d6ab21..4b9bfef7974 100644 --- a/tests/ui/atomic_ordering_exchange.stderr +++ b/tests/ui/atomic_ordering_exchange.stderr @@ -79,7 +79,7 @@ LL | let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::Release); | = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead -error: compare_exchange's failure ordering may not stronger than the success ordering of `Release` +error: compare_exchange's failure ordering may not be stronger than the success ordering of `Release` --> $DIR/atomic_ordering_exchange.rs:35:57 | LL | let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Acquire); @@ -87,7 +87,7 @@ LL | let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Acquire); | = help: consider using ordering mode `Relaxed` instead -error: compare_exchange's failure ordering may not stronger than the success ordering of `Release` +error: compare_exchange's failure ordering may not be stronger than the success ordering of `Release` --> $DIR/atomic_ordering_exchange.rs:36:57 | LL | let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::SeqCst); @@ -95,7 +95,7 @@ LL | let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::SeqCst); | = help: consider using ordering mode `Relaxed` instead -error: compare_exchange's failure ordering may not stronger than the success ordering of `Relaxed` +error: compare_exchange's failure ordering may not be stronger than the success ordering of `Relaxed` --> $DIR/atomic_ordering_exchange.rs:39:57 | LL | let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::SeqCst); @@ -103,7 +103,7 @@ LL | let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::SeqCst); | = help: consider using ordering mode `Relaxed` instead -error: compare_exchange's failure ordering may not stronger than the success ordering of `Relaxed` +error: compare_exchange's failure ordering may not be stronger than the success ordering of `Relaxed` --> $DIR/atomic_ordering_exchange.rs:40:57 | LL | let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Acquire); @@ -111,7 +111,7 @@ LL | let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Acquire); | = help: consider using ordering mode `Relaxed` instead -error: compare_exchange's failure ordering may not stronger than the success ordering of `Acquire` +error: compare_exchange's failure ordering may not be stronger than the success ordering of `Acquire` --> $DIR/atomic_ordering_exchange.rs:43:57 | LL | let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::SeqCst); @@ -119,7 +119,7 @@ LL | let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::SeqCst); | = help: consider using ordering modes `Acquire` or `Relaxed` instead -error: compare_exchange's failure ordering may not stronger than the success ordering of `AcqRel` +error: compare_exchange's failure ordering may not be stronger than the success ordering of `AcqRel` --> $DIR/atomic_ordering_exchange.rs:44:56 | LL | let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::SeqCst); diff --git a/tests/ui/atomic_ordering_exchange_weak.stderr b/tests/ui/atomic_ordering_exchange_weak.stderr index 7210431bd1b..de7026f3ffa 100644 --- a/tests/ui/atomic_ordering_exchange_weak.stderr +++ b/tests/ui/atomic_ordering_exchange_weak.stderr @@ -79,7 +79,7 @@ LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::SeqCst, Ordering:: | = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead -error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Release` +error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `Release` --> $DIR/atomic_ordering_exchange_weak.rs:37:67 | LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::Acquire); @@ -87,7 +87,7 @@ LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering: | = help: consider using ordering mode `Relaxed` instead -error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Release` +error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `Release` --> $DIR/atomic_ordering_exchange_weak.rs:38:67 | LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::SeqCst); @@ -95,7 +95,7 @@ LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering: | = help: consider using ordering mode `Relaxed` instead -error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Relaxed` +error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `Relaxed` --> $DIR/atomic_ordering_exchange_weak.rs:41:67 | LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::SeqCst); @@ -103,7 +103,7 @@ LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering: | = help: consider using ordering mode `Relaxed` instead -error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Relaxed` +error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `Relaxed` --> $DIR/atomic_ordering_exchange_weak.rs:42:67 | LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::Acquire); @@ -111,7 +111,7 @@ LL | let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering: | = help: consider using ordering mode `Relaxed` instead -error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `Acquire` +error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `Acquire` --> $DIR/atomic_ordering_exchange_weak.rs:45:67 | LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Acquire, Ordering::SeqCst); @@ -119,7 +119,7 @@ LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Acquire, Ordering: | = help: consider using ordering modes `Acquire` or `Relaxed` instead -error: compare_exchange_weak's failure ordering may not stronger than the success ordering of `AcqRel` +error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `AcqRel` --> $DIR/atomic_ordering_exchange_weak.rs:46:66 | LL | let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::AcqRel, Ordering::SeqCst); diff --git a/tests/ui/atomic_ordering_fetch_update.stderr b/tests/ui/atomic_ordering_fetch_update.stderr index 362e104a244..694548ece97 100644 --- a/tests/ui/atomic_ordering_fetch_update.stderr +++ b/tests/ui/atomic_ordering_fetch_update.stderr @@ -79,7 +79,7 @@ LL | let _ = x.fetch_update(Ordering::SeqCst, Ordering::Release, |old| Some( | = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead -error: fetch_update's failure ordering may not stronger than the success ordering of `Release` +error: fetch_update's failure ordering may not be stronger than the success ordering of `Release` --> $DIR/atomic_ordering_fetch_update.rs:35:47 | LL | let _ = x.fetch_update(Ordering::Release, Ordering::Acquire, |old| Some(old + 1)); @@ -87,7 +87,7 @@ LL | let _ = x.fetch_update(Ordering::Release, Ordering::Acquire, |old| Some | = help: consider using ordering mode `Relaxed` instead -error: fetch_update's failure ordering may not stronger than the success ordering of `Release` +error: fetch_update's failure ordering may not be stronger than the success ordering of `Release` --> $DIR/atomic_ordering_fetch_update.rs:36:47 | LL | let _ = x.fetch_update(Ordering::Release, Ordering::SeqCst, |old| Some(old + 1)); @@ -95,7 +95,7 @@ LL | let _ = x.fetch_update(Ordering::Release, Ordering::SeqCst, |old| Some( | = help: consider using ordering mode `Relaxed` instead -error: fetch_update's failure ordering may not stronger than the success ordering of `Relaxed` +error: fetch_update's failure ordering may not be stronger than the success ordering of `Relaxed` --> $DIR/atomic_ordering_fetch_update.rs:39:47 | LL | let _ = x.fetch_update(Ordering::Relaxed, Ordering::SeqCst, |old| Some(old + 1)); @@ -103,7 +103,7 @@ LL | let _ = x.fetch_update(Ordering::Relaxed, Ordering::SeqCst, |old| Some( | = help: consider using ordering mode `Relaxed` instead -error: fetch_update's failure ordering may not stronger than the success ordering of `Relaxed` +error: fetch_update's failure ordering may not be stronger than the success ordering of `Relaxed` --> $DIR/atomic_ordering_fetch_update.rs:40:47 | LL | let _ = x.fetch_update(Ordering::Relaxed, Ordering::Acquire, |old| Some(old + 1)); @@ -111,7 +111,7 @@ LL | let _ = x.fetch_update(Ordering::Relaxed, Ordering::Acquire, |old| Some | = help: consider using ordering mode `Relaxed` instead -error: fetch_update's failure ordering may not stronger than the success ordering of `Acquire` +error: fetch_update's failure ordering may not be stronger than the success ordering of `Acquire` --> $DIR/atomic_ordering_fetch_update.rs:43:47 | LL | let _ = x.fetch_update(Ordering::Acquire, Ordering::SeqCst, |old| Some(old + 1)); @@ -119,7 +119,7 @@ LL | let _ = x.fetch_update(Ordering::Acquire, Ordering::SeqCst, |old| Some( | = help: consider using ordering modes `Acquire` or `Relaxed` instead -error: fetch_update's failure ordering may not stronger than the success ordering of `AcqRel` +error: fetch_update's failure ordering may not be stronger than the success ordering of `AcqRel` --> $DIR/atomic_ordering_fetch_update.rs:44:46 | LL | let _ = x.fetch_update(Ordering::AcqRel, Ordering::SeqCst, |old| Some(old + 1)); -- cgit 1.4.1-3-g733a5 From 2d56512580919483268c3e3bf2e028c8614805f2 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 10 Sep 2020 14:18:05 +0200 Subject: Cleanup of rustup --- clippy_lints/src/temporary_assignment.rs | 9 +++------ tests/ui/temporary_assignment.rs | 6 ------ tests/ui/temporary_assignment.stderr | 8 ++++---- 3 files changed, 7 insertions(+), 16 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/temporary_assignment.rs b/clippy_lints/src/temporary_assignment.rs index 2b6ddadd4c1..fb891866364 100644 --- a/clippy_lints/src/temporary_assignment.rs +++ b/clippy_lints/src/temporary_assignment.rs @@ -21,11 +21,8 @@ declare_clippy_lint! { "assignments to temporaries" } -fn is_temporary(_cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - match &expr.kind { - ExprKind::Struct(..) | ExprKind::Tup(..) => true, - _ => false, - } +fn is_temporary(expr: &Expr<'_>) -> bool { + matches!(&expr.kind, ExprKind::Struct(..) | ExprKind::Tup(..)) } declare_lint_pass!(TemporaryAssignment => [TEMPORARY_ASSIGNMENT]); @@ -37,7 +34,7 @@ impl<'tcx> LateLintPass<'tcx> for TemporaryAssignment { while let ExprKind::Field(f, _) | ExprKind::Index(f, _) = &base.kind { base = f; } - if is_temporary(cx, base) && !is_adjusted(cx, base) { + if is_temporary(base) && !is_adjusted(cx, base) { span_lint(cx, TEMPORARY_ASSIGNMENT, expr.span, "assignment to temporary"); } } diff --git a/tests/ui/temporary_assignment.rs b/tests/ui/temporary_assignment.rs index d6f56d40c5d..ac4c1bc6597 100644 --- a/tests/ui/temporary_assignment.rs +++ b/tests/ui/temporary_assignment.rs @@ -1,5 +1,4 @@ #![warn(clippy::temporary_assignment)] -#![allow(const_item_mutation)] use std::ops::{Deref, DerefMut}; @@ -54,11 +53,6 @@ fn main() { ArrayStruct { array: [0] }.array[0] = 1; (0, 0).0 = 1; - A.0 = 2; - B.field = 2; - C.structure.field = 2; - D.array[0] = 2; - // no error s.field = 1; t.0 = 1; diff --git a/tests/ui/temporary_assignment.stderr b/tests/ui/temporary_assignment.stderr index 4cc32c79f05..7d79901a28d 100644 --- a/tests/ui/temporary_assignment.stderr +++ b/tests/ui/temporary_assignment.stderr @@ -1,5 +1,5 @@ error: assignment to temporary - --> $DIR/temporary_assignment.rs:48:5 + --> $DIR/temporary_assignment.rs:47:5 | LL | Struct { field: 0 }.field = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | Struct { field: 0 }.field = 1; = note: `-D clippy::temporary-assignment` implied by `-D warnings` error: assignment to temporary - --> $DIR/temporary_assignment.rs:49:5 + --> $DIR/temporary_assignment.rs:48:5 | LL | / MultiStruct { LL | | structure: Struct { field: 0 }, @@ -17,13 +17,13 @@ LL | | .field = 1; | |______________^ error: assignment to temporary - --> $DIR/temporary_assignment.rs:54:5 + --> $DIR/temporary_assignment.rs:53:5 | LL | ArrayStruct { array: [0] }.array[0] = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: assignment to temporary - --> $DIR/temporary_assignment.rs:55:5 + --> $DIR/temporary_assignment.rs:54:5 | LL | (0, 0).0 = 1; | ^^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From 2487f8f461134f93459415642f730ac6c4a2d659 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Fri, 11 Sep 2020 16:52:25 +0200 Subject: into_iter_on_ref: rephrase lint message: will not move the x -> will not consume the x imo that's a bit clearer. --- clippy_lints/src/methods/mod.rs | 2 +- tests/ui/into_iter_on_ref.stderr | 54 ++++++++++++++++++++-------------------- 2 files changed, 28 insertions(+), 28 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index ba69c8266b1..98e027b6d22 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3374,7 +3374,7 @@ fn lint_into_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, self_ref_ty: Ty<'_ INTO_ITER_ON_REF, method_span, &format!( - "this `.into_iter()` call is equivalent to `.{}()` and will not move the `{}`", + "this `.into_iter()` call is equivalent to `.{}()` and will not consume the `{}`", method_name, kind, ), "call directly", diff --git a/tests/ui/into_iter_on_ref.stderr b/tests/ui/into_iter_on_ref.stderr index 1cd6400b019..28003b365bb 100644 --- a/tests/ui/into_iter_on_ref.stderr +++ b/tests/ui/into_iter_on_ref.stderr @@ -1,4 +1,4 @@ -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Vec` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Vec` --> $DIR/into_iter_on_ref.rs:14:30 | LL | let _ = (&vec![1, 2, 3]).into_iter(); //~ WARN equivalent to .iter() @@ -6,157 +6,157 @@ LL | let _ = (&vec![1, 2, 3]).into_iter(); //~ WARN equivalent to .iter() | = note: `-D clippy::into-iter-on-ref` implied by `-D warnings` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `slice` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `slice` --> $DIR/into_iter_on_ref.rs:15:46 | LL | let _ = vec![1, 2, 3].into_boxed_slice().into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `slice` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `slice` --> $DIR/into_iter_on_ref.rs:16:41 | LL | let _ = std::rc::Rc::from(&[X][..]).into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `slice` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `slice` --> $DIR/into_iter_on_ref.rs:17:44 | LL | let _ = std::sync::Arc::from(&[X][..]).into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `array` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `array` --> $DIR/into_iter_on_ref.rs:19:32 | LL | let _ = (&&&&&&&[1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `array` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `array` --> $DIR/into_iter_on_ref.rs:20:36 | LL | let _ = (&&&&mut &&&[1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `array` +error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `array` --> $DIR/into_iter_on_ref.rs:21:40 | LL | let _ = (&mut &mut &mut [1, 2, 3]).into_iter(); //~ ERROR equivalent to .iter_mut() | ^^^^^^^^^ help: call directly: `iter_mut` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Option` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Option` --> $DIR/into_iter_on_ref.rs:23:24 | LL | let _ = (&Some(4)).into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `Option` +error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `Option` --> $DIR/into_iter_on_ref.rs:24:28 | LL | let _ = (&mut Some(5)).into_iter(); //~ WARN equivalent to .iter_mut() | ^^^^^^^^^ help: call directly: `iter_mut` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Result` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Result` --> $DIR/into_iter_on_ref.rs:25:32 | LL | let _ = (&Ok::<_, i32>(6)).into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `Result` +error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `Result` --> $DIR/into_iter_on_ref.rs:26:37 | LL | let _ = (&mut Err::(7)).into_iter(); //~ WARN equivalent to .iter_mut() | ^^^^^^^^^ help: call directly: `iter_mut` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Vec` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Vec` --> $DIR/into_iter_on_ref.rs:27:34 | LL | let _ = (&Vec::::new()).into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `Vec` +error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `Vec` --> $DIR/into_iter_on_ref.rs:28:38 | LL | let _ = (&mut Vec::::new()).into_iter(); //~ WARN equivalent to .iter_mut() | ^^^^^^^^^ help: call directly: `iter_mut` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `BTreeMap` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `BTreeMap` --> $DIR/into_iter_on_ref.rs:29:44 | LL | let _ = (&BTreeMap::::new()).into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `BTreeMap` +error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `BTreeMap` --> $DIR/into_iter_on_ref.rs:30:48 | LL | let _ = (&mut BTreeMap::::new()).into_iter(); //~ WARN equivalent to .iter_mut() | ^^^^^^^^^ help: call directly: `iter_mut` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `VecDeque` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `VecDeque` --> $DIR/into_iter_on_ref.rs:31:39 | LL | let _ = (&VecDeque::::new()).into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `VecDeque` +error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `VecDeque` --> $DIR/into_iter_on_ref.rs:32:43 | LL | let _ = (&mut VecDeque::::new()).into_iter(); //~ WARN equivalent to .iter_mut() | ^^^^^^^^^ help: call directly: `iter_mut` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `LinkedList` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `LinkedList` --> $DIR/into_iter_on_ref.rs:33:41 | LL | let _ = (&LinkedList::::new()).into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `LinkedList` +error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `LinkedList` --> $DIR/into_iter_on_ref.rs:34:45 | LL | let _ = (&mut LinkedList::::new()).into_iter(); //~ WARN equivalent to .iter_mut() | ^^^^^^^^^ help: call directly: `iter_mut` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `HashMap` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `HashMap` --> $DIR/into_iter_on_ref.rs:35:43 | LL | let _ = (&HashMap::::new()).into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not move the `HashMap` +error: this `.into_iter()` call is equivalent to `.iter_mut()` and will not consume the `HashMap` --> $DIR/into_iter_on_ref.rs:36:47 | LL | let _ = (&mut HashMap::::new()).into_iter(); //~ WARN equivalent to .iter_mut() | ^^^^^^^^^ help: call directly: `iter_mut` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `BTreeSet` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `BTreeSet` --> $DIR/into_iter_on_ref.rs:38:39 | LL | let _ = (&BTreeSet::::new()).into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `BinaryHeap` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `BinaryHeap` --> $DIR/into_iter_on_ref.rs:39:41 | LL | let _ = (&BinaryHeap::::new()).into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `HashSet` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `HashSet` --> $DIR/into_iter_on_ref.rs:40:38 | LL | let _ = (&HashSet::::new()).into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `Path` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Path` --> $DIR/into_iter_on_ref.rs:41:43 | LL | let _ = std::path::Path::new("12/34").into_iter(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `PathBuf` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `PathBuf` --> $DIR/into_iter_on_ref.rs:42:47 | LL | let _ = std::path::PathBuf::from("12/34").into_iter(); //~ ERROR equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: this `.into_iter()` call is equivalent to `.iter()` and will not move the `array` +error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `array` --> $DIR/into_iter_on_ref.rs:44:26 | LL | let _ = (&[1, 2, 3]).into_iter().next(); //~ WARN equivalent to .iter() -- cgit 1.4.1-3-g733a5 From 7ba1a8fec42ca11c169bcff3650f9c1e108b6743 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Sun, 13 Sep 2020 12:42:14 +0200 Subject: useless_conversion: show type in error message. changelog: useless_conversion: show type in error message. --- clippy_lints/src/useless_conversion.rs | 10 +++++----- tests/ui/useless_conversion.stderr | 22 +++++++++++----------- tests/ui/useless_conversion_try.stderr | 18 +++++++++--------- 3 files changed, 25 insertions(+), 25 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 615440e15f3..4e4a206a583 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -72,7 +72,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - "useless conversion to the same type", + &format!("useless conversion to the same type: `{}`", b), "consider removing `.into()`", sugg, Applicability::MachineApplicable, // snippet @@ -95,7 +95,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - "useless conversion to the same type", + &format!("useless conversion to the same type: `{}`", b), "consider removing `.into_iter()`", sugg, Applicability::MachineApplicable, // snippet @@ -116,7 +116,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - "useless conversion to the same type", + &format!("useless conversion to the same type: `{}`", b), None, "consider removing `.try_into()`", ); @@ -147,7 +147,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - "useless conversion to the same type", + &format!("useless conversion to the same type: `{}`", b), None, &hint, ); @@ -166,7 +166,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { cx, USELESS_CONVERSION, e.span, - "useless conversion to the same type", + &format!("useless conversion to the same type: `{}`", b), &sugg_msg, sugg.to_string(), Applicability::MachineApplicable, // snippet diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index f1e880d2696..11c6efb25cc 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -1,4 +1,4 @@ -error: useless conversion to the same type +error: useless conversion to the same type: `T` --> $DIR/useless_conversion.rs:6:13 | LL | let _ = T::from(val); @@ -10,61 +10,61 @@ note: the lint level is defined here LL | #![deny(clippy::useless_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: useless conversion to the same type +error: useless conversion to the same type: `T` --> $DIR/useless_conversion.rs:7:5 | LL | val.into() | ^^^^^^^^^^ help: consider removing `.into()`: `val` -error: useless conversion to the same type +error: useless conversion to the same type: `i32` --> $DIR/useless_conversion.rs:19:22 | LL | let _: i32 = 0i32.into(); | ^^^^^^^^^^^ help: consider removing `.into()`: `0i32` -error: useless conversion to the same type +error: useless conversion to the same type: `std::string::String` --> $DIR/useless_conversion.rs:60:21 | LL | let _: String = "foo".to_string().into(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `"foo".to_string()` -error: useless conversion to the same type +error: useless conversion to the same type: `std::string::String` --> $DIR/useless_conversion.rs:61:21 | LL | let _: String = From::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `From::from()`: `"foo".to_string()` -error: useless conversion to the same type +error: useless conversion to the same type: `std::string::String` --> $DIR/useless_conversion.rs:62:13 | LL | let _ = String::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `"foo".to_string()` -error: useless conversion to the same type +error: useless conversion to the same type: `std::string::String` --> $DIR/useless_conversion.rs:63:13 | LL | let _ = String::from(format!("A: {:04}", 123)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `format!("A: {:04}", 123)` -error: useless conversion to the same type +error: useless conversion to the same type: `std::str::Lines` --> $DIR/useless_conversion.rs:64:13 | LL | let _ = "".lines().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()` -error: useless conversion to the same type +error: useless conversion to the same type: `std::vec::IntoIter` --> $DIR/useless_conversion.rs:65:13 | LL | let _ = vec![1, 2, 3].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![1, 2, 3].into_iter()` -error: useless conversion to the same type +error: useless conversion to the same type: `std::string::String` --> $DIR/useless_conversion.rs:66:21 | LL | let _: String = format!("Hello {}", "world").into(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `format!("Hello {}", "world")` -error: useless conversion to the same type +error: useless conversion to the same type: `i32` --> $DIR/useless_conversion.rs:71:13 | LL | let _ = i32::from(a + b) * 3; diff --git a/tests/ui/useless_conversion_try.stderr b/tests/ui/useless_conversion_try.stderr index b765727c168..2e0d9129bfb 100644 --- a/tests/ui/useless_conversion_try.stderr +++ b/tests/ui/useless_conversion_try.stderr @@ -1,4 +1,4 @@ -error: useless conversion to the same type +error: useless conversion to the same type: `T` --> $DIR/useless_conversion_try.rs:6:13 | LL | let _ = T::try_from(val).unwrap(); @@ -11,7 +11,7 @@ LL | #![deny(clippy::useless_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: consider removing `T::try_from()` -error: useless conversion to the same type +error: useless conversion to the same type: `T` --> $DIR/useless_conversion_try.rs:7:5 | LL | val.try_into().unwrap() @@ -19,7 +19,7 @@ LL | val.try_into().unwrap() | = help: consider removing `.try_into()` -error: useless conversion to the same type +error: useless conversion to the same type: `std::string::String` --> $DIR/useless_conversion_try.rs:29:21 | LL | let _: String = "foo".to_string().try_into().unwrap(); @@ -27,7 +27,7 @@ LL | let _: String = "foo".to_string().try_into().unwrap(); | = help: consider removing `.try_into()` -error: useless conversion to the same type +error: useless conversion to the same type: `std::string::String` --> $DIR/useless_conversion_try.rs:30:21 | LL | let _: String = TryFrom::try_from("foo".to_string()).unwrap(); @@ -35,7 +35,7 @@ LL | let _: String = TryFrom::try_from("foo".to_string()).unwrap(); | = help: consider removing `TryFrom::try_from()` -error: useless conversion to the same type +error: useless conversion to the same type: `std::string::String` --> $DIR/useless_conversion_try.rs:31:13 | LL | let _ = String::try_from("foo".to_string()).unwrap(); @@ -43,7 +43,7 @@ LL | let _ = String::try_from("foo".to_string()).unwrap(); | = help: consider removing `String::try_from()` -error: useless conversion to the same type +error: useless conversion to the same type: `std::string::String` --> $DIR/useless_conversion_try.rs:32:13 | LL | let _ = String::try_from(format!("A: {:04}", 123)).unwrap(); @@ -51,7 +51,7 @@ LL | let _ = String::try_from(format!("A: {:04}", 123)).unwrap(); | = help: consider removing `String::try_from()` -error: useless conversion to the same type +error: useless conversion to the same type: `std::string::String` --> $DIR/useless_conversion_try.rs:33:21 | LL | let _: String = format!("Hello {}", "world").try_into().unwrap(); @@ -59,7 +59,7 @@ LL | let _: String = format!("Hello {}", "world").try_into().unwrap(); | = help: consider removing `.try_into()` -error: useless conversion to the same type +error: useless conversion to the same type: `std::string::String` --> $DIR/useless_conversion_try.rs:34:21 | LL | let _: String = "".to_owned().try_into().unwrap(); @@ -67,7 +67,7 @@ LL | let _: String = "".to_owned().try_into().unwrap(); | = help: consider removing `.try_into()` -error: useless conversion to the same type +error: useless conversion to the same type: `std::string::String` --> $DIR/useless_conversion_try.rs:35:27 | LL | let _: String = match String::from("_").try_into() { -- cgit 1.4.1-3-g733a5 From 9ff7e5d98413d10dd74e3b9509c7a58f6dd6818b Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Sun, 13 Sep 2020 23:23:16 +0900 Subject: Downgrade `verbose_bit_mask` to pedantic --- clippy_lints/src/bit_mask.rs | 2 +- clippy_lints/src/lib.rs | 3 +-- src/lintlist/mod.rs | 2 +- tests/ui/trailing_zeros.rs | 1 + tests/ui/trailing_zeros.stderr | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/bit_mask.rs b/clippy_lints/src/bit_mask.rs index 81a34021e8a..a4ee54076ee 100644 --- a/clippy_lints/src/bit_mask.rs +++ b/clippy_lints/src/bit_mask.rs @@ -90,7 +90,7 @@ declare_clippy_lint! { /// if x & 0b1111 == 0 { } /// ``` pub VERBOSE_BIT_MASK, - style, + pedantic, "expressions where a bit mask is less readable than the corresponding method call" } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 1795fd10fa1..c017c5cb5d0 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1157,6 +1157,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(&attrs::INLINE_ALWAYS), LintId::of(&await_holding_lock::AWAIT_HOLDING_LOCK), + LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&checked_conversions::CHECKED_CONVERSIONS), LintId::of(&copies::MATCH_SAME_ARMS), LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION), @@ -1254,7 +1255,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&attrs::USELESS_ATTRIBUTE), LintId::of(&bit_mask::BAD_BIT_MASK), LintId::of(&bit_mask::INEFFECTIVE_BIT_MASK), - LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&blacklisted_name::BLACKLISTED_NAME), LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), LintId::of(&booleans::LOGIC_BUG), @@ -1512,7 +1512,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&assign_ops::ASSIGN_OP_PATTERN), LintId::of(&attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), LintId::of(&attrs::UNKNOWN_CLIPPY_LINTS), - LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&blacklisted_name::BLACKLISTED_NAME), LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), LintId::of(&collapsible_if::COLLAPSIBLE_IF), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 04d486438b1..a7d38c93433 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2637,7 +2637,7 @@ pub static ref ALL_LINTS: Vec = vec![ }, Lint { name: "verbose_bit_mask", - group: "style", + group: "pedantic", desc: "expressions where a bit mask is less readable than the corresponding method call", deprecation: None, module: "bit_mask", diff --git a/tests/ui/trailing_zeros.rs b/tests/ui/trailing_zeros.rs index 1cef8c2cfc9..fbdc977b769 100644 --- a/tests/ui/trailing_zeros.rs +++ b/tests/ui/trailing_zeros.rs @@ -1,4 +1,5 @@ #![allow(unused_parens)] +#![warn(clippy::verbose_bit_mask)] fn main() { let x: i32 = 42; diff --git a/tests/ui/trailing_zeros.stderr b/tests/ui/trailing_zeros.stderr index 320d9cc3f64..79855111830 100644 --- a/tests/ui/trailing_zeros.stderr +++ b/tests/ui/trailing_zeros.stderr @@ -1,5 +1,5 @@ error: bit mask could be simplified with a call to `trailing_zeros` - --> $DIR/trailing_zeros.rs:5:13 + --> $DIR/trailing_zeros.rs:6:13 | LL | let _ = (x & 0b1111 == 0); // suggest trailing_zeros | ^^^^^^^^^^^^^^^^^ help: try: `x.trailing_zeros() >= 4` @@ -7,7 +7,7 @@ LL | let _ = (x & 0b1111 == 0); // suggest trailing_zeros = note: `-D clippy::verbose-bit-mask` implied by `-D warnings` error: bit mask could be simplified with a call to `trailing_zeros` - --> $DIR/trailing_zeros.rs:6:13 + --> $DIR/trailing_zeros.rs:7:13 | LL | let _ = x & 0b1_1111 == 0; // suggest trailing_zeros | ^^^^^^^^^^^^^^^^^ help: try: `x.trailing_zeros() >= 5` -- cgit 1.4.1-3-g733a5 From d1f0f04a488d027fdf91e08cdf25df00fb677205 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Mon, 14 Sep 2020 06:11:35 +0200 Subject: New lint: `manual-strip` Add a new lint, `manual-strip`, that suggests using the `str::strip_prefix` and `str::strip_suffix` methods introduced in Rust 1.45 when the same functionality is performed 'manually'. Closes #5734 --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 + clippy_lints/src/manual_strip.rs | 246 +++++++++++++++++++++++++++++++++++++++ clippy_lints/src/utils/paths.rs | 3 + src/lintlist/mod.rs | 7 ++ tests/ui/manual_strip.rs | 59 ++++++++++ tests/ui/manual_strip.stderr | 132 +++++++++++++++++++++ 7 files changed, 453 insertions(+) create mode 100644 clippy_lints/src/manual_strip.rs create mode 100644 tests/ui/manual_strip.rs create mode 100644 tests/ui/manual_strip.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 285a2ff8060..a6fafdf5357 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1672,6 +1672,7 @@ Released 2018-09-13 [`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy [`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive [`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic +[`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip [`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap [`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names [`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c017c5cb5d0..38ddc69c8cb 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -230,6 +230,7 @@ mod macro_use; mod main_recursion; mod manual_async_fn; mod manual_non_exhaustive; +mod manual_strip; mod map_clone; mod map_identity; mod map_unit_fn; @@ -626,6 +627,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &main_recursion::MAIN_RECURSION, &manual_async_fn::MANUAL_ASYNC_FN, &manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, + &manual_strip::MANUAL_STRIP, &map_clone::MAP_CLONE, &map_identity::MAP_IDENTITY, &map_unit_fn::OPTION_MAP_UNIT_FN, @@ -1109,6 +1111,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box self_assignment::SelfAssignment); store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); + store.register_late_pass(|| box manual_strip::ManualStrip); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1335,6 +1338,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&main_recursion::MAIN_RECURSION), LintId::of(&manual_async_fn::MANUAL_ASYNC_FN), LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), + LintId::of(&manual_strip::MANUAL_STRIP), LintId::of(&map_clone::MAP_CLONE), LintId::of(&map_identity::MAP_IDENTITY), LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN), @@ -1626,6 +1630,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::EXPLICIT_COUNTER_LOOP), LintId::of(&loops::MUT_RANGE_BOUND), LintId::of(&loops::WHILE_LET_LOOP), + LintId::of(&manual_strip::MANUAL_STRIP), LintId::of(&map_identity::MAP_IDENTITY), LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN), LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN), diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs new file mode 100644 index 00000000000..127938aecd6 --- /dev/null +++ b/clippy_lints/src/manual_strip.rs @@ -0,0 +1,246 @@ +use crate::consts::{constant, Constant}; +use crate::utils::usage::mutated_variables; +use crate::utils::{ + eq_expr_value, higher, match_def_path, multispan_sugg, paths, qpath_res, snippet, span_lint_and_then, +}; + +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_hir::def::Res; +use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; +use rustc_hir::BinOpKind; +use rustc_hir::{BorrowKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Spanned; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** + /// Suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing using + /// the pattern's length. + /// + /// **Why is this bad?** + /// Using `str:strip_{prefix,suffix}` is safer and may have better performance as there is no + /// slicing which may panic and the compiler does not need to insert this panic code. It is + /// also sometimes more readable as it removes the need for duplicating or storing the pattern + /// used by `str::{starts,ends}_with` and in the slicing. + /// + /// **Known problems:** + /// None. + /// + /// **Example:** + /// + /// ```rust + /// let s = "hello, world!"; + /// if s.starts_with("hello, ") { + /// assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); + /// } + /// ``` + /// Use instead: + /// ```rust + /// let s = "hello, world!"; + /// if let Some(end) = s.strip_prefix("hello, ") { + /// assert_eq!(end.to_uppercase(), "WORLD!"); + /// } + /// ``` + pub MANUAL_STRIP, + complexity, + "suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing" +} + +declare_lint_pass!(ManualStrip => [MANUAL_STRIP]); + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum StripKind { + Prefix, + Suffix, +} + +impl<'tcx> LateLintPass<'tcx> for ManualStrip { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + if let Some((cond, then, _)) = higher::if_block(&expr); + if let ExprKind::MethodCall(_, _, [target_arg, pattern], _) = cond.kind; + if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(cond.hir_id); + if let ExprKind::Path(target_path) = &target_arg.kind; + then { + let strip_kind = if match_def_path(cx, method_def_id, &paths::STR_STARTS_WITH) { + StripKind::Prefix + } else if match_def_path(cx, method_def_id, &paths::STR_ENDS_WITH) { + StripKind::Suffix + } else { + return; + }; + let target_res = qpath_res(cx, &target_path, target_arg.hir_id); + if target_res == Res::Err { + return; + }; + + if_chain! { + if let Res::Local(hir_id) = target_res; + if let Some(used_mutably) = mutated_variables(then, cx); + if used_mutably.contains(&hir_id); + then { + return; + } + } + + let strippings = find_stripping(cx, strip_kind, target_res, pattern, then); + if !strippings.is_empty() { + + let kind_word = match strip_kind { + StripKind::Prefix => "prefix", + StripKind::Suffix => "suffix", + }; + + let test_span = expr.span.until(then.span); + span_lint_and_then(cx, MANUAL_STRIP, strippings[0], &format!("stripping a {} manually", kind_word), |diag| { + diag.span_note(test_span, &format!("the {} was tested here", kind_word)); + multispan_sugg( + diag, + &format!("try using the `strip_{}` method", kind_word), + vec![(test_span, + format!("if let Some() = {}.strip_{}({}) ", + snippet(cx, target_arg.span, ".."), + kind_word, + snippet(cx, pattern.span, "..")))] + .into_iter().chain(strippings.into_iter().map(|span| (span, "".into()))), + ) + }); + } + } + } + } +} + +// Returns `Some(arg)` if `expr` matches `arg.len()` and `None` otherwise. +fn len_arg<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { + if_chain! { + if let ExprKind::MethodCall(_, _, [arg], _) = expr.kind; + if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); + if match_def_path(cx, method_def_id, &paths::STR_LEN); + then { + Some(arg) + } + else { + None + } + } +} + +// Returns the length of the `expr` if it's a constant string or char. +fn constant_length(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { + let (value, _) = constant(cx, cx.typeck_results(), expr)?; + match value { + Constant::Str(value) => Some(value.len() as u128), + Constant::Char(value) => Some(value.len_utf8() as u128), + _ => None, + } +} + +// Tests if `expr` equals the length of the pattern. +fn eq_pattern_length<'tcx>(cx: &LateContext<'tcx>, pattern: &Expr<'_>, expr: &'tcx Expr<'_>) -> bool { + if let ExprKind::Lit(Spanned { + node: LitKind::Int(n, _), + .. + }) = expr.kind + { + constant_length(cx, pattern).map_or(false, |length| length == n) + } else { + len_arg(cx, expr).map_or(false, |arg| eq_expr_value(cx, pattern, arg)) + } +} + +// Tests if `expr` is a `&str`. +fn is_ref_str(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + match cx.typeck_results().expr_ty_adjusted(&expr).kind() { + ty::Ref(_, ty, _) => ty.is_str(), + _ => false, + } +} + +// Removes the outer `AddrOf` expression if needed. +fn peel_ref<'a>(expr: &'a Expr<'_>) -> &'a Expr<'a> { + if let ExprKind::AddrOf(BorrowKind::Ref, _, unref) = &expr.kind { + unref + } else { + expr + } +} + +// Find expressions where `target` is stripped using the length of `pattern`. +// We'll suggest replacing these expressions with the result of the `strip_{prefix,suffix}` +// method. +fn find_stripping<'tcx>( + cx: &LateContext<'tcx>, + strip_kind: StripKind, + target: Res, + pattern: &'tcx Expr<'_>, + expr: &'tcx Expr<'_>, +) -> Vec { + struct StrippingFinder<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + strip_kind: StripKind, + target: Res, + pattern: &'tcx Expr<'tcx>, + results: Vec, + } + + impl<'a, 'tcx> Visitor<'tcx> for StrippingFinder<'a, 'tcx> { + type Map = Map<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + + fn visit_expr(&mut self, ex: &'tcx Expr<'_>) { + if_chain! { + if is_ref_str(self.cx, ex); + let unref = peel_ref(ex); + if let ExprKind::Index(indexed, index) = &unref.kind; + if let Some(range) = higher::range(index); + if let higher::Range { start, end, .. } = range; + if let ExprKind::Path(path) = &indexed.kind; + if qpath_res(self.cx, path, ex.hir_id) == self.target; + then { + match (self.strip_kind, start, end) { + (StripKind::Prefix, Some(start), None) => { + if eq_pattern_length(self.cx, self.pattern, start) { + self.results.push(ex.span); + return; + } + }, + (StripKind::Suffix, None, Some(end)) => { + if_chain! { + if let ExprKind::Binary(Spanned { node: BinOpKind::Sub, .. }, left, right) = end.kind; + if let Some(left_arg) = len_arg(self.cx, left); + if let ExprKind::Path(left_path) = &left_arg.kind; + if qpath_res(self.cx, left_path, left_arg.hir_id) == self.target; + if eq_pattern_length(self.cx, self.pattern, right); + then { + self.results.push(ex.span); + return; + } + } + }, + _ => {} + } + } + } + + walk_expr(self, ex); + } + } + + let mut finder = StrippingFinder { + cx, + strip_kind, + target, + pattern, + results: vec![], + }; + walk_expr(&mut finder, expr); + finder.results +} diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 65320d6a0e0..f0f7719e2fd 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -115,6 +115,9 @@ pub const STD_MEM_TRANSMUTE: [&str; 3] = ["std", "mem", "transmute"]; pub const STD_PTR_NULL: [&str; 3] = ["std", "ptr", "null"]; pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"]; pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"]; +pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "", "ends_with"]; +pub const STR_LEN: [&str; 4] = ["core", "str", "", "len"]; +pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "", "starts_with"]; pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]; pub const TO_OWNED: [&str; 3] = ["alloc", "borrow", "ToOwned"]; pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"]; diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index a7d38c93433..8bceef80abf 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1144,6 +1144,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "methods", }, + Lint { + name: "manual_strip", + group: "complexity", + desc: "suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing", + deprecation: None, + module: "manual_strip", + }, Lint { name: "manual_swap", group: "complexity", diff --git a/tests/ui/manual_strip.rs b/tests/ui/manual_strip.rs new file mode 100644 index 00000000000..d1b4772c7de --- /dev/null +++ b/tests/ui/manual_strip.rs @@ -0,0 +1,59 @@ +#![warn(clippy::manual_strip)] + +fn main() { + let s = "abc"; + + if s.starts_with("ab") { + str::to_string(&s["ab".len()..]); + s["ab".len()..].to_string(); + + str::to_string(&s[2..]); + s[2..].to_string(); + } + + if s.ends_with("bc") { + str::to_string(&s[..s.len() - "bc".len()]); + s[..s.len() - "bc".len()].to_string(); + + str::to_string(&s[..s.len() - 2]); + s[..s.len() - 2].to_string(); + } + + // Character patterns + if s.starts_with('a') { + str::to_string(&s[1..]); + s[1..].to_string(); + } + + // Variable prefix + let prefix = "ab"; + if s.starts_with(prefix) { + str::to_string(&s[prefix.len()..]); + } + + // Constant prefix + const PREFIX: &str = "ab"; + if s.starts_with(PREFIX) { + str::to_string(&s[PREFIX.len()..]); + str::to_string(&s[2..]); + } + + // Constant target + const TARGET: &str = "abc"; + if TARGET.starts_with(prefix) { + str::to_string(&TARGET[prefix.len()..]); + } + + // String target - not mutated. + let s1: String = "abc".into(); + if s1.starts_with("ab") { + s1[2..].to_uppercase(); + } + + // String target - mutated. (Don't lint.) + let mut s2: String = "abc".into(); + if s2.starts_with("ab") { + s2.push('d'); + s2[2..].to_uppercase(); + } +} diff --git a/tests/ui/manual_strip.stderr b/tests/ui/manual_strip.stderr new file mode 100644 index 00000000000..1352a8713d4 --- /dev/null +++ b/tests/ui/manual_strip.stderr @@ -0,0 +1,132 @@ +error: stripping a prefix manually + --> $DIR/manual_strip.rs:7:24 + | +LL | str::to_string(&s["ab".len()..]); + | ^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::manual-strip` implied by `-D warnings` +note: the prefix was tested here + --> $DIR/manual_strip.rs:6:5 + | +LL | if s.starts_with("ab") { + | ^^^^^^^^^^^^^^^^^^^^^^^ +help: try using the `strip_prefix` method + | +LL | if let Some() = s.strip_prefix("ab") { +LL | str::to_string(); +LL | .to_string(); +LL | +LL | str::to_string(); +LL | .to_string(); + | + +error: stripping a suffix manually + --> $DIR/manual_strip.rs:15:24 + | +LL | str::to_string(&s[..s.len() - "bc".len()]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the suffix was tested here + --> $DIR/manual_strip.rs:14:5 + | +LL | if s.ends_with("bc") { + | ^^^^^^^^^^^^^^^^^^^^^ +help: try using the `strip_suffix` method + | +LL | if let Some() = s.strip_suffix("bc") { +LL | str::to_string(); +LL | .to_string(); +LL | +LL | str::to_string(); +LL | .to_string(); + | + +error: stripping a prefix manually + --> $DIR/manual_strip.rs:24:24 + | +LL | str::to_string(&s[1..]); + | ^^^^^^^ + | +note: the prefix was tested here + --> $DIR/manual_strip.rs:23:5 + | +LL | if s.starts_with('a') { + | ^^^^^^^^^^^^^^^^^^^^^^ +help: try using the `strip_prefix` method + | +LL | if let Some() = s.strip_prefix('a') { +LL | str::to_string(); +LL | .to_string(); + | + +error: stripping a prefix manually + --> $DIR/manual_strip.rs:31:24 + | +LL | str::to_string(&s[prefix.len()..]); + | ^^^^^^^^^^^^^^^^^^ + | +note: the prefix was tested here + --> $DIR/manual_strip.rs:30:5 + | +LL | if s.starts_with(prefix) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +help: try using the `strip_prefix` method + | +LL | if let Some() = s.strip_prefix(prefix) { +LL | str::to_string(); + | + +error: stripping a prefix manually + --> $DIR/manual_strip.rs:37:24 + | +LL | str::to_string(&s[PREFIX.len()..]); + | ^^^^^^^^^^^^^^^^^^ + | +note: the prefix was tested here + --> $DIR/manual_strip.rs:36:5 + | +LL | if s.starts_with(PREFIX) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +help: try using the `strip_prefix` method + | +LL | if let Some() = s.strip_prefix(PREFIX) { +LL | str::to_string(); +LL | str::to_string(); + | + +error: stripping a prefix manually + --> $DIR/manual_strip.rs:44:24 + | +LL | str::to_string(&TARGET[prefix.len()..]); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the prefix was tested here + --> $DIR/manual_strip.rs:43:5 + | +LL | if TARGET.starts_with(prefix) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: try using the `strip_prefix` method + | +LL | if let Some() = TARGET.strip_prefix(prefix) { +LL | str::to_string(); + | + +error: stripping a prefix manually + --> $DIR/manual_strip.rs:50:9 + | +LL | s1[2..].to_uppercase(); + | ^^^^^^^ + | +note: the prefix was tested here + --> $DIR/manual_strip.rs:49:5 + | +LL | if s1.starts_with("ab") { + | ^^^^^^^^^^^^^^^^^^^^^^^^ +help: try using the `strip_prefix` method + | +LL | if let Some() = s1.strip_prefix("ab") { +LL | .to_uppercase(); + | + +error: aborting due to 7 previous errors + -- cgit 1.4.1-3-g733a5 From 15244a88df5cfd475df010ad945474c658749192 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Mon, 14 Sep 2020 06:11:35 +0200 Subject: Fix `manual-strip` dogfood errors --- clippy_lints/src/doc.rs | 2 +- clippy_lints/src/loops.rs | 8 +++----- clippy_lints/src/misc_early.rs | 6 +++--- clippy_lints/src/redundant_clone.rs | 5 ++--- tests/ui/let_if_seq.rs | 1 + tests/ui/let_if_seq.stderr | 8 ++++---- 6 files changed, 14 insertions(+), 16 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 50121a054c7..62bb70af06e 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -534,7 +534,7 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span) { return false; } - let s = if s.ends_with('s') { &s[..s.len() - 1] } else { s }; + let s = s.strip_suffix('s').unwrap_or(s); s.chars().all(char::is_alphanumeric) && s.chars().filter(|&c| c.is_uppercase()).take(2).count() > 1 diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 6c54c07869a..8f5675a61b9 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2601,11 +2601,9 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont span, NEEDLESS_COLLECT_MSG, |diag| { - let (arg, pred) = if contains_arg.starts_with('&') { - ("x", &contains_arg[1..]) - } else { - ("&x", &*contains_arg) - }; + let (arg, pred) = contains_arg + .strip_prefix('&') + .map_or(("&x", &*contains_arg), |s| ("x", s)); diag.span_suggestion( span, "replace with", diff --git a/clippy_lints/src/misc_early.rs b/clippy_lints/src/misc_early.rs index 02789735c17..9cb1cfb915d 100644 --- a/clippy_lints/src/misc_early.rs +++ b/clippy_lints/src/misc_early.rs @@ -377,8 +377,8 @@ impl EarlyLintPass for MiscEarlyLints { if let PatKind::Ident(_, ident, None) = arg.pat.kind { let arg_name = ident.to_string(); - if arg_name.starts_with('_') { - if let Some(correspondence) = registered_names.get(&arg_name[1..]) { + if let Some(arg_name) = arg_name.strip_prefix('_') { + if let Some(correspondence) = registered_names.get(arg_name) { span_lint( cx, DUPLICATE_UNDERSCORE_ARGUMENT, @@ -386,7 +386,7 @@ impl EarlyLintPass for MiscEarlyLints { &format!( "`{}` already exists, having another argument having almost the same \ name makes code comprehension and documentation more difficult", - arg_name[1..].to_owned() + arg_name ), ); } diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 57a45e628db..1a7f36fbdad 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -239,10 +239,9 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { ); let mut app = Applicability::MaybeIncorrect; - let mut call_snip = &snip[dot + 1..]; + let call_snip = &snip[dot + 1..]; // Machine applicable when `call_snip` looks like `foobar()` - if call_snip.ends_with("()") { - call_snip = call_snip[..call_snip.len()-2].trim(); + if let Some(call_snip) = call_snip.strip_suffix("()").map(str::trim) { if call_snip.as_bytes().iter().all(|b| b.is_ascii_alphabetic() || *b == b'_') { app = Applicability::MachineApplicable; } diff --git a/tests/ui/let_if_seq.rs b/tests/ui/let_if_seq.rs index 802beeb4be6..32a67f181df 100644 --- a/tests/ui/let_if_seq.rs +++ b/tests/ui/let_if_seq.rs @@ -33,6 +33,7 @@ fn issue985_alt() -> i32 { x } +#[allow(clippy::manual_strip)] fn issue975() -> String { let mut udn = "dummy".to_string(); if udn.starts_with("uuid:") { diff --git a/tests/ui/let_if_seq.stderr b/tests/ui/let_if_seq.stderr index c53a63a541b..7de560c7348 100644 --- a/tests/ui/let_if_seq.stderr +++ b/tests/ui/let_if_seq.stderr @@ -1,5 +1,5 @@ error: `if _ { .. } else { .. }` is an expression - --> $DIR/let_if_seq.rs:63:5 + --> $DIR/let_if_seq.rs:64:5 | LL | / let mut foo = 0; LL | | if f() { @@ -11,7 +11,7 @@ LL | | } = note: you might not need `mut` at all error: `if _ { .. } else { .. }` is an expression - --> $DIR/let_if_seq.rs:68:5 + --> $DIR/let_if_seq.rs:69:5 | LL | / let mut bar = 0; LL | | if f() { @@ -25,7 +25,7 @@ LL | | } = note: you might not need `mut` at all error: `if _ { .. } else { .. }` is an expression - --> $DIR/let_if_seq.rs:76:5 + --> $DIR/let_if_seq.rs:77:5 | LL | / let quz; LL | | if f() { @@ -36,7 +36,7 @@ LL | | } | |_____^ help: it is more idiomatic to write: `let quz = if f() { 42 } else { 0 };` error: `if _ { .. } else { .. }` is an expression - --> $DIR/let_if_seq.rs:105:5 + --> $DIR/let_if_seq.rs:106:5 | LL | / let mut baz = 0; LL | | if f() { -- cgit 1.4.1-3-g733a5 From 1b5317f68b2b55803d5051e9945f9a33817fccef Mon Sep 17 00:00:00 2001 From: Robin Schoonover Date: Sun, 13 Sep 2020 21:52:25 -0600 Subject: Add rc_buffer lint for Rc and other buffer types --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/types.rs | 84 ++++++++++++++++++++++++++++++++++++++++- clippy_lints/src/utils/paths.rs | 1 + src/lintlist/mod.rs | 7 ++++ tests/ui/rc_buffer.rs | 13 +++++++ tests/ui/rc_buffer.stderr | 28 ++++++++++++++ 7 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 tests/ui/rc_buffer.rs create mode 100644 tests/ui/rc_buffer.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 285a2ff8060..8922f5e7027 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1775,6 +1775,7 @@ Released 2018-09-13 [`range_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_plus_one [`range_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_step_by_zero [`range_zip_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_zip_with_len +[`rc_buffer`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer [`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation [`redundant_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone [`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c017c5cb5d0..239eeb10bb4 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -837,6 +837,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &types::LET_UNIT_VALUE, &types::LINKEDLIST, &types::OPTION_OPTION, + &types::RC_BUFFER, &types::REDUNDANT_ALLOCATION, &types::TYPE_COMPLEXITY, &types::UNIT_ARG, @@ -1804,6 +1805,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE), LintId::of(&redundant_pub_crate::REDUNDANT_PUB_CRATE), LintId::of(&transmute::USELESS_TRANSMUTE), + LintId::of(&types::RC_BUFFER), LintId::of(&use_self::USE_SELF), ]); } diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index b6d405cca77..5f3a2e0b6d4 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -215,11 +215,41 @@ declare_clippy_lint! { "redundant allocation" } +declare_clippy_lint! { + /// **What it does:** Checks for Rc and Arc when T is a mutable buffer type such as String or Vec + /// + /// **Why is this bad?** Expressions such as Rc have no advantage over Rc, since + /// it is larger and involves an extra level of indirection, and doesn't implement Borrow. + /// + /// While mutating a buffer type would still be possible with Rc::get_mut(), it only + /// works if there are no additional references yet, which defeats the purpose of + /// enclosing it in a shared ownership type. Instead, additionally wrapping the inner + /// type with an interior mutable container (such as RefCell or Mutex) would normally + /// be used. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore + /// # use std::rc::Rc; + /// fn foo(interned: Rc) { ... } + /// ``` + /// + /// Better: + /// + /// ```rust,ignore + /// fn foo(interned: Rc) { ... } + /// ``` + pub RC_BUFFER, + nursery, + "shared ownership of a buffer type" +} + pub struct Types { vec_box_size_threshold: u64, } -impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION]); +impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION, RC_BUFFER]); impl<'tcx> LateLintPass<'tcx> for Types { fn check_fn(&mut self, cx: &LateContext<'_>, _: FnKind<'_>, decl: &FnDecl<'_>, _: &Body<'_>, _: Span, id: HirId) { @@ -272,6 +302,19 @@ fn match_type_parameter(cx: &LateContext<'_>, qpath: &QPath<'_>, path: &[&str]) None } +fn match_buffer_type(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<&'static str> { + if match_type_parameter(cx, qpath, &paths::STRING).is_some() { + return Some("str"); + } + if match_type_parameter(cx, qpath, &paths::OS_STRING).is_some() { + return Some("std::ffi::OsStr"); + } + if match_type_parameter(cx, qpath, &paths::PATH_BUF).is_some() { + return Some("std::path::Path"); + } + None +} + fn match_borrows_parameter(_cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option { let last = last_path_segment(qpath); if_chain! { @@ -385,6 +428,45 @@ impl Types { ); return; // don't recurse into the type } + if let Some(alternate) = match_buffer_type(cx, qpath) { + span_lint_and_sugg( + cx, + RC_BUFFER, + hir_ty.span, + "usage of `Rc` when T is a buffer type", + "try", + format!("Rc<{}>", alternate), + Applicability::MachineApplicable, + ); + return; // don't recurse into the type + } + if match_type_parameter(cx, qpath, &paths::VEC).is_some() { + let vec_ty = match &last_path_segment(qpath).args.unwrap().args[0] { + GenericArg::Type(ty) => match &ty.kind { + TyKind::Path(qpath) => qpath, + _ => return, + }, + _ => return, + }; + let inner_span = match &last_path_segment(&vec_ty).args.unwrap().args[0] { + GenericArg::Type(ty) => ty.span, + _ => return, + }; + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + RC_BUFFER, + hir_ty.span, + "usage of `Rc` when T is a buffer type", + "try", + format!( + "Rc<[{}]>", + snippet_with_applicability(cx, inner_span, "..", &mut applicability) + ), + Applicability::MachineApplicable, + ); + return; // don't recurse into the type + } if let Some(span) = match_borrows_parameter(cx, qpath) { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 65320d6a0e0..2df11d2efcf 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -113,6 +113,7 @@ pub const STD_CONVERT_IDENTITY: [&str; 3] = ["std", "convert", "identity"]; pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"]; pub const STD_MEM_TRANSMUTE: [&str; 3] = ["std", "mem", "transmute"]; pub const STD_PTR_NULL: [&str; 3] = ["std", "ptr", "null"]; +pub const STRING: [&str; 3] = ["alloc", "string", "String"]; pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"]; pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"]; pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]; diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index a7d38c93433..d0c6a1d63d9 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1851,6 +1851,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "ranges", }, + Lint { + name: "rc_buffer", + group: "nursery", + desc: "shared ownership of a buffer type", + deprecation: None, + module: "types", + }, Lint { name: "redundant_allocation", group: "perf", diff --git a/tests/ui/rc_buffer.rs b/tests/ui/rc_buffer.rs new file mode 100644 index 00000000000..c8c2bec67ee --- /dev/null +++ b/tests/ui/rc_buffer.rs @@ -0,0 +1,13 @@ +use std::ffi::OsString; +use std::path::PathBuf; +use std::rc::Rc; + +#[warn(clippy::rc_buffer)] +struct S { + a: Rc, + b: Rc, + c: Rc>, + d: Rc, +} + +fn main() {} diff --git a/tests/ui/rc_buffer.stderr b/tests/ui/rc_buffer.stderr new file mode 100644 index 00000000000..641a13a2251 --- /dev/null +++ b/tests/ui/rc_buffer.stderr @@ -0,0 +1,28 @@ +error: usage of `Rc` when T is a buffer type + --> $DIR/rc_buffer.rs:7:8 + | +LL | a: Rc, + | ^^^^^^^^^^ help: try: `Rc` + | + = note: `-D clippy::rc-buffer` implied by `-D warnings` + +error: usage of `Rc` when T is a buffer type + --> $DIR/rc_buffer.rs:8:8 + | +LL | b: Rc, + | ^^^^^^^^^^^ help: try: `Rc` + +error: usage of `Rc` when T is a buffer type + --> $DIR/rc_buffer.rs:9:8 + | +LL | c: Rc>, + | ^^^^^^^^^^^ help: try: `Rc<[u8]>` + +error: usage of `Rc` when T is a buffer type + --> $DIR/rc_buffer.rs:10:8 + | +LL | d: Rc, + | ^^^^^^^^^^^^ help: try: `Rc` + +error: aborting due to 4 previous errors + -- cgit 1.4.1-3-g733a5 From 2dd7175d60e070c7ee2b4609bdb17eae16e381f0 Mon Sep 17 00:00:00 2001 From: Robin Schoonover Date: Sun, 13 Sep 2020 22:17:10 -0600 Subject: Apply rc_buffer lint to Arc --- clippy_lints/src/types.rs | 40 ++++++++++++++++++++++++++++++++++++++++ tests/ui/rc_buffer_arc.rs | 13 +++++++++++++ tests/ui/rc_buffer_arc.stderr | 28 ++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 tests/ui/rc_buffer_arc.rs create mode 100644 tests/ui/rc_buffer_arc.stderr (limited to 'tests') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 5f3a2e0b6d4..da04d07885b 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -480,6 +480,46 @@ impl Types { ); return; // don't recurse into the type } + } else if cx.tcx.is_diagnostic_item(sym::Arc, def_id) { + if let Some(alternate) = match_buffer_type(cx, qpath) { + span_lint_and_sugg( + cx, + RC_BUFFER, + hir_ty.span, + "usage of `Arc` when T is a buffer type", + "try", + format!("Arc<{}>", alternate), + Applicability::MachineApplicable, + ); + return; // don't recurse into the type + } + if match_type_parameter(cx, qpath, &paths::VEC).is_some() { + let vec_ty = match &last_path_segment(qpath).args.unwrap().args[0] { + GenericArg::Type(ty) => match &ty.kind { + TyKind::Path(qpath) => qpath, + _ => return, + }, + _ => return, + }; + let inner_span = match &last_path_segment(&vec_ty).args.unwrap().args[0] { + GenericArg::Type(ty) => ty.span, + _ => return, + }; + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + RC_BUFFER, + hir_ty.span, + "usage of `Arc` when T is a buffer type", + "try", + format!( + "Arc<[{}]>", + snippet_with_applicability(cx, inner_span, "..", &mut applicability) + ), + Applicability::MachineApplicable, + ); + return; // don't recurse into the type + } } else if cx.tcx.is_diagnostic_item(sym!(vec_type), def_id) { if_chain! { // Get the _ part of Vec<_> diff --git a/tests/ui/rc_buffer_arc.rs b/tests/ui/rc_buffer_arc.rs new file mode 100644 index 00000000000..a878b0ab336 --- /dev/null +++ b/tests/ui/rc_buffer_arc.rs @@ -0,0 +1,13 @@ +use std::ffi::OsString; +use std::path::PathBuf; +use std::sync::Arc; + +#[warn(clippy::rc_buffer)] +struct S { + a: Arc, + b: Arc, + c: Arc>, + d: Arc, +} + +fn main() {} diff --git a/tests/ui/rc_buffer_arc.stderr b/tests/ui/rc_buffer_arc.stderr new file mode 100644 index 00000000000..c4b01621046 --- /dev/null +++ b/tests/ui/rc_buffer_arc.stderr @@ -0,0 +1,28 @@ +error: usage of `Arc` when T is a buffer type + --> $DIR/rc_buffer_arc.rs:7:8 + | +LL | a: Arc, + | ^^^^^^^^^^^ help: try: `Arc` + | + = note: `-D clippy::rc-buffer` implied by `-D warnings` + +error: usage of `Arc` when T is a buffer type + --> $DIR/rc_buffer_arc.rs:8:8 + | +LL | b: Arc, + | ^^^^^^^^^^^^ help: try: `Arc` + +error: usage of `Arc` when T is a buffer type + --> $DIR/rc_buffer_arc.rs:9:8 + | +LL | c: Arc>, + | ^^^^^^^^^^^^ help: try: `Arc<[u8]>` + +error: usage of `Arc` when T is a buffer type + --> $DIR/rc_buffer_arc.rs:10:8 + | +LL | d: Arc, + | ^^^^^^^^^^^^^ help: try: `Arc` + +error: aborting due to 4 previous errors + -- cgit 1.4.1-3-g733a5 From 4d73ccaa9419b393b9c94f977ec0e158897feeb3 Mon Sep 17 00:00:00 2001 From: Haraman Johal Date: Tue, 15 Sep 2020 00:20:31 +0100 Subject: clarify margin of error in wording of float comparison operator lint messages --- clippy_lints/src/misc.rs | 6 +++--- tests/ui/float_cmp.stderr | 22 +++++++++++----------- tests/ui/float_cmp_const.stderr | 30 +++++++++++++++--------------- 3 files changed, 29 insertions(+), 29 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index d4a50dd9013..81e6214f143 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -411,16 +411,16 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { if !is_comparing_arrays { diag.span_suggestion( expr.span, - "consider comparing them within some error", + "consider comparing them within some margin of error", format!( - "({}).abs() {} error", + "({}).abs() {} error_margin", lhs - rhs, if op == BinOpKind::Eq { '<' } else { '>' } ), Applicability::HasPlaceholders, // snippet ); } - diag.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error`"); + diag.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`"); }); } else if op == BinOpKind::Rem && is_integer_const(cx, right, 1) { span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0"); diff --git a/tests/ui/float_cmp.stderr b/tests/ui/float_cmp.stderr index 2d454e8e70d..f7c380fc915 100644 --- a/tests/ui/float_cmp.stderr +++ b/tests/ui/float_cmp.stderr @@ -2,34 +2,34 @@ error: strict comparison of `f32` or `f64` --> $DIR/float_cmp.rs:65:5 | LL | ONE as f64 != 2.0; - | ^^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(ONE as f64 - 2.0).abs() > error` + | ^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(ONE as f64 - 2.0).abs() > error_margin` | = note: `-D clippy::float-cmp` implied by `-D warnings` - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` --> $DIR/float_cmp.rs:70:5 | LL | x == 1.0; - | ^^^^^^^^ help: consider comparing them within some error: `(x - 1.0).abs() < error` + | ^^^^^^^^ help: consider comparing them within some margin of error: `(x - 1.0).abs() < error_margin` | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` --> $DIR/float_cmp.rs:73:5 | LL | twice(x) != twice(ONE as f64); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(twice(x) - twice(ONE as f64)).abs() > error` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(twice(x) - twice(ONE as f64)).abs() > error_margin` | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` --> $DIR/float_cmp.rs:93:5 | LL | NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(NON_ZERO_ARRAY[i] - NON_ZERO_ARRAY[j]).abs() < error` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(NON_ZERO_ARRAY[i] - NON_ZERO_ARRAY[j]).abs() < error_margin` | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` arrays --> $DIR/float_cmp.rs:98:5 @@ -37,15 +37,15 @@ error: strict comparison of `f32` or `f64` arrays LL | a1 == a2; | ^^^^^^^^ | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` --> $DIR/float_cmp.rs:99:5 | LL | a1[0] == a2[0]; - | ^^^^^^^^^^^^^^ help: consider comparing them within some error: `(a1[0] - a2[0]).abs() < error` + | ^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(a1[0] - a2[0]).abs() < error_margin` | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: aborting due to 6 previous errors diff --git a/tests/ui/float_cmp_const.stderr b/tests/ui/float_cmp_const.stderr index 19dc4a284b7..5d0455363e8 100644 --- a/tests/ui/float_cmp_const.stderr +++ b/tests/ui/float_cmp_const.stderr @@ -2,58 +2,58 @@ error: strict comparison of `f32` or `f64` constant --> $DIR/float_cmp_const.rs:20:5 | LL | 1f32 == ONE; - | ^^^^^^^^^^^ help: consider comparing them within some error: `(1f32 - ONE).abs() < error` + | ^^^^^^^^^^^ help: consider comparing them within some margin of error: `(1f32 - ONE).abs() < error_margin` | = note: `-D clippy::float-cmp-const` implied by `-D warnings` - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` constant --> $DIR/float_cmp_const.rs:21:5 | LL | TWO == ONE; - | ^^^^^^^^^^ help: consider comparing them within some error: `(TWO - ONE).abs() < error` + | ^^^^^^^^^^ help: consider comparing them within some margin of error: `(TWO - ONE).abs() < error_margin` | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` constant --> $DIR/float_cmp_const.rs:22:5 | LL | TWO != ONE; - | ^^^^^^^^^^ help: consider comparing them within some error: `(TWO - ONE).abs() > error` + | ^^^^^^^^^^ help: consider comparing them within some margin of error: `(TWO - ONE).abs() > error_margin` | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` constant --> $DIR/float_cmp_const.rs:23:5 | LL | ONE + ONE == TWO; - | ^^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(ONE + ONE - TWO).abs() < error` + | ^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(ONE + ONE - TWO).abs() < error_margin` | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` constant --> $DIR/float_cmp_const.rs:25:5 | LL | x as f32 == ONE; - | ^^^^^^^^^^^^^^^ help: consider comparing them within some error: `(x as f32 - ONE).abs() < error` + | ^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(x as f32 - ONE).abs() < error_margin` | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` constant --> $DIR/float_cmp_const.rs:28:5 | LL | v == ONE; - | ^^^^^^^^ help: consider comparing them within some error: `(v - ONE).abs() < error` + | ^^^^^^^^ help: consider comparing them within some margin of error: `(v - ONE).abs() < error_margin` | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` constant --> $DIR/float_cmp_const.rs:29:5 | LL | v != ONE; - | ^^^^^^^^ help: consider comparing them within some error: `(v - ONE).abs() > error` + | ^^^^^^^^ help: consider comparing them within some margin of error: `(v - ONE).abs() > error_margin` | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` constant arrays --> $DIR/float_cmp_const.rs:61:5 @@ -61,7 +61,7 @@ error: strict comparison of `f32` or `f64` constant arrays LL | NON_ZERO_ARRAY == NON_ZERO_ARRAY2; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error` + = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: aborting due to 8 previous errors -- cgit 1.4.1-3-g733a5 From 8afa7ed6aec37c5423cffe12dbc854c954799fb3 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sun, 6 Sep 2020 23:32:48 +0200 Subject: Add internal lint MatchTypeOnDiagItem --- clippy_lints/src/lib.rs | 3 + clippy_lints/src/utils/internal_lints.rs | 115 ++++++++++++++++++++++++++++++- clippy_lints/src/utils/mod.rs | 5 ++ tests/ui/match_type_on_diag_item.rs | 50 ++++++++++++++ tests/ui/match_type_on_diag_item.stderr | 33 +++++++++ 5 files changed, 203 insertions(+), 3 deletions(-) create mode 100644 tests/ui/match_type_on_diag_item.rs create mode 100644 tests/ui/match_type_on_diag_item.stderr (limited to 'tests') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 44afd7e82fe..b60a80e07d7 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -867,6 +867,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &utils::internal_lints::COMPILER_LINT_FUNCTIONS, &utils::internal_lints::DEFAULT_LINT, &utils::internal_lints::LINT_WITHOUT_LINT_PASS, + &utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM, &utils::internal_lints::OUTER_EXPN_EXPN_DATA, &utils::internal_lints::PRODUCE_ICE, &vec::USELESS_VEC, @@ -1112,6 +1113,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box self_assignment::SelfAssignment); store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); + store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1240,6 +1242,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&utils::internal_lints::COMPILER_LINT_FUNCTIONS), LintId::of(&utils::internal_lints::DEFAULT_LINT), 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), ]); diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 8fa5d22210a..5beb4be5b29 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -1,6 +1,6 @@ use crate::utils::{ - is_expn_of, match_def_path, match_qpath, match_type, method_calls, paths, run_lints, snippet, span_lint, - span_lint_and_help, span_lint_and_sugg, walk_ptrs_ty, SpanlessEq, + is_expn_of, match_def_path, match_qpath, match_type, method_calls, path_to_res, paths, qpath_res, run_lints, + snippet, span_lint, span_lint_and_help, span_lint_and_sugg, walk_ptrs_ty, SpanlessEq, }; use if_chain::if_chain; use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, NodeId}; @@ -11,7 +11,7 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::hir_id::CRATE_HIR_ID; use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; -use rustc_hir::{Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Path, StmtKind, Ty, TyKind}; +use rustc_hir::{Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Node, Path, StmtKind, Ty, TyKind}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; @@ -206,6 +206,29 @@ declare_clippy_lint! { "found collapsible `span_lint_and_then` calls" } +declare_clippy_lint! { + /// **What it does:** Checks for calls to `utils::match_type()` on a type diagnostic item + /// and suggests to use `utils::is_type_diagnostic_item()` instead. + /// + /// **Why is this bad?** `utils::is_type_diagnostic_item()` does not require hardcoded paths. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// Bad: + /// ```rust,ignore + /// utils::match_type(cx, ty, &paths::VEC) + /// ``` + /// + /// Good: + /// ```rust,ignore + /// utils::is_type_diagnostic_item(cx, ty, sym!(vec_type)) + /// ``` + pub MATCH_TYPE_ON_DIAGNOSTIC_ITEM, + internal, + "using `utils::match_type()` instead of `utils::is_type_diagnostic_item()`" +} + declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]); impl EarlyLintPass for ClippyLintsInternal { @@ -652,3 +675,89 @@ fn suggest_note( Applicability::MachineApplicable, ); } + +declare_lint_pass!(MatchTypeOnDiagItem => [MATCH_TYPE_ON_DIAGNOSTIC_ITEM]); + +impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if !run_lints(cx, &[MATCH_TYPE_ON_DIAGNOSTIC_ITEM], expr.hir_id) { + return; + } + + if_chain! { + // Check if this is a call to utils::match_type() + if let ExprKind::Call(fn_path, [context, ty, ty_path]) = expr.kind; + if let ExprKind::Path(fn_qpath) = &fn_path.kind; + if match_qpath(&fn_qpath, &["utils", "match_type"]); + // Extract the path to the matched type + if let Some(segments) = path_to_matched_type(cx, ty_path); + let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect(); + if let Some(ty_did) = path_to_res(cx, &segments[..]).and_then(|res| res.opt_def_id()); + // Check if the matched type is a diagnostic item + let diag_items = cx.tcx.diagnostic_items(ty_did.krate); + if let Some(item_name) = diag_items.iter().find_map(|(k, v)| if *v == ty_did { Some(k) } else { None }); + then { + let cx_snippet = snippet(cx, context.span, "_"); + let ty_snippet = snippet(cx, ty.span, "_"); + + span_lint_and_sugg( + cx, + MATCH_TYPE_ON_DIAGNOSTIC_ITEM, + expr.span, + "usage of `utils::match_type() on a type diagnostic item`", + "try", + format!("utils::is_type_diagnostic_item({}, {}, sym!({}))", cx_snippet, ty_snippet, item_name), + Applicability::MaybeIncorrect, + ); + } + } + } +} + +fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option> { + use rustc_hir::ItemKind; + + match &expr.kind { + ExprKind::AddrOf(.., expr) => return path_to_matched_type(cx, expr), + ExprKind::Path(qpath) => match qpath_res(cx, qpath, expr.hir_id) { + Res::Local(hir_id) => { + let parent_id = cx.tcx.hir().get_parent_node(hir_id); + if let Some(Node::Local(local)) = cx.tcx.hir().find(parent_id) { + if let Some(init) = local.init { + return path_to_matched_type(cx, init); + } + } + }, + Res::Def(DefKind::Const | DefKind::Static, def_id) => { + if let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(def_id) { + if let ItemKind::Const(.., body_id) | ItemKind::Static(.., body_id) = item.kind { + let body = cx.tcx.hir().body(body_id); + return path_to_matched_type(cx, &body.value); + } + } + }, + _ => {}, + }, + ExprKind::Array(exprs) => { + let segments: Vec = exprs + .iter() + .filter_map(|expr| { + if let ExprKind::Lit(lit) = &expr.kind { + if let LitKind::Str(sym, _) = lit.node { + return Some(sym.as_str()); + } + } + + None + }) + .collect(); + + if segments.len() == exprs.len() { + return Some(segments); + } + }, + _ => {}, + } + + None +} diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 3ebbfed6456..8db7e693e62 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -130,6 +130,9 @@ pub fn is_wild<'tcx>(pat: &impl std::ops::Deref>) -> bool { } /// Checks if type is struct, enum or union type with the given def path. +/// +/// If the type is a diagnostic item, use `is_type_diagnostic_item` instead. +/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem` pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool { match ty.kind() { ty::Adt(adt, _) => match_def_path(cx, adt.did, path), @@ -138,6 +141,8 @@ pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool { } /// Checks if the type is equal to a diagnostic item +/// +/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem` pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool { match ty.kind() { ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did), diff --git a/tests/ui/match_type_on_diag_item.rs b/tests/ui/match_type_on_diag_item.rs new file mode 100644 index 00000000000..fe950b0aa7c --- /dev/null +++ b/tests/ui/match_type_on_diag_item.rs @@ -0,0 +1,50 @@ +#![deny(clippy::internal)] +#![feature(rustc_private)] + +extern crate rustc_hir; +extern crate rustc_lint; +extern crate rustc_middle; +#[macro_use] +extern crate rustc_session; +use rustc_hir::Expr; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::Ty; + +mod paths { + pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"]; +} + +mod utils { + use super::*; + + pub fn match_type(_cx: &LateContext<'_>, _ty: Ty<'_>, _path: &[&str]) -> bool { + false + } +} + +use utils::match_type; + +declare_lint! { + pub TEST_LINT, + Warn, + "" +} + +declare_lint_pass!(Pass => [TEST_LINT]); + +static OPTION: [&str; 3] = ["core", "option", "Option"]; + +impl<'tcx> LateLintPass<'tcx> for Pass { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr) { + let ty = cx.typeck_results().expr_ty(expr); + + let _ = match_type(cx, ty, &paths::VEC); + let _ = match_type(cx, ty, &OPTION); + let _ = match_type(cx, ty, &["core", "result", "Result"]); + + let rc_path = &["alloc", "rc", "Rc"]; + let _ = utils::match_type(cx, ty, rc_path); + } +} + +fn main() {} diff --git a/tests/ui/match_type_on_diag_item.stderr b/tests/ui/match_type_on_diag_item.stderr new file mode 100644 index 00000000000..c89137eb758 --- /dev/null +++ b/tests/ui/match_type_on_diag_item.stderr @@ -0,0 +1,33 @@ +error: usage of `utils::match_type() on a type diagnostic item` + --> $DIR/match_type_on_diag_item.rs:41:17 + | +LL | let _ = match_type(cx, ty, &paths::VEC); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(vec_type))` + | +note: the lint level is defined here + --> $DIR/match_type_on_diag_item.rs:1:9 + | +LL | #![deny(clippy::internal)] + | ^^^^^^^^^^^^^^^^ + = note: `#[deny(clippy::match_type_on_diagnostic_item)]` implied by `#[deny(clippy::internal)]` + +error: usage of `utils::match_type() on a type diagnostic item` + --> $DIR/match_type_on_diag_item.rs:42:17 + | +LL | let _ = match_type(cx, ty, &OPTION); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(option_type))` + +error: usage of `utils::match_type() on a type diagnostic item` + --> $DIR/match_type_on_diag_item.rs:43:17 + | +LL | let _ = match_type(cx, ty, &["core", "result", "Result"]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(result_type))` + +error: usage of `utils::match_type() on a type diagnostic item` + --> $DIR/match_type_on_diag_item.rs:46:17 + | +LL | let _ = utils::match_type(cx, ty, rc_path); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(Rc))` + +error: aborting due to 4 previous errors + -- cgit 1.4.1-3-g733a5 From d0b5663d30457d1a9ec4f98eb9baff7123b77172 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Tue, 15 Sep 2020 10:31:50 +0200 Subject: Fix usage of backquotes in suggestion --- clippy_lints/src/utils/internal_lints.rs | 2 +- tests/ui/match_type_on_diag_item.stderr | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 5beb4be5b29..f201494a024 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -704,7 +704,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem { cx, MATCH_TYPE_ON_DIAGNOSTIC_ITEM, expr.span, - "usage of `utils::match_type() on a type diagnostic item`", + "usage of `utils::match_type()` on a type diagnostic item", "try", format!("utils::is_type_diagnostic_item({}, {}, sym!({}))", cx_snippet, ty_snippet, item_name), Applicability::MaybeIncorrect, diff --git a/tests/ui/match_type_on_diag_item.stderr b/tests/ui/match_type_on_diag_item.stderr index c89137eb758..5e5fe9e3a3e 100644 --- a/tests/ui/match_type_on_diag_item.stderr +++ b/tests/ui/match_type_on_diag_item.stderr @@ -1,4 +1,4 @@ -error: usage of `utils::match_type() on a type diagnostic item` +error: usage of `utils::match_type()` on a type diagnostic item --> $DIR/match_type_on_diag_item.rs:41:17 | LL | let _ = match_type(cx, ty, &paths::VEC); @@ -11,19 +11,19 @@ LL | #![deny(clippy::internal)] | ^^^^^^^^^^^^^^^^ = note: `#[deny(clippy::match_type_on_diagnostic_item)]` implied by `#[deny(clippy::internal)]` -error: usage of `utils::match_type() on a type diagnostic item` +error: usage of `utils::match_type()` on a type diagnostic item --> $DIR/match_type_on_diag_item.rs:42:17 | LL | let _ = match_type(cx, ty, &OPTION); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(option_type))` -error: usage of `utils::match_type() on a type diagnostic item` +error: usage of `utils::match_type()` on a type diagnostic item --> $DIR/match_type_on_diag_item.rs:43:17 | LL | let _ = match_type(cx, ty, &["core", "result", "Result"]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(result_type))` -error: usage of `utils::match_type() on a type diagnostic item` +error: usage of `utils::match_type()` on a type diagnostic item --> $DIR/match_type_on_diag_item.rs:46:17 | LL | let _ = utils::match_type(cx, ty, rc_path); -- cgit 1.4.1-3-g733a5 From ecbe9ac0e9b68b171e4a48db40e91e2e44370c2a Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Tue, 15 Sep 2020 21:30:01 +0200 Subject: manual-strip: Add additional test --- tests/ui/manual_strip.rs | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'tests') diff --git a/tests/ui/manual_strip.rs b/tests/ui/manual_strip.rs index d1b4772c7de..cbb84eb5c7e 100644 --- a/tests/ui/manual_strip.rs +++ b/tests/ui/manual_strip.rs @@ -56,4 +56,11 @@ fn main() { s2.push('d'); s2[2..].to_uppercase(); } + + // Target not stripped. (Don't lint.) + let s3 = String::from("abcd"); + let s4 = String::from("efgh"); + if s3.starts_with("ab") { + s4[2..].to_string(); + } } -- cgit 1.4.1-3-g733a5 From 6ba36bcfd3802d9520d0ac48dabfe6dc06d8dc82 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Sat, 22 Aug 2020 00:43:04 +0200 Subject: option_if_let_else - distinguish pure from impure else expressions --- clippy_lints/src/methods/bind_instead_of_map.rs | 21 ++-- clippy_lints/src/methods/mod.rs | 66 ++++------- clippy_lints/src/methods/unnecessary_lazy_eval.rs | 76 ++----------- clippy_lints/src/option_if_let_else.rs | 33 +++--- clippy_lints/src/utils/eager_or_lazy.rs | 128 ++++++++++++++++++++++ clippy_lints/src/utils/mod.rs | 1 + clippy_lints/src/utils/sugg.rs | 2 +- clippy_lints/src/utils/usage.rs | 66 +++++++++++ tests/ui/option_if_let_else.fixed | 10 ++ tests/ui/option_if_let_else.rs | 15 +++ tests/ui/option_if_let_else.stderr | 36 ++++-- tests/ui/unnecessary_lazy_eval.fixed | 42 +++---- tests/ui/unnecessary_lazy_eval.rs | 26 +++-- tests/ui/unnecessary_lazy_eval.stderr | 112 +++++++++++++------ 14 files changed, 420 insertions(+), 214 deletions(-) create mode 100644 clippy_lints/src/utils/eager_or_lazy.rs (limited to 'tests') diff --git a/clippy_lints/src/methods/bind_instead_of_map.rs b/clippy_lints/src/methods/bind_instead_of_map.rs index 498f12518f8..ae37942e55a 100644 --- a/clippy_lints/src/methods/bind_instead_of_map.rs +++ b/clippy_lints/src/methods/bind_instead_of_map.rs @@ -12,6 +12,7 @@ use rustc_middle::hir::map::Map; use rustc_span::Span; pub(crate) struct OptionAndThenSome; + impl BindInsteadOfMap for OptionAndThenSome { const TYPE_NAME: &'static str = "Option"; const TYPE_QPATH: &'static [&'static str] = &paths::OPTION; @@ -24,6 +25,7 @@ impl BindInsteadOfMap for OptionAndThenSome { } pub(crate) struct ResultAndThenOk; + impl BindInsteadOfMap for ResultAndThenOk { const TYPE_NAME: &'static str = "Result"; const TYPE_QPATH: &'static [&'static str] = &paths::RESULT; @@ -36,6 +38,7 @@ impl BindInsteadOfMap for ResultAndThenOk { } pub(crate) struct ResultOrElseErrInfo; + impl BindInsteadOfMap for ResultOrElseErrInfo { const TYPE_NAME: &'static str = "Result"; const TYPE_QPATH: &'static [&'static str] = &paths::RESULT; @@ -120,9 +123,9 @@ pub(crate) trait BindInsteadOfMap { } } - fn lint_closure(cx: &LateContext<'_>, expr: &hir::Expr<'_>, closure_expr: &hir::Expr<'_>) { + fn lint_closure(cx: &LateContext<'_>, expr: &hir::Expr<'_>, closure_expr: &hir::Expr<'_>) -> bool { let mut suggs = Vec::new(); - let can_sugg = find_all_ret_expressions(cx, closure_expr, |ret_expr| { + let can_sugg: bool = find_all_ret_expressions(cx, closure_expr, |ret_expr| { if_chain! { if !in_macro(ret_expr.span); if let hir::ExprKind::Call(ref func_path, ref args) = ret_expr.kind; @@ -153,12 +156,13 @@ pub(crate) trait BindInsteadOfMap { ) }); } + can_sugg } /// Lint use of `_.and_then(|x| Some(y))` for `Option`s - fn lint(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { + fn lint(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) -> bool { if !match_type(cx, cx.typeck_results().expr_ty(&args[0]), Self::TYPE_QPATH) { - return; + return false; } match args[1].kind { @@ -166,8 +170,10 @@ pub(crate) trait BindInsteadOfMap { let closure_body = cx.tcx.hir().body(body_id); let closure_expr = remove_blocks(&closure_body.value); - if !Self::lint_closure_autofixable(cx, expr, args, closure_expr, closure_args_span) { - Self::lint_closure(cx, expr, closure_expr); + if Self::lint_closure_autofixable(cx, expr, args, closure_expr, closure_args_span) { + true + } else { + Self::lint_closure(cx, expr, closure_expr) } }, // `_.and_then(Some)` case, which is no-op. @@ -181,8 +187,9 @@ pub(crate) trait BindInsteadOfMap { snippet(cx, args[0].span, "..").into(), Applicability::MachineApplicable, ); + true }, - _ => {}, + _ => false, } } } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 98e027b6d22..914f9f94e77 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -25,14 +25,15 @@ use rustc_span::source_map::Span; use rustc_span::symbol::{sym, SymbolStr}; use crate::consts::{constant, Constant}; +use crate::utils::eager_or_lazy::is_lazyness_candidate; use crate::utils::usage::mutated_variables; use crate::utils::{ contains_ty, get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro, - is_copy, is_ctor_or_promotable_const_function, is_expn_of, is_type_diagnostic_item, iter_input_pats, - last_path_segment, match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls, - method_chain_args, paths, remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability, - snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, - span_lint_and_then, sugg, walk_ptrs_ty, walk_ptrs_ty_depth, SpanlessEq, + is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath, + match_trait_method, match_type, match_var, method_calls, method_chain_args, paths, remove_blocks, return_ty, + single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint, + span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty, + walk_ptrs_ty_depth, SpanlessEq, }; declare_clippy_lint! { @@ -1454,18 +1455,21 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["unwrap_or", "map"] => option_map_unwrap_or::lint(cx, expr, arg_lists[1], arg_lists[0], method_spans[1]), ["unwrap_or_else", "map"] => { if !lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]) { - unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "unwrap_or"); + unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "unwrap_or"); } }, ["map_or", ..] => lint_map_or_none(cx, expr, arg_lists[0]), ["and_then", ..] => { - unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], false, "and"); - bind_instead_of_map::OptionAndThenSome::lint(cx, expr, arg_lists[0]); - bind_instead_of_map::ResultAndThenOk::lint(cx, expr, arg_lists[0]); + let biom_option_linted = bind_instead_of_map::OptionAndThenSome::lint(cx, expr, arg_lists[0]); + let biom_result_linted = bind_instead_of_map::ResultAndThenOk::lint(cx, expr, arg_lists[0]); + if !biom_option_linted && !biom_result_linted { + unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "and"); + } }, ["or_else", ..] => { - unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], false, "or"); - bind_instead_of_map::ResultOrElseErrInfo::lint(cx, expr, arg_lists[0]); + if !bind_instead_of_map::ResultOrElseErrInfo::lint(cx, expr, arg_lists[0]) { + unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "or"); + } }, ["next", "filter"] => lint_filter_next(cx, expr, arg_lists[1]), ["next", "skip_while"] => lint_skip_while_next(cx, expr, arg_lists[1]), @@ -1508,9 +1512,9 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["is_file", ..] => lint_filetype_is_file(cx, expr, arg_lists[0]), ["map", "as_ref"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], false), ["map", "as_mut"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], true), - ["unwrap_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "unwrap_or"), - ["get_or_insert_with", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "get_or_insert"), - ["ok_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], true, "ok_or"), + ["unwrap_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "unwrap_or"), + ["get_or_insert_with", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "get_or_insert"), + ["ok_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "ok_or"), _ => {}, } @@ -1714,37 +1718,6 @@ fn lint_or_fun_call<'tcx>( name: &str, args: &'tcx [hir::Expr<'_>], ) { - // Searches an expression for method calls or function calls that aren't ctors - struct FunCallFinder<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - found: bool, - } - - impl<'a, 'tcx> intravisit::Visitor<'tcx> for FunCallFinder<'a, 'tcx> { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { - let call_found = match &expr.kind { - // ignore enum and struct constructors - hir::ExprKind::Call(..) => !is_ctor_or_promotable_const_function(self.cx, expr), - hir::ExprKind::MethodCall(..) => true, - _ => false, - }; - - if call_found { - self.found |= true; - } - - if !self.found { - intravisit::walk_expr(self, expr); - } - } - - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { - intravisit::NestedVisitorMap::None - } - } - /// Checks for `unwrap_or(T::new())` or `unwrap_or(T::default())`. fn check_unwrap_or_default( cx: &LateContext<'_>, @@ -1825,8 +1798,7 @@ fn lint_or_fun_call<'tcx>( if_chain! { if know_types.iter().any(|k| k.2.contains(&name)); - let mut finder = FunCallFinder { cx: &cx, found: false }; - if { finder.visit_expr(&arg); finder.found }; + if is_lazyness_candidate(cx, arg); if !contains_return(&arg); let self_ty = cx.typeck_results().expr_ty(self_expr); diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs index 31517659c34..08b3eab9b7c 100644 --- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -1,78 +1,17 @@ -use crate::utils::{is_type_diagnostic_item, match_qpath, snippet, span_lint_and_sugg}; -use if_chain::if_chain; +use crate::utils::{eager_or_lazy, usage}; +use crate::utils::{is_type_diagnostic_item, snippet, span_lint_and_sugg}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use super::UNNECESSARY_LAZY_EVALUATIONS; -// Return true if the expression is an accessor of any of the arguments -fn expr_uses_argument(expr: &hir::Expr<'_>, params: &[hir::Param<'_>]) -> bool { - params.iter().any(|arg| { - if_chain! { - if let hir::PatKind::Binding(_, _, ident, _) = arg.pat.kind; - if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = expr.kind; - if let [p, ..] = path.segments; - then { - ident.name == p.ident.name - } else { - false - } - } - }) -} - -fn match_any_qpath(path: &hir::QPath<'_>, paths: &[&[&str]]) -> bool { - paths.iter().any(|candidate| match_qpath(path, candidate)) -} - -fn can_simplify(expr: &hir::Expr<'_>, params: &[hir::Param<'_>], variant_calls: bool) -> bool { - match expr.kind { - // Closures returning literals can be unconditionally simplified - hir::ExprKind::Lit(_) => true, - - hir::ExprKind::Index(ref object, ref index) => { - // arguments are not being indexed into - if expr_uses_argument(object, params) { - false - } else { - // arguments are not used as index - !expr_uses_argument(index, params) - } - }, - - // Reading fields can be simplified if the object is not an argument of the closure - hir::ExprKind::Field(ref object, _) => !expr_uses_argument(object, params), - - // Paths can be simplified if the root is not the argument, this also covers None - hir::ExprKind::Path(_) => !expr_uses_argument(expr, params), - - // Calls to Some, Ok, Err can be considered literals if they don't derive an argument - hir::ExprKind::Call(ref func, ref args) => if_chain! { - if variant_calls; // Disable lint when rules conflict with bind_instead_of_map - if let hir::ExprKind::Path(ref path) = func.kind; - if match_any_qpath(path, &[&["Some"], &["Ok"], &["Err"]]); - then { - // Recursively check all arguments - args.iter().all(|arg| can_simplify(arg, params, variant_calls)) - } else { - false - } - }, - - // For anything more complex than the above, a closure is probably the right solution, - // or the case is handled by an other lint - _ => false, - } -} - /// lint use of `_else(simple closure)` for `Option`s and `Result`s that can be /// replaced with `(return value of simple closure)` pub(super) fn lint<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, args: &'tcx [hir::Expr<'_>], - allow_variant_calls: bool, simplify_using: &str, ) { let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(option_type)); @@ -81,10 +20,13 @@ pub(super) fn lint<'tcx>( if is_option || is_result { if let hir::ExprKind::Closure(_, _, eid, _, _) = args[1].kind { let body = cx.tcx.hir().body(eid); - let ex = &body.value; - let params = &body.params; + let body_expr = &body.value; + + if usage::BindingUsageFinder::are_params_used(cx, body) { + return; + } - if can_simplify(ex, params, allow_variant_calls) { + if eager_or_lazy::is_eagerness_candidate(cx, body_expr) { let msg = if is_option { "unnecessary closure used to substitute value for `Option::None`" } else { @@ -101,7 +43,7 @@ pub(super) fn lint<'tcx>( "{0}.{1}({2})", snippet(cx, args[0].span, ".."), simplify_using, - snippet(cx, ex.span, ".."), + snippet(cx, body_expr.span, ".."), ), Applicability::MachineApplicable, ); diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 9494efe736c..5e2652b48cb 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -1,4 +1,5 @@ use crate::utils; +use crate::utils::eager_or_lazy; use crate::utils::sugg::Sugg; use crate::utils::{match_type, paths, span_lint_and_sugg}; use if_chain::if_chain; @@ -13,22 +14,16 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// **What it does:** /// Lints usage of `if let Some(v) = ... { y } else { x }` which is more - /// idiomatically done with `Option::map_or` (if the else bit is a simple - /// expression) or `Option::map_or_else` (if the else bit is a longer - /// block). + /// idiomatically done with `Option::map_or` (if the else bit is a pure + /// expression) or `Option::map_or_else` (if the else bit is an impure + /// expresion). /// /// **Why is this bad?** /// Using the dedicated functions of the Option type is clearer and /// more concise than an if let expression. /// /// **Known problems:** - /// This lint uses whether the block is just an expression or if it has - /// more statements to decide whether to use `Option::map_or` or - /// `Option::map_or_else`. If you have a single expression which calls - /// an expensive function, then it would be more efficient to use - /// `Option::map_or_else`, but this lint would suggest `Option::map_or`. - /// - /// Also, this lint uses a deliberately conservative metric for checking + /// This lint uses a deliberately conservative metric for checking /// if the inside of either body contains breaks or continues which will /// cause it to not suggest a fix if either block contains a loop with /// continues or breaks contained within the loop. @@ -92,6 +87,7 @@ struct OptionIfLetElseOccurence { struct ReturnBreakContinueMacroVisitor { seen_return_break_continue: bool, } + impl ReturnBreakContinueMacroVisitor { fn new() -> ReturnBreakContinueMacroVisitor { ReturnBreakContinueMacroVisitor { @@ -99,6 +95,7 @@ impl ReturnBreakContinueMacroVisitor { } } } + impl<'tcx> Visitor<'tcx> for ReturnBreakContinueMacroVisitor { type Map = Map<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap { @@ -157,7 +154,7 @@ fn extract_body_from_arm<'a>(arm: &'a Arm<'a>) -> Option<&'a Expr<'a>> { } /// If this is the else body of an if/else expression, then we need to wrap -/// it in curcly braces. Otherwise, we don't. +/// it in curly braces. Otherwise, we don't. fn should_wrap_in_braces(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { utils::get_enclosing_block(cx, expr.hir_id).map_or(false, |parent| { if let Some(Expr { @@ -199,7 +196,10 @@ fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: boo /// If this expression is the option if let/else construct we're detecting, then /// this function returns an `OptionIfLetElseOccurence` struct with details if /// this construct is found, or None if this construct is not found. -fn detect_option_if_let_else(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { +fn detect_option_if_let_else<'tcx>( + cx: &'_ LateContext<'tcx>, + expr: &'_ Expr<'tcx>, +) -> Option { if_chain! { if !utils::in_macro(expr.span); // Don't lint macros, because it behaves weirdly if let ExprKind::Match(cond_expr, arms, MatchSource::IfLetDesugar{contains_else_clause: true}) = &expr.kind; @@ -214,10 +214,7 @@ fn detect_option_if_let_else(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option "map_or_else", - _ => "map_or", - }; + let method_sugg = if eager_or_lazy::is_eagerness_candidate(cx, none_body) { "map_or" } else { "map_or_else" }; let capture_name = id.name.to_ident_string(); let wrap_braces = should_wrap_in_braces(cx, expr); let (as_ref, as_mut) = match &cond_expr.kind { @@ -243,8 +240,8 @@ fn detect_option_if_let_else(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option LateLintPass<'a> for OptionIfLetElse { - fn check_expr(&mut self, cx: &LateContext<'a>, expr: &Expr<'_>) { +impl<'tcx> LateLintPass<'tcx> for OptionIfLetElse { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { if let Some(detection) = detect_option_if_let_else(cx, expr) { span_lint_and_sugg( cx, diff --git a/clippy_lints/src/utils/eager_or_lazy.rs b/clippy_lints/src/utils/eager_or_lazy.rs new file mode 100644 index 00000000000..6938d9971d9 --- /dev/null +++ b/clippy_lints/src/utils/eager_or_lazy.rs @@ -0,0 +1,128 @@ +//! Utilities for evaluating whether eagerly evaluated expressions can be made lazy and vice versa. +//! +//! Things to consider: +//! - has the expression side-effects? +//! - is the expression computationally expensive? +//! +//! See lints: +//! - unnecessary-lazy-evaluations +//! - or-fun-call +//! - option-if-let-else + +use crate::utils::is_ctor_or_promotable_const_function; +use rustc_hir::def::{DefKind, Res}; + +use rustc_hir::intravisit; +use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; + +use rustc_hir::{Block, Expr, ExprKind, Path, QPath}; +use rustc_lint::LateContext; +use rustc_middle::hir::map::Map; + +/// Is the expr pure (is it free from side-effects)? +/// This function is named so to stress that it isn't exhaustive and returns FNs. +fn identify_some_pure_patterns(expr: &Expr<'_>) -> bool { + match expr.kind { + ExprKind::Lit(..) | ExprKind::Path(..) | ExprKind::Field(..) => true, + ExprKind::AddrOf(_, _, addr_of_expr) => identify_some_pure_patterns(addr_of_expr), + ExprKind::Tup(tup_exprs) => tup_exprs.iter().all(|expr| identify_some_pure_patterns(expr)), + ExprKind::Struct(_, fields, expr) => { + fields.iter().all(|f| identify_some_pure_patterns(f.expr)) + && expr.map_or(true, |e| identify_some_pure_patterns(e)) + }, + ExprKind::Call( + &Expr { + kind: + ExprKind::Path(QPath::Resolved( + _, + Path { + res: Res::Def(DefKind::Ctor(..) | DefKind::Variant, ..), + .. + }, + )), + .. + }, + args, + ) => args.iter().all(|expr| identify_some_pure_patterns(expr)), + ExprKind::Block( + &Block { + stmts, + expr: Some(expr), + .. + }, + _, + ) => stmts.is_empty() && identify_some_pure_patterns(expr), + ExprKind::Box(..) + | ExprKind::Array(..) + | ExprKind::Call(..) + | ExprKind::MethodCall(..) + | ExprKind::Binary(..) + | ExprKind::Unary(..) + | ExprKind::Cast(..) + | ExprKind::Type(..) + | ExprKind::DropTemps(..) + | ExprKind::Loop(..) + | ExprKind::Match(..) + | ExprKind::Closure(..) + | ExprKind::Block(..) + | ExprKind::Assign(..) + | ExprKind::AssignOp(..) + | ExprKind::Index(..) + | ExprKind::Break(..) + | ExprKind::Continue(..) + | ExprKind::Ret(..) + | ExprKind::InlineAsm(..) + | ExprKind::LlvmInlineAsm(..) + | ExprKind::Repeat(..) + | ExprKind::Yield(..) + | ExprKind::Err => false, + } +} + +/// Identify some potentially computationally expensive patterns. +/// This function is named so to stress that its implementation is non-exhaustive. +/// It returns FNs and FPs. +fn identify_some_potentially_expensive_patterns<'a, 'tcx>(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + // Searches an expression for method calls or function calls that aren't ctors + struct FunCallFinder<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + found: bool, + } + + impl<'a, 'tcx> intravisit::Visitor<'tcx> for FunCallFinder<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { + let call_found = match &expr.kind { + // ignore enum and struct constructors + ExprKind::Call(..) => !is_ctor_or_promotable_const_function(self.cx, expr), + ExprKind::MethodCall(..) => true, + _ => false, + }; + + if call_found { + self.found |= true; + } + + if !self.found { + intravisit::walk_expr(self, expr); + } + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + } + + let mut finder = FunCallFinder { cx, found: false }; + finder.visit_expr(expr); + finder.found +} + +pub fn is_eagerness_candidate<'a, 'tcx>(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + !identify_some_potentially_expensive_patterns(cx, expr) && identify_some_pure_patterns(expr) +} + +pub fn is_lazyness_candidate<'a, 'tcx>(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + identify_some_potentially_expensive_patterns(cx, expr) +} diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 3ebbfed6456..7efeef62690 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -10,6 +10,7 @@ pub mod comparisons; pub mod conf; pub mod constants; mod diagnostics; +pub mod eager_or_lazy; pub mod higher; mod hir_utils; pub mod inspector; diff --git a/clippy_lints/src/utils/sugg.rs b/clippy_lints/src/utils/sugg.rs index 811fde388d1..ec8b7e59b59 100644 --- a/clippy_lints/src/utils/sugg.rs +++ b/clippy_lints/src/utils/sugg.rs @@ -49,7 +49,7 @@ impl<'a> Sugg<'a> { /// Convenience function around `hir_opt` for suggestions with a default /// text. pub fn hir(cx: &LateContext<'_>, expr: &hir::Expr<'_>, default: &'a str) -> Self { - Self::hir_opt(cx, expr).unwrap_or_else(|| Sugg::NonParen(Cow::Borrowed(default))) + Self::hir_opt(cx, expr).unwrap_or(Sugg::NonParen(Cow::Borrowed(default))) } /// Same as `hir`, but it adapts the applicability level by following rules: diff --git a/clippy_lints/src/utils/usage.rs b/clippy_lints/src/utils/usage.rs index 4a64b935ac9..ea1dc3be29b 100644 --- a/clippy_lints/src/utils/usage.rs +++ b/clippy_lints/src/utils/usage.rs @@ -1,6 +1,8 @@ use crate::utils::match_var; use rustc_data_structures::fx::FxHashSet; +use rustc_hir as hir; use rustc_hir::def::Res; +use rustc_hir::intravisit; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::{Expr, HirId, Path}; use rustc_infer::infer::TyCtxtInferExt; @@ -108,3 +110,67 @@ pub fn is_unused<'tcx>(ident: &'tcx Ident, body: &'tcx Expr<'_>) -> bool { walk_expr(&mut visitor, body); !visitor.used } + +pub struct ParamBindingIdCollector { + binding_hir_ids: Vec, +} +impl<'tcx> ParamBindingIdCollector { + fn collect_binding_hir_ids(body: &'tcx hir::Body<'tcx>) -> Vec { + let mut finder = ParamBindingIdCollector { + binding_hir_ids: Vec::new(), + }; + finder.visit_body(body); + finder.binding_hir_ids + } +} +impl<'tcx> intravisit::Visitor<'tcx> for ParamBindingIdCollector { + type Map = Map<'tcx>; + + fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) { + if let hir::PatKind::Binding(_, hir_id, ..) = param.pat.kind { + self.binding_hir_ids.push(hir_id); + } + } + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::None + } +} + +pub struct BindingUsageFinder<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + binding_ids: Vec, + usage_found: bool, +} +impl<'a, 'tcx> BindingUsageFinder<'a, 'tcx> { + pub fn are_params_used(cx: &'a LateContext<'tcx>, body: &'tcx hir::Body<'tcx>) -> bool { + let mut finder = BindingUsageFinder { + cx, + binding_ids: ParamBindingIdCollector::collect_binding_hir_ids(body), + usage_found: false, + }; + finder.visit_body(body); + finder.usage_found + } +} +impl<'a, 'tcx> intravisit::Visitor<'tcx> for BindingUsageFinder<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { + if !self.usage_found { + intravisit::walk_expr(self, expr); + } + } + + fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, _: hir::HirId) { + if let hir::def::Res::Local(id) = path.res { + if self.binding_ids.contains(&id) { + self.usage_found = true; + } + } + } + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { + intravisit::NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) + } +} diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed index 695a460cc4e..a7fb00a2705 100644 --- a/tests/ui/option_if_let_else.fixed +++ b/tests/ui/option_if_let_else.fixed @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::option_if_let_else)] +#![allow(clippy::redundant_closure)] fn bad1(string: Option<&str>) -> (bool, &str) { string.map_or((false, "hello"), |x| (true, x)) @@ -36,6 +37,14 @@ fn longer_body(arg: Option) -> u32 { }) } +fn impure_else(arg: Option) { + let side_effect = || { + println!("return 1"); + 1 + }; + let _ = arg.map_or_else(|| side_effect(), |x| x); +} + fn test_map_or_else(arg: Option) { let _ = arg.map_or_else(|| { let mut y = 1; @@ -71,4 +80,5 @@ fn main() { let _ = longer_body(None); test_map_or_else(None); let _ = negative_tests(None); + let _ = impure_else(None); } diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs index dee80d26bd9..895fd86321f 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -1,5 +1,6 @@ // run-rustfix #![warn(clippy::option_if_let_else)] +#![allow(clippy::redundant_closure)] fn bad1(string: Option<&str>) -> (bool, &str) { if let Some(x) = string { @@ -52,6 +53,19 @@ fn longer_body(arg: Option) -> u32 { } } +fn impure_else(arg: Option) { + let side_effect = || { + println!("return 1"); + 1 + }; + let _ = if let Some(x) = arg { + x + } else { + // map_or_else must be suggested + side_effect() + }; +} + fn test_map_or_else(arg: Option) { let _ = if let Some(x) = arg { x * x * x * x @@ -89,4 +103,5 @@ fn main() { let _ = longer_body(None); test_map_or_else(None); let _ = negative_tests(None); + let _ = impure_else(None); } diff --git a/tests/ui/option_if_let_else.stderr b/tests/ui/option_if_let_else.stderr index 7005850efaf..b69fe767682 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:5:5 + --> $DIR/option_if_let_else.rs:6:5 | LL | / if let Some(x) = string { LL | | (true, x) @@ -11,7 +11,7 @@ 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:15:12 + --> $DIR/option_if_let_else.rs:16:12 | LL | } else if let Some(x) = string { | ____________^ @@ -22,19 +22,19 @@ LL | | } | |_____^ help: try: `{ string.map_or(Some((false, "")), |x| Some((true, x))) }` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:23:13 + --> $DIR/option_if_let_else.rs:24: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:24:13 + --> $DIR/option_if_let_else.rs:25: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:25:13 + --> $DIR/option_if_let_else.rs:26:13 | LL | let _ = if let Some(s) = &mut num { | _____________^ @@ -54,13 +54,13 @@ LL | }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:31:13 + --> $DIR/option_if_let_else.rs:32: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:32:13 + --> $DIR/option_if_let_else.rs:33:13 | LL | let _ = if let Some(mut s) = num { | _____________^ @@ -80,7 +80,7 @@ LL | }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:38:13 + --> $DIR/option_if_let_else.rs:39:13 | LL | let _ = if let Some(ref mut s) = num { | _____________^ @@ -100,7 +100,7 @@ LL | }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:47:5 + --> $DIR/option_if_let_else.rs:48:5 | LL | / if let Some(x) = arg { LL | | let y = x * x; @@ -119,7 +119,19 @@ LL | }) | error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:56:13 + --> $DIR/option_if_let_else.rs:61:13 + | +LL | let _ = if let Some(x) = arg { + | _____________^ +LL | | x +LL | | } else { +LL | | // map_or_else must be suggested +LL | | side_effect() +LL | | }; + | |_____^ help: try: `arg.map_or_else(|| side_effect(), |x| x)` + +error: use Option::map_or_else instead of an if let/else + --> $DIR/option_if_let_else.rs:70:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -142,10 +154,10 @@ LL | }, |x| x * x * x * x); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:85:13 + --> $DIR/option_if_let_else.rs:99:13 | LL | let _ = if let Some(x) = optional { x + 2 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)` -error: aborting due to 11 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/unnecessary_lazy_eval.fixed b/tests/ui/unnecessary_lazy_eval.fixed index fa66e68794e..4980c111499 100644 --- a/tests/ui/unnecessary_lazy_eval.fixed +++ b/tests/ui/unnecessary_lazy_eval.fixed @@ -2,6 +2,7 @@ #![warn(clippy::unnecessary_lazy_evaluations)] #![allow(clippy::redundant_closure)] #![allow(clippy::bind_instead_of_map)] +#![allow(clippy::map_identity)] struct Deep(Option); @@ -34,13 +35,13 @@ fn main() { let _ = opt.unwrap_or(2); let _ = opt.unwrap_or(astronomers_pi); let _ = opt.unwrap_or(ext_str.some_field); - let _ = opt.unwrap_or(ext_arr[0]); + let _ = opt.unwrap_or_else(|| ext_arr[0]); let _ = opt.and(ext_opt); let _ = opt.or(ext_opt); let _ = opt.or(None); let _ = opt.get_or_insert(2); let _ = opt.ok_or(2); - let _ = opt.ok_or(ext_arr[0]); + let _ = nested_tuple_opt.unwrap_or(Some((1, 2))); // Cases when unwrap is not called on a simple variable let _ = Some(10).unwrap_or(2); @@ -60,7 +61,6 @@ fn main() { // Should not lint - Option let _ = opt.unwrap_or_else(|| ext_str.return_some_field()); let _ = nested_opt.unwrap_or_else(|| Some(some_call())); - let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); let _ = nested_tuple_opt.unwrap_or_else(|| Some((some_call(), some_call()))); let _ = opt.or_else(some_call); let _ = opt.or_else(|| some_call()); @@ -69,13 +69,16 @@ fn main() { let _ = deep.0.get_or_insert_with(|| some_call()); let _ = deep.0.or_else(some_call); let _ = deep.0.or_else(|| some_call()); + let _ = opt.ok_or_else(|| ext_arr[0]); - // These are handled by bind_instead_of_map + // should not lint, bind_instead_of_map takes priority let _ = Some(10).and_then(|idx| Some(ext_arr[idx])); let _ = Some(10).and_then(|idx| Some(idx)); - let _: Option = None.or_else(|| Some(3)); - let _ = deep.0.or_else(|| Some(3)); - let _ = opt.or_else(|| Some(3)); + + // should lint, bind_instead_of_map doesn't apply + let _: Option = None.or(Some(3)); + let _ = deep.0.or(Some(3)); + let _ = opt.or(Some(3)); // Should lint - Result let res: Result = Err(5); @@ -92,26 +95,27 @@ fn main() { let _ = res2.unwrap_or_else(|err| err.return_some_field()); let _ = res2.unwrap_or_else(|_| ext_str.return_some_field()); + // should not lint, bind_instead_of_map takes priority let _: Result = res.and_then(|x| Ok(x)); - let _: Result = res.and_then(|x| Err(x)); - - let _: Result = res.or_else(|err| Ok(err)); let _: Result = res.or_else(|err| Err(err)); - // These are handled by bind_instead_of_map let _: Result = res.and_then(|_| Ok(2)); let _: Result = res.and_then(|_| Ok(astronomers_pi)); let _: Result = res.and_then(|_| Ok(ext_str.some_field)); - let _: Result = res.and_then(|_| Err(2)); - let _: Result = res.and_then(|_| Err(astronomers_pi)); - let _: Result = res.and_then(|_| Err(ext_str.some_field)); - - let _: Result = res.or_else(|_| Ok(2)); - let _: Result = res.or_else(|_| Ok(astronomers_pi)); - let _: Result = res.or_else(|_| Ok(ext_str.some_field)); - let _: Result = res.or_else(|_| Err(2)); let _: Result = res.or_else(|_| Err(astronomers_pi)); let _: Result = res.or_else(|_| Err(ext_str.some_field)); + + // should lint, bind_instead_of_map doesn't apply + let _: Result = res.and_then(|x| Err(x)); + let _: Result = res.or_else(|err| Ok(err)); + + let _: Result = res.and(Err(2)); + let _: Result = res.and(Err(astronomers_pi)); + let _: Result = res.and(Err(ext_str.some_field)); + + let _: Result = res.or(Ok(2)); + let _: Result = res.or(Ok(astronomers_pi)); + let _: Result = res.or(Ok(ext_str.some_field)); } diff --git a/tests/ui/unnecessary_lazy_eval.rs b/tests/ui/unnecessary_lazy_eval.rs index 04f47d1aa29..0b270939ec2 100644 --- a/tests/ui/unnecessary_lazy_eval.rs +++ b/tests/ui/unnecessary_lazy_eval.rs @@ -2,6 +2,7 @@ #![warn(clippy::unnecessary_lazy_evaluations)] #![allow(clippy::redundant_closure)] #![allow(clippy::bind_instead_of_map)] +#![allow(clippy::map_identity)] struct Deep(Option); @@ -40,7 +41,7 @@ fn main() { let _ = opt.or_else(|| None); let _ = opt.get_or_insert_with(|| 2); let _ = opt.ok_or_else(|| 2); - let _ = opt.ok_or_else(|| ext_arr[0]); + let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); // Cases when unwrap is not called on a simple variable let _ = Some(10).unwrap_or_else(|| 2); @@ -60,7 +61,6 @@ fn main() { // Should not lint - Option let _ = opt.unwrap_or_else(|| ext_str.return_some_field()); let _ = nested_opt.unwrap_or_else(|| Some(some_call())); - let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); let _ = nested_tuple_opt.unwrap_or_else(|| Some((some_call(), some_call()))); let _ = opt.or_else(some_call); let _ = opt.or_else(|| some_call()); @@ -69,10 +69,13 @@ fn main() { let _ = deep.0.get_or_insert_with(|| some_call()); let _ = deep.0.or_else(some_call); let _ = deep.0.or_else(|| some_call()); + let _ = opt.ok_or_else(|| ext_arr[0]); - // These are handled by bind_instead_of_map + // should not lint, bind_instead_of_map takes priority let _ = Some(10).and_then(|idx| Some(ext_arr[idx])); let _ = Some(10).and_then(|idx| Some(idx)); + + // should lint, bind_instead_of_map doesn't apply let _: Option = None.or_else(|| Some(3)); let _ = deep.0.or_else(|| Some(3)); let _ = opt.or_else(|| Some(3)); @@ -92,17 +95,22 @@ fn main() { let _ = res2.unwrap_or_else(|err| err.return_some_field()); let _ = res2.unwrap_or_else(|_| ext_str.return_some_field()); + // should not lint, bind_instead_of_map takes priority let _: Result = res.and_then(|x| Ok(x)); - let _: Result = res.and_then(|x| Err(x)); - - let _: Result = res.or_else(|err| Ok(err)); let _: Result = res.or_else(|err| Err(err)); - // These are handled by bind_instead_of_map let _: Result = res.and_then(|_| Ok(2)); let _: Result = res.and_then(|_| Ok(astronomers_pi)); let _: Result = res.and_then(|_| Ok(ext_str.some_field)); + let _: Result = res.or_else(|_| Err(2)); + let _: Result = res.or_else(|_| Err(astronomers_pi)); + let _: Result = res.or_else(|_| Err(ext_str.some_field)); + + // should lint, bind_instead_of_map doesn't apply + let _: Result = res.and_then(|x| Err(x)); + let _: Result = res.or_else(|err| Ok(err)); + let _: Result = res.and_then(|_| Err(2)); let _: Result = res.and_then(|_| Err(astronomers_pi)); let _: Result = res.and_then(|_| Err(ext_str.some_field)); @@ -110,8 +118,4 @@ fn main() { let _: Result = res.or_else(|_| Ok(2)); let _: Result = res.or_else(|_| Ok(astronomers_pi)); let _: Result = res.or_else(|_| Ok(ext_str.some_field)); - - let _: Result = res.or_else(|_| Err(2)); - let _: Result = res.or_else(|_| Err(astronomers_pi)); - let _: Result = res.or_else(|_| Err(ext_str.some_field)); } diff --git a/tests/ui/unnecessary_lazy_eval.stderr b/tests/ui/unnecessary_lazy_eval.stderr index 5c1b2eb1f14..1cf7ac46346 100644 --- a/tests/ui/unnecessary_lazy_eval.stderr +++ b/tests/ui/unnecessary_lazy_eval.stderr @@ -1,5 +1,5 @@ error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:34:13 + --> $DIR/unnecessary_lazy_eval.rs:35:13 | LL | let _ = opt.unwrap_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(2)` @@ -7,142 +7,190 @@ LL | let _ = opt.unwrap_or_else(|| 2); = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:35:13 + --> $DIR/unnecessary_lazy_eval.rs:36:13 | LL | let _ = opt.unwrap_or_else(|| astronomers_pi); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(astronomers_pi)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:36:13 + --> $DIR/unnecessary_lazy_eval.rs:37:13 | LL | let _ = opt.unwrap_or_else(|| ext_str.some_field); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(ext_str.some_field)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:37:13 - | -LL | let _ = opt.unwrap_or_else(|| ext_arr[0]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(ext_arr[0])` - -error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:38:13 + --> $DIR/unnecessary_lazy_eval.rs:39:13 | LL | let _ = opt.and_then(|_| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `opt.and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:39:13 + --> $DIR/unnecessary_lazy_eval.rs:40:13 | LL | let _ = opt.or_else(|| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:40:13 + --> $DIR/unnecessary_lazy_eval.rs:41:13 | LL | let _ = opt.or_else(|| None); | ^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:41:13 + --> $DIR/unnecessary_lazy_eval.rs:42:13 | LL | let _ = opt.get_or_insert_with(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `opt.get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:42:13 + --> $DIR/unnecessary_lazy_eval.rs:43:13 | LL | let _ = opt.ok_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `opt.ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:43:13 + --> $DIR/unnecessary_lazy_eval.rs:44:13 | -LL | let _ = opt.ok_or_else(|| ext_arr[0]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `opt.ok_or(ext_arr[0])` +LL | let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `nested_tuple_opt.unwrap_or(Some((1, 2)))` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:46:13 + --> $DIR/unnecessary_lazy_eval.rs:47:13 | LL | let _ = Some(10).unwrap_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `Some(10).unwrap_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:47:13 + --> $DIR/unnecessary_lazy_eval.rs:48:13 | LL | let _ = Some(10).and_then(|_| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `Some(10).and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:48:28 + --> $DIR/unnecessary_lazy_eval.rs:49:28 | LL | let _: Option = None.or_else(|| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:49:13 + --> $DIR/unnecessary_lazy_eval.rs:50:13 | LL | let _ = None.get_or_insert_with(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `None.get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:50:35 + --> $DIR/unnecessary_lazy_eval.rs:51:35 | LL | let _: Result = None.ok_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `None.ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:51:28 + --> $DIR/unnecessary_lazy_eval.rs:52:28 | LL | let _: Option = None.or_else(|| None); | ^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:54:13 + --> $DIR/unnecessary_lazy_eval.rs:55:13 | LL | let _ = deep.0.unwrap_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `deep.0.unwrap_or(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:55:13 + --> $DIR/unnecessary_lazy_eval.rs:56:13 | LL | let _ = deep.0.and_then(|_| ext_opt); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `deep.0.and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:56:13 + --> $DIR/unnecessary_lazy_eval.rs:57:13 | LL | let _ = deep.0.or_else(|| None); | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `deep.0.or(None)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:57:13 + --> $DIR/unnecessary_lazy_eval.rs:58:13 | LL | let _ = deep.0.get_or_insert_with(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `deep.0.get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` - --> $DIR/unnecessary_lazy_eval.rs:58:13 + --> $DIR/unnecessary_lazy_eval.rs:59:13 | LL | let _ = deep.0.ok_or_else(|| 2); | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `deep.0.ok_or(2)` +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:79:28 + | +LL | let _: Option = None.or_else(|| Some(3)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(Some(3))` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:80:13 + | +LL | let _ = deep.0.or_else(|| Some(3)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `deep.0.or(Some(3))` + +error: unnecessary closure used to substitute value for `Option::None` + --> $DIR/unnecessary_lazy_eval.rs:81:13 + | +LL | let _ = opt.or_else(|| Some(3)); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(Some(3))` + error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:84:13 + --> $DIR/unnecessary_lazy_eval.rs:87:13 | LL | let _ = res2.unwrap_or_else(|_| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(2)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:85:13 + --> $DIR/unnecessary_lazy_eval.rs:88:13 | LL | let _ = res2.unwrap_or_else(|_| astronomers_pi); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(astronomers_pi)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:86:13 + --> $DIR/unnecessary_lazy_eval.rs:89:13 | LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(ext_str.some_field)` -error: aborting due to 24 previous errors +error: unnecessary closure used to substitute value for `Result::Err` + --> $DIR/unnecessary_lazy_eval.rs:114:35 + | +LL | let _: Result = res.and_then(|_| Err(2)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `res.and(Err(2))` + +error: unnecessary closure used to substitute value for `Result::Err` + --> $DIR/unnecessary_lazy_eval.rs:115:35 + | +LL | let _: Result = res.and_then(|_| Err(astronomers_pi)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `res.and(Err(astronomers_pi))` + +error: unnecessary closure used to substitute value for `Result::Err` + --> $DIR/unnecessary_lazy_eval.rs:116:35 + | +LL | let _: Result = res.and_then(|_| Err(ext_str.some_field)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `res.and(Err(ext_str.some_field))` + +error: unnecessary closure used to substitute value for `Result::Err` + --> $DIR/unnecessary_lazy_eval.rs:118:35 + | +LL | let _: Result = res.or_else(|_| Ok(2)); + | ^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `res.or(Ok(2))` + +error: unnecessary closure used to substitute value for `Result::Err` + --> $DIR/unnecessary_lazy_eval.rs:119:35 + | +LL | let _: Result = res.or_else(|_| Ok(astronomers_pi)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `res.or(Ok(astronomers_pi))` + +error: unnecessary closure used to substitute value for `Result::Err` + --> $DIR/unnecessary_lazy_eval.rs:120:35 + | +LL | let _: Result = res.or_else(|_| Ok(ext_str.some_field)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `res.or(Ok(ext_str.some_field))` + +error: aborting due to 32 previous errors -- cgit 1.4.1-3-g733a5 From d0ddbb9d0d0d6b9b6adb51b259819ec270421642 Mon Sep 17 00:00:00 2001 From: Robin Schoonover Date: Tue, 15 Sep 2020 21:10:50 -0600 Subject: Extend testing of rc_buffer lint --- tests/ui/rc_buffer.rs | 23 +++++++++++--- tests/ui/rc_buffer.stderr | 50 ++++++++++++++++++++++-------- tests/ui/rc_buffer_arc.rs | 24 ++++++++++---- tests/ui/rc_buffer_arc.stderr | 50 ++++++++++++++++++++++-------- tests/ui/rc_buffer_redefined_string.rs | 12 +++++++ tests/ui/rc_buffer_redefined_string.stderr | 0 6 files changed, 122 insertions(+), 37 deletions(-) create mode 100644 tests/ui/rc_buffer_redefined_string.rs create mode 100644 tests/ui/rc_buffer_redefined_string.stderr (limited to 'tests') diff --git a/tests/ui/rc_buffer.rs b/tests/ui/rc_buffer.rs index c8c2bec67ee..1fa98643936 100644 --- a/tests/ui/rc_buffer.rs +++ b/tests/ui/rc_buffer.rs @@ -1,13 +1,26 @@ +#![warn(clippy::rc_buffer)] + +use std::cell::RefCell; use std::ffi::OsString; use std::path::PathBuf; use std::rc::Rc; -#[warn(clippy::rc_buffer)] struct S { - a: Rc, - b: Rc, - c: Rc>, - d: Rc, + // triggers lint + bad1: Rc, + bad2: Rc, + bad3: Rc>, + bad4: Rc, + // does not trigger lint + good1: Rc>, } +// triggers lint +fn func_bad1(_: Rc) {} +fn func_bad2(_: Rc) {} +fn func_bad3(_: Rc>) {} +fn func_bad4(_: Rc) {} +// does not trigger lint +fn func_good1(_: Rc>) {} + fn main() {} diff --git a/tests/ui/rc_buffer.stderr b/tests/ui/rc_buffer.stderr index 641a13a2251..e4cc169af07 100644 --- a/tests/ui/rc_buffer.stderr +++ b/tests/ui/rc_buffer.stderr @@ -1,28 +1,52 @@ error: usage of `Rc` when T is a buffer type - --> $DIR/rc_buffer.rs:7:8 + --> $DIR/rc_buffer.rs:10:11 | -LL | a: Rc, - | ^^^^^^^^^^ help: try: `Rc` +LL | bad1: Rc, + | ^^^^^^^^^^ help: try: `Rc` | = note: `-D clippy::rc-buffer` implied by `-D warnings` error: usage of `Rc` when T is a buffer type - --> $DIR/rc_buffer.rs:8:8 + --> $DIR/rc_buffer.rs:11:11 | -LL | b: Rc, - | ^^^^^^^^^^^ help: try: `Rc` +LL | bad2: Rc, + | ^^^^^^^^^^^ help: try: `Rc` error: usage of `Rc` when T is a buffer type - --> $DIR/rc_buffer.rs:9:8 + --> $DIR/rc_buffer.rs:12:11 | -LL | c: Rc>, - | ^^^^^^^^^^^ help: try: `Rc<[u8]>` +LL | bad3: Rc>, + | ^^^^^^^^^^^ help: try: `Rc<[u8]>` error: usage of `Rc` when T is a buffer type - --> $DIR/rc_buffer.rs:10:8 + --> $DIR/rc_buffer.rs:13:11 | -LL | d: Rc, - | ^^^^^^^^^^^^ help: try: `Rc` +LL | bad4: Rc, + | ^^^^^^^^^^^^ help: try: `Rc` -error: aborting due to 4 previous errors +error: usage of `Rc` when T is a buffer type + --> $DIR/rc_buffer.rs:19:17 + | +LL | fn func_bad1(_: Rc) {} + | ^^^^^^^^^^ help: try: `Rc` + +error: usage of `Rc` when T is a buffer type + --> $DIR/rc_buffer.rs:20:17 + | +LL | fn func_bad2(_: Rc) {} + | ^^^^^^^^^^^ help: try: `Rc` + +error: usage of `Rc` when T is a buffer type + --> $DIR/rc_buffer.rs:21:17 + | +LL | fn func_bad3(_: Rc>) {} + | ^^^^^^^^^^^ help: try: `Rc<[u8]>` + +error: usage of `Rc` when T is a buffer type + --> $DIR/rc_buffer.rs:22:17 + | +LL | fn func_bad4(_: Rc) {} + | ^^^^^^^^^^^^ help: try: `Rc` + +error: aborting due to 8 previous errors diff --git a/tests/ui/rc_buffer_arc.rs b/tests/ui/rc_buffer_arc.rs index a878b0ab336..5d586584817 100644 --- a/tests/ui/rc_buffer_arc.rs +++ b/tests/ui/rc_buffer_arc.rs @@ -1,13 +1,25 @@ +#![warn(clippy::rc_buffer)] + use std::ffi::OsString; use std::path::PathBuf; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; -#[warn(clippy::rc_buffer)] struct S { - a: Arc, - b: Arc, - c: Arc>, - d: Arc, + // triggers lint + bad1: Arc, + bad2: Arc, + bad3: Arc>, + bad4: Arc, + // does not trigger lint + good1: Arc>, } +// triggers lint +fn func_bad1(_: Arc) {} +fn func_bad2(_: Arc) {} +fn func_bad3(_: Arc>) {} +fn func_bad4(_: Arc) {} +// does not trigger lint +fn func_good1(_: Arc>) {} + fn main() {} diff --git a/tests/ui/rc_buffer_arc.stderr b/tests/ui/rc_buffer_arc.stderr index c4b01621046..8252270d2ac 100644 --- a/tests/ui/rc_buffer_arc.stderr +++ b/tests/ui/rc_buffer_arc.stderr @@ -1,28 +1,52 @@ error: usage of `Arc` when T is a buffer type - --> $DIR/rc_buffer_arc.rs:7:8 + --> $DIR/rc_buffer_arc.rs:9:11 | -LL | a: Arc, - | ^^^^^^^^^^^ help: try: `Arc` +LL | bad1: Arc, + | ^^^^^^^^^^^ help: try: `Arc` | = note: `-D clippy::rc-buffer` implied by `-D warnings` error: usage of `Arc` when T is a buffer type - --> $DIR/rc_buffer_arc.rs:8:8 + --> $DIR/rc_buffer_arc.rs:10:11 | -LL | b: Arc, - | ^^^^^^^^^^^^ help: try: `Arc` +LL | bad2: Arc, + | ^^^^^^^^^^^^ help: try: `Arc` error: usage of `Arc` when T is a buffer type - --> $DIR/rc_buffer_arc.rs:9:8 + --> $DIR/rc_buffer_arc.rs:11:11 | -LL | c: Arc>, - | ^^^^^^^^^^^^ help: try: `Arc<[u8]>` +LL | bad3: Arc>, + | ^^^^^^^^^^^^ help: try: `Arc<[u8]>` error: usage of `Arc` when T is a buffer type - --> $DIR/rc_buffer_arc.rs:10:8 + --> $DIR/rc_buffer_arc.rs:12:11 | -LL | d: Arc, - | ^^^^^^^^^^^^^ help: try: `Arc` +LL | bad4: Arc, + | ^^^^^^^^^^^^^ help: try: `Arc` -error: aborting due to 4 previous errors +error: usage of `Arc` when T is a buffer type + --> $DIR/rc_buffer_arc.rs:18:17 + | +LL | fn func_bad1(_: Arc) {} + | ^^^^^^^^^^^ help: try: `Arc` + +error: usage of `Arc` when T is a buffer type + --> $DIR/rc_buffer_arc.rs:19:17 + | +LL | fn func_bad2(_: Arc) {} + | ^^^^^^^^^^^^ help: try: `Arc` + +error: usage of `Arc` when T is a buffer type + --> $DIR/rc_buffer_arc.rs:20:17 + | +LL | fn func_bad3(_: Arc>) {} + | ^^^^^^^^^^^^ help: try: `Arc<[u8]>` + +error: usage of `Arc` when T is a buffer type + --> $DIR/rc_buffer_arc.rs:21:17 + | +LL | fn func_bad4(_: Arc) {} + | ^^^^^^^^^^^^^ help: try: `Arc` + +error: aborting due to 8 previous errors diff --git a/tests/ui/rc_buffer_redefined_string.rs b/tests/ui/rc_buffer_redefined_string.rs new file mode 100644 index 00000000000..5d31a848cf7 --- /dev/null +++ b/tests/ui/rc_buffer_redefined_string.rs @@ -0,0 +1,12 @@ +#![warn(clippy::rc_buffer)] + +use std::rc::Rc; + +struct String; + +struct S { + // does not trigger lint + good1: Rc, +} + +fn main() {} diff --git a/tests/ui/rc_buffer_redefined_string.stderr b/tests/ui/rc_buffer_redefined_string.stderr new file mode 100644 index 00000000000..e69de29bb2d -- cgit 1.4.1-3-g733a5 From 0261e341fd435f0b42954510fb436cb0b289cdac Mon Sep 17 00:00:00 2001 From: Andy Russell Date: Mon, 14 Sep 2020 14:58:39 -0400 Subject: {print,write}-with-newline: do not suggest empty format string --- clippy_lints/src/write.rs | 6 +++++- tests/ui/print_with_newline.rs | 1 + tests/ui/print_with_newline.stderr | 23 +++++++++++++++++------ tests/ui/write_with_newline.rs | 1 + tests/ui/write_with_newline.stderr | 23 +++++++++++++++++------ 5 files changed, 41 insertions(+), 13 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index e653240d049..fac63bcb993 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -322,11 +322,15 @@ impl EarlyLintPass for Write { } /// Given a format string that ends in a newline and its span, calculates the span of the -/// newline. +/// newline, or the format string itself if the format string consists solely of a newline. fn newline_span(fmtstr: &StrLit) -> Span { let sp = fmtstr.span; let contents = &fmtstr.symbol.as_str(); + if *contents == r"\n" { + return sp; + } + let newline_sp_hi = sp.hi() - match fmtstr.style { StrStyle::Cooked => BytePos(1), diff --git a/tests/ui/print_with_newline.rs b/tests/ui/print_with_newline.rs index 3f710540e90..a43a1fc4f52 100644 --- a/tests/ui/print_with_newline.rs +++ b/tests/ui/print_with_newline.rs @@ -9,6 +9,7 @@ fn main() { print!("Hello {}\n", "world"); print!("Hello {} {}\n", "world", "#2"); print!("{}\n", 1265); + print!("\n"); // these are all fine print!(""); diff --git a/tests/ui/print_with_newline.stderr b/tests/ui/print_with_newline.stderr index 05fe88915d6..54b3ad75b31 100644 --- a/tests/ui/print_with_newline.stderr +++ b/tests/ui/print_with_newline.stderr @@ -44,7 +44,18 @@ LL | println!("{}", 1265); | ^^^^^^^ -- error: using `print!()` with a format string that ends in a single newline - --> $DIR/print_with_newline.rs:30:5 + --> $DIR/print_with_newline.rs:12:5 + | +LL | print!("/n"); + | ^^^^^^^^^^^^ + | +help: use `println!` instead + | +LL | println!(); + | ^^^^^^^ -- + +error: using `print!()` with a format string that ends in a single newline + --> $DIR/print_with_newline.rs:31:5 | LL | print!("//n"); // should fail | ^^^^^^^^^^^^^^ @@ -55,7 +66,7 @@ LL | println!("/"); // should fail | ^^^^^^^ -- error: using `print!()` with a format string that ends in a single newline - --> $DIR/print_with_newline.rs:37:5 + --> $DIR/print_with_newline.rs:38:5 | LL | / print!( LL | | " @@ -70,7 +81,7 @@ LL | "" | error: using `print!()` with a format string that ends in a single newline - --> $DIR/print_with_newline.rs:41:5 + --> $DIR/print_with_newline.rs:42:5 | LL | / print!( LL | | r" @@ -85,7 +96,7 @@ LL | r"" | error: using `print!()` with a format string that ends in a single newline - --> $DIR/print_with_newline.rs:49:5 + --> $DIR/print_with_newline.rs:50:5 | LL | print!("/r/n"); //~ ERROR | ^^^^^^^^^^^^^^^ @@ -96,7 +107,7 @@ LL | println!("/r"); //~ ERROR | ^^^^^^^ -- error: using `print!()` with a format string that ends in a single newline - --> $DIR/print_with_newline.rs:50:5 + --> $DIR/print_with_newline.rs:51:5 | LL | print!("foo/rbar/n") // ~ ERROR | ^^^^^^^^^^^^^^^^^^^^ @@ -106,5 +117,5 @@ help: use `println!` instead LL | println!("foo/rbar") // ~ ERROR | ^^^^^^^ -- -error: aborting due to 9 previous errors +error: aborting due to 10 previous errors diff --git a/tests/ui/write_with_newline.rs b/tests/ui/write_with_newline.rs index 93afd73d111..1c1b1b58402 100644 --- a/tests/ui/write_with_newline.rs +++ b/tests/ui/write_with_newline.rs @@ -14,6 +14,7 @@ fn main() { write!(&mut v, "Hello {}\n", "world"); write!(&mut v, "Hello {} {}\n", "world", "#2"); write!(&mut v, "{}\n", 1265); + write!(&mut v, "\n"); // These should be fine write!(&mut v, ""); diff --git a/tests/ui/write_with_newline.stderr b/tests/ui/write_with_newline.stderr index 2473329ca72..a14e86122ee 100644 --- a/tests/ui/write_with_newline.stderr +++ b/tests/ui/write_with_newline.stderr @@ -44,7 +44,18 @@ LL | writeln!(&mut v, "{}", 1265); | ^^^^^^^ -- error: using `write!()` with a format string that ends in a single newline - --> $DIR/write_with_newline.rs:35:5 + --> $DIR/write_with_newline.rs:17:5 + | +LL | write!(&mut v, "/n"); + | ^^^^^^^^^^^^^^^^^^^^ + | +help: use `writeln!()` instead + | +LL | writeln!(&mut v, ); + | ^^^^^^^ -- + +error: using `write!()` with a format string that ends in a single newline + --> $DIR/write_with_newline.rs:36:5 | LL | write!(&mut v, "//n"); // should fail | ^^^^^^^^^^^^^^^^^^^^^^ @@ -55,7 +66,7 @@ LL | writeln!(&mut v, "/"); // should fail | ^^^^^^^ -- error: using `write!()` with a format string that ends in a single newline - --> $DIR/write_with_newline.rs:42:5 + --> $DIR/write_with_newline.rs:43:5 | LL | / write!( LL | | &mut v, @@ -72,7 +83,7 @@ LL | "" | error: using `write!()` with a format string that ends in a single newline - --> $DIR/write_with_newline.rs:47:5 + --> $DIR/write_with_newline.rs:48:5 | LL | / write!( LL | | &mut v, @@ -89,7 +100,7 @@ LL | r"" | error: using `write!()` with a format string that ends in a single newline - --> $DIR/write_with_newline.rs:56:5 + --> $DIR/write_with_newline.rs:57:5 | LL | write!(&mut v, "/r/n"); //~ ERROR | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -100,7 +111,7 @@ LL | writeln!(&mut v, "/r"); //~ ERROR | ^^^^^^^ -- error: using `write!()` with a format string that ends in a single newline - --> $DIR/write_with_newline.rs:57:5 + --> $DIR/write_with_newline.rs:58:5 | LL | write!(&mut v, "foo/rbar/n"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -110,5 +121,5 @@ help: use `writeln!()` instead LL | writeln!(&mut v, "foo/rbar"); | ^^^^^^^ -- -error: aborting due to 9 previous errors +error: aborting due to 10 previous errors -- cgit 1.4.1-3-g733a5 From 79da7474b160e2230467f84ff0df3a23658eb4a6 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Wed, 16 Sep 2020 19:59:51 +0200 Subject: option_if_let_else - change misleading test file section --- tests/ui/unnecessary_lazy_eval.fixed | 7 ++++--- tests/ui/unnecessary_lazy_eval.rs | 7 ++++--- tests/ui/unnecessary_lazy_eval.stderr | 12 ++++++------ 3 files changed, 14 insertions(+), 12 deletions(-) (limited to 'tests') diff --git a/tests/ui/unnecessary_lazy_eval.fixed b/tests/ui/unnecessary_lazy_eval.fixed index 4980c111499..4ba2a0a5dbc 100644 --- a/tests/ui/unnecessary_lazy_eval.fixed +++ b/tests/ui/unnecessary_lazy_eval.fixed @@ -108,9 +108,6 @@ fn main() { let _: Result = res.or_else(|_| Err(ext_str.some_field)); // should lint, bind_instead_of_map doesn't apply - let _: Result = res.and_then(|x| Err(x)); - let _: Result = res.or_else(|err| Ok(err)); - let _: Result = res.and(Err(2)); let _: Result = res.and(Err(astronomers_pi)); let _: Result = res.and(Err(ext_str.some_field)); @@ -118,4 +115,8 @@ fn main() { let _: Result = res.or(Ok(2)); let _: Result = res.or(Ok(astronomers_pi)); let _: Result = res.or(Ok(ext_str.some_field)); + + // neither bind_instead_of_map nor unnecessary_lazy_eval applies here + let _: Result = res.and_then(|x| Err(x)); + let _: Result = res.or_else(|err| Ok(err)); } diff --git a/tests/ui/unnecessary_lazy_eval.rs b/tests/ui/unnecessary_lazy_eval.rs index 0b270939ec2..466915217e4 100644 --- a/tests/ui/unnecessary_lazy_eval.rs +++ b/tests/ui/unnecessary_lazy_eval.rs @@ -108,9 +108,6 @@ fn main() { let _: Result = res.or_else(|_| Err(ext_str.some_field)); // should lint, bind_instead_of_map doesn't apply - let _: Result = res.and_then(|x| Err(x)); - let _: Result = res.or_else(|err| Ok(err)); - let _: Result = res.and_then(|_| Err(2)); let _: Result = res.and_then(|_| Err(astronomers_pi)); let _: Result = res.and_then(|_| Err(ext_str.some_field)); @@ -118,4 +115,8 @@ fn main() { let _: Result = res.or_else(|_| Ok(2)); let _: Result = res.or_else(|_| Ok(astronomers_pi)); let _: Result = res.or_else(|_| Ok(ext_str.some_field)); + + // neither bind_instead_of_map nor unnecessary_lazy_eval applies here + let _: Result = res.and_then(|x| Err(x)); + let _: Result = res.or_else(|err| Ok(err)); } diff --git a/tests/ui/unnecessary_lazy_eval.stderr b/tests/ui/unnecessary_lazy_eval.stderr index 1cf7ac46346..44dcd0cafbb 100644 --- a/tests/ui/unnecessary_lazy_eval.stderr +++ b/tests/ui/unnecessary_lazy_eval.stderr @@ -157,37 +157,37 @@ LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(ext_str.some_field)` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:114:35 + --> $DIR/unnecessary_lazy_eval.rs:111:35 | LL | let _: Result = res.and_then(|_| Err(2)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `res.and(Err(2))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:115:35 + --> $DIR/unnecessary_lazy_eval.rs:112:35 | LL | let _: Result = res.and_then(|_| Err(astronomers_pi)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `res.and(Err(astronomers_pi))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:116:35 + --> $DIR/unnecessary_lazy_eval.rs:113:35 | LL | let _: Result = res.and_then(|_| Err(ext_str.some_field)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `res.and(Err(ext_str.some_field))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:118:35 + --> $DIR/unnecessary_lazy_eval.rs:115:35 | LL | let _: Result = res.or_else(|_| Ok(2)); | ^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `res.or(Ok(2))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:119:35 + --> $DIR/unnecessary_lazy_eval.rs:116:35 | LL | let _: Result = res.or_else(|_| Ok(astronomers_pi)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `res.or(Ok(astronomers_pi))` error: unnecessary closure used to substitute value for `Result::Err` - --> $DIR/unnecessary_lazy_eval.rs:120:35 + --> $DIR/unnecessary_lazy_eval.rs:117:35 | LL | let _: Result = res.or_else(|_| Ok(ext_str.some_field)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `res.or(Ok(ext_str.some_field))` -- cgit 1.4.1-3-g733a5 From 2ce2d6b40e85bf543b3a9e38cd08c7065b357807 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Fri, 11 Sep 2020 14:41:54 +1200 Subject: fix a FP in `indexing_slicing` treat refs to arrays the same as arrays in `indexing_slicing` and `out_of_bounds_indexing` --- clippy_lints/src/indexing_slicing.rs | 2 +- tests/ui/indexing_slicing_index.rs | 3 ++- tests/ui/indexing_slicing_index.stderr | 20 ++++++-------------- tests/ui/indexing_slicing_slice.stderr | 26 +++++++------------------- 4 files changed, 16 insertions(+), 35 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs index a28eda8be15..741195f3b10 100644 --- a/clippy_lints/src/indexing_slicing.rs +++ b/clippy_lints/src/indexing_slicing.rs @@ -88,7 +88,7 @@ declare_lint_pass!(IndexingSlicing => [INDEXING_SLICING, OUT_OF_BOUNDS_INDEXING] impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Index(ref array, ref index) = &expr.kind { - let ty = cx.typeck_results().expr_ty(array); + let ty = cx.typeck_results().expr_ty(array).peel_refs(); if let Some(range) = higher::range(index) { // Ranged indexes, i.e., &x[n..m], &x[n..], &x[..n] and &x[..] if let ty::Array(_, s) = ty.kind() { diff --git a/tests/ui/indexing_slicing_index.rs b/tests/ui/indexing_slicing_index.rs index 000d5269930..ca8ca53c80c 100644 --- a/tests/ui/indexing_slicing_index.rs +++ b/tests/ui/indexing_slicing_index.rs @@ -15,7 +15,8 @@ fn main() { x[3]; // Ok, should not produce stderr. let y = &x; - y[0]; + y[0]; // Ok, referencing shouldn't affect this lint. See the issue 6021 + y[4]; // Ok, rustc will handle references too. let v = vec![0; 5]; v[0]; diff --git a/tests/ui/indexing_slicing_index.stderr b/tests/ui/indexing_slicing_index.stderr index 2b3f9be2dfb..2f6c9e2f4e5 100644 --- a/tests/ui/indexing_slicing_index.stderr +++ b/tests/ui/indexing_slicing_index.stderr @@ -8,15 +8,7 @@ LL | x[index]; = help: Consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic. - --> $DIR/indexing_slicing_index.rs:18:5 - | -LL | y[0]; - | ^^^^ - | - = help: Consider using `.get(n)` or `.get_mut(n)` instead - -error: indexing may panic. - --> $DIR/indexing_slicing_index.rs:21:5 + --> $DIR/indexing_slicing_index.rs:22:5 | LL | v[0]; | ^^^^ @@ -24,7 +16,7 @@ LL | v[0]; = help: Consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic. - --> $DIR/indexing_slicing_index.rs:22:5 + --> $DIR/indexing_slicing_index.rs:23:5 | LL | v[10]; | ^^^^^ @@ -32,7 +24,7 @@ LL | v[10]; = help: Consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic. - --> $DIR/indexing_slicing_index.rs:23:5 + --> $DIR/indexing_slicing_index.rs:24:5 | LL | v[1 << 3]; | ^^^^^^^^^ @@ -40,7 +32,7 @@ LL | v[1 << 3]; = help: Consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic. - --> $DIR/indexing_slicing_index.rs:29:5 + --> $DIR/indexing_slicing_index.rs:30:5 | LL | v[N]; | ^^^^ @@ -48,12 +40,12 @@ LL | v[N]; = help: Consider using `.get(n)` or `.get_mut(n)` instead error: indexing may panic. - --> $DIR/indexing_slicing_index.rs:30:5 + --> $DIR/indexing_slicing_index.rs:31:5 | LL | v[M]; | ^^^^ | = help: Consider using `.get(n)` or `.get_mut(n)` instead -error: aborting due to 7 previous errors +error: aborting due to 6 previous errors diff --git a/tests/ui/indexing_slicing_slice.stderr b/tests/ui/indexing_slicing_slice.stderr index ec6c157ac1a..2231deee833 100644 --- a/tests/ui/indexing_slicing_slice.stderr +++ b/tests/ui/indexing_slicing_slice.stderr @@ -71,29 +71,17 @@ LL | &x[1..][..5]; | = help: Consider using `.get(..n)`or `.get_mut(..n)` instead -error: slicing may panic. - --> $DIR/indexing_slicing_slice.rs:24:6 - | -LL | &y[1..2]; - | ^^^^^^^ - | - = help: Consider using `.get(n..m)` or `.get_mut(n..m)` instead - -error: slicing may panic. - --> $DIR/indexing_slicing_slice.rs:25:6 +error: range is out of bounds + --> $DIR/indexing_slicing_slice.rs:25:12 | LL | &y[0..=4]; - | ^^^^^^^^ - | - = help: Consider using `.get(n..m)` or `.get_mut(n..m)` instead + | ^ -error: slicing may panic. - --> $DIR/indexing_slicing_slice.rs:26:6 +error: range is out of bounds + --> $DIR/indexing_slicing_slice.rs:26:11 | LL | &y[..=4]; - | ^^^^^^^ - | - = help: Consider using `.get(..n)`or `.get_mut(..n)` instead + | ^ error: slicing may panic. --> $DIR/indexing_slicing_slice.rs:31:6 @@ -133,5 +121,5 @@ LL | &v[..100]; | = help: Consider using `.get(..n)`or `.get_mut(..n)` instead -error: aborting due to 17 previous errors +error: aborting due to 16 previous errors -- cgit 1.4.1-3-g733a5 From d655c0a938c16d30ae6824713165c749eff7210f Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Tue, 25 Aug 2020 15:13:40 +1200 Subject: Change the criteria of `interior_mutable_const` * stop linting associated types and generic type parameters * start linting ones in trait impls whose corresponding definitions in the traits are generic * remove the `is_copy` check as presumably the only purpose of it is to allow generics with `Copy` bounds as `Freeze` is internal and generics are no longer linted * remove the term 'copy' from the tests as being `Copy` no longer have meaning --- clippy_lints/src/non_copy_const.rs | 93 +++++++++++++++++--------- tests/ui/borrow_interior_mutable_const.rs | 20 +++++- tests/ui/borrow_interior_mutable_const.stderr | 44 +++++++----- tests/ui/declare_interior_mutable_const.rs | 62 +++++++++-------- tests/ui/declare_interior_mutable_const.stderr | 62 ++++++----------- 5 files changed, 156 insertions(+), 125 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 73eabd4207e..28c68a2b68c 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -6,14 +6,17 @@ use std::ptr; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind, UnOp}; +use rustc_infer::traits::specialization_graph; use rustc_lint::{LateContext, LateLintPass, Lint}; use rustc_middle::ty::adjustment::Adjust; -use rustc_middle::ty::{Ty, TypeFlags}; +use rustc_middle::ty::fold::TypeFoldable as _; +use rustc_middle::ty::{AssocKind, Ty, TypeFlags}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{InnerSpan, Span, DUMMY_SP}; use rustc_typeck::hir_ty_to_ty; -use crate::utils::{in_constant, is_copy, qpath_res, span_lint_and_then}; +use crate::utils::{in_constant, qpath_res, span_lint_and_then}; +use if_chain::if_chain; declare_clippy_lint! { /// **What it does:** Checks for declaration of `const` items which is interior @@ -83,11 +86,10 @@ declare_clippy_lint! { "referencing `const` with interior mutability" } -#[allow(dead_code)] #[derive(Copy, Clone)] enum Source { Item { item: Span }, - Assoc { item: Span, ty: Span }, + Assoc { item: Span }, Expr { expr: Span }, } @@ -110,10 +112,15 @@ impl Source { } fn verify_ty_bound<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, source: Source) { - if ty.is_freeze(cx.tcx.at(DUMMY_SP), cx.param_env) || is_copy(cx, ty) { - // An `UnsafeCell` is `!Copy`, and an `UnsafeCell` is also the only type which - // is `!Freeze`, thus if our type is `Copy` we can be sure it must be `Freeze` - // as well. + // Ignore types whose layout is unknown since `is_freeze` reports every generic types as `!Freeze`, + // making it indistinguishable from `UnsafeCell`. i.e. it isn't a tool to prove a type is + // 'unfrozen'. However, this code causes a false negative in which + // a type contains a layout-unknown type, but also a unsafe cell like `const CELL: Cell`. + // Yet, it's better than `ty.has_type_flags(TypeFlags::HAS_TY_PARAM | TypeFlags::HAS_PROJECTION)` + // since it works when a pointer indirection involves (`Cell<*const T>`). + // Making up a `ParamEnv` where every generic params and assoc types are `Freeze`is another option; + // but I'm not sure whether it's a decent way, if possible. + if cx.tcx.layout_of(cx.param_env.and(ty)).is_err() || ty.is_freeze(cx.tcx.at(DUMMY_SP), cx.param_env) { return; } @@ -127,11 +134,7 @@ fn verify_ty_bound<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, source: Source) { let const_kw_span = span.from_inner(InnerSpan::new(0, 5)); diag.span_label(const_kw_span, "make this a static item (maybe with lazy_static)"); }, - Source::Assoc { ty: ty_span, .. } => { - if ty.flags().intersects(TypeFlags::HAS_FREE_LOCAL_NAMES) { - diag.span_label(ty_span, &format!("consider requiring `{}` to be `Copy`", ty)); - } - }, + Source::Assoc { .. } => (), Source::Expr { .. } => { diag.help("assign this const to a local or static variable, and use the variable here"); }, @@ -152,14 +155,10 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx TraitItem<'_>) { if let TraitItemKind::Const(hir_ty, ..) = &trait_item.kind { let ty = hir_ty_to_ty(cx.tcx, hir_ty); - verify_ty_bound( - cx, - ty, - Source::Assoc { - ty: hir_ty.span, - item: trait_item.span, - }, - ); + // Normalize assoc types because ones originated from generic params + // bounded other traits could have their bound. + let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); + verify_ty_bound(cx, normalized, Source::Assoc { item: trait_item.span }); } } @@ -167,17 +166,47 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { if let ImplItemKind::Const(hir_ty, ..) = &impl_item.kind { let item_hir_id = cx.tcx.hir().get_parent_node(impl_item.hir_id); let item = cx.tcx.hir().expect_item(item_hir_id); - // Ensure the impl is an inherent impl. - if let ItemKind::Impl { of_trait: None, .. } = item.kind { - let ty = hir_ty_to_ty(cx.tcx, hir_ty); - verify_ty_bound( - cx, - ty, - Source::Assoc { - ty: hir_ty.span, - item: impl_item.span, - }, - ); + + match &item.kind { + ItemKind::Impl { + of_trait: Some(of_trait_ref), + .. + } => { + if_chain! { + // Lint a trait impl item only when the definition is a generic type, + // assuming a assoc const is not meant to be a interior mutable type. + if let Some(of_trait_def_id) = of_trait_ref.trait_def_id(); + if let Some(of_assoc_item) = specialization_graph::Node::Trait(of_trait_def_id) + .item(cx.tcx, impl_item.ident, AssocKind::Const, of_trait_def_id); + if cx.tcx + // Normalize assoc types because ones originated from generic params + // bounded other traits could have their bound at the trait defs; + // and, in that case, the definition is *not* generic. + .normalize_erasing_regions( + cx.tcx.param_env(of_trait_def_id), + cx.tcx.type_of(of_assoc_item.def_id), + ) + .has_type_flags(TypeFlags::HAS_PROJECTION | TypeFlags::HAS_TY_PARAM); + then { + let ty = hir_ty_to_ty(cx.tcx, hir_ty); + let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); + verify_ty_bound( + cx, + normalized, + Source::Assoc { + item: impl_item.span, + }, + ); + } + } + }, + ItemKind::Impl { of_trait: None, .. } => { + let ty = hir_ty_to_ty(cx.tcx, hir_ty); + // Normalize assoc types originated from generic params. + let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); + verify_ty_bound(cx, normalized, Source::Assoc { item: impl_item.span }); + }, + _ => (), } } } diff --git a/tests/ui/borrow_interior_mutable_const.rs b/tests/ui/borrow_interior_mutable_const.rs index 39f87510548..9fcc9ece49b 100644 --- a/tests/ui/borrow_interior_mutable_const.rs +++ b/tests/ui/borrow_interior_mutable_const.rs @@ -19,16 +19,30 @@ const NO_ANN: &dyn Display = &70; static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING); const ONCE_INIT: Once = Once::new(); -trait Trait: Copy { - type NonCopyType; +trait Trait { + type AssocType; const ATOMIC: AtomicUsize; + const INPUT: T; + const ASSOC: Self::AssocType; + + fn function() { + let _ = &Self::INPUT; + let _ = &Self::ASSOC; + } } impl Trait for u64 { - type NonCopyType = u16; + type AssocType = AtomicUsize; const ATOMIC: AtomicUsize = AtomicUsize::new(9); + const INPUT: u32 = 10; + const ASSOC: Self::AssocType = AtomicUsize::new(11); + + fn function() { + let _ = &Self::INPUT; + let _ = &Self::ASSOC; //~ ERROR interior mutability + } } // This is just a pointer that can be safely dereferended, diff --git a/tests/ui/borrow_interior_mutable_const.stderr b/tests/ui/borrow_interior_mutable_const.stderr index 5800af7e960..ed726a6b46e 100644 --- a/tests/ui/borrow_interior_mutable_const.stderr +++ b/tests/ui/borrow_interior_mutable_const.stderr @@ -1,14 +1,22 @@ error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:66:5 + --> $DIR/borrow_interior_mutable_const.rs:44:18 + | +LL | let _ = &Self::ASSOC; //~ ERROR interior mutability + | ^^^^^^^^^^^ + | + = note: `-D clippy::borrow-interior-mutable-const` implied by `-D warnings` + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/borrow_interior_mutable_const.rs:80:5 | LL | ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability | ^^^^^^ | - = note: `-D clippy::borrow-interior-mutable-const` implied by `-D warnings` = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:67:16 + --> $DIR/borrow_interior_mutable_const.rs:81:16 | LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability | ^^^^^^ @@ -16,7 +24,7 @@ LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutabi = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:70:22 + --> $DIR/borrow_interior_mutable_const.rs:84:22 | LL | let _once_ref = &ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -24,7 +32,7 @@ LL | let _once_ref = &ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:71:25 + --> $DIR/borrow_interior_mutable_const.rs:85:25 | LL | let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -32,7 +40,7 @@ LL | let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:72:27 + --> $DIR/borrow_interior_mutable_const.rs:86:27 | LL | let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -40,7 +48,7 @@ LL | let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:73:26 + --> $DIR/borrow_interior_mutable_const.rs:87:26 | LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -48,7 +56,7 @@ LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:84:14 + --> $DIR/borrow_interior_mutable_const.rs:98:14 | LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -56,7 +64,7 @@ LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:85:14 + --> $DIR/borrow_interior_mutable_const.rs:99:14 | LL | let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -64,7 +72,7 @@ LL | let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:86:19 + --> $DIR/borrow_interior_mutable_const.rs:100:19 | LL | let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -72,7 +80,7 @@ LL | let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:87:14 + --> $DIR/borrow_interior_mutable_const.rs:101:14 | LL | let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -80,7 +88,7 @@ LL | let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:88:13 + --> $DIR/borrow_interior_mutable_const.rs:102:13 | LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -88,7 +96,7 @@ LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mu = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:94:13 + --> $DIR/borrow_interior_mutable_const.rs:108:13 | LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -96,7 +104,7 @@ LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:99:5 + --> $DIR/borrow_interior_mutable_const.rs:113:5 | LL | CELL.set(2); //~ ERROR interior mutability | ^^^^ @@ -104,7 +112,7 @@ LL | CELL.set(2); //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:100:16 + --> $DIR/borrow_interior_mutable_const.rs:114:16 | LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability | ^^^^ @@ -112,7 +120,7 @@ LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:113:5 + --> $DIR/borrow_interior_mutable_const.rs:127:5 | LL | u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability | ^^^^^^^^^^^ @@ -120,12 +128,12 @@ LL | u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:114:16 + --> $DIR/borrow_interior_mutable_const.rs:128:16 | LL | assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability | ^^^^^^^^^^^ | = help: assign this const to a local or static variable, and use the variable here -error: aborting due to 16 previous errors +error: aborting due to 17 previous errors diff --git a/tests/ui/declare_interior_mutable_const.rs b/tests/ui/declare_interior_mutable_const.rs index b4003ed8932..7471b360540 100644 --- a/tests/ui/declare_interior_mutable_const.rs +++ b/tests/ui/declare_interior_mutable_const.rs @@ -34,60 +34,64 @@ static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING); #[allow(clippy::declare_interior_mutable_const)] const ONCE_INIT: Once = Once::new(); -trait Trait: Copy { - type NonCopyType; +struct Wrapper(T); + +trait Trait> { + type AssocType; + type AssocType2; + type AssocType3; const ATOMIC: AtomicUsize; //~ ERROR interior mutable const INTEGER: u64; const STRING: String; - const SELF: Self; // (no error) + const SELF: Self; const INPUT: T; - //~^ ERROR interior mutable - //~| HELP consider requiring `T` to be `Copy` - const ASSOC: Self::NonCopyType; - //~^ ERROR interior mutable - //~| HELP consider requiring `>::NonCopyType` to be `Copy` + const INPUT_ASSOC: T::AssocType4; + const INPUT_ASSOC_2: T::AssocType5; //~ ERROR interior mutable + const ASSOC: Self::AssocType; + const ASSOC_2: Self::AssocType2; + const WRAPPED_ASSOC_2: Wrapper; + const WRAPPED_ASSOC_3: Wrapper; const AN_INPUT: T = Self::INPUT; - //~^ ERROR interior mutable - //~| ERROR consider requiring `T` to be `Copy` - declare_const!(ANOTHER_INPUT: T = Self::INPUT); //~ ERROR interior mutable + declare_const!(ANOTHER_INPUT: T = Self::INPUT); + declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); //~ ERROR interior mutable } trait Trait2 { - type CopyType: Copy; + type AssocType4; + type AssocType5; const SELF_2: Self; - //~^ ERROR interior mutable - //~| HELP consider requiring `Self` to be `Copy` - const ASSOC_2: Self::CopyType; // (no error) + const ASSOC_4: Self::AssocType4; } -// we don't lint impl of traits, because an impl has no power to change the interface. -impl Trait for u64 { - type NonCopyType = u16; +impl> Trait for u64 { + type AssocType = u16; + type AssocType2 = AtomicUsize; + type AssocType3 = T; const ATOMIC: AtomicUsize = AtomicUsize::new(9); const INTEGER: u64 = 10; const STRING: String = String::new(); const SELF: Self = 11; - const INPUT: u32 = 12; - const ASSOC: Self::NonCopyType = 13; + const INPUT: T = T::SELF_2; + const INPUT_ASSOC: T::AssocType4 = T::ASSOC_4; + const INPUT_ASSOC_2: T::AssocType5 = AtomicUsize::new(16); + const ASSOC: Self::AssocType = 13; + const ASSOC_2: Self::AssocType2 = AtomicUsize::new(15); //~ ERROR interior mutable + const WRAPPED_ASSOC_2: Wrapper = Wrapper(AtomicUsize::new(16)); //~ ERROR interior mutable + const WRAPPED_ASSOC_3: Wrapper = Wrapper(T::SELF_2); } struct Local(T, U); -impl, U: Trait2> Local { - const ASSOC_3: AtomicUsize = AtomicUsize::new(14); //~ ERROR interior mutable +impl, U: Trait2> Local { + const ASSOC_5: AtomicUsize = AtomicUsize::new(14); //~ ERROR interior mutable const COW: Cow<'static, str> = Cow::Borrowed("tuvwxy"); - const T_SELF: T = T::SELF_2; const U_SELF: U = U::SELF_2; - //~^ ERROR interior mutable - //~| HELP consider requiring `U` to be `Copy` - const T_ASSOC: T::NonCopyType = T::ASSOC; - //~^ ERROR interior mutable - //~| HELP consider requiring `>::NonCopyType` to be `Copy` - const U_ASSOC: U::CopyType = U::ASSOC_2; + const T_ASSOC: T::AssocType = T::ASSOC; + const U_ASSOC: U::AssocType5 = AtomicUsize::new(17); //~ ERROR interior mutable } fn main() {} diff --git a/tests/ui/declare_interior_mutable_const.stderr b/tests/ui/declare_interior_mutable_const.stderr index 6a9a57361f9..0fcb726db46 100644 --- a/tests/ui/declare_interior_mutable_const.stderr +++ b/tests/ui/declare_interior_mutable_const.stderr @@ -36,34 +36,16 @@ LL | declare_const!(_ONCE: Once = Once::new()); //~ ERROR interior mutable = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:40:5 + --> $DIR/declare_interior_mutable_const.rs:44:5 | LL | const ATOMIC: AtomicUsize; //~ ERROR interior mutable | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:44:5 - | -LL | const INPUT: T; - | ^^^^^^^^^^^^^-^ - | | - | consider requiring `T` to be `Copy` - -error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:47:5 + --> $DIR/declare_interior_mutable_const.rs:50:5 | -LL | const ASSOC: Self::NonCopyType; - | ^^^^^^^^^^^^^-----------------^ - | | - | consider requiring `>::NonCopyType` to be `Copy` - -error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:51:5 - | -LL | const AN_INPUT: T = Self::INPUT; - | ^^^^^^^^^^^^^^^^-^^^^^^^^^^^^^^^ - | | - | consider requiring `T` to be `Copy` +LL | const INPUT_ASSOC_2: T::AssocType5; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a `const` item should never be interior mutable --> $DIR/declare_interior_mutable_const.rs:16:9 @@ -71,40 +53,34 @@ error: a `const` item should never be interior mutable LL | const $name: $ty = $e; | ^^^^^^^^^^^^^^^^^^^^^^ ... -LL | declare_const!(ANOTHER_INPUT: T = Self::INPUT); //~ ERROR interior mutable - | ----------------------------------------------- in this macro invocation +LL | declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); //~ ERROR interior mutable + | ----------------------------------------------------------- in this macro invocation | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:60:5 + --> $DIR/declare_interior_mutable_const.rs:82:5 | -LL | const SELF_2: Self; - | ^^^^^^^^^^^^^^----^ - | | - | consider requiring `Self` to be `Copy` +LL | const ASSOC_2: Self::AssocType2 = AtomicUsize::new(15); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:81:5 + --> $DIR/declare_interior_mutable_const.rs:83:5 | -LL | const ASSOC_3: AtomicUsize = AtomicUsize::new(14); //~ ERROR interior mutable - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const WRAPPED_ASSOC_2: Wrapper = Wrapper(AtomicUsize::new(16)); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:84:5 + --> $DIR/declare_interior_mutable_const.rs:90:5 | -LL | const U_SELF: U = U::SELF_2; - | ^^^^^^^^^^^^^^-^^^^^^^^^^^^^ - | | - | consider requiring `U` to be `Copy` +LL | const ASSOC_5: AtomicUsize = AtomicUsize::new(14); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:87:5 + --> $DIR/declare_interior_mutable_const.rs:94:5 | -LL | const T_ASSOC: T::NonCopyType = T::ASSOC; - | ^^^^^^^^^^^^^^^--------------^^^^^^^^^^^^ - | | - | consider requiring `>::NonCopyType` to be `Copy` +LL | const U_ASSOC: U::AssocType5 = AtomicUsize::new(17); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 13 previous errors +error: aborting due to 11 previous errors -- cgit 1.4.1-3-g733a5 From 2fc9064921ce0afd2c07c5b576f95c7adf731541 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Thu, 17 Sep 2020 19:37:42 +1200 Subject: rewrite the test and fix a minor fp * rewrite the test for `declare_interior_mutable_const from scratch` * fix a minor false positive where `Cell<"const T>` gets linted twice --- clippy_lints/src/non_copy_const.rs | 24 ++-- tests/ui/declare_interior_mutable_const.rs | 159 ++++++++++++++++++------- tests/ui/declare_interior_mutable_const.stderr | 58 +++++---- 3 files changed, 166 insertions(+), 75 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 28c68a2b68c..bb44eeb6adc 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -9,8 +9,7 @@ use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, Node, Tr use rustc_infer::traits::specialization_graph; use rustc_lint::{LateContext, LateLintPass, Lint}; use rustc_middle::ty::adjustment::Adjust; -use rustc_middle::ty::fold::TypeFoldable as _; -use rustc_middle::ty::{AssocKind, Ty, TypeFlags}; +use rustc_middle::ty::{AssocKind, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{InnerSpan, Span, DUMMY_SP}; use rustc_typeck::hir_ty_to_ty; @@ -178,15 +177,18 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { if let Some(of_trait_def_id) = of_trait_ref.trait_def_id(); if let Some(of_assoc_item) = specialization_graph::Node::Trait(of_trait_def_id) .item(cx.tcx, impl_item.ident, AssocKind::Const, of_trait_def_id); - if cx.tcx - // Normalize assoc types because ones originated from generic params - // bounded other traits could have their bound at the trait defs; - // and, in that case, the definition is *not* generic. - .normalize_erasing_regions( - cx.tcx.param_env(of_trait_def_id), - cx.tcx.type_of(of_assoc_item.def_id), - ) - .has_type_flags(TypeFlags::HAS_PROJECTION | TypeFlags::HAS_TY_PARAM); + if cx + .tcx + .layout_of(cx.tcx.param_env(of_trait_def_id).and( + // Normalize assoc types because ones originated from generic params + // bounded other traits could have their bound at the trait defs; + // and, in that case, the definition is *not* generic. + cx.tcx.normalize_erasing_regions( + cx.tcx.param_env(of_trait_def_id), + cx.tcx.type_of(of_assoc_item.def_id), + ), + )) + .is_err(); then { let ty = hir_ty_to_ty(cx.tcx, hir_ty); let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); diff --git a/tests/ui/declare_interior_mutable_const.rs b/tests/ui/declare_interior_mutable_const.rs index 7471b360540..646d3ec8b47 100644 --- a/tests/ui/declare_interior_mutable_const.rs +++ b/tests/ui/declare_interior_mutable_const.rs @@ -34,64 +34,135 @@ static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING); #[allow(clippy::declare_interior_mutable_const)] const ONCE_INIT: Once = Once::new(); -struct Wrapper(T); - -trait Trait> { - type AssocType; - type AssocType2; - type AssocType3; - +// a constant whose type is a concrete type should be linted at the definition site. +trait ConcreteTypes { const ATOMIC: AtomicUsize; //~ ERROR interior mutable const INTEGER: u64; const STRING: String; - const SELF: Self; - const INPUT: T; - const INPUT_ASSOC: T::AssocType4; - const INPUT_ASSOC_2: T::AssocType5; //~ ERROR interior mutable - const ASSOC: Self::AssocType; - const ASSOC_2: Self::AssocType2; - const WRAPPED_ASSOC_2: Wrapper; - const WRAPPED_ASSOC_3: Wrapper; - - const AN_INPUT: T = Self::INPUT; - declare_const!(ANOTHER_INPUT: T = Self::INPUT); declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); //~ ERROR interior mutable } -trait Trait2 { - type AssocType4; - type AssocType5; +impl ConcreteTypes for u64 { + const ATOMIC: AtomicUsize = AtomicUsize::new(9); + const INTEGER: u64 = 10; + const STRING: String = String::new(); +} - const SELF_2: Self; - const ASSOC_4: Self::AssocType4; +// a helper trait used below +trait ConstDefault { + const DEFAULT: Self; } -impl> Trait for u64 { - type AssocType = u16; - type AssocType2 = AtomicUsize; - type AssocType3 = T; +// a constant whose type is a generic type should be linted at the implementation site. +trait GenericTypes { + const TO_REMAIN_GENERIC: T; + const TO_BE_CONCRETE: U; - const ATOMIC: AtomicUsize = AtomicUsize::new(9); - const INTEGER: u64 = 10; - const STRING: String = String::new(); - const SELF: Self = 11; - const INPUT: T = T::SELF_2; - const INPUT_ASSOC: T::AssocType4 = T::ASSOC_4; - const INPUT_ASSOC_2: T::AssocType5 = AtomicUsize::new(16); - const ASSOC: Self::AssocType = 13; - const ASSOC_2: Self::AssocType2 = AtomicUsize::new(15); //~ ERROR interior mutable - const WRAPPED_ASSOC_2: Wrapper = Wrapper(AtomicUsize::new(16)); //~ ERROR interior mutable - const WRAPPED_ASSOC_3: Wrapper = Wrapper(T::SELF_2); + const HAVING_DEFAULT: T = Self::TO_REMAIN_GENERIC; + declare_const!(IN_MACRO: T = Self::TO_REMAIN_GENERIC); +} + +impl GenericTypes for u64 { + const TO_REMAIN_GENERIC: T = T::DEFAULT; + const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11); //~ ERROR interior mutable +} + +// a helper type used below +struct Wrapper(T); + +// a constant whose type is an associated type should be linted at the implementation site, too. +trait AssocTypes { + type ToBeFrozen; + type ToBeUnfrozen; + type ToBeGenericParam; + + const TO_BE_FROZEN: Self::ToBeFrozen; + const TO_BE_UNFROZEN: Self::ToBeUnfrozen; + const WRAPPED_TO_BE_UNFROZEN: Wrapper; + // to ensure it can handle things when a generic type remains after normalization. + const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper; +} + +impl AssocTypes for Vec { + type ToBeFrozen = u16; + type ToBeUnfrozen = AtomicUsize; + type ToBeGenericParam = T; + + const TO_BE_FROZEN: Self::ToBeFrozen = 12; + const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13); //~ ERROR interior mutable + const WRAPPED_TO_BE_UNFROZEN: Wrapper = Wrapper(AtomicUsize::new(14)); //~ ERROR interior mutable + const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper = Wrapper(T::DEFAULT); } -struct Local(T, U); +// a helper trait used below +trait AssocTypesHelper { + type NotToBeBounded; + type ToBeBounded; -impl, U: Trait2> Local { - const ASSOC_5: AtomicUsize = AtomicUsize::new(14); //~ ERROR interior mutable + const NOT_TO_BE_BOUNDED: Self::NotToBeBounded; +} + +// a constant whose type is an assoc type originated from a generic param bounded at the definition +// site should be linted at there. +trait AssocTypesFromGenericParam +where + T: AssocTypesHelper, +{ + const NOT_BOUNDED: T::NotToBeBounded; + const BOUNDED: T::ToBeBounded; //~ ERROR interior mutable +} + +impl AssocTypesFromGenericParam for u64 +where + T: AssocTypesHelper, +{ + // an associated type could remain unknown in a trait impl. + const NOT_BOUNDED: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED; + const BOUNDED: T::ToBeBounded = AtomicUsize::new(15); +} + +trait SelfType { + const SELF: Self; +} + +impl SelfType for u64 { + const SELF: Self = 16; +} + +impl SelfType for AtomicUsize { + // this (interior mutable `Self` const) exists in `parking_lot`. + // `const_trait_impl` will replace it in the future, hopefully. + const SELF: Self = AtomicUsize::new(17); //~ ERROR interior mutable +} + +// Even though a constant contains a generic type, if it also have a interior mutable type, +// it should be linted at the definition site. +trait BothOfCellAndGeneric { + // this is a false negative in the current implementation. + const DIRECT: Cell; + const INDIRECT: Cell<*const T>; //~ ERROR interior mutable +} + +impl BothOfCellAndGeneric for u64 { + const DIRECT: Cell = Cell::new(T::DEFAULT); + const INDIRECT: Cell<*const T> = Cell::new(std::ptr::null()); +} + +struct Local(T); + +// a constant in an inherent impl are essentially the same as a normal const item +// except there can be a generic or associated type. +impl Local +where + T: ConstDefault + AssocTypesHelper, +{ + const ATOMIC: AtomicUsize = AtomicUsize::new(18); //~ ERROR interior mutable const COW: Cow<'static, str> = Cow::Borrowed("tuvwxy"); - const U_SELF: U = U::SELF_2; - const T_ASSOC: T::AssocType = T::ASSOC; - const U_ASSOC: U::AssocType5 = AtomicUsize::new(17); //~ ERROR interior mutable + + const GENERIC_TYPE: T = T::DEFAULT; + + const ASSOC_TYPE: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED; + const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); //~ ERROR interior mutable } fn main() {} diff --git a/tests/ui/declare_interior_mutable_const.stderr b/tests/ui/declare_interior_mutable_const.stderr index 0fcb726db46..0a0b818b8b7 100644 --- a/tests/ui/declare_interior_mutable_const.stderr +++ b/tests/ui/declare_interior_mutable_const.stderr @@ -36,17 +36,11 @@ LL | declare_const!(_ONCE: Once = Once::new()); //~ ERROR interior mutable = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:44:5 + --> $DIR/declare_interior_mutable_const.rs:39:5 | LL | const ATOMIC: AtomicUsize; //~ ERROR interior mutable | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:50:5 - | -LL | const INPUT_ASSOC_2: T::AssocType5; //~ ERROR interior mutable - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error: a `const` item should never be interior mutable --> $DIR/declare_interior_mutable_const.rs:16:9 | @@ -59,28 +53,52 @@ LL | declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); //~ ERROR i = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:82:5 + --> $DIR/declare_interior_mutable_const.rs:67:5 + | +LL | const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/declare_interior_mutable_const.rs:92:5 + | +LL | const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/declare_interior_mutable_const.rs:93:5 + | +LL | const WRAPPED_TO_BE_UNFROZEN: Wrapper = Wrapper(AtomicUsize::new(14)); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/declare_interior_mutable_const.rs:112:5 + | +LL | const BOUNDED: T::ToBeBounded; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/declare_interior_mutable_const.rs:135:5 | -LL | const ASSOC_2: Self::AssocType2 = AtomicUsize::new(15); //~ ERROR interior mutable - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const SELF: Self = AtomicUsize::new(17); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:83:5 + --> $DIR/declare_interior_mutable_const.rs:143:5 | -LL | const WRAPPED_ASSOC_2: Wrapper = Wrapper(AtomicUsize::new(16)); //~ ERROR interior mutable - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const INDIRECT: Cell<*const T>; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:90:5 + --> $DIR/declare_interior_mutable_const.rs:159:5 | -LL | const ASSOC_5: AtomicUsize = AtomicUsize::new(14); //~ ERROR interior mutable - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const ATOMIC: AtomicUsize = AtomicUsize::new(18); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:94:5 + --> $DIR/declare_interior_mutable_const.rs:165:5 | -LL | const U_ASSOC: U::AssocType5 = AtomicUsize::new(17); //~ ERROR interior mutable - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 11 previous errors +error: aborting due to 14 previous errors -- cgit 1.4.1-3-g733a5 From d5af360bb2de24235d2873e926d0b6f21135ae38 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Thu, 17 Sep 2020 21:14:14 +1200 Subject: add `WRAPPED_SELF: Option` in the test --- tests/ui/declare_interior_mutable_const.rs | 8 +++++++- tests/ui/declare_interior_mutable_const.stderr | 16 +++++++++++----- 2 files changed, 18 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/tests/ui/declare_interior_mutable_const.rs b/tests/ui/declare_interior_mutable_const.rs index 646d3ec8b47..3afcdca2f04 100644 --- a/tests/ui/declare_interior_mutable_const.rs +++ b/tests/ui/declare_interior_mutable_const.rs @@ -121,18 +121,24 @@ where const BOUNDED: T::ToBeBounded = AtomicUsize::new(15); } -trait SelfType { +// a constant whose type is `Self` should be linted at the implementation site as well. +// (`Option` requires `Sized` bound.) +trait SelfType: Sized { const SELF: Self; + // this was the one in the original issue (#5050). + const WRAPPED_SELF: Option; } impl SelfType for u64 { const SELF: Self = 16; + const WRAPPED_SELF: Option = Some(20); } impl SelfType for AtomicUsize { // this (interior mutable `Self` const) exists in `parking_lot`. // `const_trait_impl` will replace it in the future, hopefully. const SELF: Self = AtomicUsize::new(17); //~ ERROR interior mutable + const WRAPPED_SELF: Option = Some(AtomicUsize::new(21)); //~ ERROR interior mutable } // Even though a constant contains a generic type, if it also have a interior mutable type, diff --git a/tests/ui/declare_interior_mutable_const.stderr b/tests/ui/declare_interior_mutable_const.stderr index 0a0b818b8b7..5cb10be88d8 100644 --- a/tests/ui/declare_interior_mutable_const.stderr +++ b/tests/ui/declare_interior_mutable_const.stderr @@ -77,28 +77,34 @@ LL | const BOUNDED: T::ToBeBounded; //~ ERROR interior mutable | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:135:5 + --> $DIR/declare_interior_mutable_const.rs:140:5 | LL | const SELF: Self = AtomicUsize::new(17); //~ ERROR interior mutable | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:143:5 + --> $DIR/declare_interior_mutable_const.rs:141:5 + | +LL | const WRAPPED_SELF: Option = Some(AtomicUsize::new(21)); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/declare_interior_mutable_const.rs:149:5 | LL | const INDIRECT: Cell<*const T>; //~ ERROR interior mutable | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:159:5 + --> $DIR/declare_interior_mutable_const.rs:165:5 | LL | const ATOMIC: AtomicUsize = AtomicUsize::new(18); //~ ERROR interior mutable | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:165:5 + --> $DIR/declare_interior_mutable_const.rs:171:5 | LL | const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); //~ ERROR interior mutable | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 14 previous errors +error: aborting due to 15 previous errors -- cgit 1.4.1-3-g733a5 From 4117ae1175430087441c9b34145f148d49c2f08c Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Mon, 21 Sep 2020 15:32:26 +0200 Subject: Split redundant_pattern_matching tests This is to avoid the 200 lines stderr file limit --- tests/ui/redundant_pattern_matching.fixed | 71 +-------- tests/ui/redundant_pattern_matching.rs | 86 +---------- tests/ui/redundant_pattern_matching.stderr | 174 +++------------------- tests/ui/redundant_pattern_matching_option.fixed | 85 +++++++++++ tests/ui/redundant_pattern_matching_option.rs | 100 +++++++++++++ tests/ui/redundant_pattern_matching_option.stderr | 134 +++++++++++++++++ 6 files changed, 351 insertions(+), 299 deletions(-) create mode 100644 tests/ui/redundant_pattern_matching_option.fixed create mode 100644 tests/ui/redundant_pattern_matching_option.rs create mode 100644 tests/ui/redundant_pattern_matching_option.stderr (limited to 'tests') diff --git a/tests/ui/redundant_pattern_matching.fixed b/tests/ui/redundant_pattern_matching.fixed index 8084fdefdc2..17d908336d5 100644 --- a/tests/ui/redundant_pattern_matching.fixed +++ b/tests/ui/redundant_pattern_matching.fixed @@ -18,39 +18,14 @@ fn main() { if Err::(42).is_err() {} - if None::<()>.is_none() {} - - if Some(42).is_some() {} - - if Some(42).is_some() { - foo(); - } else { - bar(); - } - - while Some(42).is_some() {} - - while Some(42).is_none() {} - - while None::<()>.is_none() {} - while Ok::(10).is_ok() {} while Ok::(10).is_err() {} - let mut v = vec![1, 2, 3]; - while v.pop().is_some() { - foo(); - } - if Ok::(42).is_ok() {} if Err::(42).is_err() {} - if None::.is_none() {} - - if Some(42).is_some() {} - if let Ok(x) = Ok::(42) { println!("{}", x); } @@ -63,48 +38,24 @@ fn main() { Err::(42).is_ok(); - Some(42).is_some(); - - None::<()>.is_none(); - - let _ = None::<()>.is_none(); - let _ = if Ok::(4).is_ok() { true } else { false }; - let opt = Some(false); - let x = if opt.is_some() { true } else { false }; - takes_bool(x); - issue5504(); issue6067(); - let _ = if gen_opt().is_some() { + let _ = if gen_res().is_ok() { 1 - } else if gen_opt().is_none() { - 2 - } else if gen_res().is_ok() { - 3 } else if gen_res().is_err() { - 4 + 2 } else { - 5 + 3 }; } -fn gen_opt() -> Option<()> { - None -} - fn gen_res() -> Result<(), ()> { Ok(()) } -fn takes_bool(_: bool) {} - -fn foo() {} - -fn bar() {} - macro_rules! m { () => { Some(42u32) @@ -129,30 +80,18 @@ fn issue5504() { } // Methods that are unstable const should not be suggested within a const context, see issue #5697. -// However, in Rust 1.48.0 the methods `is_ok` and `is_err` of `Result`, and `is_some` and `is_none` -// of `Option` were stabilized as const, so the following should be linted. +// However, in Rust 1.48.0 the methods `is_ok` and `is_err` of `Result` were stabilized as const, +// so the following should be linted. const fn issue6067() { if Ok::(42).is_ok() {} if Err::(42).is_err() {} - if Some(42).is_some() {} - - if None::<()>.is_none() {} - while Ok::(10).is_ok() {} while Ok::(10).is_err() {} - while Some(42).is_some() {} - - while None::<()>.is_none() {} - Ok::(42).is_ok(); Err::(42).is_err(); - - Some(42).is_some(); - - None::<()>.is_none(); } diff --git a/tests/ui/redundant_pattern_matching.rs b/tests/ui/redundant_pattern_matching.rs index 48a32cb1c7b..d57fbb14ae4 100644 --- a/tests/ui/redundant_pattern_matching.rs +++ b/tests/ui/redundant_pattern_matching.rs @@ -18,39 +18,14 @@ fn main() { if let Err(_) = Err::(42) {} - if let None = None::<()> {} - - if let Some(_) = Some(42) {} - - if let Some(_) = Some(42) { - foo(); - } else { - bar(); - } - - while let Some(_) = Some(42) {} - - while let None = Some(42) {} - - while let None = None::<()> {} - while let Ok(_) = Ok::(10) {} while let Err(_) = Ok::(10) {} - let mut v = vec![1, 2, 3]; - while let Some(_) = v.pop() { - foo(); - } - if Ok::(42).is_ok() {} if Err::(42).is_err() {} - if None::.is_none() {} - - if Some(42).is_some() {} - if let Ok(x) = Ok::(42) { println!("{}", x); } @@ -75,57 +50,24 @@ fn main() { Err(_) => false, }; - match Some(42) { - Some(_) => true, - None => false, - }; - - match None::<()> { - Some(_) => false, - None => true, - }; - - let _ = match None::<()> { - Some(_) => false, - None => true, - }; - let _ = if let Ok(_) = Ok::(4) { true } else { false }; - let opt = Some(false); - let x = if let Some(_) = opt { true } else { false }; - takes_bool(x); - issue5504(); issue6067(); - let _ = if let Some(_) = gen_opt() { + let _ = if let Ok(_) = gen_res() { 1 - } else if let None = gen_opt() { - 2 - } else if let Ok(_) = gen_res() { - 3 } else if let Err(_) = gen_res() { - 4 + 2 } else { - 5 + 3 }; } -fn gen_opt() -> Option<()> { - None -} - fn gen_res() -> Result<(), ()> { Ok(()) } -fn takes_bool(_: bool) {} - -fn foo() {} - -fn bar() {} - macro_rules! m { () => { Some(42u32) @@ -150,25 +92,17 @@ fn issue5504() { } // Methods that are unstable const should not be suggested within a const context, see issue #5697. -// However, in Rust 1.48.0 the methods `is_ok` and `is_err` of `Result`, and `is_some` and `is_none` -// of `Option` were stabilized as const, so the following should be linted. +// However, in Rust 1.48.0 the methods `is_ok` and `is_err` of `Result` were stabilized as const, +// so the following should be linted. const fn issue6067() { if let Ok(_) = Ok::(42) {} if let Err(_) = Err::(42) {} - if let Some(_) = Some(42) {} - - if let None = None::<()> {} - while let Ok(_) = Ok::(10) {} while let Err(_) = Ok::(10) {} - while let Some(_) = Some(42) {} - - while let None = None::<()> {} - match Ok::(42) { Ok(_) => true, Err(_) => false, @@ -178,14 +112,4 @@ const fn issue6067() { Ok(_) => false, Err(_) => true, }; - - match Some(42) { - Some(_) => true, - None => false, - }; - - match None::<()> { - Some(_) => false, - None => true, - }; } diff --git a/tests/ui/redundant_pattern_matching.stderr b/tests/ui/redundant_pattern_matching.stderr index 17185217e89..955900f3e6c 100644 --- a/tests/ui/redundant_pattern_matching.stderr +++ b/tests/ui/redundant_pattern_matching.stderr @@ -18,62 +18,20 @@ error: redundant pattern matching, consider using `is_err()` LL | if let Err(_) = Err::(42) {} | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` -error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:21: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.rs:23: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.rs:25: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.rs:31: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.rs:33: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.rs:35:15 - | -LL | while let None = None::<()> {} - | ----------^^^^------------- help: try this: `while None::<()>.is_none()` - error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:37:15 + --> $DIR/redundant_pattern_matching.rs:21:15 | LL | while let Ok(_) = Ok::(10) {} | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:39:15 + --> $DIR/redundant_pattern_matching.rs:23:15 | LL | while let Err(_) = Ok::(10) {} | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` -error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:42:15 - | -LL | while let Some(_) = v.pop() { - | ----------^^^^^^^---------- help: try this: `while v.pop().is_some()` - error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:58:5 + --> $DIR/redundant_pattern_matching.rs:33:5 | LL | / match Ok::(42) { LL | | Ok(_) => true, @@ -82,7 +40,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:63:5 + --> $DIR/redundant_pattern_matching.rs:38:5 | LL | / match Ok::(42) { LL | | Ok(_) => false, @@ -91,7 +49,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_err()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:68:5 + --> $DIR/redundant_pattern_matching.rs:43:5 | LL | / match Err::(42) { LL | | Ok(_) => false, @@ -100,7 +58,7 @@ LL | | }; | |_____^ help: try this: `Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:73:5 + --> $DIR/redundant_pattern_matching.rs:48:5 | LL | / match Err::(42) { LL | | Ok(_) => true, @@ -108,144 +66,74 @@ LL | | Err(_) => false, LL | | }; | |_____^ help: try this: `Err::(42).is_ok()` -error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:78:5 - | -LL | / match Some(42) { -LL | | Some(_) => true, -LL | | None => false, -LL | | }; - | |_____^ help: try this: `Some(42).is_some()` - -error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:83:5 - | -LL | / match None::<()> { -LL | | Some(_) => false, -LL | | None => true, -LL | | }; - | |_____^ help: try this: `None::<()>.is_none()` - -error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:88:13 - | -LL | let _ = match None::<()> { - | _____________^ -LL | | Some(_) => false, -LL | | None => true, -LL | | }; - | |_____^ help: try this: `None::<()>.is_none()` - error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:93:20 + --> $DIR/redundant_pattern_matching.rs:53:20 | LL | let _ = if let Ok(_) = Ok::(4) { true } else { false }; | -------^^^^^--------------------- help: try this: `if Ok::(4).is_ok()` -error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:96:20 - | -LL | let x = 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.rs:102: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.rs:104:19 - | -LL | } else if let None = gen_opt() { - | -------^^^^------------ help: try this: `if gen_opt().is_none()` - error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:106:19 + --> $DIR/redundant_pattern_matching.rs:58:20 | -LL | } else if let Ok(_) = gen_res() { - | -------^^^^^------------ help: try this: `if gen_res().is_ok()` +LL | let _ = if let Ok(_) = gen_res() { + | -------^^^^^------------ help: try this: `if gen_res().is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:108:19 + --> $DIR/redundant_pattern_matching.rs:60:19 | LL | } else if let Err(_) = gen_res() { | -------^^^^^^------------ help: try this: `if gen_res().is_err()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:141:19 + --> $DIR/redundant_pattern_matching.rs:83:19 | LL | while let Some(_) = r#try!(result_opt()) {} | ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:142:16 + --> $DIR/redundant_pattern_matching.rs:84:16 | LL | if let Some(_) = r#try!(result_opt()) {} | -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:148:12 + --> $DIR/redundant_pattern_matching.rs:90:12 | LL | if let Some(_) = m!() {} | -------^^^^^^^------- help: try this: `if m!().is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:149:15 + --> $DIR/redundant_pattern_matching.rs:91:15 | LL | while let Some(_) = m!() {} | ----------^^^^^^^------- help: try this: `while m!().is_some()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:156:12 + --> $DIR/redundant_pattern_matching.rs:98:12 | LL | if let Ok(_) = Ok::(42) {} | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:158:12 + --> $DIR/redundant_pattern_matching.rs:100:12 | LL | if let Err(_) = Err::(42) {} | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` -error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:160: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.rs:162:12 - | -LL | if let None = None::<()> {} - | -------^^^^------------- help: try this: `if None::<()>.is_none()` - error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:164:15 + --> $DIR/redundant_pattern_matching.rs:102:15 | LL | while let Ok(_) = Ok::(10) {} | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:166:15 + --> $DIR/redundant_pattern_matching.rs:104:15 | LL | while let Err(_) = Ok::(10) {} | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` -error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:168: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.rs:170:15 - | -LL | while let None = None::<()> {} - | ----------^^^^------------- help: try this: `while None::<()>.is_none()` - error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:172:5 + --> $DIR/redundant_pattern_matching.rs:106:5 | LL | / match Ok::(42) { LL | | Ok(_) => true, @@ -254,7 +142,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:177:5 + --> $DIR/redundant_pattern_matching.rs:111:5 | LL | / match Err::(42) { LL | | Ok(_) => false, @@ -262,23 +150,5 @@ LL | | Err(_) => true, LL | | }; | |_____^ help: try this: `Err::(42).is_err()` -error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:182:5 - | -LL | / match Some(42) { -LL | | Some(_) => true, -LL | | None => false, -LL | | }; - | |_____^ help: try this: `Some(42).is_some()` - -error: redundant pattern matching, consider using `is_none()` - --> $DIR/redundant_pattern_matching.rs:187:5 - | -LL | / match None::<()> { -LL | | Some(_) => false, -LL | | None => true, -LL | | }; - | |_____^ help: try this: `None::<()>.is_none()` - -error: aborting due to 41 previous errors +error: aborting due to 22 previous errors diff --git a/tests/ui/redundant_pattern_matching_option.fixed b/tests/ui/redundant_pattern_matching_option.fixed new file mode 100644 index 00000000000..499b975b2bb --- /dev/null +++ b/tests/ui/redundant_pattern_matching_option.fixed @@ -0,0 +1,85 @@ +// run-rustfix + +#![warn(clippy::all)] +#![warn(clippy::redundant_pattern_matching)] +#![allow( + clippy::unit_arg, + unused_must_use, + clippy::needless_bool, + clippy::match_like_matches_macro, + deprecated +)] + +fn main() { + if None::<()>.is_none() {} + + if Some(42).is_some() {} + + if Some(42).is_some() { + foo(); + } else { + bar(); + } + + while Some(42).is_some() {} + + while Some(42).is_none() {} + + while None::<()>.is_none() {} + + let mut v = vec![1, 2, 3]; + while v.pop().is_some() { + foo(); + } + + if None::.is_none() {} + + if Some(42).is_some() {} + + Some(42).is_some(); + + None::<()>.is_none(); + + let _ = None::<()>.is_none(); + + let opt = Some(false); + let x = if opt.is_some() { true } else { false }; + takes_bool(x); + + issue6067(); + + let _ = if gen_opt().is_some() { + 1 + } else if gen_opt().is_none() { + 2 + } else { + 3 + }; +} + +fn gen_opt() -> Option<()> { + None +} + +fn takes_bool(_: bool) {} + +fn foo() {} + +fn bar() {} + +// Methods that are unstable const should not be suggested within a const context, see issue #5697. +// However, in Rust 1.48.0 the methods `is_some` and `is_none` of `Option` were stabilized as const, +// so the following should be linted. +const fn issue6067() { + if Some(42).is_some() {} + + if None::<()>.is_none() {} + + while Some(42).is_some() {} + + while None::<()>.is_none() {} + + Some(42).is_some(); + + None::<()>.is_none(); +} diff --git a/tests/ui/redundant_pattern_matching_option.rs b/tests/ui/redundant_pattern_matching_option.rs new file mode 100644 index 00000000000..2a98435e790 --- /dev/null +++ b/tests/ui/redundant_pattern_matching_option.rs @@ -0,0 +1,100 @@ +// run-rustfix + +#![warn(clippy::all)] +#![warn(clippy::redundant_pattern_matching)] +#![allow( + clippy::unit_arg, + unused_must_use, + clippy::needless_bool, + clippy::match_like_matches_macro, + deprecated +)] + +fn main() { + if let None = None::<()> {} + + if let Some(_) = Some(42) {} + + if let Some(_) = Some(42) { + foo(); + } else { + bar(); + } + + while let Some(_) = Some(42) {} + + while let None = Some(42) {} + + while let None = None::<()> {} + + let mut v = vec![1, 2, 3]; + while let Some(_) = v.pop() { + foo(); + } + + if None::.is_none() {} + + if Some(42).is_some() {} + + match Some(42) { + Some(_) => true, + None => false, + }; + + match None::<()> { + Some(_) => false, + None => true, + }; + + let _ = match None::<()> { + Some(_) => false, + None => true, + }; + + let opt = Some(false); + let x = if let Some(_) = opt { true } else { false }; + takes_bool(x); + + issue6067(); + + let _ = if let Some(_) = gen_opt() { + 1 + } else if let None = gen_opt() { + 2 + } else { + 3 + }; +} + +fn gen_opt() -> Option<()> { + None +} + +fn takes_bool(_: bool) {} + +fn foo() {} + +fn bar() {} + +// Methods that are unstable const should not be suggested within a const context, see issue #5697. +// However, in Rust 1.48.0 the methods `is_some` and `is_none` of `Option` were stabilized as const, +// so the following should be linted. +const fn issue6067() { + if let Some(_) = Some(42) {} + + if let None = None::<()> {} + + while let Some(_) = Some(42) {} + + while let None = None::<()> {} + + match Some(42) { + Some(_) => true, + None => false, + }; + + match None::<()> { + Some(_) => false, + None => true, + }; +} diff --git a/tests/ui/redundant_pattern_matching_option.stderr b/tests/ui/redundant_pattern_matching_option.stderr new file mode 100644 index 00000000000..eebb3448491 --- /dev/null +++ b/tests/ui/redundant_pattern_matching_option.stderr @@ -0,0 +1,134 @@ +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching_option.rs:14:12 + | +LL | if let None = None::<()> {} + | -------^^^^------------- help: try this: `if None::<()>.is_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: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: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: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: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: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: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:39:5 + | +LL | / match Some(42) { +LL | | Some(_) => true, +LL | | None => false, +LL | | }; + | |_____^ help: try this: `Some(42).is_some()` + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching_option.rs:44:5 + | +LL | / match None::<()> { +LL | | Some(_) => false, +LL | | None => true, +LL | | }; + | |_____^ help: try this: `None::<()>.is_none()` + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching_option.rs:49:13 + | +LL | let _ = match None::<()> { + | _____________^ +LL | | Some(_) => false, +LL | | None => true, +LL | | }; + | |_____^ help: try this: `None::<()>.is_none()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching_option.rs:55:20 + | +LL | let x = 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:60: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:62: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:83: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:85: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:87: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:89: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:91:5 + | +LL | / match Some(42) { +LL | | Some(_) => true, +LL | | None => false, +LL | | }; + | |_____^ help: try this: `Some(42).is_some()` + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching_option.rs:96:5 + | +LL | / match None::<()> { +LL | | Some(_) => false, +LL | | None => true, +LL | | }; + | |_____^ help: try this: `None::<()>.is_none()` + +error: aborting due to 19 previous errors + -- cgit 1.4.1-3-g733a5 From d4f158fa5cd5f4ae1cc690b37b0a8850cb013b69 Mon Sep 17 00:00:00 2001 From: Aleksei Latyshev Date: Sun, 20 Sep 2020 12:38:23 +0300 Subject: Forbid redundant_pattern_matching triggering in macros - remove ice-2636 test --- clippy_lints/src/matches.rs | 2 +- tests/ui/crashes/ice-2636.rs | 22 ---------------------- tests/ui/crashes/ice-2636.stderr | 17 ----------------- tests/ui/redundant_pattern_matching.fixed | 12 ++++++++++++ tests/ui/redundant_pattern_matching.rs | 12 ++++++++++++ tests/ui/redundant_pattern_matching.stderr | 24 ++++++++++++------------ 6 files changed, 37 insertions(+), 52 deletions(-) delete mode 100644 tests/ui/crashes/ice-2636.rs delete mode 100644 tests/ui/crashes/ice-2636.stderr (limited to 'tests') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 6f47687c410..b1a4e06d4c3 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -502,7 +502,7 @@ impl_lint_pass!(Matches => [ impl<'tcx> LateLintPass<'tcx> for Matches { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if in_external_macro(cx.sess(), expr.span) { + if in_external_macro(cx.sess(), expr.span) || in_macro(expr.span) { return; } diff --git a/tests/ui/crashes/ice-2636.rs b/tests/ui/crashes/ice-2636.rs deleted file mode 100644 index e0b58157590..00000000000 --- a/tests/ui/crashes/ice-2636.rs +++ /dev/null @@ -1,22 +0,0 @@ -#![allow(dead_code)] - -enum Foo { - A, - B, - C, -} - -macro_rules! test_hash { - ($foo:expr, $($t:ident => $ord:expr),+ ) => { - use self::Foo::*; - match $foo { - $ ( & $t => $ord, - )* - }; - }; -} - -fn main() { - let a = Foo::A; - test_hash!(&a, A => 0, B => 1, C => 2); -} diff --git a/tests/ui/crashes/ice-2636.stderr b/tests/ui/crashes/ice-2636.stderr deleted file mode 100644 index 53799b4fbf1..00000000000 --- a/tests/ui/crashes/ice-2636.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error: you don't need to add `&` to both the expression and the patterns - --> $DIR/ice-2636.rs:12:9 - | -LL | / match $foo { -LL | | $ ( & $t => $ord, -LL | | )* -LL | | }; - | |_________^ -... -LL | test_hash!(&a, A => 0, B => 1, C => 2); - | --------------------------------------- in this macro invocation - | - = note: `-D clippy::match-ref-pats` implied by `-D warnings` - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to previous error - diff --git a/tests/ui/redundant_pattern_matching.fixed b/tests/ui/redundant_pattern_matching.fixed index 17d908336d5..fe8f62503b7 100644 --- a/tests/ui/redundant_pattern_matching.fixed +++ b/tests/ui/redundant_pattern_matching.fixed @@ -42,6 +42,7 @@ fn main() { issue5504(); issue6067(); + issue6065(); let _ = if gen_res().is_ok() { 1 @@ -79,6 +80,17 @@ fn issue5504() { while m!().is_some() {} } +fn issue6065() { + macro_rules! if_let_in_macro { + ($pat:pat, $x:expr) => { + if let Some($pat) = $x {} + }; + } + + // shouldn't be linted + if_let_in_macro!(_, Some(42)); +} + // Methods that are unstable const should not be suggested within a const context, see issue #5697. // However, in Rust 1.48.0 the methods `is_ok` and `is_err` of `Result` were stabilized as const, // so the following should be linted. diff --git a/tests/ui/redundant_pattern_matching.rs b/tests/ui/redundant_pattern_matching.rs index d57fbb14ae4..09426a6e590 100644 --- a/tests/ui/redundant_pattern_matching.rs +++ b/tests/ui/redundant_pattern_matching.rs @@ -54,6 +54,7 @@ fn main() { issue5504(); issue6067(); + issue6065(); let _ = if let Ok(_) = gen_res() { 1 @@ -91,6 +92,17 @@ fn issue5504() { while let Some(_) = m!() {} } +fn issue6065() { + macro_rules! if_let_in_macro { + ($pat:pat, $x:expr) => { + if let Some($pat) = $x {} + }; + } + + // shouldn't be linted + if_let_in_macro!(_, Some(42)); +} + // Methods that are unstable const should not be suggested within a const context, see issue #5697. // However, in Rust 1.48.0 the methods `is_ok` and `is_err` of `Result` were stabilized as const, // so the following should be linted. diff --git a/tests/ui/redundant_pattern_matching.stderr b/tests/ui/redundant_pattern_matching.stderr index 955900f3e6c..3473ceea00e 100644 --- a/tests/ui/redundant_pattern_matching.stderr +++ b/tests/ui/redundant_pattern_matching.stderr @@ -73,67 +73,67 @@ LL | let _ = if let Ok(_) = Ok::(4) { true } else { false }; | -------^^^^^--------------------- help: try this: `if Ok::(4).is_ok()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:58:20 + --> $DIR/redundant_pattern_matching.rs:59:20 | LL | let _ = if let Ok(_) = gen_res() { | -------^^^^^------------ help: try this: `if gen_res().is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:60:19 + --> $DIR/redundant_pattern_matching.rs:61:19 | LL | } else if let Err(_) = gen_res() { | -------^^^^^^------------ help: try this: `if gen_res().is_err()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:83:19 + --> $DIR/redundant_pattern_matching.rs:84:19 | LL | while let Some(_) = r#try!(result_opt()) {} | ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:84:16 + --> $DIR/redundant_pattern_matching.rs:85:16 | LL | if let Some(_) = r#try!(result_opt()) {} | -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:90:12 + --> $DIR/redundant_pattern_matching.rs:91:12 | LL | if let Some(_) = m!() {} | -------^^^^^^^------- help: try this: `if m!().is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:91:15 + --> $DIR/redundant_pattern_matching.rs:92:15 | LL | while let Some(_) = m!() {} | ----------^^^^^^^------- help: try this: `while m!().is_some()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:98:12 + --> $DIR/redundant_pattern_matching.rs:110:12 | LL | if let Ok(_) = Ok::(42) {} | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:100:12 + --> $DIR/redundant_pattern_matching.rs:112:12 | LL | if let Err(_) = Err::(42) {} | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:102:15 + --> $DIR/redundant_pattern_matching.rs:114:15 | LL | while let Ok(_) = Ok::(10) {} | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:104:15 + --> $DIR/redundant_pattern_matching.rs:116:15 | LL | while let Err(_) = Ok::(10) {} | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:106:5 + --> $DIR/redundant_pattern_matching.rs:118:5 | LL | / match Ok::(42) { LL | | Ok(_) => true, @@ -142,7 +142,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:111:5 + --> $DIR/redundant_pattern_matching.rs:123:5 | LL | / match Err::(42) { LL | | Ok(_) => false, -- cgit 1.4.1-3-g733a5 From 5e393c7747d081c45414060f81016e9ea3cb961f Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Wed, 10 Jun 2020 14:05:32 +1200 Subject: Fix a FP in `explicit_counter_loop` Fix a false positive in `explicit_counter_loop` where the loop counter is used after incremented, adjust the test so that counters are incremented at the end of the loop and add the test for this false positive. --- clippy_lints/src/loops.rs | 6 +++++- tests/ui/explicit_counter_loop.rs | 29 +++++++++++++++++++++-------- 2 files changed, 26 insertions(+), 9 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 3410341a1e3..7f998c63f49 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2134,7 +2134,7 @@ enum VarState { DontWarn, } -/// Scan a for loop for variables that are incremented exactly once. +/// Scan a for loop for variables that are incremented exactly once and not used after that. struct IncrementVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, // context reference states: FxHashMap, // incremented variables @@ -2154,6 +2154,10 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { if let Some(def_id) = var_def_id(self.cx, expr) { if let Some(parent) = get_parent_expr(self.cx, expr) { let state = self.states.entry(def_id).or_insert(VarState::Initial); + if *state == VarState::IncrOnce { + *state = VarState::DontWarn; + return; + } match parent.kind { ExprKind::AssignOp(op, ref lhs, ref rhs) => { diff --git a/tests/ui/explicit_counter_loop.rs b/tests/ui/explicit_counter_loop.rs index aa6ef162fe4..81d8221bd13 100644 --- a/tests/ui/explicit_counter_loop.rs +++ b/tests/ui/explicit_counter_loop.rs @@ -38,54 +38,54 @@ mod issue_1219 { let text = "banana"; let mut count = 0; for ch in text.chars() { + println!("{}", count); if ch == 'a' { continue; } count += 1; - println!("{}", count); } // should not trigger the lint because the count is conditional let text = "banana"; let mut count = 0; for ch in text.chars() { + println!("{}", count); if ch == 'a' { count += 1; } - println!("{}", count); } // should trigger the lint because the count is not conditional let text = "banana"; let mut count = 0; for ch in text.chars() { + println!("{}", count); count += 1; if ch == 'a' { continue; } - println!("{}", count); } // should trigger the lint because the count is not conditional let text = "banana"; let mut count = 0; for ch in text.chars() { + println!("{}", count); count += 1; for i in 0..2 { let _ = 123; } - println!("{}", count); } // should not trigger the lint because the count is incremented multiple times let text = "banana"; let mut count = 0; for ch in text.chars() { + println!("{}", count); count += 1; for i in 0..2 { count += 1; } - println!("{}", count); } } } @@ -96,30 +96,30 @@ mod issue_3308 { let mut skips = 0; let erasures = vec![]; for i in 0..10 { + println!("{}", skips); while erasures.contains(&(i + skips)) { skips += 1; } - println!("{}", skips); } // should not trigger the lint because the count is incremented multiple times let mut skips = 0; for i in 0..10 { + println!("{}", skips); let mut j = 0; while j < 5 { skips += 1; j += 1; } - println!("{}", skips); } // should not trigger the lint because the count is incremented multiple times let mut skips = 0; for i in 0..10 { + println!("{}", skips); for j in 0..5 { skips += 1; } - println!("{}", skips); } } } @@ -145,3 +145,16 @@ mod issue_4732 { let _closure = || println!("index: {}", index); } } + +mod issue_4677 { + pub fn test() { + let slice = &[1, 2, 3]; + + // should not trigger the lint because the count is used after incremented + let mut count = 0; + for _i in slice { + count += 1; + println!("{}", count); + } + } +} -- cgit 1.4.1-3-g733a5 From 3e294b22a43be349262405715cf4885296c284ba Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 23 Sep 2020 00:34:56 +0200 Subject: Revert "or_fn_call: ignore nullary associated const fns" This reverts commit 7a66e6502dc3c7085b3f4078c01d4957d96175ed. --- clippy_lints/src/utils/mod.rs | 2 +- tests/ui/or_fun_call.fixed | 17 ++++++----------- tests/ui/or_fun_call.rs | 17 ++++++----------- tests/ui/or_fun_call.stderr | 20 ++++++++++++++++---- 4 files changed, 29 insertions(+), 27 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index ea52741b7cc..3a005e2513e 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -893,7 +893,7 @@ pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_ return match res { def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true, // FIXME: check the constness of the arguments, see https://github.com/rust-lang/rust-clippy/pull/5682#issuecomment-638681210 - def::Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) if has_no_arguments(cx, def_id) => { + def::Res::Def(DefKind::Fn, def_id) if has_no_arguments(cx, def_id) => { const_eval::is_const_fn(cx.tcx, def_id) }, def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id), diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 5fb568672d3..67faa8bd4a0 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -58,6 +58,12 @@ fn or_fun_call() { let without_default = Some(Foo); without_default.unwrap_or_else(Foo::new); + let mut map = HashMap::::new(); + map.entry(42).or_insert_with(String::new); + + let mut btree = BTreeMap::::new(); + btree.entry(42).or_insert_with(String::new); + let stringy = Some(String::from("")); let _ = stringy.unwrap_or_else(|| "".to_owned()); @@ -116,17 +122,6 @@ pub fn skip_const_fn_with_no_args() { Some(42) } let _ = None.or(foo()); - - // See issue #5693. - let mut map = std::collections::HashMap::new(); - map.insert(1, vec![1]); - map.entry(1).or_insert(vec![]); - - let mut map = HashMap::::new(); - map.entry(42).or_insert(String::new()); - - let mut btree = BTreeMap::::new(); - btree.entry(42).or_insert(String::new()); } fn main() {} diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 737b0f7e55b..9867e2eedcf 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -58,6 +58,12 @@ fn or_fun_call() { let without_default = Some(Foo); without_default.unwrap_or(Foo::new()); + let mut map = HashMap::::new(); + map.entry(42).or_insert(String::new()); + + let mut btree = BTreeMap::::new(); + btree.entry(42).or_insert(String::new()); + let stringy = Some(String::from("")); let _ = stringy.unwrap_or("".to_owned()); @@ -116,17 +122,6 @@ pub fn skip_const_fn_with_no_args() { Some(42) } let _ = None.or(foo()); - - // See issue #5693. - let mut map = std::collections::HashMap::new(); - map.insert(1, vec![1]); - map.entry(1).or_insert(vec![]); - - let mut map = HashMap::::new(); - map.entry(42).or_insert(String::new()); - - let mut btree = BTreeMap::::new(); - btree.entry(42).or_insert(String::new()); } fn main() {} diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index b8a436993f3..bc5978b538f 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -60,23 +60,35 @@ error: use of `unwrap_or` followed by a function call LL | without_default.unwrap_or(Foo::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(Foo::new)` +error: use of `or_insert` followed by a function call + --> $DIR/or_fun_call.rs:62:19 + | +LL | map.entry(42).or_insert(String::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)` + +error: use of `or_insert` followed by a function call + --> $DIR/or_fun_call.rs:65:21 + | +LL | btree.entry(42).or_insert(String::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)` + error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:62:21 + --> $DIR/or_fun_call.rs:68:21 | LL | let _ = stringy.unwrap_or("".to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "".to_owned())` error: use of `or` followed by a function call - --> $DIR/or_fun_call.rs:87:35 + --> $DIR/or_fun_call.rs:93:35 | LL | let _ = Some("a".to_string()).or(Some("b".to_string())); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some("b".to_string()))` error: use of `or` followed by a function call - --> $DIR/or_fun_call.rs:91:10 + --> $DIR/or_fun_call.rs:97:10 | LL | .or(Some(Bar(b, Duration::from_secs(2)))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some(Bar(b, Duration::from_secs(2))))` -error: aborting due to 13 previous errors +error: aborting due to 15 previous errors -- cgit 1.4.1-3-g733a5 From ce83d8d4d1b28e73888a616d3ffbf19c6a620588 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 23 Sep 2020 00:39:00 +0200 Subject: Revert "Avoid or_fun_call for const_fn with no args" This reverts commit 5d66bd7bb3fd701d70ec11217e3f89fabe5cb0a7. --- clippy_lints/src/utils/mod.rs | 9 --------- tests/ui/or_fun_call.fixed | 8 -------- tests/ui/or_fun_call.rs | 8 -------- 3 files changed, 25 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 3a005e2513e..92cb31fcf85 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -46,7 +46,6 @@ use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable}; -use rustc_mir::const_eval; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::original_sp; use rustc_span::symbol::{self, kw, Symbol}; @@ -883,19 +882,11 @@ pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { /// Checks if an expression is constructing a tuple-like enum variant or struct pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - fn has_no_arguments(cx: &LateContext<'_>, def_id: DefId) -> bool { - cx.tcx.fn_sig(def_id).skip_binder().inputs().is_empty() - } - if let ExprKind::Call(ref fun, _) = expr.kind { if let ExprKind::Path(ref qp) = fun.kind { let res = cx.qpath_res(qp, fun.hir_id); return match res { def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true, - // FIXME: check the constness of the arguments, see https://github.com/rust-lang/rust-clippy/pull/5682#issuecomment-638681210 - def::Res::Def(DefKind::Fn, def_id) if has_no_arguments(cx, def_id) => { - const_eval::is_const_fn(cx.tcx, def_id) - }, def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id), _ => false, }; diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 67faa8bd4a0..2045ffdb5f0 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -116,12 +116,4 @@ fn f() -> Option<()> { Some(()) } -// Issue 5886 - const fn (with no arguments) -pub fn skip_const_fn_with_no_args() { - const fn foo() -> Option { - Some(42) - } - let _ = None.or(foo()); -} - fn main() {} diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 9867e2eedcf..522f31b72d0 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -116,12 +116,4 @@ fn f() -> Option<()> { Some(()) } -// Issue 5886 - const fn (with no arguments) -pub fn skip_const_fn_with_no_args() { - const fn foo() -> Option { - Some(42) - } - let _ = None.or(foo()); -} - fn main() {} -- cgit 1.4.1-3-g733a5 From 9365660a2fc57210996df733efe468019c671b2f Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 23 Sep 2020 23:33:50 +0200 Subject: unnecessary sort by: avoid dereferencing closure param --- clippy_lints/src/unnecessary_sort_by.rs | 58 ++++++++++++--------------------- tests/ui/unnecessary_sort_by.fixed | 30 ++++++++--------- tests/ui/unnecessary_sort_by.rs | 10 +++--- tests/ui/unnecessary_sort_by.stderr | 52 ++++++++++++++++++++++------- 4 files changed, 81 insertions(+), 69 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/unnecessary_sort_by.rs b/clippy_lints/src/unnecessary_sort_by.rs index 9b6a9075a29..1307237dbc7 100644 --- a/clippy_lints/src/unnecessary_sort_by.rs +++ b/clippy_lints/src/unnecessary_sort_by.rs @@ -170,22 +170,12 @@ fn mirrored_exprs( } fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { - // NOTE: Vectors of references are not supported. In order to avoid hitting https://github.com/rust-lang/rust/issues/34162, - // (different unnamed lifetimes for closure arg and return type) we need to make sure the suggested - // closure parameter is not a reference in case we suggest `Reverse`. Trying to destructure more - // than one level of references would add some extra complexity as we would have to compensate - // in the closure body. - if_chain! { if let ExprKind::MethodCall(name_ident, _, args, _) = &expr.kind; if let name = name_ident.ident.name.to_ident_string(); if name == "sort_by" || name == "sort_unstable_by"; if let [vec, Expr { kind: ExprKind::Closure(_, _, closure_body_id, _, _), .. }] = args; - let vec_ty = cx.typeck_results().expr_ty(vec); - if utils::is_type_diagnostic_item(cx, vec_ty, sym!(vec_type)); - let ty = vec_ty.walk().nth(1).unwrap().expect_ty(); // T in Vec - if !matches!(&ty.kind(), ty::Ref(..)); - if utils::is_copy(cx, ty); + if utils::is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(vec), sym!(vec_type)); if let closure_body = cx.tcx.hir().body(*closure_body_id); if let &[ Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..}, @@ -210,40 +200,32 @@ 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; - then { - return Some(LintTrigger::Sort(SortDetection { vec_name, unstable })) - } else { - if !key_returns_borrow(cx, left_expr) { - return Some(LintTrigger::SortByKey(SortByKeyDetection { - vec_name, - unstable, - closure_arg, - closure_body, - reverse - })) - } + if let ExprKind::Path(QPath::Resolved(_, Path { + segments: [PathSegment { ident: left_name, .. }], .. + })) = &left_expr.kind { + if left_name == left_ident { + return Some(LintTrigger::Sort(SortDetection { vec_name, unstable })); } } + + if !expr_borrows(cx, left_expr) { + return Some(LintTrigger::SortByKey(SortByKeyDetection { + vec_name, + unstable, + closure_arg, + closure_body, + reverse + })); + } } } None } -fn key_returns_borrow(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - if let Some(def_id) = utils::fn_def_id(cx, expr) { - let output = cx.tcx.fn_sig(def_id).output(); - let ty = output.skip_binder(); - return matches!(ty.kind(), ty::Ref(..)) - || ty.walk().any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))); - } - - false +fn expr_borrows(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let ty = cx.typeck_results().expr_ty(expr); + matches!(ty.kind(), ty::Ref(..)) || ty.walk().any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))) } impl LateLintPass<'_> for UnnecessarySortBy { @@ -256,7 +238,7 @@ impl LateLintPass<'_> for UnnecessarySortBy { "use Vec::sort_by_key here instead", "try", format!( - "{}.sort{}_by_key(|&{}| {})", + "{}.sort{}_by_key(|{}| {})", trigger.vec_name, if trigger.unstable { "_unstable" } else { "" }, trigger.closure_arg, diff --git a/tests/ui/unnecessary_sort_by.fixed b/tests/ui/unnecessary_sort_by.fixed index ad0d0387db0..b45b27d8f23 100644 --- a/tests/ui/unnecessary_sort_by.fixed +++ b/tests/ui/unnecessary_sort_by.fixed @@ -13,12 +13,12 @@ fn unnecessary_sort_by() { // Forward examples vec.sort(); vec.sort_unstable(); - vec.sort_by_key(|&a| (a + 5).abs()); - vec.sort_unstable_by_key(|&a| id(-a)); + vec.sort_by_key(|a| (a + 5).abs()); + vec.sort_unstable_by_key(|a| id(-a)); // Reverse examples - vec.sort_by_key(|&b| Reverse(b)); - vec.sort_by_key(|&b| Reverse((b + 5).abs())); - vec.sort_unstable_by_key(|&b| Reverse(id(-b))); + vec.sort_by(|a, b| b.cmp(a)); // not linted to avoid suggesting `Reverse(b)` which would borrow + vec.sort_by_key(|b| Reverse((b + 5).abs())); + vec.sort_unstable_by_key(|b| Reverse(id(-b))); // Negative examples (shouldn't be changed) let c = &7; vec.sort_by(|a, b| (b - a).cmp(&(a - b))); @@ -26,10 +26,11 @@ fn unnecessary_sort_by() { vec.sort_by(|_, b| b.cmp(c)); vec.sort_unstable_by(|a, _| a.cmp(c)); - // Ignore vectors of references + // Vectors of references are fine as long as the resulting key does not borrow let mut vec: Vec<&&&isize> = vec![&&&3, &&&6, &&&1, &&&2, &&&5]; - vec.sort_by(|a, b| (***a).abs().cmp(&(***b).abs())); - vec.sort_unstable_by(|a, b| (***a).abs().cmp(&(***b).abs())); + vec.sort_by_key(|a| (***a).abs()); + vec.sort_unstable_by_key(|a| (***a).abs()); + // `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)); } @@ -68,10 +69,9 @@ mod issue_5754 { } } -// `Vec::sort_by_key` closure parameter is `F: FnMut(&T) -> K` -// The suggestion is destructuring T and we know T is not a reference, so test that non-Copy T are -// not linted. +// The closure parameter is not dereferenced anymore, so non-Copy types can be linted mod issue_6001 { + use super::*; struct Test(String); impl Test { @@ -85,11 +85,11 @@ mod issue_6001 { let mut args: Vec = vec![]; // Forward - args.sort_by(|a, b| a.name().cmp(&b.name())); - args.sort_unstable_by(|a, b| a.name().cmp(&b.name())); + args.sort_by_key(|a| a.name()); + args.sort_unstable_by_key(|a| a.name()); // Reverse - args.sort_by(|a, b| b.name().cmp(&a.name())); - args.sort_unstable_by(|a, b| b.name().cmp(&a.name())); + args.sort_by_key(|b| Reverse(b.name())); + args.sort_unstable_by_key(|b| Reverse(b.name())); } } diff --git a/tests/ui/unnecessary_sort_by.rs b/tests/ui/unnecessary_sort_by.rs index 9746f6e6849..be2abe7f701 100644 --- a/tests/ui/unnecessary_sort_by.rs +++ b/tests/ui/unnecessary_sort_by.rs @@ -16,7 +16,7 @@ fn unnecessary_sort_by() { vec.sort_by(|a, b| (a + 5).abs().cmp(&(b + 5).abs())); vec.sort_unstable_by(|a, b| id(-a).cmp(&id(-b))); // Reverse examples - vec.sort_by(|a, b| b.cmp(a)); + vec.sort_by(|a, b| b.cmp(a)); // not linted to avoid suggesting `Reverse(b)` which would borrow vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a))); // Negative examples (shouldn't be changed) @@ -26,10 +26,11 @@ fn unnecessary_sort_by() { vec.sort_by(|_, b| b.cmp(c)); vec.sort_unstable_by(|a, _| a.cmp(c)); - // Ignore vectors of references + // Vectors of references are fine as long as the resulting key does not borrow let mut vec: Vec<&&&isize> = vec![&&&3, &&&6, &&&1, &&&2, &&&5]; vec.sort_by(|a, b| (***a).abs().cmp(&(***b).abs())); vec.sort_unstable_by(|a, b| (***a).abs().cmp(&(***b).abs())); + // `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)); } @@ -68,10 +69,9 @@ mod issue_5754 { } } -// `Vec::sort_by_key` closure parameter is `F: FnMut(&T) -> K` -// The suggestion is destructuring T and we know T is not a reference, so test that non-Copy T are -// not linted. +// The closure parameter is not dereferenced anymore, so non-Copy types can be linted mod issue_6001 { + use super::*; struct Test(String); impl Test { diff --git a/tests/ui/unnecessary_sort_by.stderr b/tests/ui/unnecessary_sort_by.stderr index 70c6cf0a3b6..50607933e18 100644 --- a/tests/ui/unnecessary_sort_by.stderr +++ b/tests/ui/unnecessary_sort_by.stderr @@ -16,31 +16,61 @@ error: use Vec::sort_by_key here instead --> $DIR/unnecessary_sort_by.rs:16:5 | LL | vec.sort_by(|a, b| (a + 5).abs().cmp(&(b + 5).abs())); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&a| (a + 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 | 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:19:5 - | -LL | vec.sort_by(|a, b| b.cmp(a)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(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 | LL | vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse((b + 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 | LL | vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|&b| Reverse(id(-b)))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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 + | +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 + | +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 + | +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 + | +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 + | +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 + | +LL | args.sort_unstable_by(|a, b| b.name().cmp(&a.name())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_unstable_by_key(|b| Reverse(b.name()))` -error: aborting due to 7 previous errors +error: aborting due to 12 previous errors -- cgit 1.4.1-3-g733a5 From 2892a2b0f578edd290b3be6f5e5c3280bc160f24 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Thu, 24 Sep 2020 23:22:54 +0900 Subject: Fix FP in `print_stdout` This lint shouldn't be emitted in `build.rs` as `println!` and `print!` are used for the build script. --- clippy_lints/src/write.rs | 23 ++++++++++++++++++++--- tests/ui/build.rs | 10 ++++++++++ tests/ui/build.stderr | 0 3 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 tests/ui/build.rs create mode 100644 tests/ui/build.stderr (limited to 'tests') diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index fac63bcb993..780d474ee96 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -2,6 +2,7 @@ use std::borrow::Cow; use std::ops::Range; use crate::utils::{snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then}; +use if_chain::if_chain; use rustc_ast::ast::{Expr, ExprKind, Item, ItemKind, MacCall, StrLit, StrStyle}; use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; @@ -11,7 +12,7 @@ use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_parse::parser; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::Symbol; -use rustc_span::{BytePos, Span}; +use rustc_span::{BytePos, FileName, Span}; declare_clippy_lint! { /// **What it does:** This lint warns when you use `println!("")` to @@ -236,7 +237,15 @@ impl EarlyLintPass for Write { fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &MacCall) { if mac.path == sym!(println) { - span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`"); + let filename = cx.sess.source_map().span_to_filename(mac.span()); + if_chain! { + if let FileName::Real(filename) = filename; + if let Some(filename) = filename.local_path().file_name(); + if filename != "build.rs"; + then { + span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`"); + } + } if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { if fmt_str.symbol == Symbol::intern("") { span_lint_and_sugg( @@ -251,7 +260,15 @@ impl EarlyLintPass for Write { } } } else if mac.path == sym!(print) { - span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`"); + if_chain! { + let filename = cx.sess.source_map().span_to_filename(mac.span()); + if let FileName::Real(filename) = filename; + if let Some(filename) = filename.local_path().file_name(); + if filename != "build.rs"; + then { + span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`"); + } + } if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { if check_newlines(&fmt_str) { span_lint_and_then( diff --git a/tests/ui/build.rs b/tests/ui/build.rs new file mode 100644 index 00000000000..2d43d452a4f --- /dev/null +++ b/tests/ui/build.rs @@ -0,0 +1,10 @@ +#![warn(clippy::print_stdout)] + +fn main() { + // Fix #6041 + // + // The `print_stdout` shouldn't be linted in `build.rs` + // as these methods are used for the build script. + println!("Hello"); + print!("Hello"); +} diff --git a/tests/ui/build.stderr b/tests/ui/build.stderr new file mode 100644 index 00000000000..e69de29bb2d -- cgit 1.4.1-3-g733a5 From 774e82a566c6c3349700a8bece44d6a8c6755039 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Thu, 11 Jun 2020 11:19:44 +1200 Subject: Add the tests for `manual_memcpy` with loop counters --- tests/ui/manual_memcpy.rs | 54 ++++++++++++++++++++++++++++++++++++++ tests/ui/manual_memcpy.stderr | 60 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 111 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/tests/ui/manual_memcpy.rs b/tests/ui/manual_memcpy.rs index 0083f94798f..87bfb4fdd16 100644 --- a/tests/ui/manual_memcpy.rs +++ b/tests/ui/manual_memcpy.rs @@ -115,6 +115,60 @@ pub fn manual_copy(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { } } +#[allow(clippy::needless_range_loop, clippy::explicit_counter_loop)] +pub fn manual_copy_with_counters(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { + let mut count = 0; + for i in 3..src.len() { + dst[i] = src[count]; + count += 1; + } + + let mut count = 0; + for i in 3..src.len() { + dst[count] = src[i]; + count += 1; + } + + let mut count = 3; + for i in 0..src.len() { + dst[count] = src[i]; + count += 1; + } + + let mut count = 3; + for i in 0..src.len() { + dst[i] = src[count]; + count += 1; + } + + let mut count = 0; + for i in 3..(3 + src.len()) { + dst[i] = src[count]; + count += 1; + } + + let mut count = 3; + for i in 5..src.len() { + dst[i] = src[count - 2]; + count += 1; + } + + let mut count = 5; + for i in 3..10 { + dst[i] = src[count]; + count += 1; + } + + let mut count = 3; + let mut count = 30; + for i in 0..src.len() { + dst[count] = src[i]; + dst2[count] = src[i]; + count += 1; + count += 1; + } +} + #[warn(clippy::needless_range_loop, clippy::manual_memcpy)] pub fn manual_clone(src: &[String], dst: &mut [String]) { for i in 0..src.len() { diff --git a/tests/ui/manual_memcpy.stderr b/tests/ui/manual_memcpy.stderr index bad84a58900..a5698b08103 100644 --- a/tests/ui/manual_memcpy.stderr +++ b/tests/ui/manual_memcpy.stderr @@ -16,7 +16,7 @@ error: it looks like you're manually copying between slices --> $DIR/manual_memcpy.rs:17:14 | LL | for i in 0..src.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[10..])` + | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[10..(src.len() + 10)])` error: it looks like you're manually copying between slices --> $DIR/manual_memcpy.rs:22:14 @@ -79,10 +79,64 @@ LL | for i in 0..0 { | ^^^^ help: try replacing the loop by: `dst[..0].clone_from_slice(&src[..0])` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:120:14 + --> $DIR/manual_memcpy.rs:121:14 + | +LL | for i in 3..src.len() { + | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[3..src.len()].clone_from_slice(&src[..(src.len() - 3)])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:127:14 + | +LL | for i in 3..src.len() { + | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..(src.len() - 3)].clone_from_slice(&src[3..])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:133:14 + | +LL | for i in 0..src.len() { + | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[3..(src.len() + 3)].clone_from_slice(&src[..])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:139:14 + | +LL | for i in 0..src.len() { + | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[3..(src.len() + 3)])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:145:14 + | +LL | for i in 3..(3 + src.len()) { + | ^^^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[3..(3 + src.len())].clone_from_slice(&src[..((3 + src.len()) - 3)])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:151:14 + | +LL | for i in 5..src.len() { + | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[5..src.len()].clone_from_slice(&src[(3 - 2)..((src.len() - 2) + 3 - 5)])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:157:14 + | +LL | for i in 3..10 { + | ^^^^^ help: try replacing the loop by: `dst[3..10].clone_from_slice(&src[5..(10 + 5 - 3)])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:164:14 + | +LL | for i in 0..src.len() { + | ^^^^^^^^^^^^ + | +help: try replacing the loop by + | +LL | for i in dst[3..(src.len() + 3)].clone_from_slice(&src[..]) +LL | dst2[30..(src.len() + 30)].clone_from_slice(&src[..]) { + | + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:174:14 | LL | for i in 0..src.len() { | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..])` -error: aborting due to 13 previous errors +error: aborting due to 21 previous errors -- cgit 1.4.1-3-g733a5 From 9aad38bf614c3fb6d306f5dec4a0af606bb3c9c8 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Thu, 18 Jun 2020 10:48:44 +1200 Subject: Update `manual_memcpy.stderr` to reflect additional parentheses --- tests/ui/manual_memcpy.stderr | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/tests/ui/manual_memcpy.stderr b/tests/ui/manual_memcpy.stderr index a5698b08103..e6bef9981a3 100644 --- a/tests/ui/manual_memcpy.stderr +++ b/tests/ui/manual_memcpy.stderr @@ -58,13 +58,13 @@ error: it looks like you're manually copying between slices --> $DIR/manual_memcpy.rs:94:14 | LL | for i in from..from + src.len() { - | ^^^^^^^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..from + src.len()].clone_from_slice(&src[..(from + src.len() - from)])` + | ^^^^^^^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..(from + src.len())].clone_from_slice(&src[..(from + src.len() - from)])` error: it looks like you're manually copying between slices --> $DIR/manual_memcpy.rs:98:14 | LL | for i in from..from + 3 { - | ^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..from + 3].clone_from_slice(&src[..(from + 3 - from)])` + | ^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..(from + 3)].clone_from_slice(&src[..(from + 3 - from)])` error: it looks like you're manually copying between slices --> $DIR/manual_memcpy.rs:103:14 @@ -106,7 +106,7 @@ error: it looks like you're manually copying between slices --> $DIR/manual_memcpy.rs:145:14 | LL | for i in 3..(3 + src.len()) { - | ^^^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[3..(3 + src.len())].clone_from_slice(&src[..((3 + src.len()) - 3)])` + | ^^^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[3..((3 + src.len()))].clone_from_slice(&src[..((3 + src.len()) - 3)])` error: it looks like you're manually copying between slices --> $DIR/manual_memcpy.rs:151:14 -- cgit 1.4.1-3-g733a5 From 4ea4a972500a8ddecfc737d51eec960324dcb02f Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Thu, 18 Jun 2020 12:31:13 +1200 Subject: Add tests for bitwise operations --- tests/ui/manual_memcpy.rs | 8 ++++++++ tests/ui/manual_memcpy.stderr | 8 +++++++- 2 files changed, 15 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/ui/manual_memcpy.rs b/tests/ui/manual_memcpy.rs index 87bfb4fdd16..4846ab5eaaa 100644 --- a/tests/ui/manual_memcpy.rs +++ b/tests/ui/manual_memcpy.rs @@ -167,6 +167,14 @@ pub fn manual_copy_with_counters(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) count += 1; count += 1; } + + // make sure parentheses are added properly to bitwise operators, which have lower precedence than + // arithmetric ones + let mut count = 0 << 1; + for i in 0..1 << 1 { + dst[count] = src[i + 2]; + count += 1; + } } #[warn(clippy::needless_range_loop, clippy::manual_memcpy)] diff --git a/tests/ui/manual_memcpy.stderr b/tests/ui/manual_memcpy.stderr index e6bef9981a3..d189bde0508 100644 --- a/tests/ui/manual_memcpy.stderr +++ b/tests/ui/manual_memcpy.stderr @@ -135,8 +135,14 @@ LL | dst2[30..(src.len() + 30)].clone_from_slice(&src[..]) { error: it looks like you're manually copying between slices --> $DIR/manual_memcpy.rs:174:14 | +LL | for i in 0..1 << 1 { + | ^^^^^^^^^ help: try replacing the loop by: `dst[(0 << 1)..((1 << 1) + (0 << 1))].clone_from_slice(&src[2..((1 << 1) + 2)])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:182:14 + | LL | for i in 0..src.len() { | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..])` -error: aborting due to 21 previous errors +error: aborting due to 22 previous errors -- cgit 1.4.1-3-g733a5 From 174065fc98ef9335ea45a234aa18286cdf6c3934 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Wed, 24 Jun 2020 11:33:49 +1200 Subject: fix the multiple counters test --- tests/ui/manual_memcpy.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/tests/ui/manual_memcpy.rs b/tests/ui/manual_memcpy.rs index 4846ab5eaaa..8318fd89811 100644 --- a/tests/ui/manual_memcpy.rs +++ b/tests/ui/manual_memcpy.rs @@ -160,12 +160,12 @@ pub fn manual_copy_with_counters(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) } let mut count = 3; - let mut count = 30; + let mut count2 = 30; for i in 0..src.len() { dst[count] = src[i]; - dst2[count] = src[i]; - count += 1; + dst2[count2] = src[i]; count += 1; + count2 += 1; } // make sure parentheses are added properly to bitwise operators, which have lower precedence than -- cgit 1.4.1-3-g733a5 From 6b59675449d659123718e7d766432caa1ae0a0aa Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 25 Sep 2020 15:19:36 +0200 Subject: Remove run-pass annotations from crash tests It does not seem to be necessary --- tests/ui/crashes/associated-constant-ice.rs | 2 -- tests/ui/crashes/cc_seme.rs | 2 -- tests/ui/crashes/enum-glob-import-crate.rs | 2 -- tests/ui/crashes/ice-1588.rs | 2 -- tests/ui/crashes/ice-1782.rs | 2 -- tests/ui/crashes/ice-1969.rs | 2 -- tests/ui/crashes/ice-2499.rs | 2 -- tests/ui/crashes/ice-2594.rs | 2 -- tests/ui/crashes/ice-2727.rs | 2 -- tests/ui/crashes/ice-2760.rs | 2 -- tests/ui/crashes/ice-2774.rs | 2 -- tests/ui/crashes/ice-2862.rs | 2 -- tests/ui/crashes/ice-2865.rs | 2 -- tests/ui/crashes/ice-3151.rs | 2 -- tests/ui/crashes/ice-3462.rs | 2 -- tests/ui/crashes/ice-3741.rs | 1 - tests/ui/crashes/ice-3747.rs | 2 -- tests/ui/crashes/ice-4727.rs | 2 -- tests/ui/crashes/ice-4760.rs | 1 - tests/ui/crashes/ice-700.rs | 2 -- tests/ui/crashes/ice_exacte_size.rs | 2 -- tests/ui/crashes/if_same_then_else.rs | 2 -- tests/ui/crashes/issue-825.rs | 2 -- tests/ui/crashes/issues_loop_mut_cond.rs | 2 -- tests/ui/crashes/match_same_arms_const.rs | 2 -- tests/ui/crashes/mut_mut_macro.rs | 2 -- tests/ui/crashes/needless_borrow_fp.rs | 2 -- tests/ui/crashes/needless_lifetimes_impl_trait.rs | 2 -- tests/ui/crashes/procedural_macro.rs | 2 -- tests/ui/crashes/regressions.rs | 2 -- tests/ui/crashes/returns.rs | 2 -- tests/ui/crashes/single-match-else.rs | 2 -- tests/ui/crashes/trivial_bounds.rs | 2 -- tests/ui/crashes/used_underscore_binding_macro.rs | 2 -- 34 files changed, 66 deletions(-) (limited to 'tests') diff --git a/tests/ui/crashes/associated-constant-ice.rs b/tests/ui/crashes/associated-constant-ice.rs index 4bb833795bb..948deba3ea6 100644 --- a/tests/ui/crashes/associated-constant-ice.rs +++ b/tests/ui/crashes/associated-constant-ice.rs @@ -1,5 +1,3 @@ -// run-pass - /// Test for https://github.com/rust-lang/rust-clippy/issues/1698 pub trait Trait { diff --git a/tests/ui/crashes/cc_seme.rs b/tests/ui/crashes/cc_seme.rs index c48c7e9e6c6..98588be9cf8 100644 --- a/tests/ui/crashes/cc_seme.rs +++ b/tests/ui/crashes/cc_seme.rs @@ -1,5 +1,3 @@ -// run-pass - #[allow(dead_code)] /// Test for https://github.com/rust-lang/rust-clippy/issues/478 diff --git a/tests/ui/crashes/enum-glob-import-crate.rs b/tests/ui/crashes/enum-glob-import-crate.rs index db1fa871afe..dca32aa3b56 100644 --- a/tests/ui/crashes/enum-glob-import-crate.rs +++ b/tests/ui/crashes/enum-glob-import-crate.rs @@ -1,5 +1,3 @@ -// run-pass - #![deny(clippy::all)] #![allow(unused_imports)] diff --git a/tests/ui/crashes/ice-1588.rs b/tests/ui/crashes/ice-1588.rs index 15d0f705b36..b0a3d11bce4 100644 --- a/tests/ui/crashes/ice-1588.rs +++ b/tests/ui/crashes/ice-1588.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow(clippy::all)] /// Test for https://github.com/rust-lang/rust-clippy/issues/1588 diff --git a/tests/ui/crashes/ice-1782.rs b/tests/ui/crashes/ice-1782.rs index 1ca6b6976b3..81af88962a6 100644 --- a/tests/ui/crashes/ice-1782.rs +++ b/tests/ui/crashes/ice-1782.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow(dead_code, unused_variables)] /// Should not trigger an ICE in `SpanlessEq` / `consts::constant` diff --git a/tests/ui/crashes/ice-1969.rs b/tests/ui/crashes/ice-1969.rs index 837ec9df31a..96a8fe6c24d 100644 --- a/tests/ui/crashes/ice-1969.rs +++ b/tests/ui/crashes/ice-1969.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow(clippy::all)] /// Test for https://github.com/rust-lang/rust-clippy/issues/1969 diff --git a/tests/ui/crashes/ice-2499.rs b/tests/ui/crashes/ice-2499.rs index ffef1631775..45b3b1869dd 100644 --- a/tests/ui/crashes/ice-2499.rs +++ b/tests/ui/crashes/ice-2499.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow(dead_code, clippy::char_lit_as_u8, clippy::needless_bool)] /// Should not trigger an ICE in `SpanlessHash` / `consts::constant` diff --git a/tests/ui/crashes/ice-2594.rs b/tests/ui/crashes/ice-2594.rs index ac19f1976e9..3f3986b6fc6 100644 --- a/tests/ui/crashes/ice-2594.rs +++ b/tests/ui/crashes/ice-2594.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow(dead_code, unused_variables)] /// Should not trigger an ICE in `SpanlessHash` / `consts::constant` diff --git a/tests/ui/crashes/ice-2727.rs b/tests/ui/crashes/ice-2727.rs index d832c286033..56024abc8f5 100644 --- a/tests/ui/crashes/ice-2727.rs +++ b/tests/ui/crashes/ice-2727.rs @@ -1,5 +1,3 @@ -// run-pass - /// Test for https://github.com/rust-lang/rust-clippy/issues/2727 pub fn f(new: fn()) { diff --git a/tests/ui/crashes/ice-2760.rs b/tests/ui/crashes/ice-2760.rs index 9e5e299c336..f1a229f3f4f 100644 --- a/tests/ui/crashes/ice-2760.rs +++ b/tests/ui/crashes/ice-2760.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow( unused_variables, clippy::blacklisted_name, diff --git a/tests/ui/crashes/ice-2774.rs b/tests/ui/crashes/ice-2774.rs index 47f8e3b18ee..d44b0fae820 100644 --- a/tests/ui/crashes/ice-2774.rs +++ b/tests/ui/crashes/ice-2774.rs @@ -1,5 +1,3 @@ -// run-pass - use std::collections::HashSet; // See rust-lang/rust-clippy#2774. diff --git a/tests/ui/crashes/ice-2862.rs b/tests/ui/crashes/ice-2862.rs index 47324ce1831..8326e3663b0 100644 --- a/tests/ui/crashes/ice-2862.rs +++ b/tests/ui/crashes/ice-2862.rs @@ -1,5 +1,3 @@ -// run-pass - /// Test for https://github.com/rust-lang/rust-clippy/issues/2862 pub trait FooMap { diff --git a/tests/ui/crashes/ice-2865.rs b/tests/ui/crashes/ice-2865.rs index c4f6c0fed68..6b1ceb50569 100644 --- a/tests/ui/crashes/ice-2865.rs +++ b/tests/ui/crashes/ice-2865.rs @@ -1,5 +1,3 @@ -// run-pass - #[allow(dead_code)] /// Test for https://github.com/rust-lang/rust-clippy/issues/2865 diff --git a/tests/ui/crashes/ice-3151.rs b/tests/ui/crashes/ice-3151.rs index ffad2d06b56..fef4d7db84d 100644 --- a/tests/ui/crashes/ice-3151.rs +++ b/tests/ui/crashes/ice-3151.rs @@ -1,5 +1,3 @@ -// run-pass - /// Test for https://github.com/rust-lang/rust-clippy/issues/2865 #[derive(Clone)] diff --git a/tests/ui/crashes/ice-3462.rs b/tests/ui/crashes/ice-3462.rs index 95c7dff9be3..7d62e315da2 100644 --- a/tests/ui/crashes/ice-3462.rs +++ b/tests/ui/crashes/ice-3462.rs @@ -1,5 +1,3 @@ -// run-pass - #![warn(clippy::all)] #![allow(clippy::blacklisted_name)] #![allow(unused)] diff --git a/tests/ui/crashes/ice-3741.rs b/tests/ui/crashes/ice-3741.rs index a548415da62..1253ddcfaeb 100644 --- a/tests/ui/crashes/ice-3741.rs +++ b/tests/ui/crashes/ice-3741.rs @@ -1,5 +1,4 @@ // aux-build:proc_macro_crash.rs -// run-pass #![warn(clippy::suspicious_else_formatting)] diff --git a/tests/ui/crashes/ice-3747.rs b/tests/ui/crashes/ice-3747.rs index d0b44ebafee..cdf018cbc88 100644 --- a/tests/ui/crashes/ice-3747.rs +++ b/tests/ui/crashes/ice-3747.rs @@ -1,5 +1,3 @@ -// run-pass - /// Test for https://github.com/rust-lang/rust-clippy/issues/3747 macro_rules! a { diff --git a/tests/ui/crashes/ice-4727.rs b/tests/ui/crashes/ice-4727.rs index cdb59caec67..2a4bc83f58a 100644 --- a/tests/ui/crashes/ice-4727.rs +++ b/tests/ui/crashes/ice-4727.rs @@ -1,5 +1,3 @@ -// run-pass - #![warn(clippy::use_self)] #[path = "auxiliary/ice-4727-aux.rs"] diff --git a/tests/ui/crashes/ice-4760.rs b/tests/ui/crashes/ice-4760.rs index ead67d5ed1b..08b06961760 100644 --- a/tests/ui/crashes/ice-4760.rs +++ b/tests/ui/crashes/ice-4760.rs @@ -1,4 +1,3 @@ -// run-pass const COUNT: usize = 2; struct Thing; trait Dummy {} diff --git a/tests/ui/crashes/ice-700.rs b/tests/ui/crashes/ice-700.rs index b06df83d51a..0cbceedbd6b 100644 --- a/tests/ui/crashes/ice-700.rs +++ b/tests/ui/crashes/ice-700.rs @@ -1,5 +1,3 @@ -// run-pass - #![deny(clippy::all)] /// Test for https://github.com/rust-lang/rust-clippy/issues/700 diff --git a/tests/ui/crashes/ice_exacte_size.rs b/tests/ui/crashes/ice_exacte_size.rs index e02eb28ab86..30e4b11ec0b 100644 --- a/tests/ui/crashes/ice_exacte_size.rs +++ b/tests/ui/crashes/ice_exacte_size.rs @@ -1,5 +1,3 @@ -// run-pass - #![deny(clippy::all)] /// Test for https://github.com/rust-lang/rust-clippy/issues/1336 diff --git a/tests/ui/crashes/if_same_then_else.rs b/tests/ui/crashes/if_same_then_else.rs index 4ef992b05e7..2f913292995 100644 --- a/tests/ui/crashes/if_same_then_else.rs +++ b/tests/ui/crashes/if_same_then_else.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow(clippy::comparison_chain)] #![deny(clippy::if_same_then_else)] diff --git a/tests/ui/crashes/issue-825.rs b/tests/ui/crashes/issue-825.rs index 3d4a88ab3c4..05696e3d7d5 100644 --- a/tests/ui/crashes/issue-825.rs +++ b/tests/ui/crashes/issue-825.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow(warnings)] /// Test for https://github.com/rust-lang/rust-clippy/issues/825 diff --git a/tests/ui/crashes/issues_loop_mut_cond.rs b/tests/ui/crashes/issues_loop_mut_cond.rs index c4acd5cda1b..bb238c81ebc 100644 --- a/tests/ui/crashes/issues_loop_mut_cond.rs +++ b/tests/ui/crashes/issues_loop_mut_cond.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow(dead_code)] /// Issue: https://github.com/rust-lang/rust-clippy/issues/2596 diff --git a/tests/ui/crashes/match_same_arms_const.rs b/tests/ui/crashes/match_same_arms_const.rs index 848f0ea52ca..94c939665e6 100644 --- a/tests/ui/crashes/match_same_arms_const.rs +++ b/tests/ui/crashes/match_same_arms_const.rs @@ -1,5 +1,3 @@ -// run-pass - #![deny(clippy::match_same_arms)] /// Test for https://github.com/rust-lang/rust-clippy/issues/2427 diff --git a/tests/ui/crashes/mut_mut_macro.rs b/tests/ui/crashes/mut_mut_macro.rs index d8fbaa54146..a238e7896fc 100644 --- a/tests/ui/crashes/mut_mut_macro.rs +++ b/tests/ui/crashes/mut_mut_macro.rs @@ -1,5 +1,3 @@ -// run-pass - #![deny(clippy::mut_mut, clippy::zero_ptr, clippy::cmp_nan)] #![allow(dead_code)] diff --git a/tests/ui/crashes/needless_borrow_fp.rs b/tests/ui/crashes/needless_borrow_fp.rs index 48507efe1e9..4f61c76828d 100644 --- a/tests/ui/crashes/needless_borrow_fp.rs +++ b/tests/ui/crashes/needless_borrow_fp.rs @@ -1,5 +1,3 @@ -// run-pass - #[deny(clippy::all)] #[derive(Debug)] pub enum Error { diff --git a/tests/ui/crashes/needless_lifetimes_impl_trait.rs b/tests/ui/crashes/needless_lifetimes_impl_trait.rs index bd1fa4a0b1e..676564b2445 100644 --- a/tests/ui/crashes/needless_lifetimes_impl_trait.rs +++ b/tests/ui/crashes/needless_lifetimes_impl_trait.rs @@ -1,5 +1,3 @@ -// run-pass - #![deny(clippy::needless_lifetimes)] #![allow(dead_code)] diff --git a/tests/ui/crashes/procedural_macro.rs b/tests/ui/crashes/procedural_macro.rs index f79d9ab6460..c7468493380 100644 --- a/tests/ui/crashes/procedural_macro.rs +++ b/tests/ui/crashes/procedural_macro.rs @@ -1,5 +1,3 @@ -// run-pass - #[macro_use] extern crate clippy_mini_macro_test; diff --git a/tests/ui/crashes/regressions.rs b/tests/ui/crashes/regressions.rs index 3d5063d1a3a..a41bcb33b44 100644 --- a/tests/ui/crashes/regressions.rs +++ b/tests/ui/crashes/regressions.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow(clippy::blacklisted_name)] pub fn foo(bar: *const u8) { diff --git a/tests/ui/crashes/returns.rs b/tests/ui/crashes/returns.rs index f2153efc388..8021ed4607d 100644 --- a/tests/ui/crashes/returns.rs +++ b/tests/ui/crashes/returns.rs @@ -1,5 +1,3 @@ -// run-pass - /// Test for https://github.com/rust-lang/rust-clippy/issues/1346 #[deny(warnings)] diff --git a/tests/ui/crashes/single-match-else.rs b/tests/ui/crashes/single-match-else.rs index 3a4bbe310cc..1ba7ac08213 100644 --- a/tests/ui/crashes/single-match-else.rs +++ b/tests/ui/crashes/single-match-else.rs @@ -1,5 +1,3 @@ -// run-pass - #![warn(clippy::single_match_else)] //! Test for https://github.com/rust-lang/rust-clippy/issues/1588 diff --git a/tests/ui/crashes/trivial_bounds.rs b/tests/ui/crashes/trivial_bounds.rs index 2bb95c18a39..60105a8213f 100644 --- a/tests/ui/crashes/trivial_bounds.rs +++ b/tests/ui/crashes/trivial_bounds.rs @@ -1,5 +1,3 @@ -// run-pass - #![feature(trivial_bounds)] #![allow(unused, trivial_bounds)] diff --git a/tests/ui/crashes/used_underscore_binding_macro.rs b/tests/ui/crashes/used_underscore_binding_macro.rs index 265017c51d9..6d2124c12fe 100644 --- a/tests/ui/crashes/used_underscore_binding_macro.rs +++ b/tests/ui/crashes/used_underscore_binding_macro.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow(clippy::useless_attribute)] //issue #2910 #[macro_use] -- cgit 1.4.1-3-g733a5 From fd0656109fb7317266e372bd5cc5c4c10299f825 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 25 Sep 2020 15:20:04 +0200 Subject: Add emit=metadata to UI tests build flags This should improve the performance by avoiding codegen --- tests/compile-test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 697823712bf..f0d73e9b0e2 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -71,7 +71,7 @@ fn default_config() -> compiletest::Config { } config.target_rustcflags = Some(format!( - "-L {0} -L {1} -Dwarnings -Zui-testing {2}", + "--emit=metadata -L {0} -L {1} -Dwarnings -Zui-testing {2}", host_lib().join("deps").display(), cargo::TARGET_LIB.join("deps").display(), third_party_crates(), -- cgit 1.4.1-3-g733a5 From 1cb3c00cba1d0a583280536d3d45161dc0c82308 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 25 Sep 2020 15:46:32 +0200 Subject: Use emit=link for auxiliary proc macro crates --- tests/ui/auxiliary/proc_macro_attr.rs | 1 + tests/ui/auxiliary/proc_macro_derive.rs | 1 + tests/ui/crashes/auxiliary/proc_macro_crash.rs | 1 + 3 files changed, 3 insertions(+) (limited to 'tests') diff --git a/tests/ui/auxiliary/proc_macro_attr.rs b/tests/ui/auxiliary/proc_macro_attr.rs index e6626d57a77..01796d45f13 100644 --- a/tests/ui/auxiliary/proc_macro_attr.rs +++ b/tests/ui/auxiliary/proc_macro_attr.rs @@ -1,3 +1,4 @@ +// compile-flags: --emit=link // no-prefer-dynamic #![crate_type = "proc-macro"] diff --git a/tests/ui/auxiliary/proc_macro_derive.rs b/tests/ui/auxiliary/proc_macro_derive.rs index 05ffb55f620..3df8be6c232 100644 --- a/tests/ui/auxiliary/proc_macro_derive.rs +++ b/tests/ui/auxiliary/proc_macro_derive.rs @@ -1,3 +1,4 @@ +// compile-flags: --emit=link // no-prefer-dynamic #![crate_type = "proc-macro"] diff --git a/tests/ui/crashes/auxiliary/proc_macro_crash.rs b/tests/ui/crashes/auxiliary/proc_macro_crash.rs index 086548e58ed..619d11cefc4 100644 --- a/tests/ui/crashes/auxiliary/proc_macro_crash.rs +++ b/tests/ui/crashes/auxiliary/proc_macro_crash.rs @@ -1,3 +1,4 @@ +// compile-flags: --emit=link // no-prefer-dynamic // ^ compiletest by default builds all aux files as dylibs, but we don't want that for proc-macro // crates. If we don't set this, compiletest will override the `crate_type` attribute below and -- cgit 1.4.1-3-g733a5 From 5b484b405748fc8d7476f9a8d68d2e7227767271 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Fri, 25 Sep 2020 23:32:18 +0900 Subject: Fix the detection of build scripts --- clippy_lints/src/write.rs | 33 ++++++++++++++----------------- tests/ui/build.rs | 10 ---------- tests/ui/build.stderr | 0 tests/ui/print_stdout_build_script.rs | 12 +++++++++++ tests/ui/print_stdout_build_script.stderr | 0 5 files changed, 27 insertions(+), 28 deletions(-) delete mode 100644 tests/ui/build.rs delete mode 100644 tests/ui/build.stderr create mode 100644 tests/ui/print_stdout_build_script.rs create mode 100644 tests/ui/print_stdout_build_script.stderr (limited to 'tests') diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 780d474ee96..0e9c7098af8 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -2,7 +2,6 @@ use std::borrow::Cow; use std::ops::Range; use crate::utils::{snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then}; -use if_chain::if_chain; use rustc_ast::ast::{Expr, ExprKind, Item, ItemKind, MacCall, StrLit, StrStyle}; use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; @@ -12,7 +11,7 @@ use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_parse::parser; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::Symbol; -use rustc_span::{BytePos, FileName, Span}; +use rustc_span::{BytePos, Span}; declare_clippy_lint! { /// **What it does:** This lint warns when you use `println!("")` to @@ -236,15 +235,19 @@ impl EarlyLintPass for Write { } fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &MacCall) { + fn is_build_scripts(cx: &EarlyContext<'_>) -> bool { + // We could leverage the fact that Cargo sets the crate name + // for build scripts to `build_script_build`. + cx.sess + .opts + .crate_name + .as_ref() + .map_or(false, |crate_name| crate_name == "build_script_build") + } + if mac.path == sym!(println) { - let filename = cx.sess.source_map().span_to_filename(mac.span()); - if_chain! { - if let FileName::Real(filename) = filename; - if let Some(filename) = filename.local_path().file_name(); - if filename != "build.rs"; - then { - span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`"); - } + if !is_build_scripts(cx) { + span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`"); } if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { if fmt_str.symbol == Symbol::intern("") { @@ -260,14 +263,8 @@ impl EarlyLintPass for Write { } } } else if mac.path == sym!(print) { - if_chain! { - let filename = cx.sess.source_map().span_to_filename(mac.span()); - if let FileName::Real(filename) = filename; - if let Some(filename) = filename.local_path().file_name(); - if filename != "build.rs"; - then { - span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`"); - } + if !is_build_scripts(cx) { + span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`"); } if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { if check_newlines(&fmt_str) { diff --git a/tests/ui/build.rs b/tests/ui/build.rs deleted file mode 100644 index 2d43d452a4f..00000000000 --- a/tests/ui/build.rs +++ /dev/null @@ -1,10 +0,0 @@ -#![warn(clippy::print_stdout)] - -fn main() { - // Fix #6041 - // - // The `print_stdout` shouldn't be linted in `build.rs` - // as these methods are used for the build script. - println!("Hello"); - print!("Hello"); -} diff --git a/tests/ui/build.stderr b/tests/ui/build.stderr deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/tests/ui/print_stdout_build_script.rs b/tests/ui/print_stdout_build_script.rs new file mode 100644 index 00000000000..b84bf9124fc --- /dev/null +++ b/tests/ui/print_stdout_build_script.rs @@ -0,0 +1,12 @@ +// compile-flags: --crate-name=build_script_build + +#![warn(clippy::print_stdout)] + +fn main() { + // Fix #6041 + // + // The `print_stdout` shouldn't be linted in `build.rs` + // as these methods are used for the build script. + println!("Hello"); + print!("Hello"); +} diff --git a/tests/ui/print_stdout_build_script.stderr b/tests/ui/print_stdout_build_script.stderr new file mode 100644 index 00000000000..e69de29bb2d -- cgit 1.4.1-3-g733a5 From 1479c18396d764482aa0e56b372c5f57a97c102b Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 24 Sep 2020 15:32:03 -0500 Subject: add disallowed_method lint --- CHANGELOG.md | 1 + clippy_lints/src/disallowed_method.rs | 75 ++++++++++++++++++++++ clippy_lints/src/lib.rs | 6 ++ clippy_lints/src/utils/conf.rs | 2 + src/lintlist/mod.rs | 7 ++ tests/ui-toml/toml_disallowed_method/clippy.toml | 1 + .../conf_disallowed_method.rs | 13 ++++ .../conf_disallowed_method.stderr | 16 +++++ tests/ui/disallowed_method.rs | 56 ++++++++++++++++ tests/ui/disallowed_method.stderr | 22 +++++++ 10 files changed, 199 insertions(+) create mode 100644 clippy_lints/src/disallowed_method.rs create mode 100644 tests/ui-toml/toml_disallowed_method/clippy.toml create mode 100644 tests/ui-toml/toml_disallowed_method/conf_disallowed_method.rs create mode 100644 tests/ui-toml/toml_disallowed_method/conf_disallowed_method.stderr create mode 100644 tests/ui/disallowed_method.rs create mode 100644 tests/ui/disallowed_method.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index d1dfe36ffd8..575cbd60792 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1559,6 +1559,7 @@ Released 2018-09-13 [`deref_addrof`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_addrof [`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq [`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord +[`disallowed_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method [`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression [`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown [`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons diff --git a/clippy_lints/src/disallowed_method.rs b/clippy_lints/src/disallowed_method.rs new file mode 100644 index 00000000000..7088b2718f2 --- /dev/null +++ b/clippy_lints/src/disallowed_method.rs @@ -0,0 +1,75 @@ +use crate::utils::span_lint; + +use rustc_data_structures::fx::FxHashSet; +use rustc_lint::{LateLintPass, LateContext}; +use rustc_session::{impl_lint_pass, declare_tool_lint}; +use rustc_hir::*; +use rustc_span::Symbol; + +declare_clippy_lint! { + /// **What it does:** Lints for specific trait methods defined in clippy.toml + /// + /// **Why is this bad?** Some methods are undesirable in certain contexts, + /// and it would be beneficial to lint for them as needed. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // example code where clippy issues a warning + /// foo.bad_method(); // Foo is disallowed + /// ``` + /// Use instead: + /// ```rust + /// // example code which does not raise clippy warning + /// GoodStruct.bad_method(); // not disallowed + /// ``` + pub DISALLOWED_METHOD, + nursery, + "used disallowed method call" +} + +#[derive(Clone, Debug)] +pub struct DisallowedMethod { + disallowed: FxHashSet>, +} + +impl DisallowedMethod { + pub fn new(disallowed: FxHashSet) -> Self { + Self { + disallowed: disallowed.iter() + .map(|s| { + s.split("::").map(|seg| Symbol::intern(seg)).collect::>() + }) + .collect(), + } + } +} + +impl_lint_pass!(DisallowedMethod => [DISALLOWED_METHOD]); + +impl <'tcx> LateLintPass<'tcx> for DisallowedMethod { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::MethodCall(path, _, _args, _) = &expr.kind { + let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap(); + + let method_call = cx.get_def_path(def_id); + if self.disallowed.contains(&method_call) { + span_lint( + cx, + DISALLOWED_METHOD, + expr.span, + &format!( + "Use of a disallowed method `{}`", + method_call + .iter() + .map(|s| s.to_ident_string()) + .collect::>() + .join("::"), + ) + ); + } + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 58112ac8da5..7c886ab87d0 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -175,6 +175,7 @@ mod dbg_macro; mod default_trait_access; mod dereference; mod derive; +mod disallowed_method; mod doc; mod double_comparison; mod double_parens; @@ -525,6 +526,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &derive::DERIVE_ORD_XOR_PARTIAL_ORD, &derive::EXPL_IMPL_CLONE_ON_COPY, &derive::UNSAFE_DERIVE_DESERIALIZE, + &disallowed_method::DISALLOWED_METHOD, &doc::DOC_MARKDOWN, &doc::MISSING_ERRORS_DOC, &doc::MISSING_SAFETY_DOC, @@ -1118,6 +1120,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); store.register_late_pass(|| box manual_strip::ManualStrip); store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem); + let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::>(); + store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(disallowed_methods.clone())); + store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1807,6 +1812,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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(&disallowed_method::DISALLOWED_METHOD), LintId::of(&fallible_impl_from::FALLIBLE_IMPL_FROM), LintId::of(&floating_point_arithmetic::IMPRECISE_FLOPS), LintId::of(&floating_point_arithmetic::SUBOPTIMAL_FLOPS), diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 9c5a12ea9c8..07591ce2229 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -164,6 +164,8 @@ define_Conf! { (max_fn_params_bools, "max_fn_params_bools": u64, 3), /// Lint: WILDCARD_IMPORTS. Whether to allow certain wildcard imports (prelude, super in tests). (warn_on_all_wildcard_imports, "warn_on_all_wildcard_imports": bool, false), + /// Lint: DISALLOWED_METHOD. The list of blacklisted methods to lint about. NB: `bar` is not here since it has legitimate uses + (disallowed_methods, "disallowed_methods": Vec, ["disallowed_method::Foo::bad_method", "disallowed_method::Baz::bad_method", "disallowed_method::Quux::bad_method"].iter().map(ToString::to_string).collect()), } impl Default for Conf { diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 9603023ed06..a77bbcb0abe 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -381,6 +381,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "derive", }, + Lint { + name: "disallowed_method", + group: "nursery", + desc: "default lint description", + deprecation: None, + module: "disallowed_method", + }, Lint { name: "diverging_sub_expression", group: "complexity", diff --git a/tests/ui-toml/toml_disallowed_method/clippy.toml b/tests/ui-toml/toml_disallowed_method/clippy.toml new file mode 100644 index 00000000000..a1f515e443d --- /dev/null +++ b/tests/ui-toml/toml_disallowed_method/clippy.toml @@ -0,0 +1 @@ +disallowed-methods = ["core::iter::traits::iterator::Iterator::sum", "regex::re_unicode::Regex::is_match"] diff --git a/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.rs b/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.rs new file mode 100644 index 00000000000..3d3f0729abd --- /dev/null +++ b/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.rs @@ -0,0 +1,13 @@ +#![warn(clippy::disallowed_method)] + +extern crate regex; +use regex::Regex; + +fn main() { + let a = vec![1, 2, 3, 4]; + let re = Regex::new(r"ab.*c").unwrap(); + + re.is_match("abc"); + + a.iter().sum::(); +} diff --git a/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.stderr b/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.stderr new file mode 100644 index 00000000000..5da551cb430 --- /dev/null +++ b/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.stderr @@ -0,0 +1,16 @@ +error: Use of a disallowed method `regex::re_unicode::Regex::is_match` + --> $DIR/conf_disallowed_method.rs:10:5 + | +LL | re.is_match("abc"); + | ^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::disallowed-method` implied by `-D warnings` + +error: Use of a disallowed method `core::iter::traits::iterator::Iterator::sum` + --> $DIR/conf_disallowed_method.rs:12:5 + | +LL | a.iter().sum::(); + | ^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/disallowed_method.rs b/tests/ui/disallowed_method.rs new file mode 100644 index 00000000000..a54a04b4d2c --- /dev/null +++ b/tests/ui/disallowed_method.rs @@ -0,0 +1,56 @@ +#![warn(clippy::disallowed_method)] +#![allow(clippy::no_effect, clippy::many_single_char_names)] + +struct ImplStruct; + +trait Baz { + fn bad_method(self); +} + +impl Baz for ImplStruct { + fn bad_method(self) {} +} + +struct Foo; + +impl Foo { + fn bad_method(self) {} +} + +struct StaticStruct; + +trait Quux { + fn bad_method(); +} + +impl Quux for StaticStruct { + fn bad_method() {} +} + +struct NormalStruct; + +impl NormalStruct { + fn bad_method(self) {} +} + +struct AttrStruct { + bad_method: i32, +} + +fn main() { + let b = ImplStruct; + let f = Foo; + let c = ImplStruct; + let n = NormalStruct; + let a = AttrStruct{ bad_method: 5 }; + + // lint these + b.bad_method(); + c.bad_method(); + f.bad_method(); + // these are good + // good because not a method call (ExprKind => Call) + StaticStruct::bad_method(); + n.bad_method(); + a.bad_method; +} diff --git a/tests/ui/disallowed_method.stderr b/tests/ui/disallowed_method.stderr new file mode 100644 index 00000000000..93dabf38cfc --- /dev/null +++ b/tests/ui/disallowed_method.stderr @@ -0,0 +1,22 @@ +error: Use of a disallowed method `disallowed_method::Baz::bad_method` + --> $DIR/disallowed_method.rs:48:5 + | +LL | b.bad_method(); + | ^^^^^^^^^^^^^^ + | + = note: `-D clippy::disallowed-method` implied by `-D warnings` + +error: Use of a disallowed method `disallowed_method::Baz::bad_method` + --> $DIR/disallowed_method.rs:49:5 + | +LL | c.bad_method(); + | ^^^^^^^^^^^^^^ + +error: Use of a disallowed method `disallowed_method::Foo::bad_method` + --> $DIR/disallowed_method.rs:50:5 + | +LL | f.bad_method(); + | ^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + -- cgit 1.4.1-3-g733a5 From e1b3f85e984c9d4fba3ef5360892c88990b8391d Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 24 Sep 2020 16:00:46 -0500 Subject: run cargo dev fmt --- clippy_lints/src/disallowed_method.rs | 15 +++++++-------- tests/ui/disallowed_method.rs | 2 +- 2 files changed, 8 insertions(+), 9 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/disallowed_method.rs b/clippy_lints/src/disallowed_method.rs index 5ecdcc0e08a..42fbff8ff87 100644 --- a/clippy_lints/src/disallowed_method.rs +++ b/clippy_lints/src/disallowed_method.rs @@ -1,9 +1,9 @@ use crate::utils::span_lint; use rustc_data_structures::fx::FxHashSet; -use rustc_lint::{LateLintPass, LateContext}; -use rustc_session::{impl_lint_pass, declare_tool_lint}; use rustc_hir::*; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Symbol; declare_clippy_lint! { @@ -38,10 +38,9 @@ pub struct DisallowedMethod { impl DisallowedMethod { pub fn new(disallowed: FxHashSet) -> Self { Self { - disallowed: disallowed.iter() - .map(|s| { - s.split("::").map(|seg| Symbol::intern(seg)).collect::>() - }) + disallowed: disallowed + .iter() + .map(|s| s.split("::").map(|seg| Symbol::intern(seg)).collect::>()) .collect(), } } @@ -49,7 +48,7 @@ impl DisallowedMethod { impl_lint_pass!(DisallowedMethod => [DISALLOWED_METHOD]); -impl <'tcx> LateLintPass<'tcx> for DisallowedMethod { +impl<'tcx> LateLintPass<'tcx> for DisallowedMethod { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::MethodCall(_path, _, _args, _) = &expr.kind { let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap(); @@ -67,7 +66,7 @@ impl <'tcx> LateLintPass<'tcx> for DisallowedMethod { .map(|s| s.to_ident_string()) .collect::>() .join("::"), - ) + ), ); } } diff --git a/tests/ui/disallowed_method.rs b/tests/ui/disallowed_method.rs index a54a04b4d2c..46c9185268c 100644 --- a/tests/ui/disallowed_method.rs +++ b/tests/ui/disallowed_method.rs @@ -42,7 +42,7 @@ fn main() { let f = Foo; let c = ImplStruct; let n = NormalStruct; - let a = AttrStruct{ bad_method: 5 }; + let a = AttrStruct { bad_method: 5 }; // lint these b.bad_method(); -- cgit 1.4.1-3-g733a5 From 9eb52d2eb6c3812367276bc19c23f5d7368de664 Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 24 Sep 2020 16:15:24 -0500 Subject: update toml_unknown_key test --- tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests') 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 6fbba01416a..103ec27e7d7 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 `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`, `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`, `third-party` at line 5 column 1 +error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `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`, `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`, `third-party` at line 5 column 1 error: aborting due to previous error -- cgit 1.4.1-3-g733a5 From f9da2946d81a973b3c25aa5f4739ac7e05c27278 Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 25 Sep 2020 09:38:19 -0500 Subject: update error message, refactor disallowed_method --- clippy_lints/src/disallowed_method.rs | 17 ++++++++--------- .../conf_disallowed_method.stderr | 4 ++-- tests/ui/disallowed_method.stderr | 6 +++--- 3 files changed, 13 insertions(+), 14 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/disallowed_method.rs b/clippy_lints/src/disallowed_method.rs index eea369924a6..603f776e688 100644 --- a/clippy_lints/src/disallowed_method.rs +++ b/clippy_lints/src/disallowed_method.rs @@ -27,7 +27,7 @@ declare_clippy_lint! { /// ``` pub DISALLOWED_METHOD, nursery, - "used disallowed method call" + "use of a disallowed method call" } #[derive(Clone, Debug)] @@ -55,18 +55,17 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethod { let method_call = cx.get_def_path(def_id); if self.disallowed.contains(&method_call) { + let method = method_call + .iter() + .map(|s| s.to_ident_string()) + .collect::>() + .join("::"); + span_lint( cx, DISALLOWED_METHOD, expr.span, - &format!( - "Use of a disallowed method `{}`", - method_call - .iter() - .map(|s| s.to_ident_string()) - .collect::>() - .join("::"), - ), + &format!("use of a disallowed method `{}`", method), ); } } diff --git a/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.stderr b/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.stderr index 5da551cb430..ed91b5a6796 100644 --- a/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.stderr +++ b/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.stderr @@ -1,4 +1,4 @@ -error: Use of a disallowed method `regex::re_unicode::Regex::is_match` +error: use of a disallowed method `regex::re_unicode::Regex::is_match` --> $DIR/conf_disallowed_method.rs:10:5 | LL | re.is_match("abc"); @@ -6,7 +6,7 @@ LL | re.is_match("abc"); | = note: `-D clippy::disallowed-method` implied by `-D warnings` -error: Use of a disallowed method `core::iter::traits::iterator::Iterator::sum` +error: use of a disallowed method `core::iter::traits::iterator::Iterator::sum` --> $DIR/conf_disallowed_method.rs:12:5 | LL | a.iter().sum::(); diff --git a/tests/ui/disallowed_method.stderr b/tests/ui/disallowed_method.stderr index 93dabf38cfc..40db1b946d8 100644 --- a/tests/ui/disallowed_method.stderr +++ b/tests/ui/disallowed_method.stderr @@ -1,4 +1,4 @@ -error: Use of a disallowed method `disallowed_method::Baz::bad_method` +error: use of a disallowed method `disallowed_method::Baz::bad_method` --> $DIR/disallowed_method.rs:48:5 | LL | b.bad_method(); @@ -6,13 +6,13 @@ LL | b.bad_method(); | = note: `-D clippy::disallowed-method` implied by `-D warnings` -error: Use of a disallowed method `disallowed_method::Baz::bad_method` +error: use of a disallowed method `disallowed_method::Baz::bad_method` --> $DIR/disallowed_method.rs:49:5 | LL | c.bad_method(); | ^^^^^^^^^^^^^^ -error: Use of a disallowed method `disallowed_method::Foo::bad_method` +error: use of a disallowed method `disallowed_method::Foo::bad_method` --> $DIR/disallowed_method.rs:50:5 | LL | f.bad_method(); -- cgit 1.4.1-3-g733a5 From d18653158ddde023f8165ef7ba3c607661398abf Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 25 Sep 2020 11:03:45 -0500 Subject: remove useless test, update disallowed_method description --- clippy_lints/src/disallowed_method.rs | 4 +-- clippy_lints/src/utils/conf.rs | 2 +- tests/ui/disallowed_method.rs | 56 ----------------------------------- tests/ui/disallowed_method.stderr | 22 -------------- 4 files changed, 3 insertions(+), 81 deletions(-) delete mode 100644 tests/ui/disallowed_method.rs delete mode 100644 tests/ui/disallowed_method.stderr (limited to 'tests') diff --git a/clippy_lints/src/disallowed_method.rs b/clippy_lints/src/disallowed_method.rs index 603f776e688..581c3242e37 100644 --- a/clippy_lints/src/disallowed_method.rs +++ b/clippy_lints/src/disallowed_method.rs @@ -18,12 +18,12 @@ declare_clippy_lint! { /// /// ```rust,ignore /// // example code where clippy issues a warning - /// foo.bad_method(); // Foo is disallowed + /// foo.bad_method(); // Foo::bad_method is disallowed in the configuration /// ``` /// Use instead: /// ```rust,ignore /// // example code which does not raise clippy warning - /// goodStruct.bad_method(); // not disallowed + /// goodStruct.bad_method(); // GoodStruct::bad_method is not disallowed /// ``` pub DISALLOWED_METHOD, nursery, diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 07591ce2229..03f8c5a2c07 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -165,7 +165,7 @@ define_Conf! { /// Lint: WILDCARD_IMPORTS. Whether to allow certain wildcard imports (prelude, super in tests). (warn_on_all_wildcard_imports, "warn_on_all_wildcard_imports": bool, false), /// Lint: DISALLOWED_METHOD. The list of blacklisted methods to lint about. NB: `bar` is not here since it has legitimate uses - (disallowed_methods, "disallowed_methods": Vec, ["disallowed_method::Foo::bad_method", "disallowed_method::Baz::bad_method", "disallowed_method::Quux::bad_method"].iter().map(ToString::to_string).collect()), + (disallowed_methods, "disallowed_methods": Vec, Vec::::new()), } impl Default for Conf { diff --git a/tests/ui/disallowed_method.rs b/tests/ui/disallowed_method.rs deleted file mode 100644 index 46c9185268c..00000000000 --- a/tests/ui/disallowed_method.rs +++ /dev/null @@ -1,56 +0,0 @@ -#![warn(clippy::disallowed_method)] -#![allow(clippy::no_effect, clippy::many_single_char_names)] - -struct ImplStruct; - -trait Baz { - fn bad_method(self); -} - -impl Baz for ImplStruct { - fn bad_method(self) {} -} - -struct Foo; - -impl Foo { - fn bad_method(self) {} -} - -struct StaticStruct; - -trait Quux { - fn bad_method(); -} - -impl Quux for StaticStruct { - fn bad_method() {} -} - -struct NormalStruct; - -impl NormalStruct { - fn bad_method(self) {} -} - -struct AttrStruct { - bad_method: i32, -} - -fn main() { - let b = ImplStruct; - let f = Foo; - let c = ImplStruct; - let n = NormalStruct; - let a = AttrStruct { bad_method: 5 }; - - // lint these - b.bad_method(); - c.bad_method(); - f.bad_method(); - // these are good - // good because not a method call (ExprKind => Call) - StaticStruct::bad_method(); - n.bad_method(); - a.bad_method; -} diff --git a/tests/ui/disallowed_method.stderr b/tests/ui/disallowed_method.stderr deleted file mode 100644 index 40db1b946d8..00000000000 --- a/tests/ui/disallowed_method.stderr +++ /dev/null @@ -1,22 +0,0 @@ -error: use of a disallowed method `disallowed_method::Baz::bad_method` - --> $DIR/disallowed_method.rs:48:5 - | -LL | b.bad_method(); - | ^^^^^^^^^^^^^^ - | - = note: `-D clippy::disallowed-method` implied by `-D warnings` - -error: use of a disallowed method `disallowed_method::Baz::bad_method` - --> $DIR/disallowed_method.rs:49:5 - | -LL | c.bad_method(); - | ^^^^^^^^^^^^^^ - -error: use of a disallowed method `disallowed_method::Foo::bad_method` - --> $DIR/disallowed_method.rs:50:5 - | -LL | f.bad_method(); - | ^^^^^^^^^^^^^^ - -error: aborting due to 3 previous errors - -- cgit 1.4.1-3-g733a5 From 83294f894d477def457d54f2391a287bcb949f06 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Sat, 26 Sep 2020 23:10:25 +0900 Subject: Some small fixes --- clippy_lints/src/write.rs | 9 ++++----- tests/ui/print_stdout_build_script.rs | 2 +- tests/ui/print_stdout_build_script.stderr | 0 3 files changed, 5 insertions(+), 6 deletions(-) delete mode 100644 tests/ui/print_stdout_build_script.stderr (limited to 'tests') diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 0e9c7098af8..d9d60fffcd7 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -235,9 +235,8 @@ impl EarlyLintPass for Write { } fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &MacCall) { - fn is_build_scripts(cx: &EarlyContext<'_>) -> bool { - // We could leverage the fact that Cargo sets the crate name - // for build scripts to `build_script_build`. + fn is_build_script(cx: &EarlyContext<'_>) -> bool { + // Cargo sets the crate name for build scripts to `build_script_build` cx.sess .opts .crate_name @@ -246,7 +245,7 @@ impl EarlyLintPass for Write { } if mac.path == sym!(println) { - if !is_build_scripts(cx) { + if !is_build_script(cx) { span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`"); } if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { @@ -263,7 +262,7 @@ impl EarlyLintPass for Write { } } } else if mac.path == sym!(print) { - if !is_build_scripts(cx) { + if !is_build_script(cx) { span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`"); } if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { diff --git a/tests/ui/print_stdout_build_script.rs b/tests/ui/print_stdout_build_script.rs index b84bf9124fc..997ebef8a69 100644 --- a/tests/ui/print_stdout_build_script.rs +++ b/tests/ui/print_stdout_build_script.rs @@ -5,7 +5,7 @@ fn main() { // Fix #6041 // - // The `print_stdout` shouldn't be linted in `build.rs` + // The `print_stdout` lint shouldn't emit in `build.rs` // as these methods are used for the build script. println!("Hello"); print!("Hello"); diff --git a/tests/ui/print_stdout_build_script.stderr b/tests/ui/print_stdout_build_script.stderr deleted file mode 100644 index e69de29bb2d..00000000000 -- cgit 1.4.1-3-g733a5 From 5c71352b18ee7a48e825aefd2862b8e0d16ea45b Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Sun, 27 Sep 2020 14:52:06 +1300 Subject: Prevent unnecessary lints from triggering --- clippy_lints/src/loops.rs | 12 ++++++++---- tests/ui/manual_memcpy.rs | 1 - tests/ui/manual_memcpy.stderr | 20 ++++++++++---------- 3 files changed, 18 insertions(+), 15 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 647537933f7..f2df53aee4f 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -768,12 +768,14 @@ fn check_for_loop<'tcx>( body: &'tcx Expr<'_>, expr: &'tcx Expr<'_>, ) { - check_for_loop_range(cx, pat, arg, body, expr); + let is_manual_memcpy_triggered = detect_manual_memcpy(cx, pat, arg, body, expr); + if !is_manual_memcpy_triggered { + check_for_loop_range(cx, pat, arg, body, expr); + check_for_loop_explicit_counter(cx, pat, arg, body, expr); + } check_for_loop_arg(cx, pat, arg, expr); - check_for_loop_explicit_counter(cx, pat, arg, body, expr); check_for_loop_over_map_kv(cx, pat, arg, body, expr); check_for_mut_range_bound(cx, arg, body); - detect_manual_memcpy(cx, pat, arg, body, expr); detect_same_item_push(cx, pat, arg, body, expr); } @@ -1152,7 +1154,7 @@ fn detect_manual_memcpy<'tcx>( arg: &'tcx Expr<'_>, body: &'tcx Expr<'_>, expr: &'tcx Expr<'_>, -) { +) -> bool { if let Some(higher::Range { start: Some(start), end: Some(end), @@ -1222,9 +1224,11 @@ fn detect_manual_memcpy<'tcx>( big_sugg, Applicability::Unspecified, ); + return true; } } } + false } // Scans the body of the for loop and determines whether lint should be given diff --git a/tests/ui/manual_memcpy.rs b/tests/ui/manual_memcpy.rs index 8318fd89811..84758275dd7 100644 --- a/tests/ui/manual_memcpy.rs +++ b/tests/ui/manual_memcpy.rs @@ -115,7 +115,6 @@ pub fn manual_copy(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { } } -#[allow(clippy::needless_range_loop, clippy::explicit_counter_loop)] pub fn manual_copy_with_counters(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { let mut count = 0; for i in 3..src.len() { diff --git a/tests/ui/manual_memcpy.stderr b/tests/ui/manual_memcpy.stderr index d189bde0508..464b18984fb 100644 --- a/tests/ui/manual_memcpy.stderr +++ b/tests/ui/manual_memcpy.stderr @@ -79,49 +79,49 @@ LL | for i in 0..0 { | ^^^^ help: try replacing the loop by: `dst[..0].clone_from_slice(&src[..0])` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:121:14 + --> $DIR/manual_memcpy.rs:120:14 | LL | for i in 3..src.len() { | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[3..src.len()].clone_from_slice(&src[..(src.len() - 3)])` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:127:14 + --> $DIR/manual_memcpy.rs:126:14 | LL | for i in 3..src.len() { | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..(src.len() - 3)].clone_from_slice(&src[3..])` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:133:14 + --> $DIR/manual_memcpy.rs:132:14 | LL | for i in 0..src.len() { | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[3..(src.len() + 3)].clone_from_slice(&src[..])` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:139:14 + --> $DIR/manual_memcpy.rs:138:14 | LL | for i in 0..src.len() { | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[3..(src.len() + 3)])` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:145:14 + --> $DIR/manual_memcpy.rs:144:14 | LL | for i in 3..(3 + src.len()) { | ^^^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[3..((3 + src.len()))].clone_from_slice(&src[..((3 + src.len()) - 3)])` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:151:14 + --> $DIR/manual_memcpy.rs:150:14 | LL | for i in 5..src.len() { | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[5..src.len()].clone_from_slice(&src[(3 - 2)..((src.len() - 2) + 3 - 5)])` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:157:14 + --> $DIR/manual_memcpy.rs:156:14 | LL | for i in 3..10 { | ^^^^^ help: try replacing the loop by: `dst[3..10].clone_from_slice(&src[5..(10 + 5 - 3)])` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:164:14 + --> $DIR/manual_memcpy.rs:163:14 | LL | for i in 0..src.len() { | ^^^^^^^^^^^^ @@ -133,13 +133,13 @@ LL | dst2[30..(src.len() + 30)].clone_from_slice(&src[..]) { | error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:174:14 + --> $DIR/manual_memcpy.rs:173:14 | LL | for i in 0..1 << 1 { | ^^^^^^^^^ help: try replacing the loop by: `dst[(0 << 1)..((1 << 1) + (0 << 1))].clone_from_slice(&src[2..((1 << 1) + 2)])` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:182:14 + --> $DIR/manual_memcpy.rs:181:14 | LL | for i in 0..src.len() { | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..])` -- cgit 1.4.1-3-g733a5 From 99aceebf1c7cb382e18d66914bd9f576e529aa99 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Sun, 27 Sep 2020 16:38:41 +1300 Subject: Use the spans of the entire `for` loops for suggestions --- clippy_lints/src/loops.rs | 23 +++-- tests/ui/manual_memcpy.stderr | 196 +++++++++++++++++++++++++++--------------- 2 files changed, 140 insertions(+), 79 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index f2df53aee4f..7c8b6f483bc 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -779,6 +779,17 @@ fn check_for_loop<'tcx>( detect_same_item_push(cx, pat, arg, body, expr); } +// this function assumes the given expression is a `for` loop. +fn get_span_of_entire_for_loop(expr: &Expr<'_>) -> Span { + // for some reason this is the only way to get the `Span` + // of the entire `for` loop + if let ExprKind::Match(_, arms, _) = &expr.kind { + arms[0].body.span + } else { + unreachable!() + } +} + fn same_var<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, var: HirId) -> bool { if_chain! { if let ExprKind::Path(qpath) = &expr.kind; @@ -1138,7 +1149,7 @@ fn build_manual_memcpy_suggestion<'tcx>( }; format!( - "{}.clone_from_slice(&{}[{}..{}])", + "{}.clone_from_slice(&{}[{}..{}]);", dst, src_base_str, src_offset.maybe_par(), @@ -1218,7 +1229,7 @@ fn detect_manual_memcpy<'tcx>( span_lint_and_sugg( cx, MANUAL_MEMCPY, - expr.span, + get_span_of_entire_for_loop(expr), "it looks like you're manually copying between slices", "try replacing the loop by", big_sugg, @@ -1734,13 +1745,7 @@ fn check_for_loop_explicit_counter<'tcx>( then { let mut applicability = Applicability::MachineApplicable; - // for some reason this is the only way to get the `Span` - // of the entire `for` loop - let for_span = if let ExprKind::Match(_, arms, _) = &expr.kind { - arms[0].body.span - } else { - unreachable!() - }; + let for_span = get_span_of_entire_for_loop(expr); span_lint_and_sugg( cx, diff --git a/tests/ui/manual_memcpy.stderr b/tests/ui/manual_memcpy.stderr index 464b18984fb..db62ed90d97 100644 --- a/tests/ui/manual_memcpy.stderr +++ b/tests/ui/manual_memcpy.stderr @@ -1,148 +1,204 @@ error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:7:14 + --> $DIR/manual_memcpy.rs:7:5 | -LL | for i in 0..src.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..])` +LL | / for i in 0..src.len() { +LL | | dst[i] = src[i]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..]);` | = note: `-D clippy::manual-memcpy` implied by `-D warnings` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:12:14 + --> $DIR/manual_memcpy.rs:12:5 | -LL | for i in 0..src.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[10..(src.len() + 10)].clone_from_slice(&src[..])` +LL | / for i in 0..src.len() { +LL | | dst[i + 10] = src[i]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[10..(src.len() + 10)].clone_from_slice(&src[..]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:17:14 + --> $DIR/manual_memcpy.rs:17:5 | -LL | for i in 0..src.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[10..(src.len() + 10)])` +LL | / for i in 0..src.len() { +LL | | dst[i] = src[i + 10]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[10..(src.len() + 10)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:22:14 + --> $DIR/manual_memcpy.rs:22:5 | -LL | for i in 11..src.len() { - | ^^^^^^^^^^^^^ help: try replacing the loop by: `dst[11..src.len()].clone_from_slice(&src[(11 - 10)..(src.len() - 10)])` +LL | / for i in 11..src.len() { +LL | | dst[i] = src[i - 10]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[11..src.len()].clone_from_slice(&src[(11 - 10)..(src.len() - 10)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:27:14 + --> $DIR/manual_memcpy.rs:27:5 | -LL | for i in 0..dst.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst.clone_from_slice(&src[..dst.len()])` +LL | / for i in 0..dst.len() { +LL | | dst[i] = src[i]; +LL | | } + | |_____^ help: try replacing the loop by: `dst.clone_from_slice(&src[..dst.len()]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:40:14 + --> $DIR/manual_memcpy.rs:40:5 | -LL | for i in 10..256 { - | ^^^^^^^ +LL | / for i in 10..256 { +LL | | dst[i] = src[i - 5]; +LL | | dst2[i + 500] = src[i] +LL | | } + | |_____^ | help: try replacing the loop by | -LL | for i in dst[10..256].clone_from_slice(&src[(10 - 5)..(256 - 5)]) -LL | dst2[(10 + 500)..(256 + 500)].clone_from_slice(&src[10..256]) { +LL | dst[10..256].clone_from_slice(&src[(10 - 5)..(256 - 5)]); +LL | dst2[(10 + 500)..(256 + 500)].clone_from_slice(&src[10..256]); | error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:52:14 + --> $DIR/manual_memcpy.rs:52:5 | -LL | for i in 10..LOOP_OFFSET { - | ^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[(10 + LOOP_OFFSET)..(LOOP_OFFSET + LOOP_OFFSET)].clone_from_slice(&src[(10 - some_var)..(LOOP_OFFSET - some_var)])` +LL | / for i in 10..LOOP_OFFSET { +LL | | dst[i + LOOP_OFFSET] = src[i - some_var]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[(10 + LOOP_OFFSET)..(LOOP_OFFSET + LOOP_OFFSET)].clone_from_slice(&src[(10 - some_var)..(LOOP_OFFSET - some_var)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:65:14 + --> $DIR/manual_memcpy.rs:65:5 | -LL | for i in 0..src_vec.len() { - | ^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst_vec[..src_vec.len()].clone_from_slice(&src_vec[..])` +LL | / for i in 0..src_vec.len() { +LL | | dst_vec[i] = src_vec[i]; +LL | | } + | |_____^ help: try replacing the loop by: `dst_vec[..src_vec.len()].clone_from_slice(&src_vec[..]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:94:14 + --> $DIR/manual_memcpy.rs:94:5 | -LL | for i in from..from + src.len() { - | ^^^^^^^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..(from + src.len())].clone_from_slice(&src[..(from + src.len() - from)])` +LL | / for i in from..from + src.len() { +LL | | dst[i] = src[i - from]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[from..(from + src.len())].clone_from_slice(&src[..(from + src.len() - from)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:98:14 + --> $DIR/manual_memcpy.rs:98:5 | -LL | for i in from..from + 3 { - | ^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..(from + 3)].clone_from_slice(&src[..(from + 3 - from)])` +LL | / for i in from..from + 3 { +LL | | dst[i] = src[i - from]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[from..(from + 3)].clone_from_slice(&src[..(from + 3 - from)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:103:14 + --> $DIR/manual_memcpy.rs:103:5 | -LL | for i in 0..5 { - | ^^^^ help: try replacing the loop by: `dst[..5].clone_from_slice(&src[..5])` +LL | / for i in 0..5 { +LL | | dst[i - 0] = src[i]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[..5].clone_from_slice(&src[..5]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:108:14 + --> $DIR/manual_memcpy.rs:108:5 | -LL | for i in 0..0 { - | ^^^^ help: try replacing the loop by: `dst[..0].clone_from_slice(&src[..0])` +LL | / for i in 0..0 { +LL | | dst[i] = src[i]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[..0].clone_from_slice(&src[..0]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:120:14 + --> $DIR/manual_memcpy.rs:120:5 | -LL | for i in 3..src.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[3..src.len()].clone_from_slice(&src[..(src.len() - 3)])` +LL | / for i in 3..src.len() { +LL | | dst[i] = src[count]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[3..src.len()].clone_from_slice(&src[..(src.len() - 3)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:126:14 + --> $DIR/manual_memcpy.rs:126:5 | -LL | for i in 3..src.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..(src.len() - 3)].clone_from_slice(&src[3..])` +LL | / for i in 3..src.len() { +LL | | dst[count] = src[i]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[..(src.len() - 3)].clone_from_slice(&src[3..]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:132:14 + --> $DIR/manual_memcpy.rs:132:5 | -LL | for i in 0..src.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[3..(src.len() + 3)].clone_from_slice(&src[..])` +LL | / for i in 0..src.len() { +LL | | dst[count] = src[i]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[3..(src.len() + 3)].clone_from_slice(&src[..]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:138:14 + --> $DIR/manual_memcpy.rs:138:5 | -LL | for i in 0..src.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[3..(src.len() + 3)])` +LL | / for i in 0..src.len() { +LL | | dst[i] = src[count]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[3..(src.len() + 3)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:144:14 + --> $DIR/manual_memcpy.rs:144:5 | -LL | for i in 3..(3 + src.len()) { - | ^^^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[3..((3 + src.len()))].clone_from_slice(&src[..((3 + src.len()) - 3)])` +LL | / for i in 3..(3 + src.len()) { +LL | | dst[i] = src[count]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[3..((3 + src.len()))].clone_from_slice(&src[..((3 + src.len()) - 3)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:150:14 + --> $DIR/manual_memcpy.rs:150:5 | -LL | for i in 5..src.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[5..src.len()].clone_from_slice(&src[(3 - 2)..((src.len() - 2) + 3 - 5)])` +LL | / for i in 5..src.len() { +LL | | dst[i] = src[count - 2]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[5..src.len()].clone_from_slice(&src[(3 - 2)..((src.len() - 2) + 3 - 5)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:156:14 + --> $DIR/manual_memcpy.rs:156:5 | -LL | for i in 3..10 { - | ^^^^^ help: try replacing the loop by: `dst[3..10].clone_from_slice(&src[5..(10 + 5 - 3)])` +LL | / for i in 3..10 { +LL | | dst[i] = src[count]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[3..10].clone_from_slice(&src[5..(10 + 5 - 3)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:163:14 + --> $DIR/manual_memcpy.rs:163:5 | -LL | for i in 0..src.len() { - | ^^^^^^^^^^^^ +LL | / for i in 0..src.len() { +LL | | dst[count] = src[i]; +LL | | dst2[count2] = src[i]; +LL | | count += 1; +LL | | count2 += 1; +LL | | } + | |_____^ | help: try replacing the loop by | -LL | for i in dst[3..(src.len() + 3)].clone_from_slice(&src[..]) -LL | dst2[30..(src.len() + 30)].clone_from_slice(&src[..]) { +LL | dst[3..(src.len() + 3)].clone_from_slice(&src[..]); +LL | dst2[30..(src.len() + 30)].clone_from_slice(&src[..]); | error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:173:14 + --> $DIR/manual_memcpy.rs:173:5 | -LL | for i in 0..1 << 1 { - | ^^^^^^^^^ help: try replacing the loop by: `dst[(0 << 1)..((1 << 1) + (0 << 1))].clone_from_slice(&src[2..((1 << 1) + 2)])` +LL | / for i in 0..1 << 1 { +LL | | dst[count] = src[i + 2]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[(0 << 1)..((1 << 1) + (0 << 1))].clone_from_slice(&src[2..((1 << 1) + 2)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:181:14 + --> $DIR/manual_memcpy.rs:181:5 | -LL | for i in 0..src.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..])` +LL | / for i in 0..src.len() { +LL | | dst[i] = src[i].clone(); +LL | | } + | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..]);` error: aborting due to 22 previous errors -- cgit 1.4.1-3-g733a5 From ec94bd6cb45e337fd10ca19fadb9a71ecb67db5f Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Mon, 28 Sep 2020 02:43:45 +1300 Subject: split up the `manual_memcpy` test --- tests/ui/manual_memcpy.rs | 186 ------------------- tests/ui/manual_memcpy.stderr | 204 --------------------- tests/ui/manual_memcpy/with_loop_counters.rs | 64 +++++++ tests/ui/manual_memcpy/with_loop_counters.stderr | 93 ++++++++++ tests/ui/manual_memcpy/without_loop_counters.rs | 125 +++++++++++++ .../ui/manual_memcpy/without_loop_counters.stderr | 115 ++++++++++++ 6 files changed, 397 insertions(+), 390 deletions(-) delete mode 100644 tests/ui/manual_memcpy.rs delete mode 100644 tests/ui/manual_memcpy.stderr create mode 100644 tests/ui/manual_memcpy/with_loop_counters.rs create mode 100644 tests/ui/manual_memcpy/with_loop_counters.stderr create mode 100644 tests/ui/manual_memcpy/without_loop_counters.rs create mode 100644 tests/ui/manual_memcpy/without_loop_counters.stderr (limited to 'tests') diff --git a/tests/ui/manual_memcpy.rs b/tests/ui/manual_memcpy.rs deleted file mode 100644 index 84758275dd7..00000000000 --- a/tests/ui/manual_memcpy.rs +++ /dev/null @@ -1,186 +0,0 @@ -#![warn(clippy::needless_range_loop, clippy::manual_memcpy)] - -const LOOP_OFFSET: usize = 5000; - -pub fn manual_copy(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { - // plain manual memcpy - for i in 0..src.len() { - dst[i] = src[i]; - } - - // dst offset memcpy - for i in 0..src.len() { - dst[i + 10] = src[i]; - } - - // src offset memcpy - for i in 0..src.len() { - dst[i] = src[i + 10]; - } - - // src offset memcpy - for i in 11..src.len() { - dst[i] = src[i - 10]; - } - - // overwrite entire dst - for i in 0..dst.len() { - dst[i] = src[i]; - } - - // manual copy with branch - can't easily convert to memcpy! - for i in 0..src.len() { - dst[i] = src[i]; - if dst[i] > 5 { - break; - } - } - - // multiple copies - suggest two memcpy statements - for i in 10..256 { - dst[i] = src[i - 5]; - dst2[i + 500] = src[i] - } - - // this is a reversal - the copy lint shouldn't be triggered - for i in 10..LOOP_OFFSET { - dst[i + LOOP_OFFSET] = src[LOOP_OFFSET - i]; - } - - let some_var = 5; - // Offset in variable - for i in 10..LOOP_OFFSET { - dst[i + LOOP_OFFSET] = src[i - some_var]; - } - - // Non continuous copy - don't trigger lint - for i in 0..10 { - dst[i + i] = src[i]; - } - - let src_vec = vec![1, 2, 3, 4, 5]; - let mut dst_vec = vec![0, 0, 0, 0, 0]; - - // make sure vectors are supported - for i in 0..src_vec.len() { - dst_vec[i] = src_vec[i]; - } - - // lint should not trigger when either - // source or destination type is not - // slice-like, like DummyStruct - struct DummyStruct(i32); - - impl ::std::ops::Index for DummyStruct { - type Output = i32; - - fn index(&self, _: usize) -> &i32 { - &self.0 - } - } - - let src = DummyStruct(5); - let mut dst_vec = vec![0; 10]; - - for i in 0..10 { - dst_vec[i] = src[i]; - } - - // Simplify suggestion (issue #3004) - let src = [0, 1, 2, 3, 4]; - let mut dst = [0, 0, 0, 0, 0, 0]; - let from = 1; - - for i in from..from + src.len() { - dst[i] = src[i - from]; - } - - for i in from..from + 3 { - dst[i] = src[i - from]; - } - - #[allow(clippy::identity_op)] - for i in 0..5 { - dst[i - 0] = src[i]; - } - - #[allow(clippy::reversed_empty_ranges)] - for i in 0..0 { - dst[i] = src[i]; - } - - // `RangeTo` `for` loop - don't trigger lint - for i in 0.. { - dst[i] = src[i]; - } -} - -pub fn manual_copy_with_counters(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { - let mut count = 0; - for i in 3..src.len() { - dst[i] = src[count]; - count += 1; - } - - let mut count = 0; - for i in 3..src.len() { - dst[count] = src[i]; - count += 1; - } - - let mut count = 3; - for i in 0..src.len() { - dst[count] = src[i]; - count += 1; - } - - let mut count = 3; - for i in 0..src.len() { - dst[i] = src[count]; - count += 1; - } - - let mut count = 0; - for i in 3..(3 + src.len()) { - dst[i] = src[count]; - count += 1; - } - - let mut count = 3; - for i in 5..src.len() { - dst[i] = src[count - 2]; - count += 1; - } - - let mut count = 5; - for i in 3..10 { - dst[i] = src[count]; - count += 1; - } - - let mut count = 3; - let mut count2 = 30; - for i in 0..src.len() { - dst[count] = src[i]; - dst2[count2] = src[i]; - count += 1; - count2 += 1; - } - - // make sure parentheses are added properly to bitwise operators, which have lower precedence than - // arithmetric ones - let mut count = 0 << 1; - for i in 0..1 << 1 { - dst[count] = src[i + 2]; - count += 1; - } -} - -#[warn(clippy::needless_range_loop, clippy::manual_memcpy)] -pub fn manual_clone(src: &[String], dst: &mut [String]) { - for i in 0..src.len() { - dst[i] = src[i].clone(); - } -} - -fn main() {} diff --git a/tests/ui/manual_memcpy.stderr b/tests/ui/manual_memcpy.stderr deleted file mode 100644 index db62ed90d97..00000000000 --- a/tests/ui/manual_memcpy.stderr +++ /dev/null @@ -1,204 +0,0 @@ -error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:7:5 - | -LL | / for i in 0..src.len() { -LL | | dst[i] = src[i]; -LL | | } - | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..]);` - | - = note: `-D clippy::manual-memcpy` implied by `-D warnings` - -error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:12:5 - | -LL | / for i in 0..src.len() { -LL | | dst[i + 10] = src[i]; -LL | | } - | |_____^ help: try replacing the loop by: `dst[10..(src.len() + 10)].clone_from_slice(&src[..]);` - -error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:17:5 - | -LL | / for i in 0..src.len() { -LL | | dst[i] = src[i + 10]; -LL | | } - | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[10..(src.len() + 10)]);` - -error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:22:5 - | -LL | / for i in 11..src.len() { -LL | | dst[i] = src[i - 10]; -LL | | } - | |_____^ help: try replacing the loop by: `dst[11..src.len()].clone_from_slice(&src[(11 - 10)..(src.len() - 10)]);` - -error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:27:5 - | -LL | / for i in 0..dst.len() { -LL | | dst[i] = src[i]; -LL | | } - | |_____^ help: try replacing the loop by: `dst.clone_from_slice(&src[..dst.len()]);` - -error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:40:5 - | -LL | / for i in 10..256 { -LL | | dst[i] = src[i - 5]; -LL | | dst2[i + 500] = src[i] -LL | | } - | |_____^ - | -help: try replacing the loop by - | -LL | dst[10..256].clone_from_slice(&src[(10 - 5)..(256 - 5)]); -LL | dst2[(10 + 500)..(256 + 500)].clone_from_slice(&src[10..256]); - | - -error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:52:5 - | -LL | / for i in 10..LOOP_OFFSET { -LL | | dst[i + LOOP_OFFSET] = src[i - some_var]; -LL | | } - | |_____^ help: try replacing the loop by: `dst[(10 + LOOP_OFFSET)..(LOOP_OFFSET + LOOP_OFFSET)].clone_from_slice(&src[(10 - some_var)..(LOOP_OFFSET - some_var)]);` - -error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:65:5 - | -LL | / for i in 0..src_vec.len() { -LL | | dst_vec[i] = src_vec[i]; -LL | | } - | |_____^ help: try replacing the loop by: `dst_vec[..src_vec.len()].clone_from_slice(&src_vec[..]);` - -error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:94:5 - | -LL | / for i in from..from + src.len() { -LL | | dst[i] = src[i - from]; -LL | | } - | |_____^ help: try replacing the loop by: `dst[from..(from + src.len())].clone_from_slice(&src[..(from + src.len() - from)]);` - -error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:98:5 - | -LL | / for i in from..from + 3 { -LL | | dst[i] = src[i - from]; -LL | | } - | |_____^ help: try replacing the loop by: `dst[from..(from + 3)].clone_from_slice(&src[..(from + 3 - from)]);` - -error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:103:5 - | -LL | / for i in 0..5 { -LL | | dst[i - 0] = src[i]; -LL | | } - | |_____^ help: try replacing the loop by: `dst[..5].clone_from_slice(&src[..5]);` - -error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:108:5 - | -LL | / for i in 0..0 { -LL | | dst[i] = src[i]; -LL | | } - | |_____^ help: try replacing the loop by: `dst[..0].clone_from_slice(&src[..0]);` - -error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:120:5 - | -LL | / for i in 3..src.len() { -LL | | dst[i] = src[count]; -LL | | count += 1; -LL | | } - | |_____^ help: try replacing the loop by: `dst[3..src.len()].clone_from_slice(&src[..(src.len() - 3)]);` - -error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:126:5 - | -LL | / for i in 3..src.len() { -LL | | dst[count] = src[i]; -LL | | count += 1; -LL | | } - | |_____^ help: try replacing the loop by: `dst[..(src.len() - 3)].clone_from_slice(&src[3..]);` - -error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:132:5 - | -LL | / for i in 0..src.len() { -LL | | dst[count] = src[i]; -LL | | count += 1; -LL | | } - | |_____^ help: try replacing the loop by: `dst[3..(src.len() + 3)].clone_from_slice(&src[..]);` - -error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:138:5 - | -LL | / for i in 0..src.len() { -LL | | dst[i] = src[count]; -LL | | count += 1; -LL | | } - | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[3..(src.len() + 3)]);` - -error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:144:5 - | -LL | / for i in 3..(3 + src.len()) { -LL | | dst[i] = src[count]; -LL | | count += 1; -LL | | } - | |_____^ help: try replacing the loop by: `dst[3..((3 + src.len()))].clone_from_slice(&src[..((3 + src.len()) - 3)]);` - -error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:150:5 - | -LL | / for i in 5..src.len() { -LL | | dst[i] = src[count - 2]; -LL | | count += 1; -LL | | } - | |_____^ help: try replacing the loop by: `dst[5..src.len()].clone_from_slice(&src[(3 - 2)..((src.len() - 2) + 3 - 5)]);` - -error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:156:5 - | -LL | / for i in 3..10 { -LL | | dst[i] = src[count]; -LL | | count += 1; -LL | | } - | |_____^ help: try replacing the loop by: `dst[3..10].clone_from_slice(&src[5..(10 + 5 - 3)]);` - -error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:163:5 - | -LL | / for i in 0..src.len() { -LL | | dst[count] = src[i]; -LL | | dst2[count2] = src[i]; -LL | | count += 1; -LL | | count2 += 1; -LL | | } - | |_____^ - | -help: try replacing the loop by - | -LL | dst[3..(src.len() + 3)].clone_from_slice(&src[..]); -LL | dst2[30..(src.len() + 30)].clone_from_slice(&src[..]); - | - -error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:173:5 - | -LL | / for i in 0..1 << 1 { -LL | | dst[count] = src[i + 2]; -LL | | count += 1; -LL | | } - | |_____^ help: try replacing the loop by: `dst[(0 << 1)..((1 << 1) + (0 << 1))].clone_from_slice(&src[2..((1 << 1) + 2)]);` - -error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:181:5 - | -LL | / for i in 0..src.len() { -LL | | dst[i] = src[i].clone(); -LL | | } - | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..]);` - -error: aborting due to 22 previous errors - diff --git a/tests/ui/manual_memcpy/with_loop_counters.rs b/tests/ui/manual_memcpy/with_loop_counters.rs new file mode 100644 index 00000000000..a49ba9eb10a --- /dev/null +++ b/tests/ui/manual_memcpy/with_loop_counters.rs @@ -0,0 +1,64 @@ +#![warn(clippy::needless_range_loop, clippy::manual_memcpy)] + +pub fn manual_copy_with_counters(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { + let mut count = 0; + for i in 3..src.len() { + dst[i] = src[count]; + count += 1; + } + + let mut count = 0; + for i in 3..src.len() { + dst[count] = src[i]; + count += 1; + } + + let mut count = 3; + for i in 0..src.len() { + dst[count] = src[i]; + count += 1; + } + + let mut count = 3; + for i in 0..src.len() { + dst[i] = src[count]; + count += 1; + } + + let mut count = 0; + for i in 3..(3 + src.len()) { + dst[i] = src[count]; + count += 1; + } + + let mut count = 3; + for i in 5..src.len() { + dst[i] = src[count - 2]; + count += 1; + } + + let mut count = 5; + for i in 3..10 { + dst[i] = src[count]; + count += 1; + } + + let mut count = 3; + let mut count2 = 30; + for i in 0..src.len() { + dst[count] = src[i]; + dst2[count2] = src[i]; + count += 1; + count2 += 1; + } + + // make sure parentheses are added properly to bitwise operators, which have lower precedence than + // arithmetric ones + let mut count = 0 << 1; + for i in 0..1 << 1 { + dst[count] = src[i + 2]; + count += 1; + } +} + +fn main() {} diff --git a/tests/ui/manual_memcpy/with_loop_counters.stderr b/tests/ui/manual_memcpy/with_loop_counters.stderr new file mode 100644 index 00000000000..24393ad9b4d --- /dev/null +++ b/tests/ui/manual_memcpy/with_loop_counters.stderr @@ -0,0 +1,93 @@ +error: it looks like you're manually copying between slices + --> $DIR/with_loop_counters.rs:5:5 + | +LL | / for i in 3..src.len() { +LL | | dst[i] = src[count]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[3..src.len()].clone_from_slice(&src[..(src.len() - 3)]);` + | + = note: `-D clippy::manual-memcpy` implied by `-D warnings` + +error: it looks like you're manually copying between slices + --> $DIR/with_loop_counters.rs:11:5 + | +LL | / for i in 3..src.len() { +LL | | dst[count] = src[i]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[..(src.len() - 3)].clone_from_slice(&src[3..]);` + +error: it looks like you're manually copying between slices + --> $DIR/with_loop_counters.rs:17:5 + | +LL | / for i in 0..src.len() { +LL | | dst[count] = src[i]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[3..(src.len() + 3)].clone_from_slice(&src[..]);` + +error: it looks like you're manually copying between slices + --> $DIR/with_loop_counters.rs:23:5 + | +LL | / for i in 0..src.len() { +LL | | dst[i] = src[count]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[3..(src.len() + 3)]);` + +error: it looks like you're manually copying between slices + --> $DIR/with_loop_counters.rs:29:5 + | +LL | / for i in 3..(3 + src.len()) { +LL | | dst[i] = src[count]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[3..((3 + src.len()))].clone_from_slice(&src[..((3 + src.len()) - 3)]);` + +error: it looks like you're manually copying between slices + --> $DIR/with_loop_counters.rs:35:5 + | +LL | / for i in 5..src.len() { +LL | | dst[i] = src[count - 2]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[5..src.len()].clone_from_slice(&src[(3 - 2)..((src.len() - 2) + 3 - 5)]);` + +error: it looks like you're manually copying between slices + --> $DIR/with_loop_counters.rs:41:5 + | +LL | / for i in 3..10 { +LL | | dst[i] = src[count]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[3..10].clone_from_slice(&src[5..(10 + 5 - 3)]);` + +error: it looks like you're manually copying between slices + --> $DIR/with_loop_counters.rs:48:5 + | +LL | / for i in 0..src.len() { +LL | | dst[count] = src[i]; +LL | | dst2[count2] = src[i]; +LL | | count += 1; +LL | | count2 += 1; +LL | | } + | |_____^ + | +help: try replacing the loop by + | +LL | dst[3..(src.len() + 3)].clone_from_slice(&src[..]); +LL | dst2[30..(src.len() + 30)].clone_from_slice(&src[..]); + | + +error: it looks like you're manually copying between slices + --> $DIR/with_loop_counters.rs:58:5 + | +LL | / for i in 0..1 << 1 { +LL | | dst[count] = src[i + 2]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[(0 << 1)..((1 << 1) + (0 << 1))].clone_from_slice(&src[2..((1 << 1) + 2)]);` + +error: aborting due to 9 previous errors + diff --git a/tests/ui/manual_memcpy/without_loop_counters.rs b/tests/ui/manual_memcpy/without_loop_counters.rs new file mode 100644 index 00000000000..0083f94798f --- /dev/null +++ b/tests/ui/manual_memcpy/without_loop_counters.rs @@ -0,0 +1,125 @@ +#![warn(clippy::needless_range_loop, clippy::manual_memcpy)] + +const LOOP_OFFSET: usize = 5000; + +pub fn manual_copy(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { + // plain manual memcpy + for i in 0..src.len() { + dst[i] = src[i]; + } + + // dst offset memcpy + for i in 0..src.len() { + dst[i + 10] = src[i]; + } + + // src offset memcpy + for i in 0..src.len() { + dst[i] = src[i + 10]; + } + + // src offset memcpy + for i in 11..src.len() { + dst[i] = src[i - 10]; + } + + // overwrite entire dst + for i in 0..dst.len() { + dst[i] = src[i]; + } + + // manual copy with branch - can't easily convert to memcpy! + for i in 0..src.len() { + dst[i] = src[i]; + if dst[i] > 5 { + break; + } + } + + // multiple copies - suggest two memcpy statements + for i in 10..256 { + dst[i] = src[i - 5]; + dst2[i + 500] = src[i] + } + + // this is a reversal - the copy lint shouldn't be triggered + for i in 10..LOOP_OFFSET { + dst[i + LOOP_OFFSET] = src[LOOP_OFFSET - i]; + } + + let some_var = 5; + // Offset in variable + for i in 10..LOOP_OFFSET { + dst[i + LOOP_OFFSET] = src[i - some_var]; + } + + // Non continuous copy - don't trigger lint + for i in 0..10 { + dst[i + i] = src[i]; + } + + let src_vec = vec![1, 2, 3, 4, 5]; + let mut dst_vec = vec![0, 0, 0, 0, 0]; + + // make sure vectors are supported + for i in 0..src_vec.len() { + dst_vec[i] = src_vec[i]; + } + + // lint should not trigger when either + // source or destination type is not + // slice-like, like DummyStruct + struct DummyStruct(i32); + + impl ::std::ops::Index for DummyStruct { + type Output = i32; + + fn index(&self, _: usize) -> &i32 { + &self.0 + } + } + + let src = DummyStruct(5); + let mut dst_vec = vec![0; 10]; + + for i in 0..10 { + dst_vec[i] = src[i]; + } + + // Simplify suggestion (issue #3004) + let src = [0, 1, 2, 3, 4]; + let mut dst = [0, 0, 0, 0, 0, 0]; + let from = 1; + + for i in from..from + src.len() { + dst[i] = src[i - from]; + } + + for i in from..from + 3 { + dst[i] = src[i - from]; + } + + #[allow(clippy::identity_op)] + for i in 0..5 { + dst[i - 0] = src[i]; + } + + #[allow(clippy::reversed_empty_ranges)] + for i in 0..0 { + dst[i] = src[i]; + } + + // `RangeTo` `for` loop - don't trigger lint + for i in 0.. { + dst[i] = src[i]; + } +} + +#[warn(clippy::needless_range_loop, clippy::manual_memcpy)] +pub fn manual_clone(src: &[String], dst: &mut [String]) { + for i in 0..src.len() { + dst[i] = src[i].clone(); + } +} + +fn main() {} diff --git a/tests/ui/manual_memcpy/without_loop_counters.stderr b/tests/ui/manual_memcpy/without_loop_counters.stderr new file mode 100644 index 00000000000..54b966f6e54 --- /dev/null +++ b/tests/ui/manual_memcpy/without_loop_counters.stderr @@ -0,0 +1,115 @@ +error: it looks like you're manually copying between slices + --> $DIR/without_loop_counters.rs:7:5 + | +LL | / for i in 0..src.len() { +LL | | dst[i] = src[i]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..]);` + | + = note: `-D clippy::manual-memcpy` implied by `-D warnings` + +error: it looks like you're manually copying between slices + --> $DIR/without_loop_counters.rs:12:5 + | +LL | / for i in 0..src.len() { +LL | | dst[i + 10] = src[i]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[10..(src.len() + 10)].clone_from_slice(&src[..]);` + +error: it looks like you're manually copying between slices + --> $DIR/without_loop_counters.rs:17:5 + | +LL | / for i in 0..src.len() { +LL | | dst[i] = src[i + 10]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[10..(src.len() + 10)]);` + +error: it looks like you're manually copying between slices + --> $DIR/without_loop_counters.rs:22:5 + | +LL | / for i in 11..src.len() { +LL | | dst[i] = src[i - 10]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[11..src.len()].clone_from_slice(&src[(11 - 10)..(src.len() - 10)]);` + +error: it looks like you're manually copying between slices + --> $DIR/without_loop_counters.rs:27:5 + | +LL | / for i in 0..dst.len() { +LL | | dst[i] = src[i]; +LL | | } + | |_____^ help: try replacing the loop by: `dst.clone_from_slice(&src[..dst.len()]);` + +error: it looks like you're manually copying between slices + --> $DIR/without_loop_counters.rs:40:5 + | +LL | / for i in 10..256 { +LL | | dst[i] = src[i - 5]; +LL | | dst2[i + 500] = src[i] +LL | | } + | |_____^ + | +help: try replacing the loop by + | +LL | dst[10..256].clone_from_slice(&src[(10 - 5)..(256 - 5)]); +LL | dst2[(10 + 500)..(256 + 500)].clone_from_slice(&src[10..256]); + | + +error: it looks like you're manually copying between slices + --> $DIR/without_loop_counters.rs:52:5 + | +LL | / for i in 10..LOOP_OFFSET { +LL | | dst[i + LOOP_OFFSET] = src[i - some_var]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[(10 + LOOP_OFFSET)..(LOOP_OFFSET + LOOP_OFFSET)].clone_from_slice(&src[(10 - some_var)..(LOOP_OFFSET - some_var)]);` + +error: it looks like you're manually copying between slices + --> $DIR/without_loop_counters.rs:65:5 + | +LL | / for i in 0..src_vec.len() { +LL | | dst_vec[i] = src_vec[i]; +LL | | } + | |_____^ help: try replacing the loop by: `dst_vec[..src_vec.len()].clone_from_slice(&src_vec[..]);` + +error: it looks like you're manually copying between slices + --> $DIR/without_loop_counters.rs:94:5 + | +LL | / for i in from..from + src.len() { +LL | | dst[i] = src[i - from]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[from..(from + src.len())].clone_from_slice(&src[..(from + src.len() - from)]);` + +error: it looks like you're manually copying between slices + --> $DIR/without_loop_counters.rs:98:5 + | +LL | / for i in from..from + 3 { +LL | | dst[i] = src[i - from]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[from..(from + 3)].clone_from_slice(&src[..(from + 3 - from)]);` + +error: it looks like you're manually copying between slices + --> $DIR/without_loop_counters.rs:103:5 + | +LL | / for i in 0..5 { +LL | | dst[i - 0] = src[i]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[..5].clone_from_slice(&src[..5]);` + +error: it looks like you're manually copying between slices + --> $DIR/without_loop_counters.rs:108:5 + | +LL | / for i in 0..0 { +LL | | dst[i] = src[i]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[..0].clone_from_slice(&src[..0]);` + +error: it looks like you're manually copying between slices + --> $DIR/without_loop_counters.rs:120:5 + | +LL | / for i in 0..src.len() { +LL | | dst[i] = src[i].clone(); +LL | | } + | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..]);` + +error: aborting due to 13 previous errors + -- cgit 1.4.1-3-g733a5 From 101e76f117eeefcd2e901bab707de199b030f201 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Mon, 28 Sep 2020 19:14:39 +0200 Subject: needless arbitrary self: handle macros --- clippy_lints/src/needless_arbitrary_self_type.rs | 30 +++++++++-- tests/ui/auxiliary/proc_macro_attr.rs | 61 +++++++++++++++++++++- tests/ui/needless_arbitrary_self_type_unfixable.rs | 45 ++++++++++++++++ .../needless_arbitrary_self_type_unfixable.stderr | 10 ++++ 4 files changed, 139 insertions(+), 7 deletions(-) create mode 100644 tests/ui/needless_arbitrary_self_type_unfixable.rs create mode 100644 tests/ui/needless_arbitrary_self_type_unfixable.stderr (limited to 'tests') diff --git a/clippy_lints/src/needless_arbitrary_self_type.rs b/clippy_lints/src/needless_arbitrary_self_type.rs index 38bdd0f7ed2..7687962bdd9 100644 --- a/clippy_lints/src/needless_arbitrary_self_type.rs +++ b/clippy_lints/src/needless_arbitrary_self_type.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint_and_sugg; +use crate::utils::{in_macro, span_lint_and_sugg}; use if_chain::if_chain; use rustc_ast::ast::{BindingMode, Lifetime, Mutability, Param, PatKind, Path, TyKind}; use rustc_errors::Applicability; @@ -69,11 +69,30 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod if let [segment] = &path.segments[..]; if segment.ident.name == kw::SelfUpper; then { + // In case we have a named lifetime, we check if the name comes from expansion. + // If it does, at this point we know the rest of the parameter was written by the user, + // so let them decide what the name of the lifetime should be. + // See #6089 for more details. + let mut applicability = Applicability::MachineApplicable; let self_param = match (binding_mode, mutbl) { (Mode::Ref(None), Mutability::Mut) => "&mut self".to_string(), - (Mode::Ref(Some(lifetime)), Mutability::Mut) => format!("&{} mut self", &lifetime.ident.name), + (Mode::Ref(Some(lifetime)), Mutability::Mut) => { + if in_macro(lifetime.ident.span) { + applicability = Applicability::HasPlaceholders; + "&'_ mut self".to_string() + } else { + format!("&{} mut self", &lifetime.ident.name) + } + }, (Mode::Ref(None), Mutability::Not) => "&self".to_string(), - (Mode::Ref(Some(lifetime)), Mutability::Not) => format!("&{} self", &lifetime.ident.name), + (Mode::Ref(Some(lifetime)), Mutability::Not) => { + if in_macro(lifetime.ident.span) { + applicability = Applicability::HasPlaceholders; + "&'_ self".to_string() + } else { + format!("&{} self", &lifetime.ident.name) + } + }, (Mode::Value, Mutability::Mut) => "mut self".to_string(), (Mode::Value, Mutability::Not) => "self".to_string(), }; @@ -85,7 +104,7 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod "the type of the `self` parameter does not need to be arbitrary", "consider to change this parameter to", self_param, - Applicability::MachineApplicable, + applicability, ) } } @@ -93,7 +112,8 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod impl EarlyLintPass for NeedlessArbitrarySelfType { fn check_param(&mut self, cx: &EarlyContext<'_>, p: &Param) { - if !p.is_self() { + // Bail out if the parameter it's not a receiver or was not written by the user + if !p.is_self() || in_macro(p.span) { return; } diff --git a/tests/ui/auxiliary/proc_macro_attr.rs b/tests/ui/auxiliary/proc_macro_attr.rs index 01796d45f13..de670cdfc31 100644 --- a/tests/ui/auxiliary/proc_macro_attr.rs +++ b/tests/ui/auxiliary/proc_macro_attr.rs @@ -2,7 +2,7 @@ // no-prefer-dynamic #![crate_type = "proc-macro"] -#![feature(repr128, proc_macro_hygiene, proc_macro_quote)] +#![feature(repr128, proc_macro_hygiene, proc_macro_quote, box_patterns)] #![allow(clippy::useless_conversion)] extern crate proc_macro; @@ -12,7 +12,11 @@ extern crate syn; use proc_macro::TokenStream; use quote::{quote, quote_spanned}; use syn::parse_macro_input; -use syn::{parse_quote, ItemTrait, TraitItem}; +use syn::spanned::Spanned; +use syn::token::Star; +use syn::{ + parse_quote, FnArg, ImplItem, ItemImpl, ItemTrait, Lifetime, Pat, PatIdent, PatType, Signature, TraitItem, Type, +}; #[proc_macro_attribute] pub fn fake_async_trait(_args: TokenStream, input: TokenStream) -> TokenStream { @@ -36,3 +40,56 @@ pub fn fake_async_trait(_args: TokenStream, input: TokenStream) -> TokenStream { } TokenStream::from(quote!(#item)) } + +#[proc_macro_attribute] +pub fn rename_my_lifetimes(_args: TokenStream, input: TokenStream) -> TokenStream { + fn make_name(count: usize) -> String { + format!("'life{}", count) + } + + fn mut_receiver_of(sig: &mut Signature) -> Option<&mut FnArg> { + let arg = sig.inputs.first_mut()?; + if let FnArg::Typed(PatType { pat, .. }) = arg { + if let Pat::Ident(PatIdent { ident, .. }) = &**pat { + if ident == "self" { + return Some(arg); + } + } + } + None + } + + let mut elided = 0; + let mut item = parse_macro_input!(input as ItemImpl); + + // Look for methods having arbitrary self type taken by &mut ref + for inner in &mut item.items { + if let ImplItem::Method(method) = inner { + if let Some(FnArg::Typed(pat_type)) = mut_receiver_of(&mut method.sig) { + if let box Type::Reference(reference) = &mut pat_type.ty { + // Target only unnamed lifetimes + let name = match &reference.lifetime { + Some(lt) if lt.ident == "_" => make_name(elided), + None => make_name(elided), + _ => continue, + }; + elided += 1; + + // HACK: Syn uses `Span` from the proc_macro2 crate, and does not seem to reexport it. + // In order to avoid adding the dependency, get a default span from a non-existent token. + // A default span is needed to mark the code as coming from expansion. + let span = Star::default().span(); + + // Replace old lifetime with the named one + let lifetime = Lifetime::new(&name, span); + reference.lifetime = Some(parse_quote!(#lifetime)); + + // Add lifetime to the generics of the method + method.sig.generics.params.push(parse_quote!(#lifetime)); + } + } + } + } + + TokenStream::from(quote!(#item)) +} diff --git a/tests/ui/needless_arbitrary_self_type_unfixable.rs b/tests/ui/needless_arbitrary_self_type_unfixable.rs new file mode 100644 index 00000000000..a39d96109f1 --- /dev/null +++ b/tests/ui/needless_arbitrary_self_type_unfixable.rs @@ -0,0 +1,45 @@ +// aux-build:proc_macro_attr.rs + +#![warn(clippy::needless_arbitrary_self_type)] + +#[macro_use] +extern crate proc_macro_attr; + +mod issue_6089 { + // Check that we don't lint if the `self` parameter comes from expansion + + macro_rules! test_from_expansion { + () => { + trait T1 { + fn test(self: &Self); + } + + struct S1 {} + + impl T1 for S1 { + fn test(self: &Self) {} + } + }; + } + + test_from_expansion!(); + + // If only the lifetime name comes from expansion we will lint, but the suggestion will have + // placeholders and will not be applied automatically, as we can't reliably know the original name. + // This specific case happened with async_trait. + + trait T2 { + fn call_with_mut_self(&mut self); + } + + struct S2 {} + + // The method's signature will be expanded to: + // fn call_with_mut_self<'life0>(self: &'life0 mut Self) {} + #[rename_my_lifetimes] + impl T2 for S2 { + fn call_with_mut_self(self: &mut Self) {} + } +} + +fn main() {} diff --git a/tests/ui/needless_arbitrary_self_type_unfixable.stderr b/tests/ui/needless_arbitrary_self_type_unfixable.stderr new file mode 100644 index 00000000000..44a0e6ddeac --- /dev/null +++ b/tests/ui/needless_arbitrary_self_type_unfixable.stderr @@ -0,0 +1,10 @@ +error: the type of the `self` parameter does not need to be arbitrary + --> $DIR/needless_arbitrary_self_type_unfixable.rs:41:31 + | +LL | fn call_with_mut_self(self: &mut Self) {} + | ^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'_ mut self` + | + = note: `-D clippy::needless-arbitrary-self-type` implied by `-D warnings` + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From 124420f920fd9323e69829a35876add8c358a666 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Tue, 29 Sep 2020 23:06:08 +0200 Subject: needless-lifetime / fix master merge --- tests/ui/crashes/ice-2774.stderr | 2 +- tests/ui/crashes/needless_lifetimes_impl_trait.stderr | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/tests/ui/crashes/ice-2774.stderr b/tests/ui/crashes/ice-2774.stderr index fd502cba73a..0c2d48f938f 100644 --- a/tests/ui/crashes/ice-2774.stderr +++ b/tests/ui/crashes/ice-2774.stderr @@ -1,5 +1,5 @@ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/ice-2774.rs:17:1 + --> $DIR/ice-2774.rs:15:1 | LL | pub fn add_barfoos_to_foos<'a>(bars: &HashSet<&'a Bar>) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/crashes/needless_lifetimes_impl_trait.stderr b/tests/ui/crashes/needless_lifetimes_impl_trait.stderr index 02b86397ed5..d68bbe78802 100644 --- a/tests/ui/crashes/needless_lifetimes_impl_trait.stderr +++ b/tests/ui/crashes/needless_lifetimes_impl_trait.stderr @@ -1,11 +1,11 @@ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes_impl_trait.rs:17:5 + --> $DIR/needless_lifetimes_impl_trait.rs:15:5 | LL | fn baz<'a>(&'a self) -> impl Foo + 'a { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the lint level is defined here - --> $DIR/needless_lifetimes_impl_trait.rs:3:9 + --> $DIR/needless_lifetimes_impl_trait.rs:1:9 | LL | #![deny(clippy::needless_lifetimes)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From cb2be6f9dba0224c63180eada2d089100450391a Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Wed, 30 Sep 2020 00:33:46 +0200 Subject: needless-lifetime / pr remarks --- clippy_lints/src/lifetimes.rs | 1 + tests/ui/needless_lifetimes.rs | 18 +++++++++++++++++- tests/ui/needless_lifetimes.stderr | 20 +++++++++++++++++++- 3 files changed, 37 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 530968c191f..d7043e7bd8f 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -383,6 +383,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { let mut sub_visitor = RefVisitor::new(self.cx); sub_visitor.visit_fn_decl(decl); self.nested_elision_site_lts.append(&mut sub_visitor.all_lts()); + return; }, TyKind::TraitObject(bounds, ref lt) => { if !lt.is_elided() { diff --git a/tests/ui/needless_lifetimes.rs b/tests/ui/needless_lifetimes.rs index 548c5929c61..d482d466e44 100644 --- a/tests/ui/needless_lifetimes.rs +++ b/tests/ui/needless_lifetimes.rs @@ -336,9 +336,25 @@ mod nested_elision_sites { |i| i } // lint - fn pointer_fn_elidable<'a>(f: fn(&i32) -> &i32, i: &'a i32) -> &'a i32 { + fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 { f(i) } + + // don't lint + fn nested_fn_pointer_1<'a>(_: &'a i32) -> fn(fn(&'a i32) -> &'a i32) -> i32 { + |f| 42 + } + fn nested_fn_pointer_2<'a>(_: &'a i32) -> impl Fn(fn(&'a i32)) { + |f| () + } + + // lint + fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 { + |f| 42 + } + fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) { + |f| () + } } fn main() {} diff --git a/tests/ui/needless_lifetimes.stderr b/tests/ui/needless_lifetimes.stderr index ac38ab8effd..c8a2e8b81c0 100644 --- a/tests/ui/needless_lifetimes.stderr +++ b/tests/ui/needless_lifetimes.stderr @@ -132,5 +132,23 @@ error: explicit lifetimes given in parameter types where they could be elided (o LL | fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 22 previous errors +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:339:5 + | +LL | fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:352:5 + | +LL | fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:355:5 + | +LL | fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 25 previous errors -- cgit 1.4.1-3-g733a5 From 0690f9c5d526baa593cc62b175ef5e7f2c8f4f9c Mon Sep 17 00:00:00 2001 From: Jethro Beekman Date: Mon, 28 Sep 2020 15:30:47 +0200 Subject: Add lint for inline assembly syntax style preference --- CHANGELOG.md | 2 + clippy_lints/src/asm_syntax.rs | 125 +++++++++++++++++++++++++++++++++++++++++ clippy_lints/src/lib.rs | 7 +++ src/lintlist/mod.rs | 14 +++++ tests/ui/asm_syntax.rs | 31 ++++++++++ tests/ui/asm_syntax.stderr | 44 +++++++++++++++ 6 files changed, 223 insertions(+) create mode 100644 clippy_lints/src/asm_syntax.rs create mode 100644 tests/ui/asm_syntax.rs create mode 100644 tests/ui/asm_syntax.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 575cbd60792..0de6f4b4235 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1635,6 +1635,8 @@ Released 2018-09-13 [`inherent_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string [`inherent_to_string_shadow_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string_shadow_display [`inline_always`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_always +[`inline_asm_x86_att_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_att_syntax +[`inline_asm_x86_intel_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_intel_syntax [`inline_fn_without_body`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_fn_without_body [`int_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#int_plus_one [`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic diff --git a/clippy_lints/src/asm_syntax.rs b/clippy_lints/src/asm_syntax.rs new file mode 100644 index 00000000000..ef1f1a14afc --- /dev/null +++ b/clippy_lints/src/asm_syntax.rs @@ -0,0 +1,125 @@ +use std::fmt; + +use crate::utils::span_lint_and_help; +use rustc_ast::ast::{Expr, ExprKind, InlineAsmOptions}; +use rustc_lint::{EarlyContext, EarlyLintPass, Lint}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +#[derive(Clone, Copy, PartialEq, Eq)] +enum AsmStyle { + Intel, + Att, +} + +impl fmt::Display for AsmStyle { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + AsmStyle::Intel => f.write_str("Intel"), + AsmStyle::Att => f.write_str("AT&T"), + } + } +} + +impl std::ops::Not for AsmStyle { + type Output = AsmStyle; + + fn not(self) -> AsmStyle { + match self { + AsmStyle::Intel => AsmStyle::Att, + AsmStyle::Att => AsmStyle::Intel, + } + } +} + +fn check_expr_asm_syntax(lint: &'static Lint, cx: &EarlyContext<'_>, expr: &Expr, check_for: AsmStyle) { + if let ExprKind::InlineAsm(ref inline_asm) = expr.kind { + let style = if inline_asm.options.contains(InlineAsmOptions::ATT_SYNTAX) { + AsmStyle::Att + } else { + AsmStyle::Intel + }; + + if style == check_for { + span_lint_and_help( + cx, + lint, + expr.span, + &format!("{} x86 assembly syntax used", style), + None, + &format!("use {} x86 assembly syntax", !style), + ); + } + } +} + +declare_clippy_lint! { + /// **What it does:** Checks for usage of Intel x86 assembly syntax. + /// + /// **Why is this bad?** The lint has been enabled to indicate a preference + /// for AT&T x86 assembly syntax. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust,no_run + /// # #![feature(asm)] + /// # unsafe { let ptr = "".as_ptr(); + /// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr); + /// # } + /// ``` + /// Use instead: + /// ```rust,no_run + /// # #![feature(asm)] + /// # unsafe { let ptr = "".as_ptr(); + /// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax)); + /// # } + /// ``` + pub INLINE_ASM_X86_INTEL_SYNTAX, + restriction, + "prefer AT&T x86 assembly syntax" +} + +declare_lint_pass!(InlineAsmX86IntelSyntax => [INLINE_ASM_X86_INTEL_SYNTAX]); + +impl EarlyLintPass for InlineAsmX86IntelSyntax { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + check_expr_asm_syntax(Self::get_lints()[0], cx, expr, AsmStyle::Intel); + } +} + +declare_clippy_lint! { + /// **What it does:** Checks for usage of AT&T x86 assembly syntax. + /// + /// **Why is this bad?** The lint has been enabled to indicate a preference + /// for Intel x86 assembly syntax. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust,no_run + /// # #![feature(asm)] + /// # unsafe { let ptr = "".as_ptr(); + /// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax)); + /// # } + /// ``` + /// Use instead: + /// ```rust,no_run + /// # #![feature(asm)] + /// # unsafe { let ptr = "".as_ptr(); + /// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr); + /// # } + /// ``` + pub INLINE_ASM_X86_ATT_SYNTAX, + restriction, + "prefer Intel x86 assembly syntax" +} + +declare_lint_pass!(InlineAsmX86AttSyntax => [INLINE_ASM_X86_ATT_SYNTAX]); + +impl EarlyLintPass for InlineAsmX86AttSyntax { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + check_expr_asm_syntax(Self::get_lints()[0], cx, expr, AsmStyle::Att); + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 529c2450541..10da59c7a7a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -153,6 +153,7 @@ mod utils; mod approx_const; mod arithmetic; mod as_conversions; +mod asm_syntax; mod assertions_on_constants; mod assign_ops; mod async_yields_async; @@ -487,6 +488,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &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, @@ -1123,12 +1126,16 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem); let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::>(); store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods)); + store.register_early_pass(|| box asm_syntax::InlineAsmX86AttSyntax); + store.register_early_pass(|| box asm_syntax::InlineAsmX86IntelSyntax); 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(&else_if_without_else::ELSE_IF_WITHOUT_ELSE), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 76e655ad603..16ceb617965 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -899,6 +899,20 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "attrs", }, + Lint { + name: "inline_asm_x86_att_syntax", + group: "restriction", + desc: "prefer Intel x86 assembly syntax", + deprecation: None, + module: "asm_syntax", + }, + Lint { + name: "inline_asm_x86_intel_syntax", + group: "restriction", + desc: "prefer AT&T x86 assembly syntax", + deprecation: None, + module: "asm_syntax", + }, Lint { name: "inline_fn_without_body", group: "correctness", diff --git a/tests/ui/asm_syntax.rs b/tests/ui/asm_syntax.rs new file mode 100644 index 00000000000..049bfa604d5 --- /dev/null +++ b/tests/ui/asm_syntax.rs @@ -0,0 +1,31 @@ +#![feature(asm)] +// only-x86 only-x86_64 + +#[warn(clippy::inline_asm_x86_intel_syntax)] +mod warn_intel { + pub(super) unsafe fn use_asm() { + asm!(""); + asm!("", options()); + asm!("", options(nostack)); + asm!("", options(att_syntax)); + asm!("", options(nostack, att_syntax)); + } +} + +#[warn(clippy::inline_asm_x86_att_syntax)] +mod warn_att { + pub(super) unsafe fn use_asm() { + asm!(""); + asm!("", options()); + asm!("", options(nostack)); + asm!("", options(att_syntax)); + asm!("", options(nostack, att_syntax)); + } +} + +fn main() { + unsafe { + warn_att::use_asm(); + warn_intel::use_asm(); + } +} diff --git a/tests/ui/asm_syntax.stderr b/tests/ui/asm_syntax.stderr new file mode 100644 index 00000000000..27b51166eac --- /dev/null +++ b/tests/ui/asm_syntax.stderr @@ -0,0 +1,44 @@ +error: Intel x86 assembly syntax used + --> $DIR/asm_syntax.rs:7:9 + | +LL | asm!(""); + | ^^^^^^^^^ + | + = note: `-D clippy::inline-asm-x86-intel-syntax` implied by `-D warnings` + = help: use AT&T x86 assembly syntax + +error: Intel x86 assembly syntax used + --> $DIR/asm_syntax.rs:8:9 + | +LL | asm!("", options()); + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: use AT&T x86 assembly syntax + +error: Intel x86 assembly syntax used + --> $DIR/asm_syntax.rs:9:9 + | +LL | asm!("", options(nostack)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use AT&T x86 assembly syntax + +error: AT&T x86 assembly syntax used + --> $DIR/asm_syntax.rs:21:9 + | +LL | asm!("", options(att_syntax)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::inline-asm-x86-att-syntax` implied by `-D warnings` + = help: use Intel x86 assembly syntax + +error: AT&T x86 assembly syntax used + --> $DIR/asm_syntax.rs:22:9 + | +LL | asm!("", options(nostack, att_syntax)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use Intel x86 assembly syntax + +error: aborting due to 5 previous errors + -- cgit 1.4.1-3-g733a5 From 507561ecfcec33d4263cff17cfe953518b8c3ce6 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 30 Sep 2020 23:30:49 +0200 Subject: Update tests/ui/asm_syntax.rs Use a single `only` header command in asm_syntax test --- tests/ui/asm_syntax.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/ui/asm_syntax.rs b/tests/ui/asm_syntax.rs index 049bfa604d5..658cae397e1 100644 --- a/tests/ui/asm_syntax.rs +++ b/tests/ui/asm_syntax.rs @@ -1,5 +1,5 @@ #![feature(asm)] -// only-x86 only-x86_64 +// only-x86_64 #[warn(clippy::inline_asm_x86_intel_syntax)] mod warn_intel { -- cgit 1.4.1-3-g733a5 From 0a91fe7016cdb48d3824c218e02eca8cfdec3854 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Thu, 1 Oct 2020 23:53:05 +0900 Subject: Don't emit a lint for the suggestion leading to errors in `needless_range_loop` --- clippy_lints/src/loops.rs | 6 +++++- tests/ui/needless_range_loop2.rs | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 7f998c63f49..61b63597b16 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -3,7 +3,7 @@ use crate::utils::paths; use crate::utils::sugg::Sugg; use crate::utils::usage::{is_unused, mutated_variables}; use crate::utils::{ - get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, + contains_name, get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, match_trait_method, match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, @@ -1276,6 +1276,8 @@ fn check_for_loop_range<'tcx>( let skip = if starts_at_zero { String::new() + } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, start) { + return; } else { format!(".skip({})", snippet(cx, start.span, "..")) }; @@ -1302,6 +1304,8 @@ fn check_for_loop_range<'tcx>( if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty) { String::new() + } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, take_expr) { + return; } else { match limits { ast::RangeLimits::Closed => { diff --git a/tests/ui/needless_range_loop2.rs b/tests/ui/needless_range_loop2.rs index a82b1159161..7633316e0f8 100644 --- a/tests/ui/needless_range_loop2.rs +++ b/tests/ui/needless_range_loop2.rs @@ -82,6 +82,20 @@ fn main() { for i in 1..3 { println!("{}", arr[i]); } + + // Fix #5945 + let mut vec = vec![1, 2, 3, 4]; + for i in 0..vec.len() - 1 { + vec[i] += 1; + } + let mut vec = vec![1, 2, 3, 4]; + for i in vec.len() - 3..vec.len() { + vec[i] += 1; + } + let mut vec = vec![1, 2, 3, 4]; + for i in vec.len() - 3..vec.len() - 1 { + vec[i] += 1; + } } mod issue2277 { -- cgit 1.4.1-3-g733a5 From e91202cf683b7265eec484ad311b5a9d3b56fc3e Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Fri, 2 Oct 2020 05:43:43 +0200 Subject: Allow exponent separator Fixes #6096 --- clippy_lints/src/utils/numeric_literal.rs | 19 +++++++++++-------- tests/ui/inconsistent_digit_grouping.fixed | 4 ++++ tests/ui/inconsistent_digit_grouping.rs | 4 ++++ 3 files changed, 19 insertions(+), 8 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/utils/numeric_literal.rs b/clippy_lints/src/utils/numeric_literal.rs index 5e8800d38eb..52d3c2c1daf 100644 --- a/clippy_lints/src/utils/numeric_literal.rs +++ b/clippy_lints/src/utils/numeric_literal.rs @@ -36,8 +36,9 @@ pub struct NumericLiteral<'a> { pub integer: &'a str, /// The fraction part of the number. pub fraction: Option<&'a str>, - /// The character used as exponent separator (b'e' or b'E') and the exponent part. - pub exponent: Option<(char, &'a str)>, + /// The exponent separator (b'e' or b'E') including preceding underscore if present + /// and the exponent part. + pub exponent: Option<(&'a str, &'a str)>, /// The type suffix, including preceding underscore if present. pub suffix: Option<&'a str>, @@ -100,7 +101,7 @@ impl<'a> NumericLiteral<'a> { self.radix == Radix::Decimal } - pub fn split_digit_parts(digits: &str, float: bool) -> (&str, Option<&str>, Option<(char, &str)>) { + pub fn split_digit_parts(digits: &str, float: bool) -> (&str, Option<&str>, Option<(&str, &str)>) { let mut integer = digits; let mut fraction = None; let mut exponent = None; @@ -113,12 +114,14 @@ impl<'a> NumericLiteral<'a> { fraction = Some(&digits[i + 1..]); }, 'e' | 'E' => { - if integer.len() > i { - integer = &digits[..i]; + let exp_start = if digits[..i].ends_with('_') { i - 1 } else { i }; + + if integer.len() > exp_start { + integer = &digits[..exp_start]; } else { - fraction = Some(&digits[integer.len() + 1..i]); + fraction = Some(&digits[integer.len() + 1..exp_start]); }; - exponent = Some((c, &digits[i + 1..])); + exponent = Some((&digits[exp_start..=i], &digits[i + 1..])); break; }, _ => {}, @@ -153,7 +156,7 @@ impl<'a> NumericLiteral<'a> { } if let Some((separator, exponent)) = self.exponent { - output.push(separator); + output.push_str(separator); Self::group_digits(&mut output, exponent, group_size, true, false); } diff --git a/tests/ui/inconsistent_digit_grouping.fixed b/tests/ui/inconsistent_digit_grouping.fixed index b75f10917df..dd683e7f746 100644 --- a/tests/ui/inconsistent_digit_grouping.fixed +++ b/tests/ui/inconsistent_digit_grouping.fixed @@ -40,4 +40,8 @@ fn main() { // Ignore literals in macros let _ = mac1!(); let _ = mac2!(); + + // Issue #6096 + // Allow separating exponent with '_' + let _ = 1.025_011_10_E0; } diff --git a/tests/ui/inconsistent_digit_grouping.rs b/tests/ui/inconsistent_digit_grouping.rs index 79ce38be19b..d5d27c853c2 100644 --- a/tests/ui/inconsistent_digit_grouping.rs +++ b/tests/ui/inconsistent_digit_grouping.rs @@ -40,4 +40,8 @@ fn main() { // Ignore literals in macros let _ = mac1!(); let _ = mac2!(); + + // Issue #6096 + // Allow separating exponent with '_' + let _ = 1.025_011_10_E0; } -- cgit 1.4.1-3-g733a5 From 1402d8ae4f0c07ac48bd8297ec07901346c32d32 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Fri, 2 Oct 2020 14:18:37 +1300 Subject: fix a FN where incr exprs with no semicolon at ends --- clippy_lints/src/loops.rs | 18 ++++++++++++------ tests/ui/manual_memcpy/with_loop_counters.rs | 7 +++++++ tests/ui/manual_memcpy/with_loop_counters.stderr | 11 ++++++++++- 3 files changed, 29 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 2ae5a9acb75..ea40c2af4f7 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1014,14 +1014,20 @@ fn get_assignments<'a: 'c, 'tcx: 'c, 'c>( .iter() .filter_map(move |stmt| match stmt.kind { StmtKind::Local(..) | StmtKind::Item(..) => None, - StmtKind::Expr(e) | StmtKind::Semi(e) => if_chain! { - if let ExprKind::AssignOp(_, var, _) = e.kind; - // skip StartKind::Range - if loop_counters.iter().skip(1).any(|counter| Some(counter.id) == var_def_id(cx, var)); - then { None } else { Some(e) } - }, + StmtKind::Expr(e) | StmtKind::Semi(e) => Some(e), }) .chain((*expr).into_iter()) + .filter(move |e| { + if let ExprKind::AssignOp(_, place, _) = e.kind { + !loop_counters + .iter() + // skip StartKind::Range + .skip(1) + .any(|counter| same_var(cx, place, counter.id)) + } else { + true + } + }) .map(get_assignment) } diff --git a/tests/ui/manual_memcpy/with_loop_counters.rs b/tests/ui/manual_memcpy/with_loop_counters.rs index a49ba9eb10a..70873c9e994 100644 --- a/tests/ui/manual_memcpy/with_loop_counters.rs +++ b/tests/ui/manual_memcpy/with_loop_counters.rs @@ -59,6 +59,13 @@ pub fn manual_copy_with_counters(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) dst[count] = src[i + 2]; count += 1; } + + // make sure incrementing expressions without semicolons at the end of loops are handled correctly. + let mut count = 0; + for i in 3..src.len() { + dst[i] = src[count]; + count += 1 + } } fn main() {} diff --git a/tests/ui/manual_memcpy/with_loop_counters.stderr b/tests/ui/manual_memcpy/with_loop_counters.stderr index 24393ad9b4d..598c881b4d6 100644 --- a/tests/ui/manual_memcpy/with_loop_counters.stderr +++ b/tests/ui/manual_memcpy/with_loop_counters.stderr @@ -89,5 +89,14 @@ LL | | count += 1; LL | | } | |_____^ help: try replacing the loop by: `dst[(0 << 1)..((1 << 1) + (0 << 1))].clone_from_slice(&src[2..((1 << 1) + 2)]);` -error: aborting due to 9 previous errors +error: it looks like you're manually copying between slices + --> $DIR/with_loop_counters.rs:65:5 + | +LL | / for i in 3..src.len() { +LL | | dst[i] = src[count]; +LL | | count += 1 +LL | | } + | |_____^ help: try replacing the loop by: `dst[3..src.len()].clone_from_slice(&src[..(src.len() - 3)]);` + +error: aborting due to 10 previous errors -- cgit 1.4.1-3-g733a5 From 515ca9312393bd00af0e867fceee9aff9b6e565d Mon Sep 17 00:00:00 2001 From: Dániel Buga Date: Fri, 2 Oct 2020 11:54:31 +0200 Subject: Look for soft hyphens as well --- clippy_lints/src/unicode.rs | 14 +++++++------- tests/ui/unicode.rs | 2 ++ tests/ui/unicode.stderr | 14 ++++++++++---- 3 files changed, 19 insertions(+), 11 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/unicode.rs b/clippy_lints/src/unicode.rs index d8c57f0e7ae..d3fe60042a8 100644 --- a/clippy_lints/src/unicode.rs +++ b/clippy_lints/src/unicode.rs @@ -8,18 +8,18 @@ use rustc_span::source_map::Span; use unicode_normalization::UnicodeNormalization; declare_clippy_lint! { - /// **What it does:** Checks for the Unicode zero-width space in the code. + /// **What it does:** Checks for invisible Unicode characters in the code. /// /// **Why is this bad?** Having an invisible character in the code makes for all /// sorts of April fools, but otherwise is very much frowned upon. /// /// **Known problems:** None. /// - /// **Example:** You don't see it, but there may be a zero-width space - /// somewhere in this text. + /// **Example:** You don't see it, but there may be a zero-width space or soft hyphen + /// some­where in this text. pub ZERO_WIDTH_SPACE, correctness, - "using a zero-width space in a string literal, which is confusing" + "using an invisible character in a string literal, which is confusing" } declare_clippy_lint! { @@ -91,14 +91,14 @@ fn escape>(s: T) -> String { fn check_str(cx: &LateContext<'_>, span: Span, id: HirId) { let string = snippet(cx, span, ""); - if string.contains('\u{200B}') { + if let Some(invisible) = string.chars().find(|c| ['\u{200B}', '\u{ad}'].contains(&c)) { span_lint_and_sugg( cx, ZERO_WIDTH_SPACE, span, - "zero-width space detected", + &format!("invisible character detected: {:?}", invisible), "consider replacing the string with", - string.replace("\u{200B}", "\\u{200B}"), + string.replace("\u{200B}", "\\u{200B}").replace("\u{ad}", "\\u{AD}"), Applicability::MachineApplicable, ); } diff --git a/tests/ui/unicode.rs b/tests/ui/unicode.rs index 27db9594f3b..f3fd1c57da6 100644 --- a/tests/ui/unicode.rs +++ b/tests/ui/unicode.rs @@ -2,6 +2,8 @@ fn zero() { print!("Here >​< is a ZWS, and ​another"); print!("This\u{200B}is\u{200B}fine"); + print!("Here >­< is a SHY, and ­another"); + print!("This\u{ad}is\u{ad}fine"); } #[warn(clippy::unicode_not_nfc)] diff --git a/tests/ui/unicode.stderr b/tests/ui/unicode.stderr index 4575a132e5b..b0445b070fd 100644 --- a/tests/ui/unicode.stderr +++ b/tests/ui/unicode.stderr @@ -1,4 +1,4 @@ -error: zero-width space detected +error: invisible character detected: '/u{200b}' --> $DIR/unicode.rs:3:12 | LL | print!("Here >​< is a ZWS, and ​another"); @@ -6,8 +6,14 @@ LL | print!("Here >​< is a ZWS, and ​another"); | = note: `-D clippy::zero-width-space` implied by `-D warnings` +error: invisible character detected: '/u{ad}' + --> $DIR/unicode.rs:5:12 + | +LL | print!("Here >­< is a SHY, and ­another"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{AD}< is a SHY, and /u{AD}another"` + error: non-NFC Unicode sequence detected - --> $DIR/unicode.rs:9:12 + --> $DIR/unicode.rs:11:12 | LL | print!("̀àh?"); | ^^^^^ help: consider replacing the string with: `"̀àh?"` @@ -15,12 +21,12 @@ LL | print!("̀àh?"); = note: `-D clippy::unicode-not-nfc` implied by `-D warnings` error: literal non-ASCII character detected - --> $DIR/unicode.rs:15:12 + --> $DIR/unicode.rs:17:12 | LL | print!("Üben!"); | ^^^^^^^ help: consider replacing the string with: `"/u{dc}ben!"` | = note: `-D clippy::non-ascii-literal` implied by `-D warnings` -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From 7820cb14421f751c05d6d2d5925236c3429cd93f Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Fri, 2 Oct 2020 23:21:24 +1300 Subject: Add tests for * `dst.len()` as the end of the range with loop counters * the increment of the loop counter at the top of the loop --- tests/ui/manual_memcpy/with_loop_counters.rs | 17 +++++++++++++++++ tests/ui/manual_memcpy/with_loop_counters.stderr | 17 +++++++++++++---- 2 files changed, 30 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/tests/ui/manual_memcpy/with_loop_counters.rs b/tests/ui/manual_memcpy/with_loop_counters.rs index 70873c9e994..ba388a05a28 100644 --- a/tests/ui/manual_memcpy/with_loop_counters.rs +++ b/tests/ui/manual_memcpy/with_loop_counters.rs @@ -37,6 +37,12 @@ pub fn manual_copy_with_counters(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) count += 1; } + let mut count = 2; + for i in 0..dst.len() { + dst[i] = src[count]; + count += 1; + } + let mut count = 5; for i in 3..10 { dst[i] = src[count]; @@ -66,6 +72,17 @@ pub fn manual_copy_with_counters(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) dst[i] = src[count]; count += 1 } + + // make sure ones where the increment is not at the end of the loop. + // As a possible enhancement, one could adjust the offset in the suggestion according to + // the position. For example, if the increment is at the top of the loop; + // treating the loop counter as if it were initialized 1 greater than the original value. + let mut count = 0; + #[allow(clippy::needless_range_loop)] + for i in 0..src.len() { + count += 1; + dst[i] = src[count]; + } } fn main() {} diff --git a/tests/ui/manual_memcpy/with_loop_counters.stderr b/tests/ui/manual_memcpy/with_loop_counters.stderr index 598c881b4d6..2547b19f5d1 100644 --- a/tests/ui/manual_memcpy/with_loop_counters.stderr +++ b/tests/ui/manual_memcpy/with_loop_counters.stderr @@ -57,6 +57,15 @@ LL | | } error: it looks like you're manually copying between slices --> $DIR/with_loop_counters.rs:41:5 | +LL | / for i in 0..dst.len() { +LL | | dst[i] = src[count]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst.clone_from_slice(&src[2..(dst.len() + 2)]);` + +error: it looks like you're manually copying between slices + --> $DIR/with_loop_counters.rs:47:5 + | LL | / for i in 3..10 { LL | | dst[i] = src[count]; LL | | count += 1; @@ -64,7 +73,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[3..10].clone_from_slice(&src[5..(10 + 5 - 3)]);` error: it looks like you're manually copying between slices - --> $DIR/with_loop_counters.rs:48:5 + --> $DIR/with_loop_counters.rs:54:5 | LL | / for i in 0..src.len() { LL | | dst[count] = src[i]; @@ -81,7 +90,7 @@ LL | dst2[30..(src.len() + 30)].clone_from_slice(&src[..]); | error: it looks like you're manually copying between slices - --> $DIR/with_loop_counters.rs:58:5 + --> $DIR/with_loop_counters.rs:64:5 | LL | / for i in 0..1 << 1 { LL | | dst[count] = src[i + 2]; @@ -90,7 +99,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[(0 << 1)..((1 << 1) + (0 << 1))].clone_from_slice(&src[2..((1 << 1) + 2)]);` error: it looks like you're manually copying between slices - --> $DIR/with_loop_counters.rs:65:5 + --> $DIR/with_loop_counters.rs:71:5 | LL | / for i in 3..src.len() { LL | | dst[i] = src[count]; @@ -98,5 +107,5 @@ LL | | count += 1 LL | | } | |_____^ help: try replacing the loop by: `dst[3..src.len()].clone_from_slice(&src[..(src.len() - 3)]);` -error: aborting due to 10 previous errors +error: aborting due to 11 previous errors -- cgit 1.4.1-3-g733a5 From 998bd3b6b4d168099346e460ae42897dc3667882 Mon Sep 17 00:00:00 2001 From: Dániel Buga Date: Sat, 3 Oct 2020 00:03:33 +0200 Subject: Rename lint to invisible_characters --- CHANGELOG.md | 2 +- clippy_lints/src/lib.rs | 7 ++++--- clippy_lints/src/unicode.rs | 10 +++++----- src/lintlist/mod.rs | 14 +++++++------- tests/ui/unicode.rs | 2 +- tests/ui/unicode.stderr | 6 +++--- 6 files changed, 21 insertions(+), 20 deletions(-) (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 0de6f4b4235..617bf32f463 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1647,6 +1647,7 @@ Released 2018-09-13 [`invalid_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_ref [`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex [`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons +[`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters [`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements [`iter_cloned_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_cloned_collect [`iter_next_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_loop @@ -1922,6 +1923,5 @@ Released 2018-09-13 [`zero_divided_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_divided_by_zero [`zero_prefixed_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_prefixed_literal [`zero_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_ptr -[`zero_width_space`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_width_space [`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 10da59c7a7a..91244ec2724 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -854,9 +854,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &types::UNIT_CMP, &types::UNNECESSARY_CAST, &types::VEC_BOX, + &unicode::INVISIBLE_CHARACTERS, &unicode::NON_ASCII_LITERAL, &unicode::UNICODE_NOT_NFC, - &unicode::ZERO_WIDTH_SPACE, &unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD, &unnamed_address::FN_ADDRESS_COMPARISONS, &unnamed_address::VTABLE_ADDRESS_COMPARISONS, @@ -1511,7 +1511,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::UNIT_CMP), LintId::of(&types::UNNECESSARY_CAST), LintId::of(&types::VEC_BOX), - LintId::of(&unicode::ZERO_WIDTH_SPACE), + LintId::of(&unicode::INVISIBLE_CHARACTERS), LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), @@ -1779,7 +1779,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::ABSURD_EXTREME_COMPARISONS), LintId::of(&types::CAST_REF_TO_MUT), LintId::of(&types::UNIT_CMP), - LintId::of(&unicode::ZERO_WIDTH_SPACE), + LintId::of(&unicode::INVISIBLE_CHARACTERS), LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), @@ -1910,6 +1910,7 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) { ls.register_renamed("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles"); ls.register_renamed("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles"); ls.register_renamed("clippy::identity_conversion", "clippy::useless_conversion"); + ls.register_renamed("clippy::zero_width_space", "clippy::invisible_characters"); } // only exists to let the dogfood integration test works. diff --git a/clippy_lints/src/unicode.rs b/clippy_lints/src/unicode.rs index d3fe60042a8..d6c8d317dc2 100644 --- a/clippy_lints/src/unicode.rs +++ b/clippy_lints/src/unicode.rs @@ -17,7 +17,7 @@ declare_clippy_lint! { /// /// **Example:** You don't see it, but there may be a zero-width space or soft hyphen /// some­where in this text. - pub ZERO_WIDTH_SPACE, + pub INVISIBLE_CHARACTERS, correctness, "using an invisible character in a string literal, which is confusing" } @@ -63,7 +63,7 @@ declare_clippy_lint! { "using a Unicode literal not in NFC normal form (see [Unicode tr15](http://www.unicode.org/reports/tr15/) for further information)" } -declare_lint_pass!(Unicode => [ZERO_WIDTH_SPACE, NON_ASCII_LITERAL, UNICODE_NOT_NFC]); +declare_lint_pass!(Unicode => [INVISIBLE_CHARACTERS, NON_ASCII_LITERAL, UNICODE_NOT_NFC]); impl LateLintPass<'_> for Unicode { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { @@ -91,12 +91,12 @@ fn escape>(s: T) -> String { fn check_str(cx: &LateContext<'_>, span: Span, id: HirId) { let string = snippet(cx, span, ""); - if let Some(invisible) = string.chars().find(|c| ['\u{200B}', '\u{ad}'].contains(&c)) { + if string.chars().any(|c| ['\u{200B}', '\u{ad}'].contains(&c)) { span_lint_and_sugg( cx, - ZERO_WIDTH_SPACE, + INVISIBLE_CHARACTERS, span, - &format!("invisible character detected: {:?}", invisible), + "invisible character detected", "consider replacing the string with", string.replace("\u{200B}", "\\u{200B}").replace("\u{ad}", "\\u{AD}"), Applicability::MachineApplicable, diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 3654dbc6124..e7df733d3a2 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -969,6 +969,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "types", }, + Lint { + name: "invisible_characters", + group: "correctness", + desc: "using an invisible character in a string literal, which is confusing", + deprecation: None, + module: "unicode", + }, Lint { name: "items_after_statements", group: "pedantic", @@ -2810,13 +2817,6 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "misc", }, - Lint { - name: "zero_width_space", - group: "correctness", - desc: "using an invisible character in a string literal, which is confusing", - deprecation: None, - module: "unicode", - }, Lint { name: "zst_offset", group: "correctness", diff --git a/tests/ui/unicode.rs b/tests/ui/unicode.rs index f3fd1c57da6..b6944e04859 100644 --- a/tests/ui/unicode.rs +++ b/tests/ui/unicode.rs @@ -1,4 +1,4 @@ -#[warn(clippy::zero_width_space)] +#[warn(clippy::invisible_characters)] fn zero() { print!("Here >​< is a ZWS, and ​another"); print!("This\u{200B}is\u{200B}fine"); diff --git a/tests/ui/unicode.stderr b/tests/ui/unicode.stderr index b0445b070fd..595d80ea279 100644 --- a/tests/ui/unicode.stderr +++ b/tests/ui/unicode.stderr @@ -1,12 +1,12 @@ -error: invisible character detected: '/u{200b}' +error: invisible character detected --> $DIR/unicode.rs:3:12 | LL | print!("Here >​< is a ZWS, and ​another"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{200B}< is a ZWS, and /u{200B}another"` | - = note: `-D clippy::zero-width-space` implied by `-D warnings` + = note: `-D clippy::invisible-characters` implied by `-D warnings` -error: invisible character detected: '/u{ad}' +error: invisible character detected --> $DIR/unicode.rs:5:12 | LL | print!("Here >­< is a SHY, and ­another"); -- cgit 1.4.1-3-g733a5 From 572e4c4837e5f955cdc3751b9ad63f0bfb86beac Mon Sep 17 00:00:00 2001 From: Dániel Buga Date: Sat, 3 Oct 2020 00:07:56 +0200 Subject: Add WJ --- clippy_lints/src/unicode.rs | 7 +++++-- tests/ui/unicode.rs | 2 ++ tests/ui/unicode.stderr | 12 +++++++++--- 3 files changed, 16 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/unicode.rs b/clippy_lints/src/unicode.rs index d6c8d317dc2..93d59cc7fcd 100644 --- a/clippy_lints/src/unicode.rs +++ b/clippy_lints/src/unicode.rs @@ -91,14 +91,17 @@ fn escape>(s: T) -> String { fn check_str(cx: &LateContext<'_>, span: Span, id: HirId) { let string = snippet(cx, span, ""); - if string.chars().any(|c| ['\u{200B}', '\u{ad}'].contains(&c)) { + if string.chars().any(|c| ['\u{200B}', '\u{ad}', '\u{2060}'].contains(&c)) { span_lint_and_sugg( cx, INVISIBLE_CHARACTERS, span, "invisible character detected", "consider replacing the string with", - string.replace("\u{200B}", "\\u{200B}").replace("\u{ad}", "\\u{AD}"), + string + .replace("\u{200B}", "\\u{200B}") + .replace("\u{ad}", "\\u{AD}") + .replace("\u{2060}", "\\u{2060}"), Applicability::MachineApplicable, ); } diff --git a/tests/ui/unicode.rs b/tests/ui/unicode.rs index b6944e04859..1f596c312fe 100644 --- a/tests/ui/unicode.rs +++ b/tests/ui/unicode.rs @@ -4,6 +4,8 @@ fn zero() { print!("This\u{200B}is\u{200B}fine"); print!("Here >­< is a SHY, and ­another"); print!("This\u{ad}is\u{ad}fine"); + print!("Here >⁠< is a WJ, and ⁠another"); + print!("This\u{2060}is\u{2060}fine"); } #[warn(clippy::unicode_not_nfc)] diff --git a/tests/ui/unicode.stderr b/tests/ui/unicode.stderr index 595d80ea279..3fca463c620 100644 --- a/tests/ui/unicode.stderr +++ b/tests/ui/unicode.stderr @@ -12,8 +12,14 @@ error: invisible character detected LL | print!("Here >­< is a SHY, and ­another"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{AD}< is a SHY, and /u{AD}another"` +error: invisible character detected + --> $DIR/unicode.rs:7:12 + | +LL | print!("Here >⁠< is a WJ, and ⁠another"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{2060}< is a WJ, and /u{2060}another"` + error: non-NFC Unicode sequence detected - --> $DIR/unicode.rs:11:12 + --> $DIR/unicode.rs:13:12 | LL | print!("̀àh?"); | ^^^^^ help: consider replacing the string with: `"̀àh?"` @@ -21,12 +27,12 @@ LL | print!("̀àh?"); = note: `-D clippy::unicode-not-nfc` implied by `-D warnings` error: literal non-ASCII character detected - --> $DIR/unicode.rs:17:12 + --> $DIR/unicode.rs:19:12 | LL | print!("Üben!"); | ^^^^^^^ help: consider replacing the string with: `"/u{dc}ben!"` | = note: `-D clippy::non-ascii-literal` implied by `-D warnings` -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors -- cgit 1.4.1-3-g733a5 From be8160878ab7a8e1367acecc173f92e6f509e033 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Thu, 1 Oct 2020 10:34:53 +1300 Subject: split `interior_mutable_const` tests and clean it * remove a 'ERROR' comment from `borrow` `Vec` itself is `Freeze` as it holds the atomic in heap * remove `ONCE_INIT` from `declare` it seems like an artifact from previous spliting --- tests/ui/borrow_interior_mutable_const.rs | 133 -------------- tests/ui/borrow_interior_mutable_const.stderr | 139 -------------- tests/ui/borrow_interior_mutable_const/others.rs | 104 +++++++++++ .../ui/borrow_interior_mutable_const/others.stderr | 115 ++++++++++++ tests/ui/borrow_interior_mutable_const/traits.rs | 202 +++++++++++++++++++++ .../ui/borrow_interior_mutable_const/traits.stderr | 123 +++++++++++++ tests/ui/declare_interior_mutable_const.rs | 174 ------------------ tests/ui/declare_interior_mutable_const.stderr | 110 ----------- tests/ui/declare_interior_mutable_const/others.rs | 34 ++++ .../declare_interior_mutable_const/others.stderr | 39 ++++ tests/ui/declare_interior_mutable_const/traits.rs | 150 +++++++++++++++ .../declare_interior_mutable_const/traits.stderr | 75 ++++++++ 12 files changed, 842 insertions(+), 556 deletions(-) delete mode 100644 tests/ui/borrow_interior_mutable_const.rs delete mode 100644 tests/ui/borrow_interior_mutable_const.stderr create mode 100644 tests/ui/borrow_interior_mutable_const/others.rs create mode 100644 tests/ui/borrow_interior_mutable_const/others.stderr create mode 100644 tests/ui/borrow_interior_mutable_const/traits.rs create mode 100644 tests/ui/borrow_interior_mutable_const/traits.stderr delete mode 100644 tests/ui/declare_interior_mutable_const.rs delete mode 100644 tests/ui/declare_interior_mutable_const.stderr create mode 100644 tests/ui/declare_interior_mutable_const/others.rs create mode 100644 tests/ui/declare_interior_mutable_const/others.stderr create mode 100644 tests/ui/declare_interior_mutable_const/traits.rs create mode 100644 tests/ui/declare_interior_mutable_const/traits.stderr (limited to 'tests') diff --git a/tests/ui/borrow_interior_mutable_const.rs b/tests/ui/borrow_interior_mutable_const.rs deleted file mode 100644 index 9fcc9ece49b..00000000000 --- a/tests/ui/borrow_interior_mutable_const.rs +++ /dev/null @@ -1,133 +0,0 @@ -#![warn(clippy::borrow_interior_mutable_const)] -#![allow(clippy::declare_interior_mutable_const, clippy::ref_in_deref)] -#![allow(const_item_mutation)] - -use std::borrow::Cow; -use std::cell::{Cell, UnsafeCell}; -use std::fmt::Display; -use std::sync::atomic::{AtomicUsize, Ordering}; -use std::sync::Once; - -const ATOMIC: AtomicUsize = AtomicUsize::new(5); -const CELL: Cell = Cell::new(6); -const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec, u8) = ([ATOMIC], Vec::new(), 7); -const INTEGER: u8 = 8; -const STRING: String = String::new(); -const STR: &str = "012345"; -const COW: Cow = Cow::Borrowed("abcdef"); -const NO_ANN: &dyn Display = &70; -static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING); -const ONCE_INIT: Once = Once::new(); - -trait Trait { - type AssocType; - - const ATOMIC: AtomicUsize; - const INPUT: T; - const ASSOC: Self::AssocType; - - fn function() { - let _ = &Self::INPUT; - let _ = &Self::ASSOC; - } -} - -impl Trait for u64 { - type AssocType = AtomicUsize; - - const ATOMIC: AtomicUsize = AtomicUsize::new(9); - const INPUT: u32 = 10; - const ASSOC: Self::AssocType = AtomicUsize::new(11); - - fn function() { - let _ = &Self::INPUT; - let _ = &Self::ASSOC; //~ ERROR interior mutability - } -} - -// This is just a pointer that can be safely dereferended, -// it's semantically the same as `&'static T`; -// but it isn't allowed to make a static reference from an arbitrary integer value at the moment. -// For more information, please see the issue #5918. -pub struct StaticRef { - ptr: *const T, -} - -impl StaticRef { - /// Create a new `StaticRef` from a raw pointer - /// - /// ## Safety - /// - /// Callers must pass in a reference to statically allocated memory which - /// does not overlap with other values. - pub const unsafe fn new(ptr: *const T) -> StaticRef { - StaticRef { ptr } - } -} - -impl std::ops::Deref for StaticRef { - type Target = T; - - fn deref(&self) -> &'static T { - unsafe { &*self.ptr } - } -} - -// use a tuple to make sure referencing a field behind a pointer isn't linted. -const CELL_REF: StaticRef<(UnsafeCell,)> = unsafe { StaticRef::new(std::ptr::null()) }; - -fn main() { - ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability - assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability - - let _once = ONCE_INIT; - let _once_ref = &ONCE_INIT; //~ ERROR interior mutability - let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability - let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability - let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability - let _atomic_into_inner = ATOMIC.into_inner(); - // these should be all fine. - let _twice = (ONCE_INIT, ONCE_INIT); - let _ref_twice = &(ONCE_INIT, ONCE_INIT); - let _ref_once = &(ONCE_INIT, ONCE_INIT).0; - let _array_twice = [ONCE_INIT, ONCE_INIT]; - let _ref_array_twice = &[ONCE_INIT, ONCE_INIT]; - let _ref_array_once = &[ONCE_INIT, ONCE_INIT][0]; - - // referencing projection is still bad. - let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability - let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability - let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability - let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability - let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mutability - let _ = &*ATOMIC_TUPLE.1; //~ ERROR interior mutability - let _ = &ATOMIC_TUPLE.2; - let _ = (&&&&ATOMIC_TUPLE).0; - let _ = (&&&&ATOMIC_TUPLE).2; - let _ = ATOMIC_TUPLE.0; - let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability - let _ = ATOMIC_TUPLE.1.into_iter(); - let _ = ATOMIC_TUPLE.2; - let _ = &{ ATOMIC_TUPLE }; - - CELL.set(2); //~ ERROR interior mutability - assert_eq!(CELL.get(), 6); //~ ERROR interior mutability - - assert_eq!(INTEGER, 8); - assert!(STRING.is_empty()); - - let a = ATOMIC; - a.store(4, Ordering::SeqCst); - assert_eq!(a.load(Ordering::SeqCst), 4); - - STATIC_TUPLE.0.store(3, Ordering::SeqCst); - assert_eq!(STATIC_TUPLE.0.load(Ordering::SeqCst), 3); - assert!(STATIC_TUPLE.1.is_empty()); - - u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability - assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability - - assert_eq!(NO_ANN.to_string(), "70"); // should never lint this. - - let _ = &CELL_REF.0; -} diff --git a/tests/ui/borrow_interior_mutable_const.stderr b/tests/ui/borrow_interior_mutable_const.stderr deleted file mode 100644 index ed726a6b46e..00000000000 --- a/tests/ui/borrow_interior_mutable_const.stderr +++ /dev/null @@ -1,139 +0,0 @@ -error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:44:18 - | -LL | let _ = &Self::ASSOC; //~ ERROR interior mutability - | ^^^^^^^^^^^ - | - = note: `-D clippy::borrow-interior-mutable-const` implied by `-D warnings` - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:80:5 - | -LL | ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability - | ^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:81:16 - | -LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability - | ^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:84:22 - | -LL | let _once_ref = &ONCE_INIT; //~ ERROR interior mutability - | ^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:85:25 - | -LL | let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability - | ^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:86:27 - | -LL | let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability - | ^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:87:26 - | -LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability - | ^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:98:14 - | -LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability - | ^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:99:14 - | -LL | let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability - | ^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:100:19 - | -LL | let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability - | ^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:101:14 - | -LL | let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability - | ^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:102:13 - | -LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mutability - | ^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:108:13 - | -LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability - | ^^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:113:5 - | -LL | CELL.set(2); //~ ERROR interior mutability - | ^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:114:16 - | -LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability - | ^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:127:5 - | -LL | u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability - | ^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: a `const` item with interior mutability should not be borrowed - --> $DIR/borrow_interior_mutable_const.rs:128:16 - | -LL | assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability - | ^^^^^^^^^^^ - | - = help: assign this const to a local or static variable, and use the variable here - -error: aborting due to 17 previous errors - diff --git a/tests/ui/borrow_interior_mutable_const/others.rs b/tests/ui/borrow_interior_mutable_const/others.rs new file mode 100644 index 00000000000..ea25729d11d --- /dev/null +++ b/tests/ui/borrow_interior_mutable_const/others.rs @@ -0,0 +1,104 @@ +#![warn(clippy::borrow_interior_mutable_const)] +#![allow(clippy::declare_interior_mutable_const, clippy::ref_in_deref)] +#![allow(const_item_mutation)] + +use std::borrow::Cow; +use std::cell::{Cell, UnsafeCell}; +use std::fmt::Display; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::Once; + +const ATOMIC: AtomicUsize = AtomicUsize::new(5); +const CELL: Cell = Cell::new(6); +const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec, u8) = ([ATOMIC], Vec::new(), 7); +const INTEGER: u8 = 8; +const STRING: String = String::new(); +const STR: &str = "012345"; +const COW: Cow = Cow::Borrowed("abcdef"); +const NO_ANN: &dyn Display = &70; +static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING); +const ONCE_INIT: Once = Once::new(); + +// This is just a pointer that can be safely dereferenced, +// it's semantically the same as `&'static T`; +// but it isn't allowed to make a static reference from an arbitrary integer value at the moment. +// For more information, please see the issue #5918. +pub struct StaticRef { + ptr: *const T, +} + +impl StaticRef { + /// Create a new `StaticRef` from a raw pointer + /// + /// ## Safety + /// + /// Callers must pass in a reference to statically allocated memory which + /// does not overlap with other values. + pub const unsafe fn new(ptr: *const T) -> StaticRef { + StaticRef { ptr } + } +} + +impl std::ops::Deref for StaticRef { + type Target = T; + + fn deref(&self) -> &'static T { + unsafe { &*self.ptr } + } +} + +// use a tuple to make sure referencing a field behind a pointer isn't linted. +const CELL_REF: StaticRef<(UnsafeCell,)> = unsafe { StaticRef::new(std::ptr::null()) }; + +fn main() { + ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability + assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability + + let _once = ONCE_INIT; + let _once_ref = &ONCE_INIT; //~ ERROR interior mutability + let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability + let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability + let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability + let _atomic_into_inner = ATOMIC.into_inner(); + // these should be all fine. + let _twice = (ONCE_INIT, ONCE_INIT); + let _ref_twice = &(ONCE_INIT, ONCE_INIT); + let _ref_once = &(ONCE_INIT, ONCE_INIT).0; + let _array_twice = [ONCE_INIT, ONCE_INIT]; + let _ref_array_twice = &[ONCE_INIT, ONCE_INIT]; + let _ref_array_once = &[ONCE_INIT, ONCE_INIT][0]; + + // referencing projection is still bad. + let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability + let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability + let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability + let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability + let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mutability + let _ = &*ATOMIC_TUPLE.1; + let _ = &ATOMIC_TUPLE.2; + let _ = (&&&&ATOMIC_TUPLE).0; + let _ = (&&&&ATOMIC_TUPLE).2; + let _ = ATOMIC_TUPLE.0; + let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability + let _ = ATOMIC_TUPLE.1.into_iter(); + let _ = ATOMIC_TUPLE.2; + let _ = &{ ATOMIC_TUPLE }; + + CELL.set(2); //~ ERROR interior mutability + assert_eq!(CELL.get(), 6); //~ ERROR interior mutability + + assert_eq!(INTEGER, 8); + assert!(STRING.is_empty()); + + let a = ATOMIC; + a.store(4, Ordering::SeqCst); + assert_eq!(a.load(Ordering::SeqCst), 4); + + STATIC_TUPLE.0.store(3, Ordering::SeqCst); + assert_eq!(STATIC_TUPLE.0.load(Ordering::SeqCst), 3); + assert!(STATIC_TUPLE.1.is_empty()); + + assert_eq!(NO_ANN.to_string(), "70"); // should never lint this. + + let _ = &CELL_REF.0; +} diff --git a/tests/ui/borrow_interior_mutable_const/others.stderr b/tests/ui/borrow_interior_mutable_const/others.stderr new file mode 100644 index 00000000000..9a908cf30e9 --- /dev/null +++ b/tests/ui/borrow_interior_mutable_const/others.stderr @@ -0,0 +1,115 @@ +error: a `const` item with interior mutability should not be borrowed + --> $DIR/others.rs:54:5 + | +LL | ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability + | ^^^^^^ + | + = note: `-D clippy::borrow-interior-mutable-const` implied by `-D warnings` + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/others.rs:55:16 + | +LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability + | ^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/others.rs:58:22 + | +LL | let _once_ref = &ONCE_INIT; //~ ERROR interior mutability + | ^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/others.rs:59:25 + | +LL | let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability + | ^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/others.rs:60:27 + | +LL | let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability + | ^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/others.rs:61:26 + | +LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability + | ^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/others.rs:72:14 + | +LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability + | ^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/others.rs:73:14 + | +LL | let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability + | ^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/others.rs:74:19 + | +LL | let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability + | ^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/others.rs:75:14 + | +LL | let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability + | ^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/others.rs:76:13 + | +LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mutability + | ^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/others.rs:82:13 + | +LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability + | ^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/others.rs:87:5 + | +LL | CELL.set(2); //~ ERROR interior mutability + | ^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/others.rs:88:16 + | +LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability + | ^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: aborting due to 14 previous errors + diff --git a/tests/ui/borrow_interior_mutable_const/traits.rs b/tests/ui/borrow_interior_mutable_const/traits.rs new file mode 100644 index 00000000000..06b5d62e8f9 --- /dev/null +++ b/tests/ui/borrow_interior_mutable_const/traits.rs @@ -0,0 +1,202 @@ +#![warn(clippy::borrow_interior_mutable_const)] +#![allow(clippy::declare_interior_mutable_const)] + +// this file replicates its `declare` counterpart. Please see it for more discussions. + +use std::borrow::Cow; +use std::cell::Cell; +use std::sync::atomic::{AtomicUsize, Ordering}; + +trait ConcreteTypes { + const ATOMIC: AtomicUsize; + const STRING: String; + + fn function() { + let _ = &Self::ATOMIC; //~ ERROR interior mutable + let _ = &Self::STRING; + } +} + +impl ConcreteTypes for u64 { + const ATOMIC: AtomicUsize = AtomicUsize::new(9); + const STRING: String = String::new(); + + fn function() { + // Lint this again since implementers can choose not to borrow it. + let _ = &Self::ATOMIC; //~ ERROR interior mutable + let _ = &Self::STRING; + } +} + +// a helper trait used below +trait ConstDefault { + const DEFAULT: Self; +} + +trait GenericTypes { + const TO_REMAIN_GENERIC: T; + const TO_BE_CONCRETE: U; + + fn function() { + let _ = &Self::TO_REMAIN_GENERIC; + } +} + +impl GenericTypes for Vec { + const TO_REMAIN_GENERIC: T = T::DEFAULT; + const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11); + + fn function() { + let _ = &Self::TO_REMAIN_GENERIC; + let _ = &Self::TO_BE_CONCRETE; //~ ERROR interior mutable + } +} + +// a helper type used below +pub struct Wrapper(T); + +trait AssocTypes { + type ToBeFrozen; + type ToBeUnfrozen; + type ToBeGenericParam; + + const TO_BE_FROZEN: Self::ToBeFrozen; + const TO_BE_UNFROZEN: Self::ToBeUnfrozen; + const WRAPPED_TO_BE_UNFROZEN: Wrapper; + const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper; + + fn function() { + let _ = &Self::TO_BE_FROZEN; + let _ = &Self::WRAPPED_TO_BE_UNFROZEN; + } +} + +impl AssocTypes for Vec { + type ToBeFrozen = u16; + type ToBeUnfrozen = AtomicUsize; + type ToBeGenericParam = T; + + const TO_BE_FROZEN: Self::ToBeFrozen = 12; + const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13); + const WRAPPED_TO_BE_UNFROZEN: Wrapper = Wrapper(AtomicUsize::new(14)); + const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper = Wrapper(T::DEFAULT); + + fn function() { + let _ = &Self::TO_BE_FROZEN; + let _ = &Self::TO_BE_UNFROZEN; //~ ERROR interior mutable + let _ = &Self::WRAPPED_TO_BE_UNFROZEN; //~ ERROR interior mutable + let _ = &Self::WRAPPED_TO_BE_GENERIC_PARAM; + } +} + +// a helper trait used below +trait AssocTypesHelper { + type NotToBeBounded; + type ToBeBounded; + + const NOT_TO_BE_BOUNDED: Self::NotToBeBounded; +} + +trait AssocTypesFromGenericParam +where + T: AssocTypesHelper, +{ + const NOT_BOUNDED: T::NotToBeBounded; + const BOUNDED: T::ToBeBounded; + + fn function() { + let _ = &Self::NOT_BOUNDED; + let _ = &Self::BOUNDED; //~ ERROR interior mutable + } +} + +impl AssocTypesFromGenericParam for Vec +where + T: AssocTypesHelper, +{ + const NOT_BOUNDED: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED; + const BOUNDED: T::ToBeBounded = AtomicUsize::new(15); + + fn function() { + let _ = &Self::NOT_BOUNDED; + let _ = &Self::BOUNDED; //~ ERROR interior mutable + } +} + +trait SelfType: Sized { + const SELF: Self; + const WRAPPED_SELF: Option; + + fn function() { + let _ = &Self::SELF; + let _ = &Self::WRAPPED_SELF; + } +} + +impl SelfType for u64 { + const SELF: Self = 16; + const WRAPPED_SELF: Option = Some(20); + + fn function() { + let _ = &Self::SELF; + let _ = &Self::WRAPPED_SELF; + } +} + +impl SelfType for AtomicUsize { + const SELF: Self = AtomicUsize::new(17); + const WRAPPED_SELF: Option = Some(AtomicUsize::new(21)); + + fn function() { + let _ = &Self::SELF; //~ ERROR interior mutable + let _ = &Self::WRAPPED_SELF; //~ ERROR interior mutable + } +} + +trait BothOfCellAndGeneric { + const DIRECT: Cell; + const INDIRECT: Cell<*const T>; + + fn function() { + let _ = &Self::DIRECT; + let _ = &Self::INDIRECT; //~ ERROR interior mutable + } +} + +impl BothOfCellAndGeneric for Vec { + const DIRECT: Cell = Cell::new(T::DEFAULT); + const INDIRECT: Cell<*const T> = Cell::new(std::ptr::null()); + + fn function() { + let _ = &Self::DIRECT; + let _ = &Self::INDIRECT; //~ ERROR interior mutable + } +} + +struct Local(T); + +impl Local +where + T: ConstDefault + AssocTypesHelper, +{ + const ATOMIC: AtomicUsize = AtomicUsize::new(18); + const COW: Cow<'static, str> = Cow::Borrowed("tuvwxy"); + + const GENERIC_TYPE: T = T::DEFAULT; + + const ASSOC_TYPE: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED; + const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); + + fn function() { + let _ = &Self::ATOMIC; //~ ERROR interior mutable + let _ = &Self::COW; + let _ = &Self::GENERIC_TYPE; + let _ = &Self::ASSOC_TYPE; + let _ = &Self::BOUNDED_ASSOC_TYPE; //~ ERROR interior mutable + } +} + +fn main() { + u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability + assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability +} diff --git a/tests/ui/borrow_interior_mutable_const/traits.stderr b/tests/ui/borrow_interior_mutable_const/traits.stderr new file mode 100644 index 00000000000..8f26403abd3 --- /dev/null +++ b/tests/ui/borrow_interior_mutable_const/traits.stderr @@ -0,0 +1,123 @@ +error: a `const` item with interior mutability should not be borrowed + --> $DIR/traits.rs:15:18 + | +LL | let _ = &Self::ATOMIC; //~ ERROR interior mutable + | ^^^^^^^^^^^^ + | + = note: `-D clippy::borrow-interior-mutable-const` implied by `-D warnings` + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/traits.rs:26:18 + | +LL | let _ = &Self::ATOMIC; //~ ERROR interior mutable + | ^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/traits.rs:51:18 + | +LL | let _ = &Self::TO_BE_CONCRETE; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/traits.rs:86:18 + | +LL | let _ = &Self::TO_BE_UNFROZEN; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/traits.rs:87:18 + | +LL | let _ = &Self::WRAPPED_TO_BE_UNFROZEN; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/traits.rs:109:18 + | +LL | let _ = &Self::BOUNDED; //~ ERROR interior mutable + | ^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/traits.rs:122:18 + | +LL | let _ = &Self::BOUNDED; //~ ERROR interior mutable + | ^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/traits.rs:151:18 + | +LL | let _ = &Self::SELF; //~ ERROR interior mutable + | ^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/traits.rs:152:18 + | +LL | let _ = &Self::WRAPPED_SELF; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/traits.rs:162:18 + | +LL | let _ = &Self::INDIRECT; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/traits.rs:172:18 + | +LL | let _ = &Self::INDIRECT; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/traits.rs:191:18 + | +LL | let _ = &Self::ATOMIC; //~ ERROR interior mutable + | ^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/traits.rs:195:18 + | +LL | let _ = &Self::BOUNDED_ASSOC_TYPE; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/traits.rs:200:5 + | +LL | u64::ATOMIC.store(5, Ordering::SeqCst); //~ ERROR interior mutability + | ^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/traits.rs:201:16 + | +LL | assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ ERROR interior mutability + | ^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: aborting due to 15 previous errors + diff --git a/tests/ui/declare_interior_mutable_const.rs b/tests/ui/declare_interior_mutable_const.rs deleted file mode 100644 index 3afcdca2f04..00000000000 --- a/tests/ui/declare_interior_mutable_const.rs +++ /dev/null @@ -1,174 +0,0 @@ -#![warn(clippy::declare_interior_mutable_const)] - -use std::borrow::Cow; -use std::cell::Cell; -use std::fmt::Display; -use std::sync::atomic::AtomicUsize; -use std::sync::Once; - -const ATOMIC: AtomicUsize = AtomicUsize::new(5); //~ ERROR interior mutable -const CELL: Cell = Cell::new(6); //~ ERROR interior mutable -const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec, u8) = ([ATOMIC], Vec::new(), 7); -//~^ ERROR interior mutable - -macro_rules! declare_const { - ($name:ident: $ty:ty = $e:expr) => { - const $name: $ty = $e; - }; -} -declare_const!(_ONCE: Once = Once::new()); //~ ERROR interior mutable - -// const ATOMIC_REF: &AtomicUsize = &AtomicUsize::new(7); // This will simply trigger E0492. - -const INTEGER: u8 = 8; -const STRING: String = String::new(); -const STR: &str = "012345"; -const COW: Cow = Cow::Borrowed("abcdef"); -//^ note: a const item of Cow is used in the `postgres` package. - -const NO_ANN: &dyn Display = &70; - -static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING); -//^ there should be no lints on this line - -#[allow(clippy::declare_interior_mutable_const)] -const ONCE_INIT: Once = Once::new(); - -// a constant whose type is a concrete type should be linted at the definition site. -trait ConcreteTypes { - const ATOMIC: AtomicUsize; //~ ERROR interior mutable - const INTEGER: u64; - const STRING: String; - declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); //~ ERROR interior mutable -} - -impl ConcreteTypes for u64 { - const ATOMIC: AtomicUsize = AtomicUsize::new(9); - const INTEGER: u64 = 10; - const STRING: String = String::new(); -} - -// a helper trait used below -trait ConstDefault { - const DEFAULT: Self; -} - -// a constant whose type is a generic type should be linted at the implementation site. -trait GenericTypes { - const TO_REMAIN_GENERIC: T; - const TO_BE_CONCRETE: U; - - const HAVING_DEFAULT: T = Self::TO_REMAIN_GENERIC; - declare_const!(IN_MACRO: T = Self::TO_REMAIN_GENERIC); -} - -impl GenericTypes for u64 { - const TO_REMAIN_GENERIC: T = T::DEFAULT; - const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11); //~ ERROR interior mutable -} - -// a helper type used below -struct Wrapper(T); - -// a constant whose type is an associated type should be linted at the implementation site, too. -trait AssocTypes { - type ToBeFrozen; - type ToBeUnfrozen; - type ToBeGenericParam; - - const TO_BE_FROZEN: Self::ToBeFrozen; - const TO_BE_UNFROZEN: Self::ToBeUnfrozen; - const WRAPPED_TO_BE_UNFROZEN: Wrapper; - // to ensure it can handle things when a generic type remains after normalization. - const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper; -} - -impl AssocTypes for Vec { - type ToBeFrozen = u16; - type ToBeUnfrozen = AtomicUsize; - type ToBeGenericParam = T; - - const TO_BE_FROZEN: Self::ToBeFrozen = 12; - const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13); //~ ERROR interior mutable - const WRAPPED_TO_BE_UNFROZEN: Wrapper = Wrapper(AtomicUsize::new(14)); //~ ERROR interior mutable - const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper = Wrapper(T::DEFAULT); -} - -// a helper trait used below -trait AssocTypesHelper { - type NotToBeBounded; - type ToBeBounded; - - const NOT_TO_BE_BOUNDED: Self::NotToBeBounded; -} - -// a constant whose type is an assoc type originated from a generic param bounded at the definition -// site should be linted at there. -trait AssocTypesFromGenericParam -where - T: AssocTypesHelper, -{ - const NOT_BOUNDED: T::NotToBeBounded; - const BOUNDED: T::ToBeBounded; //~ ERROR interior mutable -} - -impl AssocTypesFromGenericParam for u64 -where - T: AssocTypesHelper, -{ - // an associated type could remain unknown in a trait impl. - const NOT_BOUNDED: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED; - const BOUNDED: T::ToBeBounded = AtomicUsize::new(15); -} - -// a constant whose type is `Self` should be linted at the implementation site as well. -// (`Option` requires `Sized` bound.) -trait SelfType: Sized { - const SELF: Self; - // this was the one in the original issue (#5050). - const WRAPPED_SELF: Option; -} - -impl SelfType for u64 { - const SELF: Self = 16; - const WRAPPED_SELF: Option = Some(20); -} - -impl SelfType for AtomicUsize { - // this (interior mutable `Self` const) exists in `parking_lot`. - // `const_trait_impl` will replace it in the future, hopefully. - const SELF: Self = AtomicUsize::new(17); //~ ERROR interior mutable - const WRAPPED_SELF: Option = Some(AtomicUsize::new(21)); //~ ERROR interior mutable -} - -// Even though a constant contains a generic type, if it also have a interior mutable type, -// it should be linted at the definition site. -trait BothOfCellAndGeneric { - // this is a false negative in the current implementation. - const DIRECT: Cell; - const INDIRECT: Cell<*const T>; //~ ERROR interior mutable -} - -impl BothOfCellAndGeneric for u64 { - const DIRECT: Cell = Cell::new(T::DEFAULT); - const INDIRECT: Cell<*const T> = Cell::new(std::ptr::null()); -} - -struct Local(T); - -// a constant in an inherent impl are essentially the same as a normal const item -// except there can be a generic or associated type. -impl Local -where - T: ConstDefault + AssocTypesHelper, -{ - const ATOMIC: AtomicUsize = AtomicUsize::new(18); //~ ERROR interior mutable - const COW: Cow<'static, str> = Cow::Borrowed("tuvwxy"); - - const GENERIC_TYPE: T = T::DEFAULT; - - const ASSOC_TYPE: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED; - const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); //~ ERROR interior mutable -} - -fn main() {} diff --git a/tests/ui/declare_interior_mutable_const.stderr b/tests/ui/declare_interior_mutable_const.stderr deleted file mode 100644 index 5cb10be88d8..00000000000 --- a/tests/ui/declare_interior_mutable_const.stderr +++ /dev/null @@ -1,110 +0,0 @@ -error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:9:1 - | -LL | const ATOMIC: AtomicUsize = AtomicUsize::new(5); //~ ERROR interior mutable - | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | make this a static item (maybe with lazy_static) - | - = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings` - -error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:10:1 - | -LL | const CELL: Cell = Cell::new(6); //~ ERROR interior mutable - | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | make this a static item (maybe with lazy_static) - -error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:11:1 - | -LL | const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec, u8) = ([ATOMIC], Vec::new(), 7); - | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | make this a static item (maybe with lazy_static) - -error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:16:9 - | -LL | const $name: $ty = $e; - | ^^^^^^^^^^^^^^^^^^^^^^ -... -LL | declare_const!(_ONCE: Once = Once::new()); //~ ERROR interior mutable - | ------------------------------------------ in this macro invocation - | - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:39:5 - | -LL | const ATOMIC: AtomicUsize; //~ ERROR interior mutable - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:16:9 - | -LL | const $name: $ty = $e; - | ^^^^^^^^^^^^^^^^^^^^^^ -... -LL | declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); //~ ERROR interior mutable - | ----------------------------------------------------------- in this macro invocation - | - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:67:5 - | -LL | const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11); //~ ERROR interior mutable - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:92:5 - | -LL | const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13); //~ ERROR interior mutable - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:93:5 - | -LL | const WRAPPED_TO_BE_UNFROZEN: Wrapper = Wrapper(AtomicUsize::new(14)); //~ ERROR interior mutable - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:112:5 - | -LL | const BOUNDED: T::ToBeBounded; //~ ERROR interior mutable - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:140:5 - | -LL | const SELF: Self = AtomicUsize::new(17); //~ ERROR interior mutable - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:141:5 - | -LL | const WRAPPED_SELF: Option = Some(AtomicUsize::new(21)); //~ ERROR interior mutable - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:149:5 - | -LL | const INDIRECT: Cell<*const T>; //~ ERROR interior mutable - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:165:5 - | -LL | const ATOMIC: AtomicUsize = AtomicUsize::new(18); //~ ERROR interior mutable - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: a `const` item should never be interior mutable - --> $DIR/declare_interior_mutable_const.rs:171:5 - | -LL | const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); //~ ERROR interior mutable - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 15 previous errors - diff --git a/tests/ui/declare_interior_mutable_const/others.rs b/tests/ui/declare_interior_mutable_const/others.rs new file mode 100644 index 00000000000..48c5e9537d6 --- /dev/null +++ b/tests/ui/declare_interior_mutable_const/others.rs @@ -0,0 +1,34 @@ +#![warn(clippy::declare_interior_mutable_const)] + +use std::borrow::Cow; +use std::cell::Cell; +use std::fmt::Display; +use std::sync::atomic::AtomicUsize; +use std::sync::Once; + +const ATOMIC: AtomicUsize = AtomicUsize::new(5); //~ ERROR interior mutable +const CELL: Cell = Cell::new(6); //~ ERROR interior mutable +const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec, u8) = ([ATOMIC], Vec::new(), 7); +//~^ ERROR interior mutable + +macro_rules! declare_const { + ($name:ident: $ty:ty = $e:expr) => { + const $name: $ty = $e; + }; +} +declare_const!(_ONCE: Once = Once::new()); //~ ERROR interior mutable + +// const ATOMIC_REF: &AtomicUsize = &AtomicUsize::new(7); // This will simply trigger E0492. + +const INTEGER: u8 = 8; +const STRING: String = String::new(); +const STR: &str = "012345"; +const COW: Cow = Cow::Borrowed("abcdef"); +//^ note: a const item of Cow is used in the `postgres` package. + +const NO_ANN: &dyn Display = &70; + +static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING); +//^ there should be no lints on this line + +fn main() {} diff --git a/tests/ui/declare_interior_mutable_const/others.stderr b/tests/ui/declare_interior_mutable_const/others.stderr new file mode 100644 index 00000000000..6153c96edc4 --- /dev/null +++ b/tests/ui/declare_interior_mutable_const/others.stderr @@ -0,0 +1,39 @@ +error: a `const` item should never be interior mutable + --> $DIR/others.rs:9:1 + | +LL | const ATOMIC: AtomicUsize = AtomicUsize::new(5); //~ ERROR interior mutable + | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | make this a static item (maybe with lazy_static) + | + = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings` + +error: a `const` item should never be interior mutable + --> $DIR/others.rs:10:1 + | +LL | const CELL: Cell = Cell::new(6); //~ ERROR interior mutable + | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | make this a static item (maybe with lazy_static) + +error: a `const` item should never be interior mutable + --> $DIR/others.rs:11:1 + | +LL | const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec, u8) = ([ATOMIC], Vec::new(), 7); + | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | make this a static item (maybe with lazy_static) + +error: a `const` item should never be interior mutable + --> $DIR/others.rs:16:9 + | +LL | const $name: $ty = $e; + | ^^^^^^^^^^^^^^^^^^^^^^ +... +LL | declare_const!(_ONCE: Once = Once::new()); //~ ERROR interior mutable + | ------------------------------------------ in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 4 previous errors + diff --git a/tests/ui/declare_interior_mutable_const/traits.rs b/tests/ui/declare_interior_mutable_const/traits.rs new file mode 100644 index 00000000000..535147ccc64 --- /dev/null +++ b/tests/ui/declare_interior_mutable_const/traits.rs @@ -0,0 +1,150 @@ +#![warn(clippy::declare_interior_mutable_const)] + +use std::borrow::Cow; +use std::cell::Cell; +use std::sync::atomic::AtomicUsize; + +macro_rules! declare_const { + ($name:ident: $ty:ty = $e:expr) => { + const $name: $ty = $e; + }; +} + +// a constant whose type is a concrete type should be linted at the definition site. +trait ConcreteTypes { + const ATOMIC: AtomicUsize; //~ ERROR interior mutable + const INTEGER: u64; + const STRING: String; + declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); //~ ERROR interior mutable +} + +impl ConcreteTypes for u64 { + const ATOMIC: AtomicUsize = AtomicUsize::new(9); + const INTEGER: u64 = 10; + const STRING: String = String::new(); +} + +// a helper trait used below +trait ConstDefault { + const DEFAULT: Self; +} + +// a constant whose type is a generic type should be linted at the implementation site. +trait GenericTypes { + const TO_REMAIN_GENERIC: T; + const TO_BE_CONCRETE: U; + + const HAVING_DEFAULT: T = Self::TO_REMAIN_GENERIC; + declare_const!(IN_MACRO: T = Self::TO_REMAIN_GENERIC); +} + +impl GenericTypes for u64 { + const TO_REMAIN_GENERIC: T = T::DEFAULT; + const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11); //~ ERROR interior mutable +} + +// a helper type used below +struct Wrapper(T); + +// a constant whose type is an associated type should be linted at the implementation site, too. +trait AssocTypes { + type ToBeFrozen; + type ToBeUnfrozen; + type ToBeGenericParam; + + const TO_BE_FROZEN: Self::ToBeFrozen; + const TO_BE_UNFROZEN: Self::ToBeUnfrozen; + const WRAPPED_TO_BE_UNFROZEN: Wrapper; + // to ensure it can handle things when a generic type remains after normalization. + const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper; +} + +impl AssocTypes for Vec { + type ToBeFrozen = u16; + type ToBeUnfrozen = AtomicUsize; + type ToBeGenericParam = T; + + const TO_BE_FROZEN: Self::ToBeFrozen = 12; + const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13); //~ ERROR interior mutable + const WRAPPED_TO_BE_UNFROZEN: Wrapper = Wrapper(AtomicUsize::new(14)); //~ ERROR interior mutable + const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper = Wrapper(T::DEFAULT); +} + +// a helper trait used below +trait AssocTypesHelper { + type NotToBeBounded; + type ToBeBounded; + + const NOT_TO_BE_BOUNDED: Self::NotToBeBounded; +} + +// a constant whose type is an assoc type originated from a generic param bounded at the definition +// site should be linted at there. +trait AssocTypesFromGenericParam +where + T: AssocTypesHelper, +{ + const NOT_BOUNDED: T::NotToBeBounded; + const BOUNDED: T::ToBeBounded; //~ ERROR interior mutable +} + +impl AssocTypesFromGenericParam for u64 +where + T: AssocTypesHelper, +{ + // an associated type could remain unknown in a trait impl. + const NOT_BOUNDED: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED; + const BOUNDED: T::ToBeBounded = AtomicUsize::new(15); +} + +// a constant whose type is `Self` should be linted at the implementation site as well. +// (`Option` requires `Sized` bound.) +trait SelfType: Sized { + const SELF: Self; + // this was the one in the original issue (#5050). + const WRAPPED_SELF: Option; +} + +impl SelfType for u64 { + const SELF: Self = 16; + const WRAPPED_SELF: Option = Some(20); +} + +impl SelfType for AtomicUsize { + // this (interior mutable `Self` const) exists in `parking_lot`. + // `const_trait_impl` will replace it in the future, hopefully. + const SELF: Self = AtomicUsize::new(17); //~ ERROR interior mutable + const WRAPPED_SELF: Option = Some(AtomicUsize::new(21)); //~ ERROR interior mutable +} + +// Even though a constant contains a generic type, if it also have a interior mutable type, +// it should be linted at the definition site. +trait BothOfCellAndGeneric { + // this is a false negative in the current implementation. + const DIRECT: Cell; + const INDIRECT: Cell<*const T>; //~ ERROR interior mutable +} + +impl BothOfCellAndGeneric for u64 { + const DIRECT: Cell = Cell::new(T::DEFAULT); + const INDIRECT: Cell<*const T> = Cell::new(std::ptr::null()); +} + +struct Local(T); + +// a constant in an inherent impl are essentially the same as a normal const item +// except there can be a generic or associated type. +impl Local +where + T: ConstDefault + AssocTypesHelper, +{ + const ATOMIC: AtomicUsize = AtomicUsize::new(18); //~ ERROR interior mutable + const COW: Cow<'static, str> = Cow::Borrowed("tuvwxy"); + + const GENERIC_TYPE: T = T::DEFAULT; + + const ASSOC_TYPE: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED; + const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); //~ ERROR interior mutable +} + +fn main() {} diff --git a/tests/ui/declare_interior_mutable_const/traits.stderr b/tests/ui/declare_interior_mutable_const/traits.stderr new file mode 100644 index 00000000000..bb77f39b62c --- /dev/null +++ b/tests/ui/declare_interior_mutable_const/traits.stderr @@ -0,0 +1,75 @@ +error: a `const` item should never be interior mutable + --> $DIR/traits.rs:15:5 + | +LL | const ATOMIC: AtomicUsize; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings` + +error: a `const` item should never be interior mutable + --> $DIR/traits.rs:9:9 + | +LL | const $name: $ty = $e; + | ^^^^^^^^^^^^^^^^^^^^^^ +... +LL | declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); //~ ERROR interior mutable + | ----------------------------------------------------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: a `const` item should never be interior mutable + --> $DIR/traits.rs:43:5 + | +LL | const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/traits.rs:68:5 + | +LL | const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/traits.rs:69:5 + | +LL | const WRAPPED_TO_BE_UNFROZEN: Wrapper = Wrapper(AtomicUsize::new(14)); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/traits.rs:88:5 + | +LL | const BOUNDED: T::ToBeBounded; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/traits.rs:116:5 + | +LL | const SELF: Self = AtomicUsize::new(17); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/traits.rs:117:5 + | +LL | const WRAPPED_SELF: Option = Some(AtomicUsize::new(21)); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/traits.rs:125:5 + | +LL | const INDIRECT: Cell<*const T>; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/traits.rs:141:5 + | +LL | const ATOMIC: AtomicUsize = AtomicUsize::new(18); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/traits.rs:147:5 + | +LL | const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 11 previous errors + -- cgit 1.4.1-3-g733a5 From f58a1695a67e2db741b7237992e696ff9a14d0ab Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Thu, 1 Oct 2020 10:51:49 +1300 Subject: fix a FP in `interior_mutable_const` fix a false positive in two `interior_mutable_const` lints where a constant with enums gets linted even if it uses a clearly unfrozen variant. Note that the code uses the MIR interpreter, which the author of #3962 thought unlikely to be a solution. This might be over-engineering; but, I think it's important to be able to work with the 'http' crate (#3825). --- clippy_lints/src/non_copy_const.rs | 170 ++++++++++++++++----- .../auxiliary/helper.rs | 16 ++ tests/ui/borrow_interior_mutable_const/enums.rs | 101 ++++++++++++ .../ui/borrow_interior_mutable_const/enums.stderr | 75 +++++++++ tests/ui/declare_interior_mutable_const/enums.rs | 123 +++++++++++++++ .../ui/declare_interior_mutable_const/enums.stderr | 89 +++++++++++ 6 files changed, 539 insertions(+), 35 deletions(-) create mode 100644 tests/ui/borrow_interior_mutable_const/auxiliary/helper.rs create mode 100644 tests/ui/borrow_interior_mutable_const/enums.rs create mode 100644 tests/ui/borrow_interior_mutable_const/enums.stderr create mode 100644 tests/ui/declare_interior_mutable_const/enums.rs create mode 100644 tests/ui/declare_interior_mutable_const/enums.stderr (limited to 'tests') diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 7b662eae775..6b0d198edcf 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -5,11 +5,15 @@ use std::ptr; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind, UnOp}; +use rustc_hir::def_id::DefId; +use rustc_hir::{ + BodyId, Expr, ExprKind, HirId, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind, UnOp, +}; use rustc_infer::traits::specialization_graph; use rustc_lint::{LateContext, LateLintPass, Lint}; +use rustc_middle::mir::interpret::{ConstValue, ErrorHandled}; use rustc_middle::ty::adjustment::Adjust; -use rustc_middle::ty::{AssocKind, Ty}; +use rustc_middle::ty::{self, AssocKind, Const, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{InnerSpan, Span, DUMMY_SP}; use rustc_typeck::hir_ty_to_ty; @@ -36,14 +40,17 @@ declare_clippy_lint! { /// `std::sync::ONCE_INIT` constant). In this case the use of `const` is legit, /// and this lint should be suppressed. /// - /// When an enum has variants with interior mutability, use of its non interior mutable - /// variants can generate false positives. See issue - /// [#3962](https://github.com/rust-lang/rust-clippy/issues/3962) + /// Even though the lint avoids triggering on a constant whose type has enums that have variants + /// with interior mutability, and its value uses non interior mutable variants (see + /// [#3962](https://github.com/rust-lang/rust-clippy/issues/3962) and + /// [#3825](https://github.com/rust-lang/rust-clippy/issues/3825) for examples); + /// it complains about associated constants without default values only based on its types; + /// which might not be preferable. + /// There're other enums plus associated constants cases that the lint cannot handle. /// /// Types that have underlying or potential interior mutability trigger the lint whether /// the interior mutable field is used or not. See issues /// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and - /// [#3825](https://github.com/rust-lang/rust-clippy/issues/3825) /// /// **Example:** /// ```rust @@ -105,6 +112,79 @@ declare_clippy_lint! { "referencing `const` with interior mutability" } +fn is_unfrozen<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + // Ignore types whose layout is unknown since `is_freeze` reports every generic types as `!Freeze`, + // making it indistinguishable from `UnsafeCell`. i.e. it isn't a tool to prove a type is + // 'unfrozen'. However, this code causes a false negative in which + // a type contains a layout-unknown type, but also a unsafe cell like `const CELL: Cell`. + // Yet, it's better than `ty.has_type_flags(TypeFlags::HAS_TY_PARAM | TypeFlags::HAS_PROJECTION)` + // since it works when a pointer indirection involves (`Cell<*const T>`). + // Making up a `ParamEnv` where every generic params and assoc types are `Freeze`is another option; + // but I'm not sure whether it's a decent way, if possible. + cx.tcx.layout_of(cx.param_env.and(ty)).is_ok() && !ty.is_freeze(cx.tcx.at(DUMMY_SP), cx.param_env) +} + +fn is_value_unfrozen_raw<'tcx>( + cx: &LateContext<'tcx>, + result: Result, ErrorHandled>, + ty: Ty<'tcx>, +) -> bool { + fn inner<'tcx>(cx: &LateContext<'tcx>, val: &'tcx Const<'tcx>) -> bool { + match val.ty.kind() { + // the fact that we have to dig into every structs to search enums + // leads us to the point checking `UnsafeCell` directly is the only option. + ty::Adt(ty_def, ..) if Some(ty_def.did) == cx.tcx.lang_items().unsafe_cell_type() => true, + ty::Array(..) | ty::Adt(..) | ty::Tuple(..) => { + let val = cx.tcx.destructure_const(cx.param_env.and(val)); + val.fields.iter().any(|field| inner(cx, field)) + }, + _ => false, + } + } + + result.map_or_else( + |err| { + // Consider `TooGeneric` cases as being unfrozen. + // This causes a false positive where an assoc const whose type is unfrozen + // have a value that is a frozen variant with a generic param (an example is + // `declare_interior_mutable_const::enums::BothOfCellAndGeneric::GENERIC_VARIANT`). + // However, it prevents a number of false negatives that is, I think, important: + // 1. assoc consts in trait defs referring to consts of themselves + // (an example is `declare_interior_mutable_const::traits::ConcreteTypes::ANOTHER_ATOMIC`). + // 2. a path expr referring to assoc consts whose type is doesn't have + // any frozen variants in trait defs (i.e. without substitute for `Self`). + // (e.g. borrowing `borrow_interior_mutable_const::trait::ConcreteTypes::ATOMIC`) + // 3. similar to the false positive above; + // but the value is an unfrozen variant, or the type has no enums. (An example is + // `declare_interior_mutable_const::enums::BothOfCellAndGeneric::UNFROZEN_VARIANT` + // and `declare_interior_mutable_const::enums::BothOfCellAndGeneric::NO_ENUM`). + // One might be able to prevent these FNs correctly, and replace this with `false`; + // e.g. implementing `has_frozen_variant` described above, and not running this function + // when the type doesn't have any frozen variants would be the 'correct' way for the 2nd + // case (that actually removes another suboptimal behavior (I won't say 'false positive') where, + // similar to 2., but with the a frozen variant) (e.g. borrowing + // `borrow_interior_mutable_const::enums::AssocConsts::TO_BE_FROZEN_VARIANT`). + // I chose this way because unfrozen enums as assoc consts are rare (or, hopefully, none). + err == ErrorHandled::TooGeneric + }, + |val| inner(cx, Const::from_value(cx.tcx, val, ty)), + ) +} + +fn is_value_unfrozen_poly<'tcx>(cx: &LateContext<'tcx>, body_id: BodyId, ty: Ty<'tcx>) -> bool { + let result = cx.tcx.const_eval_poly(body_id.hir_id.owner.to_def_id()); + is_value_unfrozen_raw(cx, result, ty) +} + +fn is_value_unfrozen_expr<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId, def_id: DefId, ty: Ty<'tcx>) -> bool { + let substs = cx.typeck_results().node_substs(hir_id); + + let result = cx + .tcx + .const_eval_resolve(cx.param_env, ty::WithOptConstParam::unknown(def_id), substs, None, None); + is_value_unfrozen_raw(cx, result, ty) +} + #[derive(Copy, Clone)] enum Source { Item { item: Span }, @@ -130,19 +210,7 @@ impl Source { } } -fn verify_ty_bound<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, source: Source) { - // Ignore types whose layout is unknown since `is_freeze` reports every generic types as `!Freeze`, - // making it indistinguishable from `UnsafeCell`. i.e. it isn't a tool to prove a type is - // 'unfrozen'. However, this code causes a false negative in which - // a type contains a layout-unknown type, but also a unsafe cell like `const CELL: Cell`. - // Yet, it's better than `ty.has_type_flags(TypeFlags::HAS_TY_PARAM | TypeFlags::HAS_PROJECTION)` - // since it works when a pointer indirection involves (`Cell<*const T>`). - // Making up a `ParamEnv` where every generic params and assoc types are `Freeze`is another option; - // but I'm not sure whether it's a decent way, if possible. - if cx.tcx.layout_of(cx.param_env.and(ty)).is_err() || ty.is_freeze(cx.tcx.at(DUMMY_SP), cx.param_env) { - return; - } - +fn lint(cx: &LateContext<'_>, source: Source) { let (lint, msg, span) = source.lint(); span_lint_and_then(cx, lint, span, msg, |diag| { if span.from_expansion() { @@ -165,24 +233,44 @@ declare_lint_pass!(NonCopyConst => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTER impl<'tcx> LateLintPass<'tcx> for NonCopyConst { fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) { - if let ItemKind::Const(hir_ty, ..) = &it.kind { + if let ItemKind::Const(hir_ty, body_id) = it.kind { let ty = hir_ty_to_ty(cx.tcx, hir_ty); - verify_ty_bound(cx, ty, Source::Item { item: it.span }); + + if is_unfrozen(cx, ty) && is_value_unfrozen_poly(cx, body_id, ty) { + lint(cx, Source::Item { item: it.span }); + } } } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx TraitItem<'_>) { - if let TraitItemKind::Const(hir_ty, ..) = &trait_item.kind { + if let TraitItemKind::Const(hir_ty, body_id_opt) = &trait_item.kind { let ty = hir_ty_to_ty(cx.tcx, hir_ty); + // Normalize assoc types because ones originated from generic params // bounded other traits could have their bound. let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); - verify_ty_bound(cx, normalized, Source::Assoc { item: trait_item.span }); + if is_unfrozen(cx, normalized) + // When there's no default value, lint it only according to its type; + // in other words, lint consts whose value *could* be unfrozen, not definitely is. + // This feels inconsistent with how the lint treats generic types, + // which avoids linting types which potentially become unfrozen. + // One could check whether a unfrozen type have a *frozen variant* + // (like `body_id_opt.map_or_else(|| !has_frozen_variant(...), ...)`), + // and do the same as the case of generic types at impl items. + // Note that it isn't sufficient to check if it has an enum + // since all of that enum's variants can be unfrozen: + // i.e. having an enum doesn't necessary mean a type has a frozen variant. + // And, implementing it isn't a trivial task; it'll probably end up + // re-implementing the trait predicate evaluation specific to `Freeze`. + && body_id_opt.map_or(true, |body_id| is_value_unfrozen_poly(cx, body_id, normalized)) + { + lint(cx, Source::Assoc { item: trait_item.span }); + } } } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) { - if let ImplItemKind::Const(hir_ty, ..) = &impl_item.kind { + if let ImplItemKind::Const(hir_ty, body_id) = &impl_item.kind { let item_hir_id = cx.tcx.hir().get_parent_node(impl_item.hir_id); let item = cx.tcx.hir().expect_item(item_hir_id); @@ -209,16 +297,23 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { ), )) .is_err(); + // If there were a function like `has_frozen_variant` described above, + // we should use here as a frozen variant is a potential to be frozen + // similar to unknown layouts. + // e.g. `layout_of(...).is_err() || has_frozen_variant(...);` then { let ty = hir_ty_to_ty(cx.tcx, hir_ty); let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); - verify_ty_bound( - cx, - normalized, - Source::Assoc { - item: impl_item.span, - }, - ); + if is_unfrozen(cx, normalized) + && is_value_unfrozen_poly(cx, *body_id, normalized) + { + lint( + cx, + Source::Assoc { + item: impl_item.span, + }, + ); + } } } }, @@ -226,7 +321,10 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { let ty = hir_ty_to_ty(cx.tcx, hir_ty); // Normalize assoc types originated from generic params. let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty); - verify_ty_bound(cx, normalized, Source::Assoc { item: impl_item.span }); + + if is_unfrozen(cx, ty) && is_value_unfrozen_poly(cx, *body_id, normalized) { + lint(cx, Source::Assoc { item: impl_item.span }); + } }, _ => (), } @@ -241,8 +339,8 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { } // Make sure it is a const item. - match qpath_res(cx, qpath, expr.hir_id) { - Res::Def(DefKind::Const | DefKind::AssocConst, _) => {}, + let item_def_id = match qpath_res(cx, qpath, expr.hir_id) { + Res::Def(DefKind::Const | DefKind::AssocConst, did) => did, _ => return, }; @@ -319,7 +417,9 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { cx.typeck_results().expr_ty(dereferenced_expr) }; - verify_ty_bound(cx, ty, Source::Expr { expr: expr.span }); + if is_unfrozen(cx, ty) && is_value_unfrozen_expr(cx, expr.hir_id, item_def_id, ty) { + lint(cx, Source::Expr { expr: expr.span }); + } } } } diff --git a/tests/ui/borrow_interior_mutable_const/auxiliary/helper.rs b/tests/ui/borrow_interior_mutable_const/auxiliary/helper.rs new file mode 100644 index 00000000000..2289f7875f0 --- /dev/null +++ b/tests/ui/borrow_interior_mutable_const/auxiliary/helper.rs @@ -0,0 +1,16 @@ +// this file solely exists to test constants defined in foreign crates. +// As the most common case is the `http` crate, it replicates `http::HeadewrName`'s structure. + +#![allow(clippy::declare_interior_mutable_const)] + +use std::sync::atomic::AtomicUsize; + +enum Private { + ToBeUnfrozen(T), + Frozen(usize), +} + +pub struct Wrapper(Private); + +pub const WRAPPED_PRIVATE_UNFROZEN_VARIANT: Wrapper = Wrapper(Private::ToBeUnfrozen(AtomicUsize::new(6))); +pub const WRAPPED_PRIVATE_FROZEN_VARIANT: Wrapper = Wrapper(Private::Frozen(7)); diff --git a/tests/ui/borrow_interior_mutable_const/enums.rs b/tests/ui/borrow_interior_mutable_const/enums.rs new file mode 100644 index 00000000000..5027db44561 --- /dev/null +++ b/tests/ui/borrow_interior_mutable_const/enums.rs @@ -0,0 +1,101 @@ +// aux-build:helper.rs + +#![warn(clippy::borrow_interior_mutable_const)] +#![allow(clippy::declare_interior_mutable_const)] + +// this file (mostly) replicates its `declare` counterpart. Please see it for more discussions. + +extern crate helper; + +use std::cell::Cell; +use std::sync::atomic::AtomicUsize; + +enum OptionalCell { + Unfrozen(Cell), + Frozen, +} + +const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(true)); +const FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; + +fn borrow_optional_cell() { + let _ = &UNFROZEN_VARIANT; //~ ERROR interior mutability + let _ = &FROZEN_VARIANT; +} + +trait AssocConsts { + const TO_BE_UNFROZEN_VARIANT: OptionalCell; + const TO_BE_FROZEN_VARIANT: OptionalCell; + + const DEFAULTED_ON_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); + const DEFAULTED_ON_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; + + fn function() { + // This is the "suboptimal behavior" mentioned in `is_value_unfrozen` + // caused by a similar reason to unfrozen types without any default values + // get linted even if it has frozen variants'. + let _ = &Self::TO_BE_FROZEN_VARIANT; //~ ERROR interior mutable + + // The lint ignores default values because an impl of this trait can set + // an unfrozen variant to `DEFAULTED_ON_FROZEN_VARIANT` and use the default impl for `function`. + let _ = &Self::DEFAULTED_ON_FROZEN_VARIANT; //~ ERROR interior mutable + } +} + +impl AssocConsts for u64 { + const TO_BE_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); + const TO_BE_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; + + fn function() { + let _ = &::TO_BE_UNFROZEN_VARIANT; //~ ERROR interior mutable + let _ = &::TO_BE_FROZEN_VARIANT; + let _ = &Self::DEFAULTED_ON_UNFROZEN_VARIANT; //~ ERROR interior mutable + let _ = &Self::DEFAULTED_ON_FROZEN_VARIANT; + } +} + +trait AssocTypes { + type ToBeUnfrozen; + + const TO_BE_UNFROZEN_VARIANT: Option; + const TO_BE_FROZEN_VARIANT: Option; + + // there's no need to test here because it's the exactly same as `trait::AssocTypes` + fn function(); +} + +impl AssocTypes for u64 { + type ToBeUnfrozen = AtomicUsize; + + const TO_BE_UNFROZEN_VARIANT: Option = Some(Self::ToBeUnfrozen::new(4)); //~ ERROR interior mutable + const TO_BE_FROZEN_VARIANT: Option = None; + + fn function() { + let _ = &::TO_BE_UNFROZEN_VARIANT; //~ ERROR interior mutable + let _ = &::TO_BE_FROZEN_VARIANT; + } +} + +enum BothOfCellAndGeneric { + Unfrozen(Cell<*const T>), + Generic(*const T), + Frozen(usize), +} + +impl BothOfCellAndGeneric { + const UNFROZEN_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); //~ ERROR interior mutable + const GENERIC_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Generic(std::ptr::null()); //~ ERROR interior mutable + const FROZEN_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Frozen(5); + + fn function() { + let _ = &Self::UNFROZEN_VARIANT; //~ ERROR interior mutability + let _ = &Self::GENERIC_VARIANT; //~ ERROR interior mutability + let _ = &Self::FROZEN_VARIANT; + } +} + +fn main() { + // constants defined in foreign crates + let _ = &helper::WRAPPED_PRIVATE_UNFROZEN_VARIANT; //~ ERROR interior mutability + let _ = &helper::WRAPPED_PRIVATE_FROZEN_VARIANT; +} diff --git a/tests/ui/borrow_interior_mutable_const/enums.stderr b/tests/ui/borrow_interior_mutable_const/enums.stderr new file mode 100644 index 00000000000..654a1ee7df6 --- /dev/null +++ b/tests/ui/borrow_interior_mutable_const/enums.stderr @@ -0,0 +1,75 @@ +error: a `const` item with interior mutability should not be borrowed + --> $DIR/enums.rs:22:14 + | +LL | let _ = &UNFROZEN_VARIANT; //~ ERROR interior mutability + | ^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::borrow-interior-mutable-const` implied by `-D warnings` + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/enums.rs:37:18 + | +LL | let _ = &Self::TO_BE_FROZEN_VARIANT; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/enums.rs:41:18 + | +LL | let _ = &Self::DEFAULTED_ON_FROZEN_VARIANT; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/enums.rs:50:18 + | +LL | let _ = &::TO_BE_UNFROZEN_VARIANT; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/enums.rs:52:18 + | +LL | let _ = &Self::DEFAULTED_ON_UNFROZEN_VARIANT; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/enums.rs:74:18 + | +LL | let _ = &::TO_BE_UNFROZEN_VARIANT; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/enums.rs:91:18 + | +LL | let _ = &Self::UNFROZEN_VARIANT; //~ ERROR interior mutability + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/enums.rs:92:18 + | +LL | let _ = &Self::GENERIC_VARIANT; //~ ERROR interior mutability + | ^^^^^^^^^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: a `const` item with interior mutability should not be borrowed + --> $DIR/enums.rs:99:14 + | +LL | let _ = &helper::WRAPPED_PRIVATE_UNFROZEN_VARIANT; //~ ERROR interior mutability + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: assign this const to a local or static variable, and use the variable here + +error: aborting due to 9 previous errors + diff --git a/tests/ui/declare_interior_mutable_const/enums.rs b/tests/ui/declare_interior_mutable_const/enums.rs new file mode 100644 index 00000000000..f44518694b8 --- /dev/null +++ b/tests/ui/declare_interior_mutable_const/enums.rs @@ -0,0 +1,123 @@ +#![warn(clippy::declare_interior_mutable_const)] + +use std::cell::Cell; +use std::sync::atomic::AtomicUsize; + +enum OptionalCell { + Unfrozen(Cell), + Frozen, +} + +// a constant with enums should be linted only when the used variant is unfrozen (#3962). +const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(true)); //~ ERROR interior mutable +const FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; + +const fn unfrozen_variant() -> OptionalCell { + OptionalCell::Unfrozen(Cell::new(false)) +} + +const fn frozen_variant() -> OptionalCell { + OptionalCell::Frozen +} + +const UNFROZEN_VARIANT_FROM_FN: OptionalCell = unfrozen_variant(); //~ ERROR interior mutable +const FROZEN_VARIANT_FROM_FN: OptionalCell = frozen_variant(); + +enum NestedInnermost { + Unfrozen(AtomicUsize), + Frozen, +} + +struct NestedInner { + inner: NestedInnermost, +} + +enum NestedOuter { + NestedInner(NestedInner), + NotNested(usize), +} + +struct NestedOutermost { + outer: NestedOuter, +} + +// a constant with enums should be linted according to its value, no matter how structs involve. +const NESTED_UNFROZEN_VARIANT: NestedOutermost = NestedOutermost { + outer: NestedOuter::NestedInner(NestedInner { + inner: NestedInnermost::Unfrozen(AtomicUsize::new(2)), + }), +}; //~ ERROR interior mutable +const NESTED_FROZEN_VARIANT: NestedOutermost = NestedOutermost { + outer: NestedOuter::NestedInner(NestedInner { + inner: NestedInnermost::Frozen, + }), +}; + +trait AssocConsts { + // When there's no default value, lint it only according to its type. + // Further details are on the corresponding code (`NonCopyConst::check_trait_item`). + const TO_BE_UNFROZEN_VARIANT: OptionalCell; //~ ERROR interior mutable + const TO_BE_FROZEN_VARIANT: OptionalCell; //~ ERROR interior mutable + + // Lint default values accordingly. + const DEFAULTED_ON_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); //~ ERROR interior mutable + const DEFAULTED_ON_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; +} + +// The lint doesn't trigger for an assoc constant in a trait impl with an unfrozen type even if it +// has enums. Further details are on the corresponding code in 'NonCopyConst::check_impl_item'. +impl AssocConsts for u64 { + const TO_BE_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); + const TO_BE_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; + + // even if this sets an unfrozen variant, the lint ignores it. + const DEFAULTED_ON_FROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); +} + +// At first, I thought I'd need to check every patterns in `trait.rs`; but, what matters +// here are values; and I think substituted generics at definitions won't appear in MIR. +trait AssocTypes { + type ToBeUnfrozen; + + const TO_BE_UNFROZEN_VARIANT: Option; + const TO_BE_FROZEN_VARIANT: Option; +} + +impl AssocTypes for u64 { + type ToBeUnfrozen = AtomicUsize; + + const TO_BE_UNFROZEN_VARIANT: Option = Some(Self::ToBeUnfrozen::new(4)); //~ ERROR interior mutable + const TO_BE_FROZEN_VARIANT: Option = None; +} + +// Use raw pointers since direct generics have a false negative at the type level. +enum BothOfCellAndGeneric { + Unfrozen(Cell<*const T>), + Generic(*const T), + Frozen(usize), +} + +impl BothOfCellAndGeneric { + const UNFROZEN_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); //~ ERROR interior mutable + + // This is a false positive. The argument about this is on `is_value_unfrozen_raw` + const GENERIC_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Generic(std::ptr::null()); //~ ERROR interior mutable + + const FROZEN_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Frozen(5); + + // This is what is likely to be a false negative when one tries to fix + // the `GENERIC_VARIANT` false positive. + const NO_ENUM: Cell<*const T> = Cell::new(std::ptr::null()); //~ ERROR interior mutable +} + +// associated types here is basically the same as the one above. +trait BothOfCellAndGenericWithAssocType { + type AssocType; + + const UNFROZEN_VARIANT: BothOfCellAndGeneric = + BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); //~ ERROR interior mutable + const GENERIC_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Generic(std::ptr::null()); //~ ERROR interior mutable + const FROZEN_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Frozen(5); +} + +fn main() {} diff --git a/tests/ui/declare_interior_mutable_const/enums.stderr b/tests/ui/declare_interior_mutable_const/enums.stderr new file mode 100644 index 00000000000..84198d54615 --- /dev/null +++ b/tests/ui/declare_interior_mutable_const/enums.stderr @@ -0,0 +1,89 @@ +error: a `const` item should never be interior mutable + --> $DIR/enums.rs:12:1 + | +LL | const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(true)); //~ ERROR interior mutable + | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | make this a static item (maybe with lazy_static) + | + = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings` + +error: a `const` item should never be interior mutable + --> $DIR/enums.rs:23:1 + | +LL | const UNFROZEN_VARIANT_FROM_FN: OptionalCell = unfrozen_variant(); //~ ERROR interior mutable + | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | make this a static item (maybe with lazy_static) + +error: a `const` item should never be interior mutable + --> $DIR/enums.rs:45:1 + | +LL | const NESTED_UNFROZEN_VARIANT: NestedOutermost = NestedOutermost { + | ^---- + | | + | _make this a static item (maybe with lazy_static) + | | +LL | | outer: NestedOuter::NestedInner(NestedInner { +LL | | inner: NestedInnermost::Unfrozen(AtomicUsize::new(2)), +LL | | }), +LL | | }; //~ ERROR interior mutable + | |__^ + +error: a `const` item should never be interior mutable + --> $DIR/enums.rs:59:5 + | +LL | const TO_BE_UNFROZEN_VARIANT: OptionalCell; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/enums.rs:60:5 + | +LL | const TO_BE_FROZEN_VARIANT: OptionalCell; //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/enums.rs:63:5 + | +LL | const DEFAULTED_ON_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/enums.rs:89:5 + | +LL | const TO_BE_UNFROZEN_VARIANT: Option = Some(Self::ToBeUnfrozen::new(4)); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/enums.rs:101:5 + | +LL | const UNFROZEN_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); //~ ERROR interior mut... + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/enums.rs:104:5 + | +LL | const GENERIC_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Generic(std::ptr::null()); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/enums.rs:110:5 + | +LL | const NO_ENUM: Cell<*const T> = Cell::new(std::ptr::null()); //~ ERROR interior mutable + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: a `const` item should never be interior mutable + --> $DIR/enums.rs:117:5 + | +LL | / const UNFROZEN_VARIANT: BothOfCellAndGeneric = +LL | | BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); //~ ERROR interior mutable + | |____________________________________________________________________^ + +error: a `const` item should never be interior mutable + --> $DIR/enums.rs:119:5 + | +LL | const GENERIC_VARIANT: BothOfCellAndGeneric = BothOfCellAndGeneric::Generic(std::ptr::null()); //~ ERROR interior mu... + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 12 previous errors + -- cgit 1.4.1-3-g733a5 From 78695bd4968d45ef6afd3433af4bf6f310128770 Mon Sep 17 00:00:00 2001 From: FliegendeWurst <2012gdwu@web.de> Date: Mon, 5 Oct 2020 12:08:57 +0200 Subject: Do not lint float fractions in `mistyped_literal_suffixes` (fixes #4706) --- clippy_lints/src/literal_representation.rs | 9 +++------ tests/ui/mistyped_literal_suffix.fixed | 6 +++--- tests/ui/mistyped_literal_suffix.rs | 6 +++--- tests/ui/mistyped_literal_suffix.stderr | 14 +------------- 4 files changed, 10 insertions(+), 25 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index a36fdca5d5d..c54103b25c2 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -264,13 +264,10 @@ impl LiteralDigitGrouping { let (part, mistyped_suffixes, missing_char) = if let Some((_, exponent)) = &mut num_lit.exponent { (exponent, &["32", "64"][..], 'f') + } else if num_lit.fraction.is_some() { + (&mut num_lit.integer, &["32", "64"][..], 'f') } else { - num_lit - .fraction - .as_mut() - .map_or((&mut num_lit.integer, &["8", "16", "32", "64"][..], 'i'), |fraction| { - (fraction, &["32", "64"][..], 'f') - }) + (&mut num_lit.integer, &["8", "16", "32", "64"][..], 'i') }; let mut split = part.rsplit('_'); diff --git a/tests/ui/mistyped_literal_suffix.fixed b/tests/ui/mistyped_literal_suffix.fixed index baee7735730..8275bfa5db7 100644 --- a/tests/ui/mistyped_literal_suffix.fixed +++ b/tests/ui/mistyped_literal_suffix.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![allow(dead_code, unused_variables, clippy::excessive_precision)] +#![allow(dead_code, unused_variables, clippy::excessive_precision, clippy::inconsistent_digit_grouping)] fn main() { let fail14 = 2_i32; @@ -12,13 +12,13 @@ fn main() { let fail20 = 2_i8; // let fail21 = 4_i16; // - let fail24 = 12.34_f64; + let ok24 = 12.34_64; let fail25 = 1E2_f32; let fail26 = 43E7_f64; let fail27 = 243E17_f32; #[allow(overflowing_literals)] let fail28 = 241_251_235E723_f64; - let fail29 = 42_279.911_f32; + let ok29 = 42279.911_32; let _ = 1.123_45E1_f32; } diff --git a/tests/ui/mistyped_literal_suffix.rs b/tests/ui/mistyped_literal_suffix.rs index 6de447f4021..8a451807aeb 100644 --- a/tests/ui/mistyped_literal_suffix.rs +++ b/tests/ui/mistyped_literal_suffix.rs @@ -1,6 +1,6 @@ // run-rustfix -#![allow(dead_code, unused_variables, clippy::excessive_precision)] +#![allow(dead_code, unused_variables, clippy::excessive_precision, clippy::inconsistent_digit_grouping)] fn main() { let fail14 = 2_32; @@ -12,13 +12,13 @@ fn main() { let fail20 = 2__8; // let fail21 = 4___16; // - let fail24 = 12.34_64; + let ok24 = 12.34_64; let fail25 = 1E2_32; let fail26 = 43E7_64; let fail27 = 243E17_32; #[allow(overflowing_literals)] let fail28 = 241251235E723_64; - let fail29 = 42279.911_32; + let ok29 = 42279.911_32; let _ = 1.12345E1_32; } diff --git a/tests/ui/mistyped_literal_suffix.stderr b/tests/ui/mistyped_literal_suffix.stderr index 48a7ae90494..f4c5adfe82c 100644 --- a/tests/ui/mistyped_literal_suffix.stderr +++ b/tests/ui/mistyped_literal_suffix.stderr @@ -36,12 +36,6 @@ error: mistyped literal suffix LL | let fail21 = 4___16; // | ^^^^^^ help: did you mean to write: `4_i16` -error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:15:18 - | -LL | let fail24 = 12.34_64; - | ^^^^^^^^ help: did you mean to write: `12.34_f64` - error: mistyped literal suffix --> $DIR/mistyped_literal_suffix.rs:16:18 | @@ -66,17 +60,11 @@ error: mistyped literal suffix LL | let fail28 = 241251235E723_64; | ^^^^^^^^^^^^^^^^ help: did you mean to write: `241_251_235E723_f64` -error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:21:18 - | -LL | let fail29 = 42279.911_32; - | ^^^^^^^^^^^^ help: did you mean to write: `42_279.911_f32` - error: mistyped literal suffix --> $DIR/mistyped_literal_suffix.rs:23:13 | LL | let _ = 1.12345E1_32; | ^^^^^^^^^^^^ help: did you mean to write: `1.123_45E1_f32` -error: aborting due to 13 previous errors +error: aborting due to 11 previous errors -- cgit 1.4.1-3-g733a5 From 428ef362d6901029bbf945d5f440f4122ebcece6 Mon Sep 17 00:00:00 2001 From: FliegendeWurst <2012gdwu@web.de> Date: Mon, 5 Oct 2020 12:23:01 +0200 Subject: Fix test formatting --- tests/ui/mistyped_literal_suffix.fixed | 7 ++++++- tests/ui/mistyped_literal_suffix.rs | 7 ++++++- tests/ui/mistyped_literal_suffix.stderr | 22 +++++++++++----------- 3 files changed, 23 insertions(+), 13 deletions(-) (limited to 'tests') diff --git a/tests/ui/mistyped_literal_suffix.fixed b/tests/ui/mistyped_literal_suffix.fixed index 8275bfa5db7..70cdb067d91 100644 --- a/tests/ui/mistyped_literal_suffix.fixed +++ b/tests/ui/mistyped_literal_suffix.fixed @@ -1,6 +1,11 @@ // run-rustfix -#![allow(dead_code, unused_variables, clippy::excessive_precision, clippy::inconsistent_digit_grouping)] +#![allow( + dead_code, + unused_variables, + clippy::excessive_precision, + clippy::inconsistent_digit_grouping +)] fn main() { let fail14 = 2_i32; diff --git a/tests/ui/mistyped_literal_suffix.rs b/tests/ui/mistyped_literal_suffix.rs index 8a451807aeb..729990af399 100644 --- a/tests/ui/mistyped_literal_suffix.rs +++ b/tests/ui/mistyped_literal_suffix.rs @@ -1,6 +1,11 @@ // run-rustfix -#![allow(dead_code, unused_variables, clippy::excessive_precision, clippy::inconsistent_digit_grouping)] +#![allow( + dead_code, + unused_variables, + clippy::excessive_precision, + clippy::inconsistent_digit_grouping +)] fn main() { let fail14 = 2_32; diff --git a/tests/ui/mistyped_literal_suffix.stderr b/tests/ui/mistyped_literal_suffix.stderr index f4c5adfe82c..b338b8aa622 100644 --- a/tests/ui/mistyped_literal_suffix.stderr +++ b/tests/ui/mistyped_literal_suffix.stderr @@ -1,5 +1,5 @@ error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:6:18 + --> $DIR/mistyped_literal_suffix.rs:11:18 | LL | let fail14 = 2_32; | ^^^^ help: did you mean to write: `2_i32` @@ -7,61 +7,61 @@ LL | let fail14 = 2_32; = note: `#[deny(clippy::mistyped_literal_suffixes)]` on by default error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:7:18 + --> $DIR/mistyped_literal_suffix.rs:12:18 | LL | let fail15 = 4_64; | ^^^^ help: did you mean to write: `4_i64` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:8:18 + --> $DIR/mistyped_literal_suffix.rs:13:18 | LL | let fail16 = 7_8; // | ^^^ help: did you mean to write: `7_i8` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:9:18 + --> $DIR/mistyped_literal_suffix.rs:14:18 | LL | let fail17 = 23_16; // | ^^^^^ help: did you mean to write: `23_i16` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:12:18 + --> $DIR/mistyped_literal_suffix.rs:17:18 | LL | let fail20 = 2__8; // | ^^^^ help: did you mean to write: `2_i8` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:13:18 + --> $DIR/mistyped_literal_suffix.rs:18:18 | LL | let fail21 = 4___16; // | ^^^^^^ help: did you mean to write: `4_i16` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:16:18 + --> $DIR/mistyped_literal_suffix.rs:21:18 | LL | let fail25 = 1E2_32; | ^^^^^^ help: did you mean to write: `1E2_f32` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:17:18 + --> $DIR/mistyped_literal_suffix.rs:22:18 | LL | let fail26 = 43E7_64; | ^^^^^^^ help: did you mean to write: `43E7_f64` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:18:18 + --> $DIR/mistyped_literal_suffix.rs:23:18 | LL | let fail27 = 243E17_32; | ^^^^^^^^^ help: did you mean to write: `243E17_f32` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:20:18 + --> $DIR/mistyped_literal_suffix.rs:25:18 | LL | let fail28 = 241251235E723_64; | ^^^^^^^^^^^^^^^^ help: did you mean to write: `241_251_235E723_f64` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:23:13 + --> $DIR/mistyped_literal_suffix.rs:28:13 | LL | let _ = 1.12345E1_32; | ^^^^^^^^^^^^ help: did you mean to write: `1.123_45E1_f32` -- cgit 1.4.1-3-g733a5 From 9b4ceee5930e5a7118ac25a0f424a7f83d01980f Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Tue, 6 Oct 2020 08:20:18 +0200 Subject: integration tests: Replace lazy_static with SyncLazy --- tests/cargo/mod.rs | 37 +++++++++++++++++-------------------- tests/compile-test.rs | 1 + tests/dogfood.rs | 7 +++---- 3 files changed, 21 insertions(+), 24 deletions(-) (limited to 'tests') diff --git a/tests/cargo/mod.rs b/tests/cargo/mod.rs index 3c385343f70..a8f3e3145f6 100644 --- a/tests/cargo/mod.rs +++ b/tests/cargo/mod.rs @@ -1,27 +1,24 @@ -use lazy_static::lazy_static; use std::env; +use std::lazy::SyncLazy; use std::path::PathBuf; -lazy_static! { - pub static ref CARGO_TARGET_DIR: PathBuf = { - match env::var_os("CARGO_TARGET_DIR") { - Some(v) => v.into(), - None => env::current_dir().unwrap().join("target"), - } - }; - pub static ref TARGET_LIB: PathBuf = { - if let Some(path) = option_env!("TARGET_LIBS") { - path.into() - } else { - let mut dir = CARGO_TARGET_DIR.clone(); - if let Some(target) = env::var_os("CARGO_BUILD_TARGET") { - dir.push(target); - } - dir.push(env!("PROFILE")); - dir +pub static CARGO_TARGET_DIR: SyncLazy = SyncLazy::new(|| match env::var_os("CARGO_TARGET_DIR") { + Some(v) => v.into(), + None => env::current_dir().unwrap().join("target"), +}); + +pub static TARGET_LIB: SyncLazy = SyncLazy::new(|| { + if let Some(path) = option_env!("TARGET_LIBS") { + path.into() + } else { + let mut dir = CARGO_TARGET_DIR.clone(); + if let Some(target) = env::var_os("CARGO_BUILD_TARGET") { + dir.push(target); } - }; -} + dir.push(env!("PROFILE")); + dir + } +}); #[must_use] pub fn is_rustc_test_suite() -> bool { diff --git a/tests/compile-test.rs b/tests/compile-test.rs index f0d73e9b0e2..0e8f7683103 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -1,4 +1,5 @@ #![feature(test)] // compiletest_rs requires this attribute +#![feature(once_cell)] use compiletest_rs as compiletest; use compiletest_rs::common::Mode as TestMode; diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 81af3d3033b..48e0478f169 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -1,15 +1,14 @@ // Dogfood cannot run on Windows #![cfg(not(windows))] +#![feature(once_cell)] -use lazy_static::lazy_static; +use std::lazy::SyncLazy; use std::path::PathBuf; use std::process::Command; mod cargo; -lazy_static! { - static ref CLIPPY_PATH: PathBuf = cargo::TARGET_LIB.join("cargo-clippy"); -} +static CLIPPY_PATH: SyncLazy = SyncLazy::new(|| cargo::TARGET_LIB.join("cargo-clippy")); #[test] fn dogfood_clippy() { -- cgit 1.4.1-3-g733a5 From 6c3611bdef09170a4b889dc759f986490a528e42 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 7 Oct 2020 00:02:28 +0200 Subject: Reinstate test for forbid blanket restriction --- tests/ui/attrs.rs | 3 --- tests/ui/attrs.stderr | 25 ++++------------------ tests/ui/blanket_clippy_restriction_lints.rs | 8 +++++++ tests/ui/blanket_clippy_restriction_lints.stderr | 27 ++++++++++++++++++++++++ 4 files changed, 39 insertions(+), 24 deletions(-) create mode 100644 tests/ui/blanket_clippy_restriction_lints.rs create mode 100644 tests/ui/blanket_clippy_restriction_lints.stderr (limited to 'tests') diff --git a/tests/ui/attrs.rs b/tests/ui/attrs.rs index 32685038067..8df6e19421e 100644 --- a/tests/ui/attrs.rs +++ b/tests/ui/attrs.rs @@ -1,8 +1,5 @@ #![warn(clippy::inline_always, clippy::deprecated_semver)] #![allow(clippy::assertions_on_constants)] -// Test that the whole restriction group is not enabled -#![warn(clippy::restriction)] -#![deny(clippy::restriction)] #![allow(clippy::missing_docs_in_private_items, clippy::panic, clippy::unreachable)] #[inline(always)] diff --git a/tests/ui/attrs.stderr b/tests/ui/attrs.stderr index 4324984dd60..df4e9e20b64 100644 --- a/tests/ui/attrs.stderr +++ b/tests/ui/attrs.stderr @@ -1,5 +1,5 @@ error: you have declared `#[inline(always)]` on `test_attr_lint`. This is usually a bad idea - --> $DIR/attrs.rs:8:1 + --> $DIR/attrs.rs:5:1 | LL | #[inline(always)] | ^^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | #[inline(always)] = note: `-D clippy::inline-always` implied by `-D warnings` error: the since field must contain a semver-compliant version - --> $DIR/attrs.rs:28:14 + --> $DIR/attrs.rs:25:14 | LL | #[deprecated(since = "forever")] | ^^^^^^^^^^^^^^^^^ @@ -15,27 +15,10 @@ LL | #[deprecated(since = "forever")] = note: `-D clippy::deprecated-semver` implied by `-D warnings` error: the since field must contain a semver-compliant version - --> $DIR/attrs.rs:31:14 + --> $DIR/attrs.rs:28:14 | LL | #[deprecated(since = "1")] | ^^^^^^^^^^^ -error: restriction lints are not meant to be all enabled - --> $DIR/attrs.rs:4:9 - | -LL | #![warn(clippy::restriction)] - | ^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::blanket-clippy-restriction-lints` implied by `-D warnings` - = help: try enabling only the lints you really need - -error: restriction lints are not meant to be all enabled - --> $DIR/attrs.rs:5:9 - | -LL | #![deny(clippy::restriction)] - | ^^^^^^^^^^^^^^^^^^^ - | - = help: try enabling only the lints you really need - -error: aborting due to 5 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/blanket_clippy_restriction_lints.rs b/tests/ui/blanket_clippy_restriction_lints.rs new file mode 100644 index 00000000000..d055f17526b --- /dev/null +++ b/tests/ui/blanket_clippy_restriction_lints.rs @@ -0,0 +1,8 @@ +#![warn(clippy::blanket_clippy_restriction_lints)] + +//! Test that the whole restriction group is not enabled +#![warn(clippy::restriction)] +#![deny(clippy::restriction)] +#![forbid(clippy::restriction)] + +fn main() {} diff --git a/tests/ui/blanket_clippy_restriction_lints.stderr b/tests/ui/blanket_clippy_restriction_lints.stderr new file mode 100644 index 00000000000..537557f8b0a --- /dev/null +++ b/tests/ui/blanket_clippy_restriction_lints.stderr @@ -0,0 +1,27 @@ +error: restriction lints are not meant to be all enabled + --> $DIR/blanket_clippy_restriction_lints.rs:4:9 + | +LL | #![warn(clippy::restriction)] + | ^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::blanket-clippy-restriction-lints` implied by `-D warnings` + = help: try enabling only the lints you really need + +error: restriction lints are not meant to be all enabled + --> $DIR/blanket_clippy_restriction_lints.rs:5:9 + | +LL | #![deny(clippy::restriction)] + | ^^^^^^^^^^^^^^^^^^^ + | + = help: try enabling only the lints you really need + +error: restriction lints are not meant to be all enabled + --> $DIR/blanket_clippy_restriction_lints.rs:6:11 + | +LL | #![forbid(clippy::restriction)] + | ^^^^^^^^^^^^^^^^^^^ + | + = help: try enabling only the lints you really need + +error: aborting due to 3 previous errors + -- cgit 1.4.1-3-g733a5 From 5bad9175fb363917ffee2c2da7223e96daa2f5ac Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Wed, 7 Oct 2020 11:37:32 +0200 Subject: New lint: Recommend using `ptr::eq` when possible This is based almost entirely on the code available in the previous PR #4596. --- clippy_lints/src/lib.rs | 5 +++ clippy_lints/src/ptr_eq.rs | 96 ++++++++++++++++++++++++++++++++++++++++++++++ tests/ui/ptr_eq.fixed | 38 ++++++++++++++++++ tests/ui/ptr_eq.rs | 38 ++++++++++++++++++ tests/ui/ptr_eq.stderr | 16 ++++++++ 5 files changed, 193 insertions(+) create mode 100644 clippy_lints/src/ptr_eq.rs create mode 100644 tests/ui/ptr_eq.fixed create mode 100644 tests/ui/ptr_eq.rs create mode 100644 tests/ui/ptr_eq.stderr (limited to 'tests') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 826a059f92a..d11eeff8032 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -281,6 +281,7 @@ mod path_buf_push_overwrite; mod pattern_type_mismatch; mod precedence; mod ptr; +mod ptr_eq; mod ptr_offset_with_cast; mod question_mark; mod ranges; @@ -778,6 +779,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &ptr::CMP_NULL, &ptr::MUT_FROM_REF, &ptr::PTR_ARG, + &ptr_eq::PTR_EQ, &ptr_offset_with_cast::PTR_OFFSET_WITH_CAST, &question_mark::QUESTION_MARK, &ranges::RANGE_MINUS_ONE, @@ -916,6 +918,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold; store.register_late_pass(move || box bit_mask::BitMask::new(verbose_bit_mask_threshold)); store.register_late_pass(|| box ptr::Ptr); + store.register_late_pass(|| box ptr_eq::PtrEq); store.register_late_pass(|| box needless_bool::NeedlessBool); store.register_late_pass(|| box needless_bool::BoolComparison); store.register_late_pass(|| box approx_const::ApproxConstant); @@ -1456,6 +1459,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ptr::CMP_NULL), 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::RANGE_ZIP_WITH_LEN), @@ -1612,6 +1616,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&panic_unimplemented::PANIC_PARAMS), LintId::of(&ptr::CMP_NULL), LintId::of(&ptr::PTR_ARG), + LintId::of(&ptr_eq::PTR_EQ), LintId::of(&question_mark::QUESTION_MARK), LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), diff --git a/clippy_lints/src/ptr_eq.rs b/clippy_lints/src/ptr_eq.rs new file mode 100644 index 00000000000..a05cb6270b7 --- /dev/null +++ b/clippy_lints/src/ptr_eq.rs @@ -0,0 +1,96 @@ +use crate::utils; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Use `std::ptr::eq` when applicable + /// + /// **Why is this bad?**`ptr::eq` can be used to compare `&T` references + /// (which coerce to `*const T` implicitly) by their address rather than + /// comparing the values they point to. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let a = &[1, 2, 3]; + /// let b = &[1, 2, 3]; + /// + /// assert!(a as *const _ as usize == b as *const _ as usize); + /// ``` + /// Use instead: + /// ```rust + /// let a = &[1, 2, 3]; + /// let b = &[1, 2, 3]; + /// + /// assert!(std::ptr::eq(a, b)); + /// ``` + pub PTR_EQ, + style, + "use `std::ptr::eq` when comparing raw pointers" +} + +declare_lint_pass!(PtrEq => [PTR_EQ]); + +static LINT_MSG: &str = "use `std::ptr::eq` when comparing raw pointers"; + +impl LateLintPass<'_> for PtrEq { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if utils::in_macro(expr.span) { + return; + } + + if let ExprKind::Binary(ref op, ref left, ref right) = expr.kind { + if BinOpKind::Eq == op.node { + let (left, right) = match (expr_as_cast_to_usize(cx, left), expr_as_cast_to_usize(cx, right)) { + (Some(lhs), Some(rhs)) => (lhs, rhs), + _ => (&**left, &**right), + }; + + if_chain! { + if let Some(left_var) = expr_as_cast_to_raw_pointer(cx, left); + if let Some(right_var) = expr_as_cast_to_raw_pointer(cx, right); + if let Some(left_snip) = utils::snippet_opt(cx, left_var.span); + if let Some(right_snip) = utils::snippet_opt(cx, right_var.span); + then { + utils::span_lint_and_sugg( + cx, + PTR_EQ, + expr.span, + LINT_MSG, + "try", + format!("std::ptr::eq({}, {})", left_snip, right_snip), + Applicability::MachineApplicable, + ); + } + } + } + } + } +} + +// If the given expression is a cast to an usize, return the lhs of the cast +// E.g., `foo as *const _ as usize` returns `foo as *const _`. +fn expr_as_cast_to_usize<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { + if cx.typeck_results().expr_ty(cast_expr) == cx.tcx.types.usize { + if let ExprKind::Cast(ref expr, _) = cast_expr.kind { + return Some(expr); + } + } + None +} + +// If the given expression is a cast to a `*const` pointer, return the lhs of the cast +// E.g., `foo as *const _` returns `foo`. +fn expr_as_cast_to_raw_pointer<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { + if cx.typeck_results().expr_ty(cast_expr).is_unsafe_ptr() { + if let ExprKind::Cast(ref expr, _) = cast_expr.kind { + return Some(expr); + } + } + None +} diff --git a/tests/ui/ptr_eq.fixed b/tests/ui/ptr_eq.fixed new file mode 100644 index 00000000000..209081e6e80 --- /dev/null +++ b/tests/ui/ptr_eq.fixed @@ -0,0 +1,38 @@ +// run-rustfix +#![warn(clippy::ptr_eq)] + +macro_rules! mac { + ($a:expr, $b:expr) => { + $a as *const _ as usize == $b as *const _ as usize + }; +} + +macro_rules! another_mac { + ($a:expr, $b:expr) => { + $a as *const _ == $b as *const _ + }; +} + +fn main() { + let a = &[1, 2, 3]; + let b = &[1, 2, 3]; + + let _ = std::ptr::eq(a, b); + let _ = std::ptr::eq(a, b); + let _ = a.as_ptr() == b as *const _; + let _ = a.as_ptr() == b.as_ptr(); + + // Do not lint + + let _ = mac!(a, b); + let _ = another_mac!(a, b); + + let a = &mut [1, 2, 3]; + let b = &mut [1, 2, 3]; + + let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; + let _ = a.as_mut_ptr() == b.as_mut_ptr(); + + let _ = a == b; + let _ = core::ptr::eq(a, b); +} diff --git a/tests/ui/ptr_eq.rs b/tests/ui/ptr_eq.rs new file mode 100644 index 00000000000..69162870807 --- /dev/null +++ b/tests/ui/ptr_eq.rs @@ -0,0 +1,38 @@ +// run-rustfix +#![warn(clippy::ptr_eq)] + +macro_rules! mac { + ($a:expr, $b:expr) => { + $a as *const _ as usize == $b as *const _ as usize + }; +} + +macro_rules! another_mac { + ($a:expr, $b:expr) => { + $a as *const _ == $b as *const _ + }; +} + +fn main() { + let a = &[1, 2, 3]; + let b = &[1, 2, 3]; + + let _ = a as *const _ as usize == b as *const _ as usize; + let _ = a as *const _ == b as *const _; + let _ = a.as_ptr() == b as *const _; + let _ = a.as_ptr() == b.as_ptr(); + + // Do not lint + + let _ = mac!(a, b); + let _ = another_mac!(a, b); + + let a = &mut [1, 2, 3]; + let b = &mut [1, 2, 3]; + + let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; + let _ = a.as_mut_ptr() == b.as_mut_ptr(); + + let _ = a == b; + let _ = core::ptr::eq(a, b); +} diff --git a/tests/ui/ptr_eq.stderr b/tests/ui/ptr_eq.stderr new file mode 100644 index 00000000000..45d8c60382b --- /dev/null +++ b/tests/ui/ptr_eq.stderr @@ -0,0 +1,16 @@ +error: use `std::ptr::eq` when comparing raw pointers + --> $DIR/ptr_eq.rs:20:13 + | +LL | let _ = a as *const _ as usize == b as *const _ as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a, b)` + | + = note: `-D clippy::ptr-eq` implied by `-D warnings` + +error: use `std::ptr::eq` when comparing raw pointers + --> $DIR/ptr_eq.rs:21:13 + | +LL | let _ = a as *const _ == b as *const _; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a, b)` + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From 11672577debb4880edb5641c470c78175b121bcb Mon Sep 17 00:00:00 2001 From: Andre Bogus Date: Thu, 8 Oct 2020 01:07:00 +0200 Subject: Fix unicode regexen with bytes::Regex fixes #6005 --- clippy_lints/src/regex.rs | 2 +- tests/ui/regex.rs | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index dfc158661cb..95594e38c9e 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -143,7 +143,7 @@ fn check_set<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { fn check_regex<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { let mut parser = regex_syntax::ParserBuilder::new() - .unicode(utf8) + .unicode(true) .allow_invalid_utf8(!utf8) .build(); diff --git a/tests/ui/regex.rs b/tests/ui/regex.rs index 9767e5bf76a..f7f3b195ccc 100644 --- a/tests/ui/regex.rs +++ b/tests/ui/regex.rs @@ -71,6 +71,9 @@ fn trivial_regex() { let non_trivial_ends_with = Regex::new("foo|bar"); let non_trivial_binary = BRegex::new("foo|bar"); let non_trivial_binary_builder = BRegexBuilder::new("foo|bar"); + + // #6005: unicode classes in bytes::Regex + let a_byte_of_unicode = BRegex::new(r"\p{C}"); } fn main() { -- cgit 1.4.1-3-g733a5 From 15150c07eaeec50138a9c96c5bdecbdf92c3101e Mon Sep 17 00:00:00 2001 From: João Paulo Taylor Ienczak Zanette Date: Wed, 7 Oct 2020 22:49:50 -0300 Subject: clippy_lint: Test for BoxedLocal false-positive in C-FFI and fix C-FFI Abi comparison. --- clippy_lints/src/escape.rs | 2 +- tests/ui/escape_analysis.rs | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index ffe81b3966c..b222475486d 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -68,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal { hir_id: HirId, ) { if let Some(header) = fn_kind.header() { - if header.abi == Abi::Cdecl { + if header.abi == Abi::C { return; } } diff --git a/tests/ui/escape_analysis.rs b/tests/ui/escape_analysis.rs index c0a52d832c0..377a0fb502f 100644 --- a/tests/ui/escape_analysis.rs +++ b/tests/ui/escape_analysis.rs @@ -174,3 +174,8 @@ mod issue_3739 { }; } } + +/// Issue #5542 +/// +/// This shouldn't warn for `boxed_local` as it is a function implemented in C. +pub extern "C" fn do_now_warn_me(_c_pointer: Box) -> () {} -- cgit 1.4.1-3-g733a5 From 418cde0389d47628b32e533c47e64874fd1050fb Mon Sep 17 00:00:00 2001 From: João Paulo Taylor Ienczak Zanette Date: Thu, 8 Oct 2020 09:06:19 -0300 Subject: clippy_lint: Fix typo (now -> not) --- tests/ui/escape_analysis.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/ui/escape_analysis.rs b/tests/ui/escape_analysis.rs index 377a0fb502f..3d5a02bf705 100644 --- a/tests/ui/escape_analysis.rs +++ b/tests/ui/escape_analysis.rs @@ -177,5 +177,5 @@ mod issue_3739 { /// Issue #5542 /// -/// This shouldn't warn for `boxed_local` as it is a function implemented in C. -pub extern "C" fn do_now_warn_me(_c_pointer: Box) -> () {} +/// This shouldn't warn for `boxed_local` as it is a function to be used in C. +pub extern "C" fn do_not_warn_me(_c_pointer: Box) -> () {} -- cgit 1.4.1-3-g733a5 From 5ae0f2644d4c402c8ea3561de1c9fa829b11b40e Mon Sep 17 00:00:00 2001 From: João Paulo Taylor Ienczak Zanette Date: Thu, 8 Oct 2020 09:07:24 -0300 Subject: clippy_lint: extern definition is in Rust, not C --- tests/ui/escape_analysis.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/ui/escape_analysis.rs b/tests/ui/escape_analysis.rs index 3d5a02bf705..7d3f4011e99 100644 --- a/tests/ui/escape_analysis.rs +++ b/tests/ui/escape_analysis.rs @@ -177,5 +177,5 @@ mod issue_3739 { /// Issue #5542 /// -/// This shouldn't warn for `boxed_local` as it is a function to be used in C. +/// This shouldn't warn for `boxed_local` as it is intended to called from non-Rust code. pub extern "C" fn do_not_warn_me(_c_pointer: Box) -> () {} -- cgit 1.4.1-3-g733a5 From b709b873632dc001b381704c1deb6be702111706 Mon Sep 17 00:00:00 2001 From: João Paulo Taylor Ienczak Zanette Date: Thu, 8 Oct 2020 18:50:52 -0300 Subject: tests: Add test function that does not specify ABI --- tests/ui/escape_analysis.rs | 3 +++ 1 file changed, 3 insertions(+) (limited to 'tests') diff --git a/tests/ui/escape_analysis.rs b/tests/ui/escape_analysis.rs index 7d3f4011e99..07004489610 100644 --- a/tests/ui/escape_analysis.rs +++ b/tests/ui/escape_analysis.rs @@ -179,3 +179,6 @@ mod issue_3739 { /// /// This shouldn't warn for `boxed_local` as it is intended to called from non-Rust code. pub extern "C" fn do_not_warn_me(_c_pointer: Box) -> () {} + +#[rustfmt::skip] // Forces rustfmt to not add ABI +pub extern fn do_not_warn_me_no_abi(_c_pointer: Box) -> () {} -- cgit 1.4.1-3-g733a5 From 7b7ddfa55da889b41e90243ac59a04eed832a71e Mon Sep 17 00:00:00 2001 From: Sebastian Andersson Date: Fri, 9 Oct 2020 20:23:03 +0200 Subject: Preserve raw strs for: format!(s) to s.to_string() lint Ie: | let s = format!(r#""hello""#); | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `r#""hello""#.to_string()` --- clippy_lints/src/format.rs | 8 ++++++-- tests/ui/format.fixed | 3 ++- tests/ui/format.stderr | 8 +++++++- 3 files changed, 15 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index d6541010bca..26da058598e 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -1,6 +1,6 @@ use crate::utils::paths; use crate::utils::{ - is_expn_of, is_type_diagnostic_item, last_path_segment, match_def_path, match_function_call, snippet, + is_expn_of, is_type_diagnostic_item, last_path_segment, match_def_path, match_function_call, snippet, snippet_opt, span_lint_and_then, }; use if_chain::if_chain; @@ -132,7 +132,11 @@ fn on_new_v1<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option [], }` if tup.is_empty() { - return Some(format!("{:?}.to_string()", s.as_str())); + if let Some(s_src) = snippet_opt(cx, lit.span) { + // Simulate macro expansion, converting {{ and }} to { and }. + let s_expand = s_src.replace("{{", "{").replace("}}", "}"); + return Some(format!("{}.to_string()", s_expand)) + } } else if s.as_str().is_empty() { return on_argumentv1_new(cx, &tup[0], arms); } diff --git a/tests/ui/format.fixed b/tests/ui/format.fixed index 30651476999..740a22a07d7 100644 --- a/tests/ui/format.fixed +++ b/tests/ui/format.fixed @@ -13,7 +13,8 @@ fn main() { "foo".to_string(); "{}".to_string(); "{} abc {}".to_string(); - "foo {}\n\" bar".to_string(); + r##"foo {} +" bar"##.to_string(); "foo".to_string(); format!("{:?}", "foo"); // Don't warn about `Debug`. diff --git a/tests/ui/format.stderr b/tests/ui/format.stderr index 9734492154e..96df7f37f77 100644 --- a/tests/ui/format.stderr +++ b/tests/ui/format.stderr @@ -25,7 +25,13 @@ LL | / format!( LL | | r##"foo {{}} LL | | " bar"## LL | | ); - | |______^ help: consider using `.to_string()`: `"foo {}/n/" bar".to_string();` + | |______^ + | +help: consider using `.to_string()` + | +LL | r##"foo {} +LL | " bar"##.to_string(); + | error: useless use of `format!` --> $DIR/format.rs:21:5 -- cgit 1.4.1-3-g733a5 From 26e4de9557e5924d001652138ec0f8f40dc40372 Mon Sep 17 00:00:00 2001 From: Andre Bogus Date: Fri, 9 Oct 2020 00:49:27 +0200 Subject: allow refs in our constant handling --- clippy_lints/src/consts.rs | 10 +++++++++- tests/ui/float_cmp.rs | 5 +++++ tests/ui/float_cmp.stderr | 12 ++++++------ 3 files changed, 20 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/consts.rs b/clippy_lints/src/consts.rs index 062c9bd2d9e..c5e33b288a9 100644 --- a/clippy_lints/src/consts.rs +++ b/clippy_lints/src/consts.rs @@ -40,6 +40,8 @@ pub enum Constant { Tuple(Vec), /// A raw pointer. RawPtr(u128), + /// A reference + Ref(Box), /// A literal with syntax error. Err(Symbol), } @@ -66,6 +68,7 @@ impl PartialEq for Constant { (&Self::Bool(l), &Self::Bool(r)) => l == r, (&Self::Vec(ref l), &Self::Vec(ref r)) | (&Self::Tuple(ref l), &Self::Tuple(ref r)) => l == r, (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => ls == rs && lv == rv, + (&Self::Ref(ref lb), &Self::Ref(ref rb)) => *lb == *rb, // TODO: are there inter-type equalities? _ => false, } @@ -110,6 +113,9 @@ impl Hash for Constant { Self::RawPtr(u) => { u.hash(state); }, + Self::Ref(ref r) => { + r.hash(state); + }, Self::Err(ref s) => { s.hash(state); }, @@ -144,6 +150,7 @@ impl Constant { x => x, } }, + (&Self::Ref(ref lb), &Self::Ref(ref rb)) => Self::partial_cmp(tcx, cmp_type, lb, rb), // TODO: are there any useful inter-type orderings? _ => None, } @@ -239,7 +246,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { ExprKind::Unary(op, ref operand) => self.expr(operand).and_then(|o| match op { UnOp::UnNot => self.constant_not(&o, self.typeck_results.expr_ty(e)), UnOp::UnNeg => self.constant_negate(&o, self.typeck_results.expr_ty(e)), - UnOp::UnDeref => Some(o), + UnOp::UnDeref => Some(if let Constant::Ref(r) = o { *r } else { o }), }), ExprKind::Binary(op, ref left, ref right) => self.binop(op, left, right), ExprKind::Call(ref callee, ref args) => { @@ -269,6 +276,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { } }, ExprKind::Index(ref arr, ref index) => self.index(arr, index), + ExprKind::AddrOf(_, _, ref inner) => self.expr(inner).map(|r| Constant::Ref(Box::new(r))), // TODO: add other expressions. _ => None, } diff --git a/tests/ui/float_cmp.rs b/tests/ui/float_cmp.rs index 9fa0e5f5c07..586784b73e6 100644 --- a/tests/ui/float_cmp.rs +++ b/tests/ui/float_cmp.rs @@ -2,6 +2,7 @@ #![allow( unused, clippy::no_effect, + clippy::op_ref, clippy::unnecessary_operation, clippy::cast_lossless, clippy::many_single_char_names @@ -116,4 +117,8 @@ fn main() { 1.23f64.signum() != x64.signum(); 1.23f64.signum() != -(x64.signum()); 1.23f64.signum() != 3.21f64.signum(); + + // the comparison should also look through references + &0.0 == &ZERO; + &&&&0.0 == &&&&ZERO; } diff --git a/tests/ui/float_cmp.stderr b/tests/ui/float_cmp.stderr index f7c380fc915..bb4051c4662 100644 --- a/tests/ui/float_cmp.stderr +++ b/tests/ui/float_cmp.stderr @@ -1,5 +1,5 @@ error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:65:5 + --> $DIR/float_cmp.rs:66:5 | LL | ONE as f64 != 2.0; | ^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(ONE as f64 - 2.0).abs() > error_margin` @@ -8,7 +8,7 @@ LL | ONE as f64 != 2.0; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:70:5 + --> $DIR/float_cmp.rs:71:5 | LL | x == 1.0; | ^^^^^^^^ help: consider comparing them within some margin of error: `(x - 1.0).abs() < error_margin` @@ -16,7 +16,7 @@ LL | x == 1.0; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:73:5 + --> $DIR/float_cmp.rs:74:5 | LL | twice(x) != twice(ONE as f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(twice(x) - twice(ONE as f64)).abs() > error_margin` @@ -24,7 +24,7 @@ LL | twice(x) != twice(ONE as f64); = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:93:5 + --> $DIR/float_cmp.rs:94:5 | LL | NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(NON_ZERO_ARRAY[i] - NON_ZERO_ARRAY[j]).abs() < error_margin` @@ -32,7 +32,7 @@ LL | NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j]; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` arrays - --> $DIR/float_cmp.rs:98:5 + --> $DIR/float_cmp.rs:99:5 | LL | a1 == a2; | ^^^^^^^^ @@ -40,7 +40,7 @@ LL | a1 == a2; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:99:5 + --> $DIR/float_cmp.rs:100:5 | LL | a1[0] == a2[0]; | ^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(a1[0] - a2[0]).abs() < error_margin` -- cgit 1.4.1-3-g733a5 From 6d88803a1c5516fda1d1030e5676d6b15be130fc Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 9 Oct 2020 22:21:47 +0200 Subject: Add regression test for ICE 6139 --- tests/ui/crashes/ice-6139.rs | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 tests/ui/crashes/ice-6139.rs (limited to 'tests') diff --git a/tests/ui/crashes/ice-6139.rs b/tests/ui/crashes/ice-6139.rs new file mode 100644 index 00000000000..f3966e47f5e --- /dev/null +++ b/tests/ui/crashes/ice-6139.rs @@ -0,0 +1,7 @@ +trait T<'a> {} + +fn foo(_: Vec>>) {} + +fn main() { + foo(vec![]); +} -- cgit 1.4.1-3-g733a5 From 52e650ae88a63b41686f646f2240de7c870e6ea6 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sat, 10 Oct 2020 15:03:49 +0200 Subject: Add test for ICE #6153 --- tests/ui/crashes/ice-6153.rs | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 tests/ui/crashes/ice-6153.rs (limited to 'tests') diff --git a/tests/ui/crashes/ice-6153.rs b/tests/ui/crashes/ice-6153.rs new file mode 100644 index 00000000000..9f73f39f10d --- /dev/null +++ b/tests/ui/crashes/ice-6153.rs @@ -0,0 +1,9 @@ +pub struct S<'a, 'e>(&'a str, &'e str); + +pub type T<'a, 'e> = std::collections::HashMap, ()>; + +impl<'e, 'a: 'e> S<'a, 'e> { + pub fn foo(_a: &str, _b: &str, _map: &T) {} +} + +fn main() {} -- cgit 1.4.1-3-g733a5 From 377d1fab1f1fe104c12cea17f7f24a8e23775942 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Sun, 11 Oct 2020 22:57:22 +0900 Subject: Remove the generated files by `update-references.sh` if they are empty --- doc/adding_lints.md | 3 ++- tests/ui-cargo/update-references.sh | 8 ++++++++ tests/ui-toml/update-references.sh | 8 ++++++++ tests/ui/update-references.sh | 12 ++++++++++++ 4 files changed, 30 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/doc/adding_lints.md b/doc/adding_lints.md index 2869c3bf7d4..344bb455aa5 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -104,7 +104,8 @@ every time before running `tests/ui/update-all-references.sh`. Running `TESTNAME=foo_functions cargo uitest` should pass then. When we commit our lint, we need to commit the generated `.stderr` files, too. In general, you should only commit files changed by `tests/ui/update-all-references.sh` for the -specific lint you are creating/editing. +specific lint you are creating/editing. Note that if the generated files are +empty, they should be removed. ### Cargo lints diff --git a/tests/ui-cargo/update-references.sh b/tests/ui-cargo/update-references.sh index 50d42678734..2ab51168bca 100755 --- a/tests/ui-cargo/update-references.sh +++ b/tests/ui-cargo/update-references.sh @@ -29,10 +29,18 @@ while [[ "$1" != "" ]]; do ! (cmp -s -- "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"); then echo updating "$MYDIR"/"$STDOUT_NAME" cp "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME" + if [[ ! -s "$MYDIR"/"$STDOUT_NAME" ]]; then + echo removing "$MYDIR"/"$STDOUT_NAME" + rm "$MYDIR"/"$STDOUT_NAME" + fi fi if [[ -f "$BUILD_DIR"/"$STDERR_NAME" ]] && \ ! (cmp -s -- "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"); then echo updating "$MYDIR"/"$STDERR_NAME" cp "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME" + if [[ ! -s "$MYDIR"/"$STDERR_NAME" ]]; then + echo removing "$MYDIR"/"$STDERR_NAME" + rm "$MYDIR"/"$STDERR_NAME" + fi fi done diff --git a/tests/ui-toml/update-references.sh b/tests/ui-toml/update-references.sh index 50d42678734..2ab51168bca 100755 --- a/tests/ui-toml/update-references.sh +++ b/tests/ui-toml/update-references.sh @@ -29,10 +29,18 @@ while [[ "$1" != "" ]]; do ! (cmp -s -- "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"); then echo updating "$MYDIR"/"$STDOUT_NAME" cp "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME" + if [[ ! -s "$MYDIR"/"$STDOUT_NAME" ]]; then + echo removing "$MYDIR"/"$STDOUT_NAME" + rm "$MYDIR"/"$STDOUT_NAME" + fi fi if [[ -f "$BUILD_DIR"/"$STDERR_NAME" ]] && \ ! (cmp -s -- "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"); then echo updating "$MYDIR"/"$STDERR_NAME" cp "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME" + if [[ ! -s "$MYDIR"/"$STDERR_NAME" ]]; then + echo removing "$MYDIR"/"$STDERR_NAME" + rm "$MYDIR"/"$STDERR_NAME" + fi fi done diff --git a/tests/ui/update-references.sh b/tests/ui/update-references.sh index 2c13c327d79..e16ed600ef8 100755 --- a/tests/ui/update-references.sh +++ b/tests/ui/update-references.sh @@ -30,15 +30,27 @@ while [[ "$1" != "" ]]; do ! (cmp -s -- "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"); then echo updating "$MYDIR"/"$STDOUT_NAME" cp "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME" + if [[ ! -s "$MYDIR"/"$STDOUT_NAME" ]]; then + echo removing "$MYDIR"/"$STDOUT_NAME" + rm "$MYDIR"/"$STDOUT_NAME" + fi fi if [[ -f "$BUILD_DIR"/"$STDERR_NAME" ]] && \ ! (cmp -s -- "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"); then echo updating "$MYDIR"/"$STDERR_NAME" cp "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME" + if [[ ! -s "$MYDIR"/"$STDERR_NAME" ]]; then + echo removing "$MYDIR"/"$STDERR_NAME" + rm "$MYDIR"/"$STDERR_NAME" + fi fi if [[ -f "$BUILD_DIR"/"$FIXED_NAME" ]] && \ ! (cmp -s -- "$BUILD_DIR"/"$FIXED_NAME" "$MYDIR"/"$FIXED_NAME"); then echo updating "$MYDIR"/"$FIXED_NAME" cp "$BUILD_DIR"/"$FIXED_NAME" "$MYDIR"/"$FIXED_NAME" + if [[ ! -s "$MYDIR"/"$FIXED_NAME" ]]; then + echo removing "$MYDIR"/"$FIXED_NAME" + rm "$MYDIR"/"$FIXED_NAME" + fi fi done -- cgit 1.4.1-3-g733a5 From 6021c231599eabcb07b3a8207bddbb3796c93eee Mon Sep 17 00:00:00 2001 From: Andre Bogus Date: Sun, 11 Oct 2020 13:27:20 +0200 Subject: New lint: result-unit-err --- CHANGELOG.md | 1 + clippy_lints/src/functions.rs | 106 +++++++++++++++++++++++++++++++++----- clippy_lints/src/lib.rs | 3 ++ src/lintlist/mod.rs | 7 +++ tests/ui/doc_errors.rs | 1 + tests/ui/doc_errors.stderr | 14 ++--- tests/ui/double_must_use.rs | 1 + tests/ui/double_must_use.stderr | 6 +-- tests/ui/result_unit_error.rs | 38 ++++++++++++++ tests/ui/result_unit_error.stderr | 35 +++++++++++++ 10 files changed, 189 insertions(+), 23 deletions(-) create mode 100644 tests/ui/result_unit_error.rs create mode 100644 tests/ui/result_unit_error.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fb6cf75d96..f21768c4498 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1918,6 +1918,7 @@ Released 2018-09-13 [`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs [`result_map_or_into_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_or_into_option [`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn +[`result_unit_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unit_err [`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges [`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition [`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push diff --git a/clippy_lints/src/functions.rs b/clippy_lints/src/functions.rs index 50b39cf4ea7..212a3100637 100644 --- a/clippy_lints/src/functions.rs +++ b/clippy_lints/src/functions.rs @@ -1,8 +1,9 @@ use crate::utils::{ - attr_by_name, attrs::is_proc_macro, is_must_use_ty, is_trait_impl_item, iter_input_pats, match_def_path, - must_use_attr, qpath_res, return_ty, snippet, snippet_opt, span_lint, span_lint_and_help, span_lint_and_then, - trait_ref_of_method, type_is_unsafe_function, + attr_by_name, attrs::is_proc_macro, is_must_use_ty, is_trait_impl_item, is_type_diagnostic_item, iter_input_pats, + last_path_segment, match_def_path, must_use_attr, qpath_res, return_ty, snippet, snippet_opt, span_lint, + span_lint_and_help, span_lint_and_then, trait_ref_of_method, type_is_unsafe_function, }; +use if_chain::if_chain; use rustc_ast::ast::Attribute; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -16,6 +17,7 @@ use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_target::spec::abi::Abi; +use rustc_typeck::hir_ty_to_ty; declare_clippy_lint! { /// **What it does:** Checks for functions with too many parameters. @@ -169,6 +171,52 @@ declare_clippy_lint! { "function or method that could take a `#[must_use]` attribute" } +declare_clippy_lint! { + /// **What it does:** Checks for public functions that return a `Result` + /// with an `Err` type of `()`. It suggests using a custom type that + /// implements [`std::error::Error`]. + /// + /// **Why is this bad?** Unit does not implement `Error` and carries no + /// further information about what went wrong. + /// + /// **Known problems:** Of course, this lint assumes that `Result` is used + /// for a fallible operation (which is after all the intended use). However + /// code may opt to (mis)use it as a basic two-variant-enum. In that case, + /// the suggestion is misguided, and the code should use a custom enum + /// instead. + /// + /// **Examples:** + /// ```rust + /// pub fn read_u8() -> Result { Err(()) } + /// ``` + /// should become + /// ```rust,should_panic + /// use std::fmt; + /// + /// #[derive(Debug)] + /// struct EndOfStream; + /// + /// impl fmt::Display for EndOfStream { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// write!(f, "End of Stream") + /// } + /// } + /// + /// impl std::error::Error for EndOfStream { } + /// + /// pub fn read_u8() -> Result { Err(EndOfStream) } + ///# fn main() { + ///# read_u8().unwrap(); + ///# } + /// ``` + /// + /// Note that there are crates that simplify creating the error type, e.g. + /// [`thiserror`](https://docs.rs/thiserror). + pub RESULT_UNIT_ERR, + style, + "public function returning `Result` with an `Err` type of `()`" +} + #[derive(Copy, Clone)] pub struct Functions { threshold: u64, @@ -188,6 +236,7 @@ impl_lint_pass!(Functions => [ MUST_USE_UNIT, DOUBLE_MUST_USE, MUST_USE_CANDIDATE, + RESULT_UNIT_ERR, ]); impl<'tcx> LateLintPass<'tcx> for Functions { @@ -233,15 +282,16 @@ impl<'tcx> LateLintPass<'tcx> for Functions { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { let attr = must_use_attr(&item.attrs); if let hir::ItemKind::Fn(ref sig, ref _generics, ref body_id) = item.kind { + let is_public = cx.access_levels.is_exported(item.hir_id); + let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); + if is_public { + check_result_unit_err(cx, &sig.decl, item.span, fn_header_span); + } if let Some(attr) = attr { - let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); check_needless_must_use(cx, &sig.decl, item.hir_id, item.span, fn_header_span, attr); return; } - if cx.access_levels.is_exported(item.hir_id) - && !is_proc_macro(cx.sess(), &item.attrs) - && attr_by_name(&item.attrs, "no_mangle").is_none() - { + if is_public && !is_proc_macro(cx.sess(), &item.attrs) && attr_by_name(&item.attrs, "no_mangle").is_none() { check_must_use_candidate( cx, &sig.decl, @@ -257,11 +307,15 @@ impl<'tcx> LateLintPass<'tcx> for Functions { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { if let hir::ImplItemKind::Fn(ref sig, ref body_id) = item.kind { + let is_public = cx.access_levels.is_exported(item.hir_id); + let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); + if is_public && trait_ref_of_method(cx, item.hir_id).is_none() { + check_result_unit_err(cx, &sig.decl, item.span, fn_header_span); + } let attr = must_use_attr(&item.attrs); if let Some(attr) = attr { - let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); check_needless_must_use(cx, &sig.decl, item.hir_id, item.span, fn_header_span, attr); - } else if cx.access_levels.is_exported(item.hir_id) + } else if is_public && !is_proc_macro(cx.sess(), &item.attrs) && trait_ref_of_method(cx, item.hir_id).is_none() { @@ -284,18 +338,21 @@ impl<'tcx> LateLintPass<'tcx> for Functions { if sig.header.abi == Abi::Rust { self.check_arg_number(cx, &sig.decl, item.span.with_hi(sig.decl.output.span().hi())); } + let is_public = cx.access_levels.is_exported(item.hir_id); + let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); + if is_public { + check_result_unit_err(cx, &sig.decl, item.span, fn_header_span); + } let attr = must_use_attr(&item.attrs); if let Some(attr) = attr { - let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); check_needless_must_use(cx, &sig.decl, item.hir_id, item.span, fn_header_span, attr); } if let hir::TraitFn::Provided(eid) = *eid { let body = cx.tcx.hir().body(eid); Self::check_raw_ptr(cx, sig.header.unsafety, &sig.decl, body, item.hir_id); - if attr.is_none() && cx.access_levels.is_exported(item.hir_id) && !is_proc_macro(cx.sess(), &item.attrs) - { + if attr.is_none() && is_public && !is_proc_macro(cx.sess(), &item.attrs) { check_must_use_candidate( cx, &sig.decl, @@ -411,6 +468,29 @@ impl<'tcx> Functions { } } +fn check_result_unit_err(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, item_span: Span, fn_header_span: Span) { + if_chain! { + if !in_external_macro(cx.sess(), item_span); + if let hir::FnRetTy::Return(ref ty) = decl.output; + if let hir::TyKind::Path(ref qpath) = ty.kind; + if is_type_diagnostic_item(cx, hir_ty_to_ty(cx.tcx, ty), sym!(result_type)); + if let Some(ref args) = last_path_segment(qpath).args; + if let [_, hir::GenericArg::Type(ref err_ty)] = args.args; + if let hir::TyKind::Tup(t) = err_ty.kind; + if t.is_empty(); + then { + span_lint_and_help( + cx, + RESULT_UNIT_ERR, + fn_header_span, + "This returns a `Result<_, ()>", + None, + "Use a custom Error type instead", + ); + } + } +} + fn check_needless_must_use( cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 097eca0af57..26a727687b1 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -582,6 +582,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &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, @@ -1327,6 +1328,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), @@ -1558,6 +1560,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&formatting::SUSPICIOUS_UNARY_OP_FORMATTING), LintId::of(&functions::DOUBLE_MUST_USE), LintId::of(&functions::MUST_USE_UNIT), + LintId::of(&functions::RESULT_UNIT_ERR), LintId::of(&if_let_some_result::IF_LET_SOME_RESULT), LintId::of(&inherent_to_string::INHERENT_TO_STRING), LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 96c9d12d75f..d0fc8f0c8a9 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2005,6 +2005,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "map_unit_fn", }, + Lint { + name: "result_unit_err", + group: "style", + desc: "public function returning `Result` with an `Err` type of `()`", + deprecation: None, + module: "functions", + }, Lint { name: "reversed_empty_ranges", group: "correctness", diff --git a/tests/ui/doc_errors.rs b/tests/ui/doc_errors.rs index 445fc8d31d7..f47b81a450e 100644 --- a/tests/ui/doc_errors.rs +++ b/tests/ui/doc_errors.rs @@ -1,5 +1,6 @@ // edition:2018 #![warn(clippy::missing_errors_doc)] +#![allow(clippy::result_unit_err)] use std::io; diff --git a/tests/ui/doc_errors.stderr b/tests/ui/doc_errors.stderr index f44d6693d30..c7b616e2897 100644 --- a/tests/ui/doc_errors.stderr +++ b/tests/ui/doc_errors.stderr @@ -1,5 +1,5 @@ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:6:1 + --> $DIR/doc_errors.rs:7:1 | LL | / pub fn pub_fn_missing_errors_header() -> Result<(), ()> { LL | | unimplemented!(); @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::missing-errors-doc` implied by `-D warnings` error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:10:1 + --> $DIR/doc_errors.rs:11:1 | LL | / pub async fn async_pub_fn_missing_errors_header() -> Result<(), ()> { LL | | unimplemented!(); @@ -17,7 +17,7 @@ LL | | } | |_^ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:15:1 + --> $DIR/doc_errors.rs:16:1 | LL | / pub fn pub_fn_returning_io_result() -> io::Result<()> { LL | | unimplemented!(); @@ -25,7 +25,7 @@ LL | | } | |_^ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:20:1 + --> $DIR/doc_errors.rs:21:1 | LL | / pub async fn async_pub_fn_returning_io_result() -> io::Result<()> { LL | | unimplemented!(); @@ -33,7 +33,7 @@ LL | | } | |_^ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:50:5 + --> $DIR/doc_errors.rs:51:5 | LL | / pub fn pub_method_missing_errors_header() -> Result<(), ()> { LL | | unimplemented!(); @@ -41,7 +41,7 @@ LL | | } | |_____^ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:55:5 + --> $DIR/doc_errors.rs:56:5 | LL | / pub async fn async_pub_method_missing_errors_header() -> Result<(), ()> { LL | | unimplemented!(); @@ -49,7 +49,7 @@ LL | | } | |_____^ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:84:5 + --> $DIR/doc_errors.rs:85:5 | LL | fn trait_method_missing_errors_header() -> Result<(), ()>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/double_must_use.rs b/tests/ui/double_must_use.rs index a48e675e4ea..05e087b08bc 100644 --- a/tests/ui/double_must_use.rs +++ b/tests/ui/double_must_use.rs @@ -1,4 +1,5 @@ #![warn(clippy::double_must_use)] +#![allow(clippy::result_unit_err)] #[must_use] pub fn must_use_result() -> Result<(), ()> { diff --git a/tests/ui/double_must_use.stderr b/tests/ui/double_must_use.stderr index bc37785294f..8290ece1cad 100644 --- a/tests/ui/double_must_use.stderr +++ b/tests/ui/double_must_use.stderr @@ -1,5 +1,5 @@ error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]` - --> $DIR/double_must_use.rs:4:1 + --> $DIR/double_must_use.rs:5:1 | LL | pub fn must_use_result() -> Result<(), ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | pub fn must_use_result() -> Result<(), ()> { = help: either add some descriptive text or remove the attribute error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]` - --> $DIR/double_must_use.rs:9:1 + --> $DIR/double_must_use.rs:10:1 | LL | pub fn must_use_tuple() -> (Result<(), ()>, u8) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | pub fn must_use_tuple() -> (Result<(), ()>, u8) { = help: either add some descriptive text or remove the attribute error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]` - --> $DIR/double_must_use.rs:14:1 + --> $DIR/double_must_use.rs:15:1 | LL | pub fn must_use_array() -> [Result<(), ()>; 1] { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/result_unit_error.rs b/tests/ui/result_unit_error.rs new file mode 100644 index 00000000000..a66f581b215 --- /dev/null +++ b/tests/ui/result_unit_error.rs @@ -0,0 +1,38 @@ +#[warn(clippy::result_unit_err)] +#[allow(unused)] + +pub fn returns_unit_error() -> Result { + Err(()) +} + +fn private_unit_errors() -> Result { + Err(()) +} + +pub trait HasUnitError { + fn get_that_error(&self) -> Result; + + fn get_this_one_too(&self) -> Result { + Err(()) + } +} + +impl HasUnitError for () { + fn get_that_error(&self) -> Result { + Ok(true) + } +} + +trait PrivateUnitError { + fn no_problem(&self) -> Result; +} + +pub struct UnitErrorHolder; + +impl UnitErrorHolder { + pub fn unit_error(&self) -> Result { + Ok(0) + } +} + +fn main() {} diff --git a/tests/ui/result_unit_error.stderr b/tests/ui/result_unit_error.stderr new file mode 100644 index 00000000000..986d9718acd --- /dev/null +++ b/tests/ui/result_unit_error.stderr @@ -0,0 +1,35 @@ +error: This returns a `Result<_, ()> + --> $DIR/result_unit_error.rs:4:1 + | +LL | pub fn returns_unit_error() -> Result { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::result-unit-err` implied by `-D warnings` + = help: Use a custom Error type instead + +error: This returns a `Result<_, ()> + --> $DIR/result_unit_error.rs:13:5 + | +LL | fn get_that_error(&self) -> Result; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: Use a custom Error type instead + +error: This returns a `Result<_, ()> + --> $DIR/result_unit_error.rs:15:5 + | +LL | fn get_this_one_too(&self) -> Result { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: Use a custom Error type instead + +error: This returns a `Result<_, ()> + --> $DIR/result_unit_error.rs:33:5 + | +LL | pub fn unit_error(&self) -> Result { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: Use a custom Error type instead + +error: aborting due to 4 previous errors + -- cgit 1.4.1-3-g733a5 From 74ae116131696e4385d5b8e5da34deaad0d25ec9 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sun, 11 Oct 2020 22:15:56 +0200 Subject: Use lowercase in error messages --- clippy_lints/src/functions.rs | 14 +++++++------- tests/ui/result_unit_error.stderr | 16 ++++++++-------- 2 files changed, 15 insertions(+), 15 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/functions.rs b/clippy_lints/src/functions.rs index 212a3100637..fd45a6da61c 100644 --- a/clippy_lints/src/functions.rs +++ b/clippy_lints/src/functions.rs @@ -194,19 +194,19 @@ declare_clippy_lint! { /// use std::fmt; /// /// #[derive(Debug)] - /// struct EndOfStream; + /// pub struct EndOfStream; /// /// impl fmt::Display for EndOfStream { - /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - /// write!(f, "End of Stream") - /// } + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// write!(f, "End of Stream") + /// } /// } /// /// impl std::error::Error for EndOfStream { } /// /// pub fn read_u8() -> Result { Err(EndOfStream) } ///# fn main() { - ///# read_u8().unwrap(); + ///# read_u8().unwrap(); ///# } /// ``` /// @@ -483,9 +483,9 @@ fn check_result_unit_err(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, item_span cx, RESULT_UNIT_ERR, fn_header_span, - "This returns a `Result<_, ()>", + "this returns a `Result<_, ()>", None, - "Use a custom Error type instead", + "use a custom Error type instead", ); } } diff --git a/tests/ui/result_unit_error.stderr b/tests/ui/result_unit_error.stderr index 986d9718acd..b8230032491 100644 --- a/tests/ui/result_unit_error.stderr +++ b/tests/ui/result_unit_error.stderr @@ -1,35 +1,35 @@ -error: This returns a `Result<_, ()> +error: this returns a `Result<_, ()> --> $DIR/result_unit_error.rs:4:1 | LL | pub fn returns_unit_error() -> Result { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::result-unit-err` implied by `-D warnings` - = help: Use a custom Error type instead + = help: use a custom Error type instead -error: This returns a `Result<_, ()> +error: this returns a `Result<_, ()> --> $DIR/result_unit_error.rs:13:5 | LL | fn get_that_error(&self) -> Result; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: Use a custom Error type instead + = help: use a custom Error type instead -error: This returns a `Result<_, ()> +error: this returns a `Result<_, ()> --> $DIR/result_unit_error.rs:15:5 | LL | fn get_this_one_too(&self) -> Result { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: Use a custom Error type instead + = help: use a custom Error type instead -error: This returns a `Result<_, ()> +error: this returns a `Result<_, ()> --> $DIR/result_unit_error.rs:33:5 | LL | pub fn unit_error(&self) -> Result { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: Use a custom Error type instead + = help: use a custom Error type instead error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From 32fdb8fb0c15ddc202eed70b82babca8d529e39b Mon Sep 17 00:00:00 2001 From: ThibsG Date: Thu, 8 Oct 2020 23:02:16 +0200 Subject: Lint on identical variable used as args in `assert_eq!` macro call --- clippy_lints/src/eq_op.rs | 37 ++++++++++++++++++++++++++++++++- clippy_lints/src/lib.rs | 2 ++ tests/ui/auxiliary/proc_macro_derive.rs | 1 + tests/ui/double_parens.rs | 2 +- tests/ui/eq_op_early.rs | 15 +++++++++++++ tests/ui/eq_op_early.stderr | 16 ++++++++++++++ tests/ui/used_underscore_binding.rs | 2 +- 7 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 tests/ui/eq_op_early.rs create mode 100644 tests/ui/eq_op_early.stderr (limited to 'tests') diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index e16ec783fab..7126c98a0b4 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -1,9 +1,13 @@ +use crate::utils::ast_utils::eq_expr; use crate::utils::{ eq_expr_value, implements_trait, in_macro, is_copy, multispan_sugg, snippet, span_lint, span_lint_and_then, }; +use if_chain::if_chain; +use rustc_ast::{ast, token}; use rustc_errors::Applicability; use rustc_hir::{BinOp, BinOpKind, BorrowKind, Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; +use rustc_parse::parser; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -23,6 +27,12 @@ declare_clippy_lint! { /// # let x = 1; /// if x + 1 == x + 1 {} /// ``` + /// or + /// ```rust + /// # let a = 3; + /// # let b = 4; + /// assert_eq!(a, a); + /// ``` pub EQ_OP, correctness, "equal operands on both sides of a comparison or bitwise combination (e.g., `x == x`)" @@ -52,6 +62,31 @@ declare_clippy_lint! { declare_lint_pass!(EqOp => [EQ_OP, OP_REF]); +impl EarlyLintPass for EqOp { + fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &ast::MacCall) { + if_chain! { + if mac.path == sym!(assert_eq); + let tokens = mac.args.inner_tokens(); + let mut parser = parser::Parser::new(&cx.sess.parse_sess, tokens, false, None); + if let Ok(left) = parser.parse_expr(); + if parser.eat(&token::Comma); + if let Ok(right) = parser.parse_expr(); + let left_expr = left.into_inner(); + let right_expr = right.into_inner(); + if eq_expr(&left_expr, &right_expr); + + then { + span_lint( + cx, + EQ_OP, + left_expr.span.to(right_expr.span), + "identical args used in this `assert_eq!` macro call", + ); + } + } + } +} + impl<'tcx> LateLintPass<'tcx> for EqOp { #[allow(clippy::similar_names, clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index fc4afde9d9e..dd99b6b9040 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -348,6 +348,7 @@ pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore) { store.register_pre_expansion_pass(|| box write::Write::default()); store.register_pre_expansion_pass(|| box attrs::EarlyAttributes); store.register_pre_expansion_pass(|| box dbg_macro::DbgMacro); + store.register_pre_expansion_pass(|| box eq_op::EqOp); } #[doc(hidden)] @@ -910,6 +911,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let vec_box_size_threshold = conf.vec_box_size_threshold; store.register_late_pass(move || box types::Types::new(vec_box_size_threshold)); store.register_late_pass(|| box booleans::NonminimalBool); + store.register_early_pass(|| box eq_op::EqOp); store.register_late_pass(|| box eq_op::EqOp); store.register_late_pass(|| box enum_clike::UnportableVariant); store.register_late_pass(|| box float_literal::FloatLiteral); diff --git a/tests/ui/auxiliary/proc_macro_derive.rs b/tests/ui/auxiliary/proc_macro_derive.rs index 3df8be6c232..e369f62f8bf 100644 --- a/tests/ui/auxiliary/proc_macro_derive.rs +++ b/tests/ui/auxiliary/proc_macro_derive.rs @@ -3,6 +3,7 @@ #![crate_type = "proc-macro"] #![feature(repr128, proc_macro_quote)] +#![allow(clippy::eq_op)] extern crate proc_macro; diff --git a/tests/ui/double_parens.rs b/tests/ui/double_parens.rs index 9c7590c7dd6..ff1dc76ab63 100644 --- a/tests/ui/double_parens.rs +++ b/tests/ui/double_parens.rs @@ -1,5 +1,5 @@ #![warn(clippy::double_parens)] -#![allow(dead_code)] +#![allow(dead_code, clippy::eq_op)] #![feature(custom_inner_attributes)] #![rustfmt::skip] diff --git a/tests/ui/eq_op_early.rs b/tests/ui/eq_op_early.rs new file mode 100644 index 00000000000..cf5660ea98d --- /dev/null +++ b/tests/ui/eq_op_early.rs @@ -0,0 +1,15 @@ +#![warn(clippy::eq_op)] + +fn main() { + let a = 1; + let b = 2; + + // lint identical args in `assert_eq!` (see #3574) + assert_eq!(a, a); + assert_eq!(a + 1, a + 1); + + // ok + assert_eq!(a, b); + assert_eq!(a, a + 1); + assert_eq!(a + 1, b + 1); +} diff --git a/tests/ui/eq_op_early.stderr b/tests/ui/eq_op_early.stderr new file mode 100644 index 00000000000..9206e9026e9 --- /dev/null +++ b/tests/ui/eq_op_early.stderr @@ -0,0 +1,16 @@ +error: identical args used in this `assert_eq!` macro call + --> $DIR/eq_op_early.rs:8:16 + | +LL | assert_eq!(a, a); + | ^^^^ + | + = note: `-D clippy::eq-op` implied by `-D warnings` + +error: identical args used in this `assert_eq!` macro call + --> $DIR/eq_op_early.rs:9:16 + | +LL | assert_eq!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/used_underscore_binding.rs b/tests/ui/used_underscore_binding.rs index 8e0243c49aa..d8bda7e8f48 100644 --- a/tests/ui/used_underscore_binding.rs +++ b/tests/ui/used_underscore_binding.rs @@ -3,7 +3,7 @@ #![feature(rustc_private)] #![warn(clippy::all)] -#![allow(clippy::blacklisted_name)] +#![allow(clippy::blacklisted_name, clippy::eq_op)] #![warn(clippy::used_underscore_binding)] #[macro_use] -- cgit 1.4.1-3-g733a5 From a3e0446afe0ebd7a420f65cd6aec1c56687f0ef5 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Tue, 13 Oct 2020 09:31:53 +0200 Subject: Extend to the `assert` macro family --- clippy_lints/src/eq_op.rs | 17 ++++++++++++++--- tests/ui/eq_op_early.rs | 25 ++++++++++++++++++++++++- tests/ui/eq_op_early.stderr | 38 +++++++++++++++++++++++++++++++++++++- 3 files changed, 75 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index 7126c98a0b4..a95d71042ee 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -7,6 +7,7 @@ use rustc_ast::{ast, token}; use rustc_errors::Applicability; use rustc_hir::{BinOp, BinOpKind, BorrowKind, Expr, ExprKind}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; +use rustc_middle::lint::in_external_macro; use rustc_parse::parser; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -64,10 +65,20 @@ declare_lint_pass!(EqOp => [EQ_OP, OP_REF]); impl EarlyLintPass for EqOp { fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &ast::MacCall) { + let macro_list = [ + sym!(assert_eq), + sym!(assert_ne), + sym!(debug_assert_eq), + sym!(debug_assert_ne), + ]; if_chain! { - if mac.path == sym!(assert_eq); + if !in_external_macro(cx.sess, mac.span()); + if mac.path.segments.len() == 1; + let macro_name = mac.path.segments[0].ident.name; + if macro_list.contains(¯o_name); let tokens = mac.args.inner_tokens(); - let mut parser = parser::Parser::new(&cx.sess.parse_sess, tokens, false, None); + let mut parser = parser::Parser::new( + &cx.sess.parse_sess, tokens, false, None); if let Ok(left) = parser.parse_expr(); if parser.eat(&token::Comma); if let Ok(right) = parser.parse_expr(); @@ -80,7 +91,7 @@ impl EarlyLintPass for EqOp { cx, EQ_OP, left_expr.span.to(right_expr.span), - "identical args used in this `assert_eq!` macro call", + &format!("identical args used in this `{}!` macro call", macro_name), ); } } diff --git a/tests/ui/eq_op_early.rs b/tests/ui/eq_op_early.rs index cf5660ea98d..25e1c6ac6b7 100644 --- a/tests/ui/eq_op_early.rs +++ b/tests/ui/eq_op_early.rs @@ -7,9 +7,32 @@ fn main() { // lint identical args in `assert_eq!` (see #3574) assert_eq!(a, a); assert_eq!(a + 1, a + 1); - // ok assert_eq!(a, b); assert_eq!(a, a + 1); assert_eq!(a + 1, b + 1); + + // lint identical args in `assert_ne!` + assert_ne!(a, a); + assert_ne!(a + 1, a + 1); + // ok + assert_ne!(a, b); + assert_ne!(a, a + 1); + assert_ne!(a + 1, b + 1); + + // lint identical args in `debug_assert_eq!` + debug_assert_eq!(a, a); + debug_assert_eq!(a + 1, a + 1); + // ok + debug_assert_eq!(a, b); + debug_assert_eq!(a, a + 1); + debug_assert_eq!(a + 1, b + 1); + + // lint identical args in `debug_assert_ne!` + debug_assert_ne!(a, a); + debug_assert_ne!(a + 1, a + 1); + // ok + debug_assert_ne!(a, b); + debug_assert_ne!(a, a + 1); + debug_assert_ne!(a + 1, b + 1); } diff --git a/tests/ui/eq_op_early.stderr b/tests/ui/eq_op_early.stderr index 9206e9026e9..1df094fae18 100644 --- a/tests/ui/eq_op_early.stderr +++ b/tests/ui/eq_op_early.stderr @@ -12,5 +12,41 @@ error: identical args used in this `assert_eq!` macro call LL | assert_eq!(a + 1, a + 1); | ^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: identical args used in this `assert_ne!` macro call + --> $DIR/eq_op_early.rs:16:16 + | +LL | assert_ne!(a, a); + | ^^^^ + +error: identical args used in this `assert_ne!` macro call + --> $DIR/eq_op_early.rs:17:16 + | +LL | assert_ne!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: identical args used in this `debug_assert_eq!` macro call + --> $DIR/eq_op_early.rs:24:22 + | +LL | debug_assert_eq!(a, a); + | ^^^^ + +error: identical args used in this `debug_assert_eq!` macro call + --> $DIR/eq_op_early.rs:25:22 + | +LL | debug_assert_eq!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: identical args used in this `debug_assert_ne!` macro call + --> $DIR/eq_op_early.rs:32:22 + | +LL | debug_assert_ne!(a, a); + | ^^^^ + +error: identical args used in this `debug_assert_ne!` macro call + --> $DIR/eq_op_early.rs:33:22 + | +LL | debug_assert_ne!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: aborting due to 8 previous errors -- cgit 1.4.1-3-g733a5 From e2124086b8107a59129e163aa120dec50add0f77 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Tue, 13 Oct 2020 11:17:51 +0200 Subject: Fix FP in `same_functions_in_if_condition` lint about condition as macro --- clippy_lints/src/copies.rs | 6 +++++- tests/ui/same_functions_in_if_condition.rs | 12 +++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 10a64769585..6c969c3ead0 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -1,4 +1,4 @@ -use crate::utils::{eq_expr_value, SpanlessEq, SpanlessHash}; +use crate::utils::{eq_expr_value, in_macro, SpanlessEq, SpanlessHash}; use crate::utils::{get_parent_expr, higher, if_sequence, snippet, span_lint_and_note, span_lint_and_then}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::{Arm, Block, Expr, ExprKind, MatchSource, Pat, PatKind}; @@ -220,6 +220,10 @@ fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { }; let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool { + // Do not lint if any expr originates from a macro + if in_macro(lhs.span) || in_macro(rhs.span) { + return false; + } // Do not spawn warning if `IFS_SAME_COND` already produced it. if eq_expr_value(cx, lhs, rhs) { return false; diff --git a/tests/ui/same_functions_in_if_condition.rs b/tests/ui/same_functions_in_if_condition.rs index 686867cf5c6..7f28f025790 100644 --- a/tests/ui/same_functions_in_if_condition.rs +++ b/tests/ui/same_functions_in_if_condition.rs @@ -77,4 +77,14 @@ fn ifs_same_cond_fn() { } } -fn main() {} +fn main() { + // macro as condition (see #6168) + let os = if cfg!(target_os = "macos") { + "macos" + } else if cfg!(target_os = "windows") { + "windows" + } else { + "linux" + }; + println!("{}", os); +} -- cgit 1.4.1-3-g733a5 From 121a047645270d5e9ac965d57c324301ea1f21c0 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Tue, 13 Oct 2020 23:46:23 +0200 Subject: Move linting of `assert` macros from early to late pass --- clippy_lints/src/eq_op.rs | 73 +++++++++++++++------------------- clippy_lints/src/lib.rs | 2 - tests/ui/eq_op.rs | 53 +++++++++++++++++++++++++ tests/ui/eq_op.stderr | 95 ++++++++++++++++++++++++++++++++++++++++++++- tests/ui/eq_op_early.rs | 38 ------------------ tests/ui/eq_op_early.stderr | 52 ------------------------- 6 files changed, 179 insertions(+), 134 deletions(-) delete mode 100644 tests/ui/eq_op_early.rs delete mode 100644 tests/ui/eq_op_early.stderr (limited to 'tests') diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index a95d71042ee..9653e62cad0 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -1,14 +1,11 @@ -use crate::utils::ast_utils::eq_expr; use crate::utils::{ - eq_expr_value, implements_trait, in_macro, is_copy, multispan_sugg, snippet, span_lint, span_lint_and_then, + eq_expr_value, implements_trait, in_macro, is_copy, is_expn_of, multispan_sugg, snippet, span_lint, + span_lint_and_then, }; use if_chain::if_chain; -use rustc_ast::{ast, token}; use rustc_errors::Applicability; -use rustc_hir::{BinOp, BinOpKind, BorrowKind, Expr, ExprKind}; -use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; -use rustc_middle::lint::in_external_macro; -use rustc_parse::parser; +use rustc_hir::{BinOp, BinOpKind, BorrowKind, Expr, ExprKind, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -63,44 +60,38 @@ declare_clippy_lint! { declare_lint_pass!(EqOp => [EQ_OP, OP_REF]); -impl EarlyLintPass for EqOp { - fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &ast::MacCall) { - let macro_list = [ - sym!(assert_eq), - sym!(assert_ne), - sym!(debug_assert_eq), - sym!(debug_assert_ne), - ]; - if_chain! { - if !in_external_macro(cx.sess, mac.span()); - if mac.path.segments.len() == 1; - let macro_name = mac.path.segments[0].ident.name; - if macro_list.contains(¯o_name); - let tokens = mac.args.inner_tokens(); - let mut parser = parser::Parser::new( - &cx.sess.parse_sess, tokens, false, None); - if let Ok(left) = parser.parse_expr(); - if parser.eat(&token::Comma); - if let Ok(right) = parser.parse_expr(); - let left_expr = left.into_inner(); - let right_expr = right.into_inner(); - if eq_expr(&left_expr, &right_expr); - - then { - span_lint( - cx, - EQ_OP, - left_expr.span.to(right_expr.span), - &format!("identical args used in this `{}!` macro call", macro_name), - ); - } - } - } -} +const ASSERT_MACRO_NAMES: [&str; 4] = ["assert_eq", "assert_ne", "debug_assert_eq", "debug_assert_ne"]; impl<'tcx> LateLintPass<'tcx> for EqOp { #[allow(clippy::similar_names, clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { + if let ExprKind::Block(ref block, _) = e.kind { + for stmt in block.stmts { + for amn in &ASSERT_MACRO_NAMES { + if_chain! { + if is_expn_of(stmt.span, amn).is_some(); + if let StmtKind::Semi(ref matchexpr) = stmt.kind; + if let ExprKind::Block(ref matchblock, _) = matchexpr.kind; + if let Some(ref matchheader) = matchblock.expr; + if let ExprKind::Match(ref headerexpr, _, _) = matchheader.kind; + if let ExprKind::Tup(ref conditions) = headerexpr.kind; + if conditions.len() == 2; + if let ExprKind::AddrOf(BorrowKind::Ref, _, ref lhs) = conditions[0].kind; + if let ExprKind::AddrOf(BorrowKind::Ref, _, ref rhs) = conditions[1].kind; + if eq_expr_value(cx, lhs, rhs); + + then { + span_lint( + cx, + EQ_OP, + lhs.span.to(rhs.span), + &format!("identical args used in this `{}!` macro call", amn), + ); + } + } + } + } + } if let ExprKind::Binary(op, ref left, ref right) = e.kind { if e.span.from_expansion() { return; diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index dd99b6b9040..fc4afde9d9e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -348,7 +348,6 @@ pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore) { store.register_pre_expansion_pass(|| box write::Write::default()); store.register_pre_expansion_pass(|| box attrs::EarlyAttributes); store.register_pre_expansion_pass(|| box dbg_macro::DbgMacro); - store.register_pre_expansion_pass(|| box eq_op::EqOp); } #[doc(hidden)] @@ -911,7 +910,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let vec_box_size_threshold = conf.vec_box_size_threshold; store.register_late_pass(move || box types::Types::new(vec_box_size_threshold)); store.register_late_pass(|| box booleans::NonminimalBool); - store.register_early_pass(|| box eq_op::EqOp); store.register_late_pass(|| box eq_op::EqOp); store.register_late_pass(|| box enum_clike::UnportableVariant); store.register_late_pass(|| box float_literal::FloatLiteral); diff --git a/tests/ui/eq_op.rs b/tests/ui/eq_op.rs index 272b0900a31..3ab4dfc439b 100644 --- a/tests/ui/eq_op.rs +++ b/tests/ui/eq_op.rs @@ -60,6 +60,8 @@ fn main() { const B: u32 = 10; const C: u32 = A / B; // ok, different named constants const D: u32 = A / A; + + check_assert_identical_args(); } #[rustfmt::skip] @@ -85,3 +87,54 @@ fn check_ignore_macro() { // checks if the lint ignores macros with `!` operator !bool_macro!(1) && !bool_macro!(""); } + +macro_rules! assert_in_macro_def { + () => { + let a = 42; + assert_eq!(a, a); + assert_ne!(a, a); + debug_assert_eq!(a, a); + debug_assert_ne!(a, a); + }; +} + +// lint identical args in assert-like macro invocations (see #3574) +fn check_assert_identical_args() { + // lint also in macro definition + assert_in_macro_def!(); + + let a = 1; + let b = 2; + + // lint identical args in `assert_eq!` + assert_eq!(a, a); + assert_eq!(a + 1, a + 1); + // ok + assert_eq!(a, b); + assert_eq!(a, a + 1); + assert_eq!(a + 1, b + 1); + + // lint identical args in `assert_ne!` + assert_ne!(a, a); + assert_ne!(a + 1, a + 1); + // ok + assert_ne!(a, b); + assert_ne!(a, a + 1); + assert_ne!(a + 1, b + 1); + + // lint identical args in `debug_assert_eq!` + debug_assert_eq!(a, a); + debug_assert_eq!(a + 1, a + 1); + // ok + debug_assert_eq!(a, b); + debug_assert_eq!(a, a + 1); + debug_assert_eq!(a + 1, b + 1); + + // lint identical args in `debug_assert_ne!` + debug_assert_ne!(a, a); + debug_assert_ne!(a + 1, a + 1); + // ok + debug_assert_ne!(a, b); + debug_assert_ne!(a, a + 1); + debug_assert_ne!(a + 1, b + 1); +} diff --git a/tests/ui/eq_op.stderr b/tests/ui/eq_op.stderr index 5b80e6078ee..21a63aec7a1 100644 --- a/tests/ui/eq_op.stderr +++ b/tests/ui/eq_op.stderr @@ -162,5 +162,98 @@ error: equal expressions as operands to `/` LL | const D: u32 = A / A; | ^^^^^ -error: aborting due to 27 previous errors +error: identical args used in this `assert_eq!` macro call + --> $DIR/eq_op.rs:94:20 + | +LL | assert_eq!(a, a); + | ^^^^ +... +LL | assert_in_macro_def!(); + | ----------------------- in this macro invocation + | + = note: `#[deny(clippy::eq_op)]` on by default + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: identical args used in this `assert_ne!` macro call + --> $DIR/eq_op.rs:95:20 + | +LL | assert_ne!(a, a); + | ^^^^ +... +LL | assert_in_macro_def!(); + | ----------------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: identical args used in this `assert_eq!` macro call + --> $DIR/eq_op.rs:110:16 + | +LL | assert_eq!(a, a); + | ^^^^ + +error: identical args used in this `assert_eq!` macro call + --> $DIR/eq_op.rs:111:16 + | +LL | assert_eq!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: identical args used in this `assert_ne!` macro call + --> $DIR/eq_op.rs:118:16 + | +LL | assert_ne!(a, a); + | ^^^^ + +error: identical args used in this `assert_ne!` macro call + --> $DIR/eq_op.rs:119:16 + | +LL | assert_ne!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: identical args used in this `debug_assert_eq!` macro call + --> $DIR/eq_op.rs:96:26 + | +LL | debug_assert_eq!(a, a); + | ^^^^ +... +LL | assert_in_macro_def!(); + | ----------------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: identical args used in this `debug_assert_ne!` macro call + --> $DIR/eq_op.rs:97:26 + | +LL | debug_assert_ne!(a, a); + | ^^^^ +... +LL | assert_in_macro_def!(); + | ----------------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: identical args used in this `debug_assert_eq!` macro call + --> $DIR/eq_op.rs:126:22 + | +LL | debug_assert_eq!(a, a); + | ^^^^ + +error: identical args used in this `debug_assert_eq!` macro call + --> $DIR/eq_op.rs:127:22 + | +LL | debug_assert_eq!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: identical args used in this `debug_assert_ne!` macro call + --> $DIR/eq_op.rs:134:22 + | +LL | debug_assert_ne!(a, a); + | ^^^^ + +error: identical args used in this `debug_assert_ne!` macro call + --> $DIR/eq_op.rs:135:22 + | +LL | debug_assert_ne!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: aborting due to 39 previous errors diff --git a/tests/ui/eq_op_early.rs b/tests/ui/eq_op_early.rs deleted file mode 100644 index 25e1c6ac6b7..00000000000 --- a/tests/ui/eq_op_early.rs +++ /dev/null @@ -1,38 +0,0 @@ -#![warn(clippy::eq_op)] - -fn main() { - let a = 1; - let b = 2; - - // lint identical args in `assert_eq!` (see #3574) - assert_eq!(a, a); - assert_eq!(a + 1, a + 1); - // ok - assert_eq!(a, b); - assert_eq!(a, a + 1); - assert_eq!(a + 1, b + 1); - - // lint identical args in `assert_ne!` - assert_ne!(a, a); - assert_ne!(a + 1, a + 1); - // ok - assert_ne!(a, b); - assert_ne!(a, a + 1); - assert_ne!(a + 1, b + 1); - - // lint identical args in `debug_assert_eq!` - debug_assert_eq!(a, a); - debug_assert_eq!(a + 1, a + 1); - // ok - debug_assert_eq!(a, b); - debug_assert_eq!(a, a + 1); - debug_assert_eq!(a + 1, b + 1); - - // lint identical args in `debug_assert_ne!` - debug_assert_ne!(a, a); - debug_assert_ne!(a + 1, a + 1); - // ok - debug_assert_ne!(a, b); - debug_assert_ne!(a, a + 1); - debug_assert_ne!(a + 1, b + 1); -} diff --git a/tests/ui/eq_op_early.stderr b/tests/ui/eq_op_early.stderr deleted file mode 100644 index 1df094fae18..00000000000 --- a/tests/ui/eq_op_early.stderr +++ /dev/null @@ -1,52 +0,0 @@ -error: identical args used in this `assert_eq!` macro call - --> $DIR/eq_op_early.rs:8:16 - | -LL | assert_eq!(a, a); - | ^^^^ - | - = note: `-D clippy::eq-op` implied by `-D warnings` - -error: identical args used in this `assert_eq!` macro call - --> $DIR/eq_op_early.rs:9:16 - | -LL | assert_eq!(a + 1, a + 1); - | ^^^^^^^^^^^^ - -error: identical args used in this `assert_ne!` macro call - --> $DIR/eq_op_early.rs:16:16 - | -LL | assert_ne!(a, a); - | ^^^^ - -error: identical args used in this `assert_ne!` macro call - --> $DIR/eq_op_early.rs:17:16 - | -LL | assert_ne!(a + 1, a + 1); - | ^^^^^^^^^^^^ - -error: identical args used in this `debug_assert_eq!` macro call - --> $DIR/eq_op_early.rs:24:22 - | -LL | debug_assert_eq!(a, a); - | ^^^^ - -error: identical args used in this `debug_assert_eq!` macro call - --> $DIR/eq_op_early.rs:25:22 - | -LL | debug_assert_eq!(a + 1, a + 1); - | ^^^^^^^^^^^^ - -error: identical args used in this `debug_assert_ne!` macro call - --> $DIR/eq_op_early.rs:32:22 - | -LL | debug_assert_ne!(a, a); - | ^^^^ - -error: identical args used in this `debug_assert_ne!` macro call - --> $DIR/eq_op_early.rs:33:22 - | -LL | debug_assert_ne!(a + 1, a + 1); - | ^^^^^^^^^^^^ - -error: aborting due to 8 previous errors - -- cgit 1.4.1-3-g733a5 From 71c29b5be8526562c3de8d3b7dc94611647ee120 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Wed, 14 Oct 2020 21:29:53 +0200 Subject: Add iterator test case for `eq_op` lint --- tests/ui/eq_op.rs | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'tests') diff --git a/tests/ui/eq_op.rs b/tests/ui/eq_op.rs index 3ab4dfc439b..20613ac6afe 100644 --- a/tests/ui/eq_op.rs +++ b/tests/ui/eq_op.rs @@ -137,4 +137,8 @@ fn check_assert_identical_args() { debug_assert_ne!(a, b); debug_assert_ne!(a, a + 1); debug_assert_ne!(a + 1, b + 1); + + let my_vec = vec![1; 5]; + let mut my_iter = my_vec.iter(); + assert_ne!(my_iter.next(), my_iter.next()); } -- cgit 1.4.1-3-g733a5 From 07b2da884cda8103af50beb327723dec8204fc61 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Tue, 6 Oct 2020 11:49:08 +0200 Subject: add lint less_concise_than_option_unwrap_or --- CHANGELOG.md | 1 + clippy_lints/src/less_concise_than.rs | 107 ++++++++++++++++++++++++++++++++ clippy_lints/src/lib.rs | 4 ++ clippy_lints/src/option_if_let_else.rs | 53 +--------------- clippy_lints/src/utils/eager_or_lazy.rs | 2 +- clippy_lints/src/utils/usage.rs | 50 ++++++++++++++- src/lintlist/mod.rs | 7 +++ tests/ui/less_concise_than.fixed | 43 +++++++++++++ tests/ui/less_concise_than.rs | 55 ++++++++++++++++ tests/ui/less_concise_than.stderr | 52 ++++++++++++++++ tests/ui/shadow.rs | 1 + tests/ui/shadow.stderr | 46 +++++++------- 12 files changed, 345 insertions(+), 76 deletions(-) create mode 100644 clippy_lints/src/less_concise_than.rs create mode 100644 tests/ui/less_concise_than.fixed create mode 100644 tests/ui/less_concise_than.rs create mode 100644 tests/ui/less_concise_than.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index f21768c4498..93ce6bb85d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1781,6 +1781,7 @@ Released 2018-09-13 [`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays [`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty [`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero +[`less_concise_than_option_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#less_concise_than_option_unwrap_or [`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return [`let_underscore_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_lock [`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use diff --git a/clippy_lints/src/less_concise_than.rs b/clippy_lints/src/less_concise_than.rs new file mode 100644 index 00000000000..097aff4b178 --- /dev/null +++ b/clippy_lints/src/less_concise_than.rs @@ -0,0 +1,107 @@ +use crate::utils; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{def, Arm, Expr, ExprKind, PatKind, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** + /// Finds patterns that can be encoded more concisely with `Option::unwrap_or`. + /// + /// **Why is this bad?** + /// Concise code helps focusing on behavior instead of boilerplate. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// match int_optional { + /// Some(v) => v, + /// None => 1, + /// } + /// ``` + /// + /// Use instead: + /// ```rust + /// int_optional.unwrap_or(1) + /// ``` + pub LESS_CONCISE_THAN_OPTION_UNWRAP_OR, + pedantic, + "finds patterns that can be encoded more concisely with `Option::unwrap_or`" +} + +declare_lint_pass!(LessConciseThan => [LESS_CONCISE_THAN_OPTION_UNWRAP_OR]); + +impl LateLintPass<'_> for LessConciseThan { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if utils::in_macro(expr.span) { + return; + } + if lint_option_unwrap_or_case(cx, expr) { + return; + } + } +} + +fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + #[allow(clippy::needless_bool)] + fn applicable_none_arm<'a>(arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> { + if_chain! { + if arms.len() == 2; + if arms.iter().all(|arm| arm.guard.is_none()); + if let Some((idx, none_arm)) = arms.iter().enumerate().find(|(_, arm)| + if_chain! { + if let PatKind::Path(ref qpath) = arm.pat.kind; + if utils::match_qpath(qpath, &utils::paths::OPTION_NONE); + then { true } + else { false } + } + ); + let some_arm = &arms[1 - idx]; + if let PatKind::TupleStruct(ref some_qpath, &[some_binding], _) = some_arm.pat.kind; + if utils::match_qpath(some_qpath, &utils::paths::OPTION_SOME); + if let PatKind::Binding(_, binding_hir_id, ..) = some_binding.kind; + if let ExprKind::Path(QPath::Resolved(_, body_path)) = some_arm.body.kind; + if let def::Res::Local(body_path_hir_id) = body_path.res; + if body_path_hir_id == binding_hir_id; + then { Some(none_arm) } + else { None } + } + } + if_chain! { + if !utils::usage::contains_return_break_continue_macro(expr); + if let ExprKind::Match (match_expr, match_arms, _) = expr.kind; + let ty = cx.typeck_results().expr_ty(match_expr); + if utils::is_type_diagnostic_item(cx, ty, sym!(option_type)); + if let Some(none_arm) = applicable_none_arm(match_arms); + if let Some(match_expr_snippet) = utils::snippet_opt(cx, match_expr.span); + if let Some(none_body_snippet) = utils::snippet_opt(cx, none_arm.body.span); + if let Some(indent) = utils::indent_of(cx, expr.span); + then { + let reindented_none_body = + utils::reindent_multiline(none_body_snippet.into(), true, Some(indent)); + let eager_eval = utils::eager_or_lazy::is_eagerness_candidate(cx, none_arm.body); + let method = if eager_eval { + "unwrap_or" + } else { + "unwrap_or_else" + }; + utils::span_lint_and_sugg( + cx, + LESS_CONCISE_THAN_OPTION_UNWRAP_OR, expr.span, + "this pattern can be more concisely encoded with `Option::unwrap_or`", + "replace with", + format!( + "{}.{}({}{})", + match_expr_snippet, + method, + if eager_eval { ""} else { "|| " }, + reindented_none_body + ), + Applicability::MachineApplicable, + ); + true + } else { false} + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 26a727687b1..2e9900815d9 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -224,6 +224,7 @@ mod large_const_arrays; mod large_enum_variant; mod large_stack_arrays; mod len_zero; +mod less_concise_than; mod let_if_seq; mod let_underscore; mod lifetimes; @@ -609,6 +610,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &large_stack_arrays::LARGE_STACK_ARRAYS, &len_zero::LEN_WITHOUT_IS_EMPTY, &len_zero::LEN_ZERO, + &less_concise_than::LESS_CONCISE_THAN_OPTION_UNWRAP_OR, &let_if_seq::USELESS_LET_IF_SEQ, &let_underscore::LET_UNDERSCORE_LOCK, &let_underscore::LET_UNDERSCORE_MUST_USE, @@ -1126,6 +1128,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box repeat_once::RepeatOnce); store.register_late_pass(|| box unwrap_in_result::UnwrapInResult); store.register_late_pass(|| box self_assignment::SelfAssignment); + store.register_late_pass(|| box less_concise_than::LessConciseThan); store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); store.register_late_pass(|| box manual_strip::ManualStrip); @@ -1210,6 +1213,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&infinite_iter::MAYBE_INFINITE_ITER), LintId::of(&items_after_statements::ITEMS_AFTER_STATEMENTS), LintId::of(&large_stack_arrays::LARGE_STACK_ARRAYS), + LintId::of(&less_concise_than::LESS_CONCISE_THAN_OPTION_UNWRAP_OR), LintId::of(&literal_representation::LARGE_DIGIT_GROUPS), LintId::of(&literal_representation::UNREADABLE_LITERAL), LintId::of(&loops::EXPLICIT_INTO_ITER_LOOP), diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 383a62da821..eb7624b25a3 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -5,10 +5,8 @@ use crate::utils::{is_type_diagnostic_item, paths, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, PatKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -84,53 +82,6 @@ struct OptionIfLetElseOccurence { wrap_braces: bool, } -struct ReturnBreakContinueMacroVisitor { - seen_return_break_continue: bool, -} - -impl ReturnBreakContinueMacroVisitor { - fn new() -> ReturnBreakContinueMacroVisitor { - ReturnBreakContinueMacroVisitor { - seen_return_break_continue: false, - } - } -} - -impl<'tcx> Visitor<'tcx> for ReturnBreakContinueMacroVisitor { - type Map = Map<'tcx>; - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - - fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { - if self.seen_return_break_continue { - // No need to look farther if we've already seen one of them - return; - } - match &ex.kind { - ExprKind::Ret(..) | ExprKind::Break(..) | ExprKind::Continue(..) => { - self.seen_return_break_continue = true; - }, - // Something special could be done here to handle while or for loop - // desugaring, as this will detect a break if there's a while loop - // or a for loop inside the expression. - _ => { - if utils::in_macro(ex.span) { - self.seen_return_break_continue = true; - } else { - rustc_hir::intravisit::walk_expr(self, ex); - } - }, - } - } -} - -fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool { - let mut recursive_visitor = ReturnBreakContinueMacroVisitor::new(); - recursive_visitor.visit_expr(expression); - recursive_visitor.seen_return_break_continue -} - /// Extracts the body of a given arm. If the arm contains only an expression, /// then it returns the expression. Otherwise, it returns the entire block fn extract_body_from_arm<'a>(arm: &'a Arm<'a>) -> Option<&'a Expr<'a>> { @@ -208,8 +159,8 @@ fn detect_option_if_let_else<'tcx>( if let PatKind::TupleStruct(struct_qpath, &[inner_pat], _) = &arms[0].pat.kind; if utils::match_qpath(struct_qpath, &paths::OPTION_SOME); if let PatKind::Binding(bind_annotation, _, id, _) = &inner_pat.kind; - if !contains_return_break_continue_macro(arms[0].body); - if !contains_return_break_continue_macro(arms[1].body); + if !utils::usage::contains_return_break_continue_macro(arms[0].body); + if !utils::usage::contains_return_break_continue_macro(arms[1].body); then { let capture_mut = if bind_annotation == &BindingAnnotation::Mutable { "mut " } else { "" }; let some_body = extract_body_from_arm(&arms[0])?; diff --git a/clippy_lints/src/utils/eager_or_lazy.rs b/clippy_lints/src/utils/eager_or_lazy.rs index 6938d9971d9..30e812c284b 100644 --- a/clippy_lints/src/utils/eager_or_lazy.rs +++ b/clippy_lints/src/utils/eager_or_lazy.rs @@ -82,7 +82,7 @@ fn identify_some_pure_patterns(expr: &Expr<'_>) -> bool { /// Identify some potentially computationally expensive patterns. /// This function is named so to stress that its implementation is non-exhaustive. /// It returns FNs and FPs. -fn identify_some_potentially_expensive_patterns<'a, 'tcx>(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { +fn identify_some_potentially_expensive_patterns<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { // Searches an expression for method calls or function calls that aren't ctors struct FunCallFinder<'a, 'tcx> { cx: &'a LateContext<'tcx>, diff --git a/clippy_lints/src/utils/usage.rs b/clippy_lints/src/utils/usage.rs index ea1dc3be29b..2fd6046ebcf 100644 --- a/clippy_lints/src/utils/usage.rs +++ b/clippy_lints/src/utils/usage.rs @@ -1,10 +1,11 @@ +use crate::utils; use crate::utils::match_var; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::intravisit; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; -use rustc_hir::{Expr, HirId, Path}; +use rustc_hir::{Expr, ExprKind, HirId, Path}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; @@ -174,3 +175,50 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for BindingUsageFinder<'a, 'tcx> { intravisit::NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) } } + +struct ReturnBreakContinueMacroVisitor { + seen_return_break_continue: bool, +} + +impl ReturnBreakContinueMacroVisitor { + fn new() -> ReturnBreakContinueMacroVisitor { + ReturnBreakContinueMacroVisitor { + seen_return_break_continue: false, + } + } +} + +impl<'tcx> Visitor<'tcx> for ReturnBreakContinueMacroVisitor { + type Map = Map<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + + fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { + if self.seen_return_break_continue { + // No need to look farther if we've already seen one of them + return; + } + match &ex.kind { + ExprKind::Ret(..) | ExprKind::Break(..) | ExprKind::Continue(..) => { + self.seen_return_break_continue = true; + }, + // Something special could be done here to handle while or for loop + // desugaring, as this will detect a break if there's a while loop + // or a for loop inside the expression. + _ => { + if utils::in_macro(ex.span) { + self.seen_return_break_continue = true; + } else { + rustc_hir::intravisit::walk_expr(self, ex); + } + }, + } + } +} + +pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool { + let mut recursive_visitor = ReturnBreakContinueMacroVisitor::new(); + recursive_visitor.visit_expr(expression); + recursive_visitor.seen_return_break_continue +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 624223ff706..6dc95fcfdb2 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1075,6 +1075,13 @@ vec![ deprecation: None, module: "len_zero", }, + Lint { + name: "less_concise_than_option_unwrap_or", + group: "pedantic", + desc: "finds patterns that can be encoded more concisely with `Option::unwrap_or`", + deprecation: None, + module: "less_concise_than", + }, Lint { name: "let_and_return", group: "style", diff --git a/tests/ui/less_concise_than.fixed b/tests/ui/less_concise_than.fixed new file mode 100644 index 00000000000..52b69ebba3e --- /dev/null +++ b/tests/ui/less_concise_than.fixed @@ -0,0 +1,43 @@ +// run-rustfix +#![warn(clippy::less_concise_than_option_unwrap_or)] +#![allow(dead_code)] + +fn unwrap_or() { + // int case + Some(1).unwrap_or(42); + + // richer none expr + Some(1).unwrap_or_else(|| 1 + 42); + + // multiline case + Some(1).unwrap_or_else(|| { + let a = 1 + 42; + let b = a + 42; + b + 42 + }); + + // string case + Some("Bob").unwrap_or("Alice"); + + // don't lint + match Some(1) { + Some(i) => i + 2, + None => 42, + }; + match Some(1) { + Some(i) => i, + None => return, + }; + for j in 0..4 { + match Some(j) { + Some(i) => i, + None => continue, + }; + match Some(j) { + Some(i) => i, + None => break, + }; + } +} + +fn main() {} diff --git a/tests/ui/less_concise_than.rs b/tests/ui/less_concise_than.rs new file mode 100644 index 00000000000..bb2a8f2050a --- /dev/null +++ b/tests/ui/less_concise_than.rs @@ -0,0 +1,55 @@ +// run-rustfix +#![warn(clippy::less_concise_than_option_unwrap_or)] +#![allow(dead_code)] + +fn unwrap_or() { + // int case + match Some(1) { + Some(i) => i, + None => 42, + }; + + // richer none expr + match Some(1) { + Some(i) => i, + None => 1 + 42, + }; + + // multiline case + match Some(1) { + Some(i) => i, + None => { + let a = 1 + 42; + let b = a + 42; + b + 42 + }, + }; + + // string case + match Some("Bob") { + Some(i) => i, + None => "Alice", + }; + + // don't lint + match Some(1) { + Some(i) => i + 2, + None => 42, + }; + match Some(1) { + Some(i) => i, + None => return, + }; + for j in 0..4 { + match Some(j) { + Some(i) => i, + None => continue, + }; + match Some(j) { + Some(i) => i, + None => break, + }; + } +} + +fn main() {} diff --git a/tests/ui/less_concise_than.stderr b/tests/ui/less_concise_than.stderr new file mode 100644 index 00000000000..e3e8a406db1 --- /dev/null +++ b/tests/ui/less_concise_than.stderr @@ -0,0 +1,52 @@ +error: this pattern can be more concisely encoded with `Option::unwrap_or` + --> $DIR/less_concise_than.rs:7:5 + | +LL | / match Some(1) { +LL | | Some(i) => i, +LL | | None => 42, +LL | | }; + | |_____^ help: replace with: `Some(1).unwrap_or(42)` + | + = note: `-D clippy::less-concise-than-option-unwrap-or` implied by `-D warnings` + +error: this pattern can be more concisely encoded with `Option::unwrap_or` + --> $DIR/less_concise_than.rs:13:5 + | +LL | / match Some(1) { +LL | | Some(i) => i, +LL | | None => 1 + 42, +LL | | }; + | |_____^ help: replace with: `Some(1).unwrap_or_else(|| 1 + 42)` + +error: this pattern can be more concisely encoded with `Option::unwrap_or` + --> $DIR/less_concise_than.rs:19:5 + | +LL | / match Some(1) { +LL | | Some(i) => i, +LL | | None => { +LL | | let a = 1 + 42; +... | +LL | | }, +LL | | }; + | |_____^ + | +help: replace with + | +LL | Some(1).unwrap_or_else(|| { +LL | let a = 1 + 42; +LL | let b = a + 42; +LL | b + 42 +LL | }); + | + +error: this pattern can be more concisely encoded with `Option::unwrap_or` + --> $DIR/less_concise_than.rs:29:5 + | +LL | / match Some("Bob") { +LL | | Some(i) => i, +LL | | None => "Alice", +LL | | }; + | |_____^ help: replace with: `Some("Bob").unwrap_or("Alice")` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/shadow.rs b/tests/ui/shadow.rs index bd91ae4e934..e7441293d45 100644 --- a/tests/ui/shadow.rs +++ b/tests/ui/shadow.rs @@ -8,6 +8,7 @@ #![allow( unused_parens, unused_variables, + clippy::less_concise_than_option_unwrap_or, clippy::missing_docs_in_private_items, clippy::single_match )] diff --git a/tests/ui/shadow.stderr b/tests/ui/shadow.stderr index 8a831375b41..7c1ad2949e9 100644 --- a/tests/ui/shadow.stderr +++ b/tests/ui/shadow.stderr @@ -1,135 +1,135 @@ error: `x` is shadowed by itself in `&mut x` - --> $DIR/shadow.rs:26:5 + --> $DIR/shadow.rs:27:5 | LL | let x = &mut x; | ^^^^^^^^^^^^^^^ | = note: `-D clippy::shadow-same` implied by `-D warnings` note: previous binding is here - --> $DIR/shadow.rs:25:13 + --> $DIR/shadow.rs:26:13 | LL | let mut x = 1; | ^ error: `x` is shadowed by itself in `{ x }` - --> $DIR/shadow.rs:27:5 + --> $DIR/shadow.rs:28:5 | LL | let x = { x }; | ^^^^^^^^^^^^^^ | note: previous binding is here - --> $DIR/shadow.rs:26:9 + --> $DIR/shadow.rs:27:9 | LL | let x = &mut x; | ^ error: `x` is shadowed by itself in `(&*x)` - --> $DIR/shadow.rs:28:5 + --> $DIR/shadow.rs:29:5 | LL | let x = (&*x); | ^^^^^^^^^^^^^^ | note: previous binding is here - --> $DIR/shadow.rs:27:9 + --> $DIR/shadow.rs:28:9 | LL | let x = { x }; | ^ error: `x` is shadowed by `{ *x + 1 }` which reuses the original value - --> $DIR/shadow.rs:29:9 + --> $DIR/shadow.rs:30:9 | LL | let x = { *x + 1 }; | ^ | = note: `-D clippy::shadow-reuse` implied by `-D warnings` note: initialization happens here - --> $DIR/shadow.rs:29:13 + --> $DIR/shadow.rs:30:13 | LL | let x = { *x + 1 }; | ^^^^^^^^^^ note: previous binding is here - --> $DIR/shadow.rs:28:9 + --> $DIR/shadow.rs:29:9 | LL | let x = (&*x); | ^ error: `x` is shadowed by `id(x)` which reuses the original value - --> $DIR/shadow.rs:30:9 + --> $DIR/shadow.rs:31:9 | LL | let x = id(x); | ^ | note: initialization happens here - --> $DIR/shadow.rs:30:13 + --> $DIR/shadow.rs:31:13 | LL | let x = id(x); | ^^^^^ note: previous binding is here - --> $DIR/shadow.rs:29:9 + --> $DIR/shadow.rs:30:9 | LL | let x = { *x + 1 }; | ^ error: `x` is shadowed by `(1, x)` which reuses the original value - --> $DIR/shadow.rs:31:9 + --> $DIR/shadow.rs:32:9 | LL | let x = (1, x); | ^ | note: initialization happens here - --> $DIR/shadow.rs:31:13 + --> $DIR/shadow.rs:32:13 | LL | let x = (1, x); | ^^^^^^ note: previous binding is here - --> $DIR/shadow.rs:30:9 + --> $DIR/shadow.rs:31:9 | LL | let x = id(x); | ^ error: `x` is shadowed by `first(x)` which reuses the original value - --> $DIR/shadow.rs:32:9 + --> $DIR/shadow.rs:33:9 | LL | let x = first(x); | ^ | note: initialization happens here - --> $DIR/shadow.rs:32:13 + --> $DIR/shadow.rs:33:13 | LL | let x = first(x); | ^^^^^^^^ note: previous binding is here - --> $DIR/shadow.rs:31:9 + --> $DIR/shadow.rs:32:9 | LL | let x = (1, x); | ^ error: `x` is being shadowed - --> $DIR/shadow.rs:34:9 + --> $DIR/shadow.rs:35:9 | LL | let x = y; | ^ | = note: `-D clippy::shadow-unrelated` implied by `-D warnings` note: initialization happens here - --> $DIR/shadow.rs:34:13 + --> $DIR/shadow.rs:35:13 | LL | let x = y; | ^ note: previous binding is here - --> $DIR/shadow.rs:32:9 + --> $DIR/shadow.rs:33:9 | LL | let x = first(x); | ^ error: `x` shadows a previous declaration - --> $DIR/shadow.rs:36:5 + --> $DIR/shadow.rs:37:5 | LL | let x; | ^^^^^^ | note: previous binding is here - --> $DIR/shadow.rs:34:9 + --> $DIR/shadow.rs:35:9 | LL | let x = y; | ^ -- cgit 1.4.1-3-g733a5 From 6d4eeeabcda6d6d25738e1e8e2b64580daefc4b9 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Sun, 11 Oct 2020 22:55:05 +0200 Subject: manual-unwrap-or / pr remarks --- CHANGELOG.md | 2 +- clippy_lints/src/lib.rs | 9 +-- clippy_lints/src/manual_unwrap_or.rs | 113 ++++++++++++++++++----------------- src/lintlist/mod.rs | 14 ++--- tests/ui/less_concise_than.fixed | 43 ------------- tests/ui/less_concise_than.rs | 55 ----------------- tests/ui/less_concise_than.stderr | 52 ---------------- tests/ui/manual_unwrap_or.fixed | 45 ++++++++++++++ tests/ui/manual_unwrap_or.rs | 60 +++++++++++++++++++ tests/ui/manual_unwrap_or.stderr | 61 +++++++++++++++++++ tests/ui/shadow.rs | 2 +- 11 files changed, 239 insertions(+), 217 deletions(-) delete mode 100644 tests/ui/less_concise_than.fixed delete mode 100644 tests/ui/less_concise_than.rs delete mode 100644 tests/ui/less_concise_than.stderr create mode 100644 tests/ui/manual_unwrap_or.fixed create mode 100644 tests/ui/manual_unwrap_or.rs create mode 100644 tests/ui/manual_unwrap_or.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 93ce6bb85d8..d82f970b8bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1781,7 +1781,6 @@ Released 2018-09-13 [`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays [`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty [`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero -[`less_concise_than_option_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#less_concise_than_option_unwrap_or [`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return [`let_underscore_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_lock [`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use @@ -1797,6 +1796,7 @@ Released 2018-09-13 [`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic [`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip [`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap +[`manual_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or [`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names [`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone [`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 2e9900815d9..d4d2f92a6a6 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -224,7 +224,6 @@ mod large_const_arrays; mod large_enum_variant; mod large_stack_arrays; mod len_zero; -mod less_concise_than; mod let_if_seq; mod let_underscore; mod lifetimes; @@ -235,6 +234,7 @@ mod main_recursion; mod manual_async_fn; mod manual_non_exhaustive; mod manual_strip; +mod manual_unwrap_or; mod map_clone; mod map_err_ignore; mod map_identity; @@ -610,7 +610,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &large_stack_arrays::LARGE_STACK_ARRAYS, &len_zero::LEN_WITHOUT_IS_EMPTY, &len_zero::LEN_ZERO, - &less_concise_than::LESS_CONCISE_THAN_OPTION_UNWRAP_OR, &let_if_seq::USELESS_LET_IF_SEQ, &let_underscore::LET_UNDERSCORE_LOCK, &let_underscore::LET_UNDERSCORE_MUST_USE, @@ -642,6 +641,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &manual_async_fn::MANUAL_ASYNC_FN, &manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, &manual_strip::MANUAL_STRIP, + &manual_unwrap_or::MANUAL_UNWRAP_OR, &map_clone::MAP_CLONE, &map_err_ignore::MAP_ERR_IGNORE, &map_identity::MAP_IDENTITY, @@ -1128,7 +1128,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box repeat_once::RepeatOnce); store.register_late_pass(|| box unwrap_in_result::UnwrapInResult); store.register_late_pass(|| box self_assignment::SelfAssignment); - store.register_late_pass(|| box less_concise_than::LessConciseThan); + store.register_late_pass(|| box manual_unwrap_or::ManualUnwrapOr); store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); store.register_late_pass(|| box manual_strip::ManualStrip); @@ -1213,7 +1213,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&infinite_iter::MAYBE_INFINITE_ITER), LintId::of(&items_after_statements::ITEMS_AFTER_STATEMENTS), LintId::of(&large_stack_arrays::LARGE_STACK_ARRAYS), - LintId::of(&less_concise_than::LESS_CONCISE_THAN_OPTION_UNWRAP_OR), LintId::of(&literal_representation::LARGE_DIGIT_GROUPS), LintId::of(&literal_representation::UNREADABLE_LITERAL), LintId::of(&loops::EXPLICIT_INTO_ITER_LOOP), @@ -1371,6 +1370,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&manual_async_fn::MANUAL_ASYNC_FN), 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_identity::MAP_IDENTITY), LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN), @@ -1666,6 +1666,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::MUT_RANGE_BOUND), LintId::of(&loops::WHILE_LET_LOOP), LintId::of(&manual_strip::MANUAL_STRIP), + LintId::of(&manual_unwrap_or::MANUAL_UNWRAP_OR), LintId::of(&map_identity::MAP_IDENTITY), LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN), LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN), diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index 097aff4b178..9d8fc863424 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -2,12 +2,14 @@ use crate::utils; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{def, Arm, Expr, ExprKind, PatKind, QPath}; +use rustc_lint::LintContext; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// **What it does:** - /// Finds patterns that can be encoded more concisely with `Option::unwrap_or`. + /// Finds patterns that reimplement `Option::unwrap_or`. /// /// **Why is this bad?** /// Concise code helps focusing on behavior instead of boilerplate. @@ -16,7 +18,7 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust - /// match int_optional { + /// match int_option { /// Some(v) => v, /// None => 1, /// } @@ -24,39 +26,35 @@ declare_clippy_lint! { /// /// Use instead: /// ```rust - /// int_optional.unwrap_or(1) + /// int_option.unwrap_or(1) /// ``` - pub LESS_CONCISE_THAN_OPTION_UNWRAP_OR, - pedantic, - "finds patterns that can be encoded more concisely with `Option::unwrap_or`" + pub MANUAL_UNWRAP_OR, + complexity, + "finds patterns that can be encoded more concisely with `Option::unwrap_or(_else)`" } -declare_lint_pass!(LessConciseThan => [LESS_CONCISE_THAN_OPTION_UNWRAP_OR]); +declare_lint_pass!(ManualUnwrapOr => [MANUAL_UNWRAP_OR]); -impl LateLintPass<'_> for LessConciseThan { +impl LateLintPass<'_> for ManualUnwrapOr { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if utils::in_macro(expr.span) { - return; - } - if lint_option_unwrap_or_case(cx, expr) { + if in_external_macro(cx.sess(), expr.span) { return; } + lint_option_unwrap_or_case(cx, expr); } } fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { - #[allow(clippy::needless_bool)] fn applicable_none_arm<'a>(arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> { if_chain! { if arms.len() == 2; if arms.iter().all(|arm| arm.guard.is_none()); if let Some((idx, none_arm)) = arms.iter().enumerate().find(|(_, arm)| - if_chain! { - if let PatKind::Path(ref qpath) = arm.pat.kind; - if utils::match_qpath(qpath, &utils::paths::OPTION_NONE); - then { true } - else { false } - } + if let PatKind::Path(ref qpath) = arm.pat.kind { + utils::match_qpath(qpath, &utils::paths::OPTION_NONE) + } else { + false + } ); let some_arm = &arms[1 - idx]; if let PatKind::TupleStruct(ref some_qpath, &[some_binding], _) = some_arm.pat.kind; @@ -65,43 +63,50 @@ fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tc if let ExprKind::Path(QPath::Resolved(_, body_path)) = some_arm.body.kind; if let def::Res::Local(body_path_hir_id) = body_path.res; if body_path_hir_id == binding_hir_id; - then { Some(none_arm) } - else { None } + if !utils::usage::contains_return_break_continue_macro(none_arm.body); + then { + Some(none_arm) + } + else { + None + } } } + if_chain! { - if !utils::usage::contains_return_break_continue_macro(expr); - if let ExprKind::Match (match_expr, match_arms, _) = expr.kind; - let ty = cx.typeck_results().expr_ty(match_expr); - if utils::is_type_diagnostic_item(cx, ty, sym!(option_type)); - if let Some(none_arm) = applicable_none_arm(match_arms); - if let Some(match_expr_snippet) = utils::snippet_opt(cx, match_expr.span); - if let Some(none_body_snippet) = utils::snippet_opt(cx, none_arm.body.span); - if let Some(indent) = utils::indent_of(cx, expr.span); - then { - let reindented_none_body = - utils::reindent_multiline(none_body_snippet.into(), true, Some(indent)); - let eager_eval = utils::eager_or_lazy::is_eagerness_candidate(cx, none_arm.body); - let method = if eager_eval { - "unwrap_or" - } else { - "unwrap_or_else" - }; - utils::span_lint_and_sugg( - cx, - LESS_CONCISE_THAN_OPTION_UNWRAP_OR, expr.span, - "this pattern can be more concisely encoded with `Option::unwrap_or`", - "replace with", - format!( - "{}.{}({}{})", - match_expr_snippet, - method, - if eager_eval { ""} else { "|| " }, - reindented_none_body - ), - Applicability::MachineApplicable, - ); - true - } else { false} + if let ExprKind::Match(scrutinee, match_arms, _) = expr.kind; + let ty = cx.typeck_results().expr_ty(scrutinee); + if utils::is_type_diagnostic_item(cx, ty, sym!(option_type)); + if let Some(none_arm) = applicable_none_arm(match_arms); + if let Some(scrutinee_snippet) = utils::snippet_opt(cx, scrutinee.span); + if let Some(none_body_snippet) = utils::snippet_opt(cx, none_arm.body.span); + if let Some(indent) = utils::indent_of(cx, expr.span); + then { + let reindented_none_body = + utils::reindent_multiline(none_body_snippet.into(), true, Some(indent)); + let eager_eval = utils::eager_or_lazy::is_eagerness_candidate(cx, none_arm.body); + let method = if eager_eval { + "unwrap_or" + } else { + "unwrap_or_else" + }; + utils::span_lint_and_sugg( + cx, + MANUAL_UNWRAP_OR, expr.span, + &format!("this pattern reimplements `Option::{}`", &method), + "replace with", + format!( + "{}.{}({}{})", + scrutinee_snippet, + method, + if eager_eval { ""} else { "|| " }, + reindented_none_body + ), + Applicability::MachineApplicable, + ); + true + } else { + false + } } } diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 6dc95fcfdb2..debd3c31d8b 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1075,13 +1075,6 @@ vec![ deprecation: None, module: "len_zero", }, - Lint { - name: "less_concise_than_option_unwrap_or", - group: "pedantic", - desc: "finds patterns that can be encoded more concisely with `Option::unwrap_or`", - deprecation: None, - module: "less_concise_than", - }, Lint { name: "let_and_return", group: "style", @@ -1187,6 +1180,13 @@ vec![ deprecation: None, module: "swap", }, + Lint { + name: "manual_unwrap_or", + group: "complexity", + desc: "finds patterns that can be encoded more concisely with `Option::unwrap_or(_else)`", + deprecation: None, + module: "manual_unwrap_or", + }, Lint { name: "many_single_char_names", group: "style", diff --git a/tests/ui/less_concise_than.fixed b/tests/ui/less_concise_than.fixed deleted file mode 100644 index 52b69ebba3e..00000000000 --- a/tests/ui/less_concise_than.fixed +++ /dev/null @@ -1,43 +0,0 @@ -// run-rustfix -#![warn(clippy::less_concise_than_option_unwrap_or)] -#![allow(dead_code)] - -fn unwrap_or() { - // int case - Some(1).unwrap_or(42); - - // richer none expr - Some(1).unwrap_or_else(|| 1 + 42); - - // multiline case - Some(1).unwrap_or_else(|| { - let a = 1 + 42; - let b = a + 42; - b + 42 - }); - - // string case - Some("Bob").unwrap_or("Alice"); - - // don't lint - match Some(1) { - Some(i) => i + 2, - None => 42, - }; - match Some(1) { - Some(i) => i, - None => return, - }; - for j in 0..4 { - match Some(j) { - Some(i) => i, - None => continue, - }; - match Some(j) { - Some(i) => i, - None => break, - }; - } -} - -fn main() {} diff --git a/tests/ui/less_concise_than.rs b/tests/ui/less_concise_than.rs deleted file mode 100644 index bb2a8f2050a..00000000000 --- a/tests/ui/less_concise_than.rs +++ /dev/null @@ -1,55 +0,0 @@ -// run-rustfix -#![warn(clippy::less_concise_than_option_unwrap_or)] -#![allow(dead_code)] - -fn unwrap_or() { - // int case - match Some(1) { - Some(i) => i, - None => 42, - }; - - // richer none expr - match Some(1) { - Some(i) => i, - None => 1 + 42, - }; - - // multiline case - match Some(1) { - Some(i) => i, - None => { - let a = 1 + 42; - let b = a + 42; - b + 42 - }, - }; - - // string case - match Some("Bob") { - Some(i) => i, - None => "Alice", - }; - - // don't lint - match Some(1) { - Some(i) => i + 2, - None => 42, - }; - match Some(1) { - Some(i) => i, - None => return, - }; - for j in 0..4 { - match Some(j) { - Some(i) => i, - None => continue, - }; - match Some(j) { - Some(i) => i, - None => break, - }; - } -} - -fn main() {} diff --git a/tests/ui/less_concise_than.stderr b/tests/ui/less_concise_than.stderr deleted file mode 100644 index e3e8a406db1..00000000000 --- a/tests/ui/less_concise_than.stderr +++ /dev/null @@ -1,52 +0,0 @@ -error: this pattern can be more concisely encoded with `Option::unwrap_or` - --> $DIR/less_concise_than.rs:7:5 - | -LL | / match Some(1) { -LL | | Some(i) => i, -LL | | None => 42, -LL | | }; - | |_____^ help: replace with: `Some(1).unwrap_or(42)` - | - = note: `-D clippy::less-concise-than-option-unwrap-or` implied by `-D warnings` - -error: this pattern can be more concisely encoded with `Option::unwrap_or` - --> $DIR/less_concise_than.rs:13:5 - | -LL | / match Some(1) { -LL | | Some(i) => i, -LL | | None => 1 + 42, -LL | | }; - | |_____^ help: replace with: `Some(1).unwrap_or_else(|| 1 + 42)` - -error: this pattern can be more concisely encoded with `Option::unwrap_or` - --> $DIR/less_concise_than.rs:19:5 - | -LL | / match Some(1) { -LL | | Some(i) => i, -LL | | None => { -LL | | let a = 1 + 42; -... | -LL | | }, -LL | | }; - | |_____^ - | -help: replace with - | -LL | Some(1).unwrap_or_else(|| { -LL | let a = 1 + 42; -LL | let b = a + 42; -LL | b + 42 -LL | }); - | - -error: this pattern can be more concisely encoded with `Option::unwrap_or` - --> $DIR/less_concise_than.rs:29:5 - | -LL | / match Some("Bob") { -LL | | Some(i) => i, -LL | | None => "Alice", -LL | | }; - | |_____^ help: replace with: `Some("Bob").unwrap_or("Alice")` - -error: aborting due to 4 previous errors - diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed new file mode 100644 index 00000000000..99d30360db1 --- /dev/null +++ b/tests/ui/manual_unwrap_or.fixed @@ -0,0 +1,45 @@ +// run-rustfix +#![allow(dead_code)] + +fn unwrap_or() { + // int case + Some(1).unwrap_or(42); + + // int case reversed + Some(1).unwrap_or(42); + + // richer none expr + Some(1).unwrap_or_else(|| 1 + 42); + + // multiline case + Some(1).unwrap_or_else(|| { + let a = 1 + 42; + let b = a + 42; + b + 42 + }); + + // string case + Some("Bob").unwrap_or("Alice"); + + // don't lint + match Some(1) { + Some(i) => i + 2, + None => 42, + }; + match Some(1) { + Some(i) => i, + None => return, + }; + for j in 0..4 { + match Some(j) { + Some(i) => i, + None => continue, + }; + match Some(j) { + Some(i) => i, + None => break, + }; + } +} + +fn main() {} diff --git a/tests/ui/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs new file mode 100644 index 00000000000..5d03d9db163 --- /dev/null +++ b/tests/ui/manual_unwrap_or.rs @@ -0,0 +1,60 @@ +// run-rustfix +#![allow(dead_code)] + +fn unwrap_or() { + // int case + match Some(1) { + Some(i) => i, + None => 42, + }; + + // int case reversed + match Some(1) { + None => 42, + Some(i) => i, + }; + + // richer none expr + match Some(1) { + Some(i) => i, + None => 1 + 42, + }; + + // multiline case + match Some(1) { + Some(i) => i, + None => { + let a = 1 + 42; + let b = a + 42; + b + 42 + }, + }; + + // string case + match Some("Bob") { + Some(i) => i, + None => "Alice", + }; + + // don't lint + match Some(1) { + Some(i) => i + 2, + None => 42, + }; + match Some(1) { + Some(i) => i, + None => return, + }; + for j in 0..4 { + match Some(j) { + Some(i) => i, + None => continue, + }; + match Some(j) { + Some(i) => i, + None => break, + }; + } +} + +fn main() {} diff --git a/tests/ui/manual_unwrap_or.stderr b/tests/ui/manual_unwrap_or.stderr new file mode 100644 index 00000000000..03da118a0c4 --- /dev/null +++ b/tests/ui/manual_unwrap_or.stderr @@ -0,0 +1,61 @@ +error: this pattern reimplements `Option::unwrap_or` + --> $DIR/manual_unwrap_or.rs:6:5 + | +LL | / match Some(1) { +LL | | Some(i) => i, +LL | | None => 42, +LL | | }; + | |_____^ help: replace with: `Some(1).unwrap_or(42)` + | + = note: `-D clippy::manual-unwrap-or` implied by `-D warnings` + +error: this pattern reimplements `Option::unwrap_or` + --> $DIR/manual_unwrap_or.rs:12:5 + | +LL | / match Some(1) { +LL | | None => 42, +LL | | Some(i) => i, +LL | | }; + | |_____^ help: replace with: `Some(1).unwrap_or(42)` + +error: this pattern reimplements `Option::unwrap_or_else` + --> $DIR/manual_unwrap_or.rs:18:5 + | +LL | / match Some(1) { +LL | | Some(i) => i, +LL | | None => 1 + 42, +LL | | }; + | |_____^ help: replace with: `Some(1).unwrap_or_else(|| 1 + 42)` + +error: this pattern reimplements `Option::unwrap_or_else` + --> $DIR/manual_unwrap_or.rs:24:5 + | +LL | / match Some(1) { +LL | | Some(i) => i, +LL | | None => { +LL | | let a = 1 + 42; +... | +LL | | }, +LL | | }; + | |_____^ + | +help: replace with + | +LL | Some(1).unwrap_or_else(|| { +LL | let a = 1 + 42; +LL | let b = a + 42; +LL | b + 42 +LL | }); + | + +error: this pattern reimplements `Option::unwrap_or` + --> $DIR/manual_unwrap_or.rs:34:5 + | +LL | / match Some("Bob") { +LL | | Some(i) => i, +LL | | None => "Alice", +LL | | }; + | |_____^ help: replace with: `Some("Bob").unwrap_or("Alice")` + +error: aborting due to 5 previous errors + diff --git a/tests/ui/shadow.rs b/tests/ui/shadow.rs index e7441293d45..e366c75335c 100644 --- a/tests/ui/shadow.rs +++ b/tests/ui/shadow.rs @@ -8,7 +8,7 @@ #![allow( unused_parens, unused_variables, - clippy::less_concise_than_option_unwrap_or, + clippy::manual_unwrap_or, clippy::missing_docs_in_private_items, clippy::single_match )] -- cgit 1.4.1-3-g733a5 From fc846c37fcc720c4a5c2e2075102c1957433e703 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Mon, 12 Oct 2020 00:06:21 +0200 Subject: manual_unwrap_or / use consts::constant_simple helper --- clippy_lints/src/manual_unwrap_or.rs | 11 +++++++---- tests/ui/manual_unwrap_or.fixed | 2 +- tests/ui/manual_unwrap_or.stderr | 4 ++-- 3 files changed, 10 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index 9d8fc863424..ced941fac1a 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -1,3 +1,4 @@ +use crate::consts::constant_simple; use crate::utils; use if_chain::if_chain; use rustc_errors::Applicability; @@ -18,15 +19,17 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust - /// match int_option { + /// let foo: Option = None; + /// match foo { /// Some(v) => v, /// None => 1, - /// } + /// }; /// ``` /// /// Use instead: /// ```rust - /// int_option.unwrap_or(1) + /// let foo: Option = None; + /// foo.unwrap_or(1); /// ``` pub MANUAL_UNWRAP_OR, complexity, @@ -84,7 +87,7 @@ fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tc then { let reindented_none_body = utils::reindent_multiline(none_body_snippet.into(), true, Some(indent)); - let eager_eval = utils::eager_or_lazy::is_eagerness_candidate(cx, none_arm.body); + let eager_eval = constant_simple(cx, cx.typeck_results(), none_arm.body).is_some(); let method = if eager_eval { "unwrap_or" } else { diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index 99d30360db1..a9cc8678c9d 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -9,7 +9,7 @@ fn unwrap_or() { Some(1).unwrap_or(42); // richer none expr - Some(1).unwrap_or_else(|| 1 + 42); + Some(1).unwrap_or(1 + 42); // multiline case Some(1).unwrap_or_else(|| { diff --git a/tests/ui/manual_unwrap_or.stderr b/tests/ui/manual_unwrap_or.stderr index 03da118a0c4..8f6835ed78d 100644 --- a/tests/ui/manual_unwrap_or.stderr +++ b/tests/ui/manual_unwrap_or.stderr @@ -18,14 +18,14 @@ LL | | Some(i) => i, LL | | }; | |_____^ help: replace with: `Some(1).unwrap_or(42)` -error: this pattern reimplements `Option::unwrap_or_else` +error: this pattern reimplements `Option::unwrap_or` --> $DIR/manual_unwrap_or.rs:18:5 | LL | / match Some(1) { LL | | Some(i) => i, LL | | None => 1 + 42, LL | | }; - | |_____^ help: replace with: `Some(1).unwrap_or_else(|| 1 + 42)` + | |_____^ help: replace with: `Some(1).unwrap_or(1 + 42)` error: this pattern reimplements `Option::unwrap_or_else` --> $DIR/manual_unwrap_or.rs:24:5 -- cgit 1.4.1-3-g733a5 From 690a6a6c0eff1a3edeb5f4c2dcbf9994760c3184 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Wed, 14 Oct 2020 22:09:28 +0200 Subject: manual-unwrap-or / remove unwrap_or_else suggestion due to ownership issues --- clippy_lints/src/manual_unwrap_or.rs | 17 +++++------------ src/lintlist/mod.rs | 2 +- tests/ui/manual_unwrap_or.fixed | 31 +++++++++++++++++++++++++++---- tests/ui/manual_unwrap_or.rs | 31 +++++++++++++++++++++++++++---- tests/ui/manual_unwrap_or.stderr | 18 +++++++++--------- 5 files changed, 69 insertions(+), 30 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index 719a8b91f66..ddb8cc25077 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -33,7 +33,7 @@ declare_clippy_lint! { /// ``` pub MANUAL_UNWRAP_OR, complexity, - "finds patterns that can be encoded more concisely with `Option::unwrap_or(_else)`" + "finds patterns that can be encoded more concisely with `Option::unwrap_or`" } declare_lint_pass!(ManualUnwrapOr => [MANUAL_UNWRAP_OR]); @@ -83,26 +83,19 @@ fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tc if let Some(scrutinee_snippet) = utils::snippet_opt(cx, scrutinee.span); if let Some(none_body_snippet) = utils::snippet_opt(cx, none_arm.body.span); if let Some(indent) = utils::indent_of(cx, expr.span); + if constant_simple(cx, cx.typeck_results(), none_arm.body).is_some(); then { let reindented_none_body = utils::reindent_multiline(none_body_snippet.into(), true, Some(indent)); - let eager_eval = constant_simple(cx, cx.typeck_results(), none_arm.body).is_some(); - let method = if eager_eval { - "unwrap_or" - } else { - "unwrap_or_else" - }; utils::span_lint_and_sugg( cx, MANUAL_UNWRAP_OR, expr.span, - &format!("this pattern reimplements `Option::{}`", &method), + "this pattern reimplements `Option::unwrap_or`", "replace with", format!( - "{}.{}({}{})", + "{}.unwrap_or({})", scrutinee_snippet, - method, - if eager_eval { "" } else { "|| " }, - reindented_none_body + reindented_none_body, ), Applicability::MachineApplicable, ); diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index debd3c31d8b..6301d623a2b 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1183,7 +1183,7 @@ vec![ Lint { name: "manual_unwrap_or", group: "complexity", - desc: "finds patterns that can be encoded more concisely with `Option::unwrap_or(_else)`", + desc: "finds patterns that can be encoded more concisely with `Option::unwrap_or`", deprecation: None, module: "manual_unwrap_or", }, diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index a9cc8678c9d..a8736f1e6ef 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -12,10 +12,11 @@ fn unwrap_or() { Some(1).unwrap_or(1 + 42); // multiline case - Some(1).unwrap_or_else(|| { - let a = 1 + 42; - let b = a + 42; - b + 42 + #[rustfmt::skip] + Some(1).unwrap_or({ + 42 + 42 + + 42 + 42 + 42 + + 42 + 42 + 42 }); // string case @@ -40,6 +41,28 @@ fn unwrap_or() { None => break, }; } + + // cases where the none arm isn't a constant expression + // are not linted due to potential ownership issues + + // ownership issue example, don't lint + struct NonCopyable; + let mut option: Option = None; + match option { + Some(x) => x, + None => { + option = Some(NonCopyable); + // some more code ... + option.unwrap() + }, + }; + + // ownership issue example, don't lint + let option: Option<&str> = None; + match option { + Some(s) => s, + None => &format!("{} {}!", "hello", "world"), + }; } fn main() {} diff --git a/tests/ui/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs index 5d03d9db163..bede8cffc32 100644 --- a/tests/ui/manual_unwrap_or.rs +++ b/tests/ui/manual_unwrap_or.rs @@ -21,13 +21,14 @@ fn unwrap_or() { }; // multiline case + #[rustfmt::skip] match Some(1) { Some(i) => i, None => { - let a = 1 + 42; - let b = a + 42; - b + 42 - }, + 42 + 42 + + 42 + 42 + 42 + + 42 + 42 + 42 + } }; // string case @@ -55,6 +56,28 @@ fn unwrap_or() { None => break, }; } + + // cases where the none arm isn't a constant expression + // are not linted due to potential ownership issues + + // ownership issue example, don't lint + struct NonCopyable; + let mut option: Option = None; + match option { + Some(x) => x, + None => { + option = Some(NonCopyable); + // some more code ... + option.unwrap() + }, + }; + + // ownership issue example, don't lint + let option: Option<&str> = None; + match option { + Some(s) => s, + None => &format!("{} {}!", "hello", "world"), + }; } fn main() {} diff --git a/tests/ui/manual_unwrap_or.stderr b/tests/ui/manual_unwrap_or.stderr index 8f6835ed78d..674f2952635 100644 --- a/tests/ui/manual_unwrap_or.stderr +++ b/tests/ui/manual_unwrap_or.stderr @@ -27,29 +27,29 @@ LL | | None => 1 + 42, LL | | }; | |_____^ help: replace with: `Some(1).unwrap_or(1 + 42)` -error: this pattern reimplements `Option::unwrap_or_else` - --> $DIR/manual_unwrap_or.rs:24:5 +error: this pattern reimplements `Option::unwrap_or` + --> $DIR/manual_unwrap_or.rs:25:5 | LL | / match Some(1) { LL | | Some(i) => i, LL | | None => { -LL | | let a = 1 + 42; +LL | | 42 + 42 ... | -LL | | }, +LL | | } LL | | }; | |_____^ | help: replace with | -LL | Some(1).unwrap_or_else(|| { -LL | let a = 1 + 42; -LL | let b = a + 42; -LL | b + 42 +LL | Some(1).unwrap_or({ +LL | 42 + 42 +LL | + 42 + 42 + 42 +LL | + 42 + 42 + 42 LL | }); | error: this pattern reimplements `Option::unwrap_or` - --> $DIR/manual_unwrap_or.rs:34:5 + --> $DIR/manual_unwrap_or.rs:35:5 | LL | / match Some("Bob") { LL | | Some(i) => i, -- cgit 1.4.1-3-g733a5 From 32e2021b75f5bb5c83bf753de76ec9ed499d12cc Mon Sep 17 00:00:00 2001 From: Chris Ayoup Date: Wed, 14 Oct 2020 23:49:48 -0400 Subject: Lint items after statements in macro expansions The items_after_statements lint was skipping all expansions. Instead we should still lint local macros. Fixes #578 --- clippy_lints/src/items_after_statements.rs | 7 ++++--- tests/ui/item_after_statement.rs | 5 ++++- tests/ui/item_after_statement.stderr | 15 ++++++++++++++- 3 files changed, 22 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/items_after_statements.rs b/clippy_lints/src/items_after_statements.rs index c8576bcfcb4..8998fae09de 100644 --- a/clippy_lints/src/items_after_statements.rs +++ b/clippy_lints/src/items_after_statements.rs @@ -2,7 +2,8 @@ use crate::utils::span_lint; use rustc_ast::ast::{Block, ItemKind, StmtKind}; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -53,7 +54,7 @@ declare_lint_pass!(ItemsAfterStatements => [ITEMS_AFTER_STATEMENTS]); impl EarlyLintPass for ItemsAfterStatements { fn check_block(&mut self, cx: &EarlyContext<'_>, item: &Block) { - if item.span.from_expansion() { + if in_external_macro(cx.sess(), item.span) { return; } @@ -67,7 +68,7 @@ impl EarlyLintPass for ItemsAfterStatements { // lint on all further items for stmt in stmts { if let StmtKind::Item(ref it) = *stmt { - if it.span.from_expansion() { + if in_external_macro(cx.sess(), it.span) { return; } if let ItemKind::MacroDef(..) = it.kind { diff --git a/tests/ui/item_after_statement.rs b/tests/ui/item_after_statement.rs index c17a7cbc8d9..377e58e4417 100644 --- a/tests/ui/item_after_statement.rs +++ b/tests/ui/item_after_statement.rs @@ -28,7 +28,10 @@ fn mac() { // do not lint this, because it needs to be after `a` macro_rules! b { () => {{ - a = 6 + a = 6; + fn say_something() { + println!("something"); + } }}; } b!(); diff --git a/tests/ui/item_after_statement.stderr b/tests/ui/item_after_statement.stderr index f8f010b5e5c..68a3c81b6a8 100644 --- a/tests/ui/item_after_statement.stderr +++ b/tests/ui/item_after_statement.stderr @@ -16,5 +16,18 @@ LL | | println!("foo"); LL | | } | |_____^ -error: aborting due to 2 previous errors +error: adding items after statements is confusing, since items exist from the start of the scope + --> $DIR/item_after_statement.rs:32:13 + | +LL | / fn say_something() { +LL | | println!("something"); +LL | | } + | |_____________^ +... +LL | b!(); + | ----- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 3 previous errors -- cgit 1.4.1-3-g733a5 From bb0ce32423aefcb8b9eb587881973f56a6a6b0ee Mon Sep 17 00:00:00 2001 From: Geoffrey Copin Date: Fri, 16 Oct 2020 00:22:35 +0200 Subject: Lint unnecessary int-to-int and float-to-float casts --- clippy_lints/src/types.rs | 43 +++++++++++++++++++------ tests/ui/eq_op.rs | 1 + tests/ui/eq_op.stderr | 54 ++++++++++++++++---------------- tests/ui/unnecessary_cast_fixable.fixed | 11 +++++-- tests/ui/unnecessary_cast_fixable.rs | 11 +++++-- tests/ui/unnecessary_cast_fixable.stderr | 32 ++++++++++++++++++- 6 files changed, 111 insertions(+), 41 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 9a948af8bfc..716d027e434 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -3,6 +3,7 @@ use std::borrow::Cow; use std::cmp::Ordering; use std::collections::BTreeMap; +use std::fmt::Display; use if_chain::if_chain; use rustc_ast::{FloatTy, IntTy, LitFloatType, LitIntType, LitKind, UintTy}; @@ -1608,18 +1609,23 @@ impl<'tcx> LateLintPass<'tcx> for Casts { let to_nbits = fp_ty_mantissa_nbits(cast_to); if from_nbits != 0 && to_nbits != 0 && from_nbits <= to_nbits && num_lit.is_decimal(); then { - span_lint_and_sugg( - cx, - UNNECESSARY_CAST, - expr.span, - &format!("casting integer literal to `{}` is unnecessary", cast_to), - "try", - format!("{}_{}", n, cast_to), - Applicability::MachineApplicable, - ); + show_unnecessary_cast(cx, expr, n , cast_from, cast_to); return; } } + + match lit.node { + LitKind::Int(num, LitIntType::Unsuffixed) if cast_to.is_integral() => { + show_unnecessary_cast(cx, expr, num, cast_from, cast_to); + return; + }, + LitKind::Float(num, LitFloatType::Unsuffixed) if cast_to.is_floating_point() => { + show_unnecessary_cast(cx, expr, num, cast_from, cast_to); + return; + }, + _ => (), + }; + match lit.node { LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => {}, _ => { @@ -1646,6 +1652,25 @@ impl<'tcx> LateLintPass<'tcx> for Casts { } } +fn show_unnecessary_cast( + cx: &LateContext<'_>, + expr: &Expr<'_>, + num: Num, + cast_from: Ty<'_>, + cast_to: Ty<'_>, +) { + let literal_kind_name = if cast_from.is_integral() { "integer" } else { "float" }; + span_lint_and_sugg( + cx, + UNNECESSARY_CAST, + expr.span, + &format!("casting {} literal to `{}` is unnecessary", literal_kind_name, cast_to), + "try", + format!("{}_{}", num, cast_to), + Applicability::MachineApplicable, + ); +} + fn lint_numeric_casts<'tcx>( cx: &LateContext<'tcx>, expr: &Expr<'tcx>, diff --git a/tests/ui/eq_op.rs b/tests/ui/eq_op.rs index 272b0900a31..4e09d19ea21 100644 --- a/tests/ui/eq_op.rs +++ b/tests/ui/eq_op.rs @@ -6,6 +6,7 @@ #[allow(clippy::no_effect, unused_variables, clippy::unnecessary_operation, clippy::short_circuit_statement)] #[allow(clippy::nonminimal_bool)] #[allow(unused)] +#[allow(clippy::unnecessary_cast)] fn main() { // simple values and comparisons 1 == 1; diff --git a/tests/ui/eq_op.stderr b/tests/ui/eq_op.stderr index 5b80e6078ee..ad81b35a766 100644 --- a/tests/ui/eq_op.stderr +++ b/tests/ui/eq_op.stderr @@ -1,5 +1,5 @@ error: equal expressions as operands to `==` - --> $DIR/eq_op.rs:11:5 + --> $DIR/eq_op.rs:12:5 | LL | 1 == 1; | ^^^^^^ @@ -7,157 +7,157 @@ LL | 1 == 1; = note: `-D clippy::eq-op` implied by `-D warnings` error: equal expressions as operands to `==` - --> $DIR/eq_op.rs:12:5 + --> $DIR/eq_op.rs:13:5 | LL | "no" == "no"; | ^^^^^^^^^^^^ error: equal expressions as operands to `!=` - --> $DIR/eq_op.rs:14:5 + --> $DIR/eq_op.rs:15:5 | LL | false != false; | ^^^^^^^^^^^^^^ error: equal expressions as operands to `<` - --> $DIR/eq_op.rs:15:5 + --> $DIR/eq_op.rs:16:5 | LL | 1.5 < 1.5; | ^^^^^^^^^ error: equal expressions as operands to `>=` - --> $DIR/eq_op.rs:16:5 + --> $DIR/eq_op.rs:17:5 | LL | 1u64 >= 1u64; | ^^^^^^^^^^^^ error: equal expressions as operands to `&` - --> $DIR/eq_op.rs:19:5 + --> $DIR/eq_op.rs:20:5 | LL | (1 as u64) & (1 as u64); | ^^^^^^^^^^^^^^^^^^^^^^^ error: equal expressions as operands to `^` - --> $DIR/eq_op.rs:20:5 + --> $DIR/eq_op.rs:21:5 | LL | 1 ^ ((((((1)))))); | ^^^^^^^^^^^^^^^^^ error: equal expressions as operands to `<` - --> $DIR/eq_op.rs:23:5 + --> $DIR/eq_op.rs:24:5 | LL | (-(2) < -(2)); | ^^^^^^^^^^^^^ error: equal expressions as operands to `==` - --> $DIR/eq_op.rs:24:5 + --> $DIR/eq_op.rs:25:5 | LL | ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: equal expressions as operands to `&` - --> $DIR/eq_op.rs:24:6 + --> $DIR/eq_op.rs:25:6 | LL | ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1)); | ^^^^^^^^^^^^^^^^^ error: equal expressions as operands to `&` - --> $DIR/eq_op.rs:24:27 + --> $DIR/eq_op.rs:25:27 | LL | ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1)); | ^^^^^^^^^^^^^^^^^ error: equal expressions as operands to `==` - --> $DIR/eq_op.rs:25:5 + --> $DIR/eq_op.rs:26:5 | LL | (1 * 2) + (3 * 4) == 1 * 2 + 3 * 4; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: equal expressions as operands to `!=` - --> $DIR/eq_op.rs:28:5 + --> $DIR/eq_op.rs:29:5 | LL | ([1] != [1]); | ^^^^^^^^^^^^ error: equal expressions as operands to `!=` - --> $DIR/eq_op.rs:29:5 + --> $DIR/eq_op.rs:30:5 | LL | ((1, 2) != (1, 2)); | ^^^^^^^^^^^^^^^^^^ error: equal expressions as operands to `==` - --> $DIR/eq_op.rs:33:5 + --> $DIR/eq_op.rs:34:5 | LL | 1 + 1 == 2; | ^^^^^^^^^^ error: equal expressions as operands to `==` - --> $DIR/eq_op.rs:34:5 + --> $DIR/eq_op.rs:35:5 | LL | 1 - 1 == 0; | ^^^^^^^^^^ error: equal expressions as operands to `-` - --> $DIR/eq_op.rs:34:5 + --> $DIR/eq_op.rs:35:5 | LL | 1 - 1 == 0; | ^^^^^ error: equal expressions as operands to `-` - --> $DIR/eq_op.rs:36:5 + --> $DIR/eq_op.rs:37:5 | LL | 1 - 1; | ^^^^^ error: equal expressions as operands to `/` - --> $DIR/eq_op.rs:37:5 + --> $DIR/eq_op.rs:38:5 | LL | 1 / 1; | ^^^^^ error: equal expressions as operands to `&&` - --> $DIR/eq_op.rs:38:5 + --> $DIR/eq_op.rs:39:5 | LL | true && true; | ^^^^^^^^^^^^ error: equal expressions as operands to `||` - --> $DIR/eq_op.rs:40:5 + --> $DIR/eq_op.rs:41:5 | LL | true || true; | ^^^^^^^^^^^^ error: equal expressions as operands to `&&` - --> $DIR/eq_op.rs:46:5 + --> $DIR/eq_op.rs:47:5 | LL | a == b && b == a; | ^^^^^^^^^^^^^^^^ error: equal expressions as operands to `&&` - --> $DIR/eq_op.rs:47:5 + --> $DIR/eq_op.rs:48:5 | LL | a != b && b != a; | ^^^^^^^^^^^^^^^^ error: equal expressions as operands to `&&` - --> $DIR/eq_op.rs:48:5 + --> $DIR/eq_op.rs:49:5 | LL | a < b && b > a; | ^^^^^^^^^^^^^^ error: equal expressions as operands to `&&` - --> $DIR/eq_op.rs:49:5 + --> $DIR/eq_op.rs:50:5 | LL | a <= b && b >= a; | ^^^^^^^^^^^^^^^^ error: equal expressions as operands to `==` - --> $DIR/eq_op.rs:52:5 + --> $DIR/eq_op.rs:53:5 | LL | a == a; | ^^^^^^ error: equal expressions as operands to `/` - --> $DIR/eq_op.rs:62:20 + --> $DIR/eq_op.rs:63:20 | LL | const D: u32 = A / A; | ^^^^^ diff --git a/tests/ui/unnecessary_cast_fixable.fixed b/tests/ui/unnecessary_cast_fixable.fixed index fb89a9fce3d..ba52fc2703f 100644 --- a/tests/ui/unnecessary_cast_fixable.fixed +++ b/tests/ui/unnecessary_cast_fixable.fixed @@ -12,12 +12,19 @@ fn main() { #[rustfmt::skip] let v = vec!(1); &v as &[i32]; - 1.0 as f64; - 1 as u64; 0x10 as f32; 0o10 as f32; 0b10 as f32; 0x11 as f64; 0o11 as f64; 0b11 as f64; + + 1_u32; + 16_i32; + 2_usize; + + 1.0_f64; + 0.5_f32; + + 1.0 as u16; } diff --git a/tests/ui/unnecessary_cast_fixable.rs b/tests/ui/unnecessary_cast_fixable.rs index 4a0c8620dc1..0d2115548fd 100644 --- a/tests/ui/unnecessary_cast_fixable.rs +++ b/tests/ui/unnecessary_cast_fixable.rs @@ -12,12 +12,19 @@ fn main() { #[rustfmt::skip] let v = vec!(1); &v as &[i32]; - 1.0 as f64; - 1 as u64; 0x10 as f32; 0o10 as f32; 0b10 as f32; 0x11 as f64; 0o11 as f64; 0b11 as f64; + + 1 as u32; + 0x10 as i32; + 0b10 as usize; + + 1.0 as f64; + 0.5 as f32; + + 1.0 as u16; } diff --git a/tests/ui/unnecessary_cast_fixable.stderr b/tests/ui/unnecessary_cast_fixable.stderr index 8ff1e5dea60..474e62c30d5 100644 --- a/tests/ui/unnecessary_cast_fixable.stderr +++ b/tests/ui/unnecessary_cast_fixable.stderr @@ -18,5 +18,35 @@ error: casting integer literal to `f64` is unnecessary LL | 100_i32 as f64; | ^^^^^^^^^^^^^^ help: try: `100_f64` -error: aborting due to 3 previous errors +error: casting integer literal to `u32` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:22:5 + | +LL | 1 as u32; + | ^^^^^^^^ help: try: `1_u32` + +error: casting integer literal to `i32` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:23:5 + | +LL | 0x10 as i32; + | ^^^^^^^^^^^ help: try: `16_i32` + +error: casting integer literal to `usize` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:24:5 + | +LL | 0b10 as usize; + | ^^^^^^^^^^^^^ help: try: `2_usize` + +error: casting float literal to `f64` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:26:5 + | +LL | 1.0 as f64; + | ^^^^^^^^^^ help: try: `1.0_f64` + +error: casting float literal to `f32` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:27:5 + | +LL | 0.5 as f32; + | ^^^^^^^^^^ help: try: `0.5_f32` + +error: aborting due to 8 previous errors -- cgit 1.4.1-3-g733a5 From 915ce3608724e6c900d1b5eb4412cac2fcace33a Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Sun, 18 Oct 2020 01:11:59 +0200 Subject: manual_unwrap_or / support Result::unwrap_or --- clippy_lints/src/manual_unwrap_or.rs | 83 ++++++++++++++++++++++++------------ src/lintlist/mod.rs | 2 +- tests/ui/manual_unwrap_or.fixed | 44 ++++++++++++++++++- tests/ui/manual_unwrap_or.rs | 59 ++++++++++++++++++++++++- tests/ui/manual_unwrap_or.stderr | 59 ++++++++++++++++++++++++- 5 files changed, 216 insertions(+), 31 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index ddb8cc25077..f3f1e31abde 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -2,7 +2,7 @@ use crate::consts::constant_simple; use crate::utils; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{def, Arm, Expr, ExprKind, PatKind, QPath}; +use rustc_hir::{def, Arm, Expr, ExprKind, Pat, PatKind, QPath}; use rustc_lint::LintContext; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; @@ -10,7 +10,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// **What it does:** - /// Finds patterns that reimplement `Option::unwrap_or`. + /// Finds patterns that reimplement `Option::unwrap_or` or `Result::unwrap_or`. /// /// **Why is this bad?** /// Concise code helps focusing on behavior instead of boilerplate. @@ -33,7 +33,7 @@ declare_clippy_lint! { /// ``` pub MANUAL_UNWRAP_OR, complexity, - "finds patterns that can be encoded more concisely with `Option::unwrap_or`" + "finds patterns that can be encoded more concisely with `Option::unwrap_or` or `Result::unwrap_or`" } declare_lint_pass!(ManualUnwrapOr => [MANUAL_UNWRAP_OR]); @@ -43,32 +43,50 @@ impl LateLintPass<'_> for ManualUnwrapOr { if in_external_macro(cx.sess(), expr.span) { return; } - lint_option_unwrap_or_case(cx, expr); + lint_manual_unwrap_or(cx, expr); } } -fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - fn applicable_none_arm<'a>(arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> { +#[derive(Copy, Clone)] +enum Case { + Option, + Result, +} + +impl Case { + fn unwrap_fn_path(&self) -> &str { + match self { + Case::Option => "Option::unwrap_or", + Case::Result => "Result::unwrap_or", + } + } +} + +fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + fn applicable_or_arm<'a>(arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> { if_chain! { if arms.len() == 2; if arms.iter().all(|arm| arm.guard.is_none()); - if let Some((idx, none_arm)) = arms.iter().enumerate().find(|(_, arm)| - if let PatKind::Path(ref qpath) = arm.pat.kind { - utils::match_qpath(qpath, &utils::paths::OPTION_NONE) - } else { - false + if let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)| + match arm.pat.kind { + PatKind::Path(ref some_qpath) => + utils::match_qpath(some_qpath, &utils::paths::OPTION_NONE), + PatKind::TupleStruct(ref err_qpath, &[Pat { kind: PatKind::Wild, .. }], _) => + utils::match_qpath(err_qpath, &utils::paths::RESULT_ERR), + _ => false, } ); - let some_arm = &arms[1 - idx]; - if let PatKind::TupleStruct(ref some_qpath, &[some_binding], _) = some_arm.pat.kind; - if utils::match_qpath(some_qpath, &utils::paths::OPTION_SOME); - if let PatKind::Binding(_, binding_hir_id, ..) = some_binding.kind; - if let ExprKind::Path(QPath::Resolved(_, body_path)) = some_arm.body.kind; + let unwrap_arm = &arms[1 - idx]; + if let PatKind::TupleStruct(ref unwrap_qpath, &[unwrap_pat], _) = unwrap_arm.pat.kind; + if utils::match_qpath(unwrap_qpath, &utils::paths::OPTION_SOME) + || utils::match_qpath(unwrap_qpath, &utils::paths::RESULT_OK); + if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind; + if let ExprKind::Path(QPath::Resolved(_, body_path)) = unwrap_arm.body.kind; if let def::Res::Local(body_path_hir_id) = body_path.res; if body_path_hir_id == binding_hir_id; - if !utils::usage::contains_return_break_continue_macro(none_arm.body); + if !utils::usage::contains_return_break_continue_macro(or_arm.body); then { - Some(none_arm) + Some(or_arm) } else { None } @@ -78,24 +96,35 @@ fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tc if_chain! { if let ExprKind::Match(scrutinee, match_arms, _) = expr.kind; let ty = cx.typeck_results().expr_ty(scrutinee); - if utils::is_type_diagnostic_item(cx, ty, sym!(option_type)); - if let Some(none_arm) = applicable_none_arm(match_arms); + if let Some(case) = if utils::is_type_diagnostic_item(cx, ty, sym!(option_type)) { + Some(Case::Option) + } else if utils::is_type_diagnostic_item(cx, ty, sym!(result_type)) { + Some(Case::Result) + } else { + None + }; + if let Some(or_arm) = applicable_or_arm(match_arms); if let Some(scrutinee_snippet) = utils::snippet_opt(cx, scrutinee.span); - if let Some(none_body_snippet) = utils::snippet_opt(cx, none_arm.body.span); + if let Some(or_body_snippet) = utils::snippet_opt(cx, or_arm.body.span); if let Some(indent) = utils::indent_of(cx, expr.span); - if constant_simple(cx, cx.typeck_results(), none_arm.body).is_some(); + if constant_simple(cx, cx.typeck_results(), or_arm.body).is_some(); then { - let reindented_none_body = - utils::reindent_multiline(none_body_snippet.into(), true, Some(indent)); + let reindented_or_body = + utils::reindent_multiline(or_body_snippet.into(), true, Some(indent)); + let wrap_in_parens = !matches!(scrutinee, Expr { kind: ExprKind::Call(..), .. }); + let l_paren = if wrap_in_parens { "(" } else { "" }; + let r_paren = if wrap_in_parens { ")" } else { "" }; utils::span_lint_and_sugg( cx, MANUAL_UNWRAP_OR, expr.span, - "this pattern reimplements `Option::unwrap_or`", + &format!("this pattern reimplements `{}`", case.unwrap_fn_path()), "replace with", format!( - "{}.unwrap_or({})", + "{}{}{}.unwrap_or({})", + l_paren, scrutinee_snippet, - reindented_none_body, + r_paren, + reindented_or_body, ), Applicability::MachineApplicable, ); diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 6301d623a2b..b930d9aedcf 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1183,7 +1183,7 @@ vec![ Lint { name: "manual_unwrap_or", group: "complexity", - desc: "finds patterns that can be encoded more concisely with `Option::unwrap_or`", + desc: "finds patterns that can be encoded more concisely with `Option::unwrap_or` or `Result::unwrap_or`", deprecation: None, module: "manual_unwrap_or", }, diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index a8736f1e6ef..ceb8985d3d5 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -1,7 +1,7 @@ // run-rustfix #![allow(dead_code)] -fn unwrap_or() { +fn option_unwrap_or() { // int case Some(1).unwrap_or(42); @@ -65,4 +65,46 @@ fn unwrap_or() { }; } +fn result_unwrap_or() { + // int case + (Ok(1) as Result).unwrap_or(42); + + // int case reversed + (Ok(1) as Result).unwrap_or(42); + + // richer none expr + (Ok(1) as Result).unwrap_or(1 + 42); + + // multiline case + #[rustfmt::skip] + (Ok(1) as Result).unwrap_or({ + 42 + 42 + + 42 + 42 + 42 + + 42 + 42 + 42 + }); + + // string case + (Ok("Bob") as Result<&str, &str>).unwrap_or("Alice"); + + // don't lint + match Ok(1) as Result { + Ok(i) => i + 2, + Err(_) => 42, + }; + match Ok(1) as Result { + Ok(i) => i, + Err(_) => return, + }; + for j in 0..4 { + match Ok(j) as Result { + Ok(i) => i, + Err(_) => continue, + }; + match Ok(j) as Result { + Ok(i) => i, + Err(_) => break, + }; + } +} + fn main() {} diff --git a/tests/ui/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs index bede8cffc32..beca1de0ed1 100644 --- a/tests/ui/manual_unwrap_or.rs +++ b/tests/ui/manual_unwrap_or.rs @@ -1,7 +1,7 @@ // run-rustfix #![allow(dead_code)] -fn unwrap_or() { +fn option_unwrap_or() { // int case match Some(1) { Some(i) => i, @@ -80,4 +80,61 @@ fn unwrap_or() { }; } +fn result_unwrap_or() { + // int case + match Ok(1) as Result { + Ok(i) => i, + Err(_) => 42, + }; + + // int case reversed + match Ok(1) as Result { + Err(_) => 42, + Ok(i) => i, + }; + + // richer none expr + match Ok(1) as Result { + Ok(i) => i, + Err(_) => 1 + 42, + }; + + // multiline case + #[rustfmt::skip] + match Ok(1) as Result { + Ok(i) => i, + Err(_) => { + 42 + 42 + + 42 + 42 + 42 + + 42 + 42 + 42 + } + }; + + // string case + match Ok("Bob") as Result<&str, &str> { + Ok(i) => i, + Err(_) => "Alice", + }; + + // don't lint + match Ok(1) as Result { + Ok(i) => i + 2, + Err(_) => 42, + }; + match Ok(1) as Result { + Ok(i) => i, + Err(_) => return, + }; + for j in 0..4 { + match Ok(j) as Result { + Ok(i) => i, + Err(_) => continue, + }; + match Ok(j) as Result { + Ok(i) => i, + Err(_) => break, + }; + } +} + fn main() {} diff --git a/tests/ui/manual_unwrap_or.stderr b/tests/ui/manual_unwrap_or.stderr index 674f2952635..5d465666caf 100644 --- a/tests/ui/manual_unwrap_or.stderr +++ b/tests/ui/manual_unwrap_or.stderr @@ -57,5 +57,62 @@ LL | | None => "Alice", LL | | }; | |_____^ help: replace with: `Some("Bob").unwrap_or("Alice")` -error: aborting due to 5 previous errors +error: this pattern reimplements `Result::unwrap_or` + --> $DIR/manual_unwrap_or.rs:85:5 + | +LL | / match Ok(1) as Result { +LL | | Ok(i) => i, +LL | | Err(_) => 42, +LL | | }; + | |_____^ help: replace with: `(Ok(1) as Result).unwrap_or(42)` + +error: this pattern reimplements `Result::unwrap_or` + --> $DIR/manual_unwrap_or.rs:91:5 + | +LL | / match Ok(1) as Result { +LL | | Err(_) => 42, +LL | | Ok(i) => i, +LL | | }; + | |_____^ help: replace with: `(Ok(1) as Result).unwrap_or(42)` + +error: this pattern reimplements `Result::unwrap_or` + --> $DIR/manual_unwrap_or.rs:97:5 + | +LL | / match Ok(1) as Result { +LL | | Ok(i) => i, +LL | | Err(_) => 1 + 42, +LL | | }; + | |_____^ help: replace with: `(Ok(1) as Result).unwrap_or(1 + 42)` + +error: this pattern reimplements `Result::unwrap_or` + --> $DIR/manual_unwrap_or.rs:104:5 + | +LL | / match Ok(1) as Result { +LL | | Ok(i) => i, +LL | | Err(_) => { +LL | | 42 + 42 +... | +LL | | } +LL | | }; + | |_____^ + | +help: replace with + | +LL | (Ok(1) as Result).unwrap_or({ +LL | 42 + 42 +LL | + 42 + 42 + 42 +LL | + 42 + 42 + 42 +LL | }); + | + +error: this pattern reimplements `Result::unwrap_or` + --> $DIR/manual_unwrap_or.rs:114:5 + | +LL | / match Ok("Bob") as Result<&str, &str> { +LL | | Ok(i) => i, +LL | | Err(_) => "Alice", +LL | | }; + | |_____^ help: replace with: `(Ok("Bob") as Result<&str, &str>).unwrap_or("Alice")` + +error: aborting due to 10 previous errors -- cgit 1.4.1-3-g733a5 From ec23db9496807f6c962b74fe0d6bf15be6c6d35b Mon Sep 17 00:00:00 2001 From: Patrick José Pereira Date: Sat, 3 Oct 2020 15:46:28 -0300 Subject: Add linter for a single element for loop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Patrick José Pereira --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 3 ++ clippy_lints/src/loops.rs | 71 +++++++++++++++++++++++++++++++++++-- src/lintlist/mod.rs | 7 ++++ tests/ui/single_element_loop.fixed | 11 ++++++ tests/ui/single_element_loop.rs | 10 ++++++ tests/ui/single_element_loop.stderr | 19 ++++++++++ 7 files changed, 119 insertions(+), 3 deletions(-) create mode 100644 tests/ui/single_element_loop.fixed create mode 100644 tests/ui/single_element_loop.rs create mode 100644 tests/ui/single_element_loop.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index d82f970b8bf..1bf25bcd0f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1936,6 +1936,7 @@ Released 2018-09-13 [`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern [`single_char_push_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_push_str [`single_component_path_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports +[`single_element_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_element_loop [`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match [`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else [`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d4d2f92a6a6..84ea2d5ca78 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -633,6 +633,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &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, @@ -1363,6 +1364,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), @@ -1664,6 +1666,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&lifetimes::NEEDLESS_LIFETIMES), LintId::of(&loops::EXPLICIT_COUNTER_LOOP), LintId::of(&loops::MUT_RANGE_BOUND), + 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), diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 63d7e3176b1..e50aa9ac15a 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -4,9 +4,10 @@ use crate::utils::sugg::Sugg; use crate::utils::usage::{is_unused, mutated_variables}; use crate::utils::{ contains_name, get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, - is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, match_trait_method, - match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_with_applicability, snippet_with_macro_callsite, - span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, SpanlessEq, + indent_of, is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, + match_trait_method, match_type, match_var, multispan_sugg, qpath_res, single_segment_path, snippet, + snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, + span_lint_and_then, sugg, SpanlessEq, }; use if_chain::if_chain; use rustc_ast::ast; @@ -452,6 +453,31 @@ declare_clippy_lint! { "the same item is pushed inside of a for loop" } +declare_clippy_lint! { + /// **What it does:** Checks whether a for loop has a single element. + /// + /// **Why is this bad?** There is no reason to have a loop of a + /// single element. + /// **Known problems:** None + /// + /// **Example:** + /// ```rust + /// let item1 = 2; + /// for item in &[item1] { + /// println!("{}", item); + /// } + /// ``` + /// could be written as + /// ```rust + /// let item1 = 2; + /// let item = &item1; + /// println!("{}", item); + /// ``` + pub SINGLE_ELEMENT_LOOP, + complexity, + "there is no reason to have a single element loop" +} + declare_lint_pass!(Loops => [ MANUAL_MEMCPY, NEEDLESS_RANGE_LOOP, @@ -469,6 +495,7 @@ declare_lint_pass!(Loops => [ MUT_RANGE_BOUND, WHILE_IMMUTABLE_CONDITION, SAME_ITEM_PUSH, + SINGLE_ELEMENT_LOOP, ]); impl<'tcx> LateLintPass<'tcx> for Loops { @@ -777,6 +804,7 @@ fn check_for_loop<'tcx>( check_for_loop_arg(cx, pat, arg, expr); check_for_loop_over_map_kv(cx, pat, arg, body, expr); check_for_mut_range_bound(cx, arg, body); + check_for_single_element_loop(cx, pat, arg, body, expr); detect_same_item_push(cx, pat, arg, body, expr); } @@ -1866,6 +1894,43 @@ fn check_for_loop_over_map_kv<'tcx>( } } +fn check_for_single_element_loop<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + arg: &'tcx Expr<'_>, + body: &'tcx Expr<'_>, + expr: &'tcx Expr<'_>, +) { + if_chain! { + if let ExprKind::AddrOf(BorrowKind::Ref, _, ref arg_expr) = arg.kind; + if let PatKind::Binding(.., target, _) = pat.kind; + if let ExprKind::Array(ref arg_expr_list) = arg_expr.kind; + if let [arg_expression] = arg_expr_list; + if let ExprKind::Path(ref list_item) = arg_expression.kind; + if let Some(list_item_name) = single_segment_path(list_item).map(|ps| ps.ident.name); + if let ExprKind::Block(ref block, _) = body.kind; + if !block.stmts.is_empty(); + + then { + let for_span = get_span_of_entire_for_loop(expr); + let mut block_str = snippet(cx, block.span, "..").into_owned(); + block_str.remove(0); + block_str.pop(); + + + span_lint_and_sugg( + cx, + SINGLE_ELEMENT_LOOP, + for_span, + "for loop over a single element", + "try", + format!("{{\n{}let {} = &{};{}}}", " ".repeat(indent_of(cx, block.stmts[0].span).unwrap_or(0)), target.name, list_item_name, block_str), + Applicability::MachineApplicable + ) + } + } +} + struct MutatePairDelegate<'a, 'tcx> { cx: &'a LateContext<'tcx>, hir_id_low: Option, diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 6301d623a2b..70369dc582a 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2125,6 +2125,13 @@ vec![ deprecation: None, module: "single_component_path_imports", }, + Lint { + name: "single_element_loop", + group: "complexity", + desc: "there is no reason to have a single element loop", + deprecation: None, + module: "loops", + }, Lint { name: "single_match", group: "style", diff --git a/tests/ui/single_element_loop.fixed b/tests/ui/single_element_loop.fixed new file mode 100644 index 00000000000..8ca068293a6 --- /dev/null +++ b/tests/ui/single_element_loop.fixed @@ -0,0 +1,11 @@ +// run-rustfix +// Tests from for_loop.rs that don't have suggestions + +#[warn(clippy::single_element_loop)] +fn main() { + let item1 = 2; + { + let item = &item1; + println!("{}", item); + } +} diff --git a/tests/ui/single_element_loop.rs b/tests/ui/single_element_loop.rs new file mode 100644 index 00000000000..57e9336a31f --- /dev/null +++ b/tests/ui/single_element_loop.rs @@ -0,0 +1,10 @@ +// run-rustfix +// Tests from for_loop.rs that don't have suggestions + +#[warn(clippy::single_element_loop)] +fn main() { + let item1 = 2; + for item in &[item1] { + println!("{}", item); + } +} diff --git a/tests/ui/single_element_loop.stderr b/tests/ui/single_element_loop.stderr new file mode 100644 index 00000000000..90be1dc3283 --- /dev/null +++ b/tests/ui/single_element_loop.stderr @@ -0,0 +1,19 @@ +error: for loop over a single element + --> $DIR/single_element_loop.rs:7:5 + | +LL | / for item in &[item1] { +LL | | println!("{}", item); +LL | | } + | |_____^ + | + = note: `-D clippy::single-element-loop` implied by `-D warnings` +help: try + | +LL | { +LL | let item = &item1; +LL | println!("{}", item); +LL | } + | + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From ba1ca19c3bec20401a4cb13e5186c4c5952e94cc Mon Sep 17 00:00:00 2001 From: Patrick José Pereira Date: Sat, 3 Oct 2020 17:01:34 -0300 Subject: tests: if_same_then_else2: Ignore single_element_loop lint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Patrick José Pereira --- tests/ui/if_same_then_else2.rs | 3 ++- tests/ui/if_same_then_else2.stderr | 24 ++++++++++++------------ 2 files changed, 14 insertions(+), 13 deletions(-) (limited to 'tests') diff --git a/tests/ui/if_same_then_else2.rs b/tests/ui/if_same_then_else2.rs index 3cc21809264..8d54f75b5d1 100644 --- a/tests/ui/if_same_then_else2.rs +++ b/tests/ui/if_same_then_else2.rs @@ -3,7 +3,8 @@ clippy::blacklisted_name, clippy::collapsible_if, clippy::ifs_same_cond, - clippy::needless_return + clippy::needless_return, + clippy::single_element_loop )] fn if_same_then_else2() -> Result<&'static str, ()> { diff --git a/tests/ui/if_same_then_else2.stderr b/tests/ui/if_same_then_else2.stderr index f5d087fe128..da2be6c8aa5 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:19:12 + --> $DIR/if_same_then_else2.rs:20:12 | LL | } else { | ____________^ @@ -13,7 +13,7 @@ LL | | } | = note: `-D clippy::if-same-then-else` implied by `-D warnings` note: same as this - --> $DIR/if_same_then_else2.rs:10:13 + --> $DIR/if_same_then_else2.rs:11:13 | LL | if true { | _____________^ @@ -26,7 +26,7 @@ LL | | } else { | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:33:12 + --> $DIR/if_same_then_else2.rs:34:12 | LL | } else { | ____________^ @@ -36,7 +36,7 @@ LL | | } | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:31:13 + --> $DIR/if_same_then_else2.rs:32:13 | LL | if true { | _____________^ @@ -45,7 +45,7 @@ LL | | } else { | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:40:12 + --> $DIR/if_same_then_else2.rs:41:12 | LL | } else { | ____________^ @@ -55,7 +55,7 @@ LL | | } | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:38:13 + --> $DIR/if_same_then_else2.rs:39:13 | LL | if true { | _____________^ @@ -64,7 +64,7 @@ LL | | } else { | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:90:12 + --> $DIR/if_same_then_else2.rs:91:12 | LL | } else { | ____________^ @@ -74,7 +74,7 @@ LL | | }; | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:88:21 + --> $DIR/if_same_then_else2.rs:89:21 | LL | let _ = if true { | _____________________^ @@ -83,7 +83,7 @@ LL | | } else { | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:97:12 + --> $DIR/if_same_then_else2.rs:98:12 | LL | } else { | ____________^ @@ -93,7 +93,7 @@ LL | | } | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:95:13 + --> $DIR/if_same_then_else2.rs:96:13 | LL | if true { | _____________^ @@ -102,7 +102,7 @@ LL | | } else { | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:122:12 + --> $DIR/if_same_then_else2.rs:123:12 | LL | } else { | ____________^ @@ -112,7 +112,7 @@ LL | | } | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:119:20 + --> $DIR/if_same_then_else2.rs:120:20 | LL | } else if true { | ____________________^ -- cgit 1.4.1-3-g733a5 From 16b5f37b5a23f475d0d94efea764c57e4572f63f Mon Sep 17 00:00:00 2001 From: ThibsG Date: Mon, 19 Oct 2020 17:31:41 +0200 Subject: Split `eq_op` ui tests to avoid file limit error in CI --- tests/ui/eq_op.rs | 57 -------------------------- tests/ui/eq_op.stderr | 95 +------------------------------------------- tests/ui/eq_op_macros.rs | 56 ++++++++++++++++++++++++++ tests/ui/eq_op_macros.stderr | 95 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 152 insertions(+), 151 deletions(-) create mode 100644 tests/ui/eq_op_macros.rs create mode 100644 tests/ui/eq_op_macros.stderr (limited to 'tests') diff --git a/tests/ui/eq_op.rs b/tests/ui/eq_op.rs index 20613ac6afe..272b0900a31 100644 --- a/tests/ui/eq_op.rs +++ b/tests/ui/eq_op.rs @@ -60,8 +60,6 @@ fn main() { const B: u32 = 10; const C: u32 = A / B; // ok, different named constants const D: u32 = A / A; - - check_assert_identical_args(); } #[rustfmt::skip] @@ -87,58 +85,3 @@ fn check_ignore_macro() { // checks if the lint ignores macros with `!` operator !bool_macro!(1) && !bool_macro!(""); } - -macro_rules! assert_in_macro_def { - () => { - let a = 42; - assert_eq!(a, a); - assert_ne!(a, a); - debug_assert_eq!(a, a); - debug_assert_ne!(a, a); - }; -} - -// lint identical args in assert-like macro invocations (see #3574) -fn check_assert_identical_args() { - // lint also in macro definition - assert_in_macro_def!(); - - let a = 1; - let b = 2; - - // lint identical args in `assert_eq!` - assert_eq!(a, a); - assert_eq!(a + 1, a + 1); - // ok - assert_eq!(a, b); - assert_eq!(a, a + 1); - assert_eq!(a + 1, b + 1); - - // lint identical args in `assert_ne!` - assert_ne!(a, a); - assert_ne!(a + 1, a + 1); - // ok - assert_ne!(a, b); - assert_ne!(a, a + 1); - assert_ne!(a + 1, b + 1); - - // lint identical args in `debug_assert_eq!` - debug_assert_eq!(a, a); - debug_assert_eq!(a + 1, a + 1); - // ok - debug_assert_eq!(a, b); - debug_assert_eq!(a, a + 1); - debug_assert_eq!(a + 1, b + 1); - - // lint identical args in `debug_assert_ne!` - debug_assert_ne!(a, a); - debug_assert_ne!(a + 1, a + 1); - // ok - debug_assert_ne!(a, b); - debug_assert_ne!(a, a + 1); - debug_assert_ne!(a + 1, b + 1); - - let my_vec = vec![1; 5]; - let mut my_iter = my_vec.iter(); - assert_ne!(my_iter.next(), my_iter.next()); -} diff --git a/tests/ui/eq_op.stderr b/tests/ui/eq_op.stderr index 21a63aec7a1..5b80e6078ee 100644 --- a/tests/ui/eq_op.stderr +++ b/tests/ui/eq_op.stderr @@ -162,98 +162,5 @@ error: equal expressions as operands to `/` LL | const D: u32 = A / A; | ^^^^^ -error: identical args used in this `assert_eq!` macro call - --> $DIR/eq_op.rs:94:20 - | -LL | assert_eq!(a, a); - | ^^^^ -... -LL | assert_in_macro_def!(); - | ----------------------- in this macro invocation - | - = note: `#[deny(clippy::eq_op)]` on by default - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: identical args used in this `assert_ne!` macro call - --> $DIR/eq_op.rs:95:20 - | -LL | assert_ne!(a, a); - | ^^^^ -... -LL | assert_in_macro_def!(); - | ----------------------- in this macro invocation - | - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: identical args used in this `assert_eq!` macro call - --> $DIR/eq_op.rs:110:16 - | -LL | assert_eq!(a, a); - | ^^^^ - -error: identical args used in this `assert_eq!` macro call - --> $DIR/eq_op.rs:111:16 - | -LL | assert_eq!(a + 1, a + 1); - | ^^^^^^^^^^^^ - -error: identical args used in this `assert_ne!` macro call - --> $DIR/eq_op.rs:118:16 - | -LL | assert_ne!(a, a); - | ^^^^ - -error: identical args used in this `assert_ne!` macro call - --> $DIR/eq_op.rs:119:16 - | -LL | assert_ne!(a + 1, a + 1); - | ^^^^^^^^^^^^ - -error: identical args used in this `debug_assert_eq!` macro call - --> $DIR/eq_op.rs:96:26 - | -LL | debug_assert_eq!(a, a); - | ^^^^ -... -LL | assert_in_macro_def!(); - | ----------------------- in this macro invocation - | - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: identical args used in this `debug_assert_ne!` macro call - --> $DIR/eq_op.rs:97:26 - | -LL | debug_assert_ne!(a, a); - | ^^^^ -... -LL | assert_in_macro_def!(); - | ----------------------- in this macro invocation - | - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: identical args used in this `debug_assert_eq!` macro call - --> $DIR/eq_op.rs:126:22 - | -LL | debug_assert_eq!(a, a); - | ^^^^ - -error: identical args used in this `debug_assert_eq!` macro call - --> $DIR/eq_op.rs:127:22 - | -LL | debug_assert_eq!(a + 1, a + 1); - | ^^^^^^^^^^^^ - -error: identical args used in this `debug_assert_ne!` macro call - --> $DIR/eq_op.rs:134:22 - | -LL | debug_assert_ne!(a, a); - | ^^^^ - -error: identical args used in this `debug_assert_ne!` macro call - --> $DIR/eq_op.rs:135:22 - | -LL | debug_assert_ne!(a + 1, a + 1); - | ^^^^^^^^^^^^ - -error: aborting due to 39 previous errors +error: aborting due to 27 previous errors diff --git a/tests/ui/eq_op_macros.rs b/tests/ui/eq_op_macros.rs new file mode 100644 index 00000000000..6b5b31a1a2e --- /dev/null +++ b/tests/ui/eq_op_macros.rs @@ -0,0 +1,56 @@ +#![warn(clippy::eq_op)] + +// lint also in macro definition +macro_rules! assert_in_macro_def { + () => { + let a = 42; + assert_eq!(a, a); + assert_ne!(a, a); + debug_assert_eq!(a, a); + debug_assert_ne!(a, a); + }; +} + +// lint identical args in assert-like macro invocations (see #3574) +fn main() { + assert_in_macro_def!(); + + let a = 1; + let b = 2; + + // lint identical args in `assert_eq!` + assert_eq!(a, a); + assert_eq!(a + 1, a + 1); + // ok + assert_eq!(a, b); + assert_eq!(a, a + 1); + assert_eq!(a + 1, b + 1); + + // lint identical args in `assert_ne!` + assert_ne!(a, a); + assert_ne!(a + 1, a + 1); + // ok + assert_ne!(a, b); + assert_ne!(a, a + 1); + assert_ne!(a + 1, b + 1); + + // lint identical args in `debug_assert_eq!` + debug_assert_eq!(a, a); + debug_assert_eq!(a + 1, a + 1); + // ok + debug_assert_eq!(a, b); + debug_assert_eq!(a, a + 1); + debug_assert_eq!(a + 1, b + 1); + + // lint identical args in `debug_assert_ne!` + debug_assert_ne!(a, a); + debug_assert_ne!(a + 1, a + 1); + // ok + debug_assert_ne!(a, b); + debug_assert_ne!(a, a + 1); + debug_assert_ne!(a + 1, b + 1); + + let my_vec = vec![1; 5]; + let mut my_iter = my_vec.iter(); + assert_ne!(my_iter.next(), my_iter.next()); +} diff --git a/tests/ui/eq_op_macros.stderr b/tests/ui/eq_op_macros.stderr new file mode 100644 index 00000000000..fb9378108b9 --- /dev/null +++ b/tests/ui/eq_op_macros.stderr @@ -0,0 +1,95 @@ +error: identical args used in this `assert_eq!` macro call + --> $DIR/eq_op_macros.rs:7:20 + | +LL | assert_eq!(a, a); + | ^^^^ +... +LL | assert_in_macro_def!(); + | ----------------------- in this macro invocation + | + = note: `-D clippy::eq-op` implied by `-D warnings` + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: identical args used in this `assert_ne!` macro call + --> $DIR/eq_op_macros.rs:8:20 + | +LL | assert_ne!(a, a); + | ^^^^ +... +LL | assert_in_macro_def!(); + | ----------------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: identical args used in this `assert_eq!` macro call + --> $DIR/eq_op_macros.rs:22:16 + | +LL | assert_eq!(a, a); + | ^^^^ + +error: identical args used in this `assert_eq!` macro call + --> $DIR/eq_op_macros.rs:23:16 + | +LL | assert_eq!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: identical args used in this `assert_ne!` macro call + --> $DIR/eq_op_macros.rs:30:16 + | +LL | assert_ne!(a, a); + | ^^^^ + +error: identical args used in this `assert_ne!` macro call + --> $DIR/eq_op_macros.rs:31:16 + | +LL | assert_ne!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: identical args used in this `debug_assert_eq!` macro call + --> $DIR/eq_op_macros.rs:9:26 + | +LL | debug_assert_eq!(a, a); + | ^^^^ +... +LL | assert_in_macro_def!(); + | ----------------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: identical args used in this `debug_assert_ne!` macro call + --> $DIR/eq_op_macros.rs:10:26 + | +LL | debug_assert_ne!(a, a); + | ^^^^ +... +LL | assert_in_macro_def!(); + | ----------------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: identical args used in this `debug_assert_eq!` macro call + --> $DIR/eq_op_macros.rs:38:22 + | +LL | debug_assert_eq!(a, a); + | ^^^^ + +error: identical args used in this `debug_assert_eq!` macro call + --> $DIR/eq_op_macros.rs:39:22 + | +LL | debug_assert_eq!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: identical args used in this `debug_assert_ne!` macro call + --> $DIR/eq_op_macros.rs:46:22 + | +LL | debug_assert_ne!(a, a); + | ^^^^ + +error: identical args used in this `debug_assert_ne!` macro call + --> $DIR/eq_op_macros.rs:47:22 + | +LL | debug_assert_ne!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: aborting due to 12 previous errors + -- cgit 1.4.1-3-g733a5 From 65b52d84f83586752bff2834410e131290dc0155 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Tue, 20 Oct 2020 00:38:13 +0200 Subject: needless-lifetime / multiple where clause predicates regression --- clippy_lints/src/lifetimes.rs | 6 ++++-- tests/ui/needless_lifetimes.rs | 11 +++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index d7043e7bd8f..c8a5a9c9431 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -414,7 +414,7 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, where_clause: &'tcx WhereCl let mut visitor = RefVisitor::new(cx); // walk the type F, it may not contain LT refs walk_ty(&mut visitor, &pred.bounded_ty); - if !visitor.lts.is_empty() { + if !visitor.all_lts().is_empty() { return true; } // if the bounds define new lifetimes, they are fine to occur @@ -424,7 +424,9 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, where_clause: &'tcx WhereCl walk_param_bound(&mut visitor, bound); } // and check that all lifetimes are allowed - return visitor.all_lts().iter().any(|it| !allowed_lts.contains(it)); + if visitor.all_lts().iter().any(|it| !allowed_lts.contains(it)) { + return true; + } }, WherePredicate::EqPredicate(ref pred) => { let mut visitor = RefVisitor::new(cx); diff --git a/tests/ui/needless_lifetimes.rs b/tests/ui/needless_lifetimes.rs index d482d466e44..6001ef37eb7 100644 --- a/tests/ui/needless_lifetimes.rs +++ b/tests/ui/needless_lifetimes.rs @@ -357,4 +357,15 @@ mod nested_elision_sites { } } +mod issue6159 { + use std::ops::Deref; + pub fn apply_deref<'a, T, F, R>(x: &'a T, f: F) -> R + where + T: Deref, + F: FnOnce(&'a T::Target) -> R, + { + f(x.deref()) + } +} + fn main() {} -- cgit 1.4.1-3-g733a5 From 57bf80f77626b134faaf2cd95664403627fba0da Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Thu, 10 Sep 2020 15:53:36 -0400 Subject: Add lint for holding RefCell Ref across an await --- clippy_lints/src/await_holding_refcell_ref.rs | 83 +++++++++++++++++++++++++++ clippy_lints/src/lib.rs | 4 ++ clippy_lints/src/utils/paths.rs | 2 + tests/ui/await_holding_refcell_ref.rs | 71 +++++++++++++++++++++++ tests/ui/await_holding_refcell_ref.stderr | 77 +++++++++++++++++++++++++ 5 files changed, 237 insertions(+) create mode 100644 clippy_lints/src/await_holding_refcell_ref.rs create mode 100644 tests/ui/await_holding_refcell_ref.rs create mode 100644 tests/ui/await_holding_refcell_ref.stderr (limited to 'tests') diff --git a/clippy_lints/src/await_holding_refcell_ref.rs b/clippy_lints/src/await_holding_refcell_ref.rs new file mode 100644 index 00000000000..9a75911acbe --- /dev/null +++ b/clippy_lints/src/await_holding_refcell_ref.rs @@ -0,0 +1,83 @@ +use crate::utils::{match_def_path, paths, span_lint_and_note}; +use rustc_hir::def_id::DefId; +use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::GeneratorInteriorTypeCause; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for calls to await while holding a + /// `RefCell` `Ref` or `RefMut`. + /// + /// **Why is this bad?** `RefCell` refs only check for exclusive mutable access + /// at runtime. Holding onto a `RefCell` ref across an `await` suspension point + /// risks panics from a mutable ref shared while other refs are outstanding. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust,ignore + /// use std::cell::RefCell; + /// + /// async fn foo(x: &RefCell) { + /// let b = x.borrow_mut()(); + /// *ref += 1; + /// bar.await; + /// } + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// use std::cell::RefCell; + /// + /// async fn foo(x: &RefCell) { + /// { + /// let b = x.borrow_mut(); + /// *ref += 1; + /// } + /// bar.await; + /// } + /// ``` + pub AWAIT_HOLDING_REFCELL_REF, + pedantic, + "Inside an async function, holding a RefCell ref while calling await" +} + +declare_lint_pass!(AwaitHoldingRefCellRef => [AWAIT_HOLDING_REFCELL_REF]); + +impl LateLintPass<'_> for AwaitHoldingRefCellRef { + fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) { + use AsyncGeneratorKind::{Block, Closure, Fn}; + if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind { + let body_id = BodyId { + hir_id: body.value.hir_id, + }; + let def_id = cx.tcx.hir().body_owner_def_id(body_id); + let typeck_results = cx.tcx.typeck(def_id); + check_interior_types(cx, &typeck_results.generator_interior_types, body.value.span); + } + } +} + +fn check_interior_types(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { + for ty_cause in ty_causes { + if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() { + if is_refcell_ref(cx, adt.did) { + span_lint_and_note( + cx, + AWAIT_HOLDING_REFCELL_REF, + ty_cause.span, + "this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await.", + ty_cause.scope_span.or(Some(span)), + "these are all the await points this ref is held through", + ); + } + } + } +} + +fn is_refcell_ref(cx: &LateContext<'_>, def_id: DefId) -> bool { + match_def_path(cx, def_id, &paths::REFCELL_REF) || match_def_path(cx, def_id, &paths::REFCELL_REFMUT) +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d4d2f92a6a6..69719986104 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -161,6 +161,7 @@ mod async_yields_async; mod atomic_ordering; mod attrs; mod await_holding_lock; +mod await_holding_refcell_ref; mod bit_mask; mod blacklisted_name; mod blocks_in_if_conditions; @@ -510,6 +511,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &attrs::UNKNOWN_CLIPPY_LINTS, &attrs::USELESS_ATTRIBUTE, &await_holding_lock::AWAIT_HOLDING_LOCK, + &await_holding_refcell_ref::AWAIT_HOLDING_REFCELL_REF, &bit_mask::BAD_BIT_MASK, &bit_mask::INEFFECTIVE_BIT_MASK, &bit_mask::VERBOSE_BIT_MASK, @@ -906,6 +908,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: // end register lints, do not remove this comment, it’s used in `update_lints` store.register_late_pass(|| box await_holding_lock::AwaitHoldingLock); + store.register_late_pass(|| box await_holding_refcell_ref::AwaitHoldingRefCellRef); store.register_late_pass(|| box serde_api::SerdeAPI); store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default()); @@ -1189,6 +1192,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(&attrs::INLINE_ALWAYS), LintId::of(&await_holding_lock::AWAIT_HOLDING_LOCK), + LintId::of(&await_holding_refcell_ref::AWAIT_HOLDING_REFCELL_REF), LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&checked_conversions::CHECKED_CONVERSIONS), LintId::of(&copies::MATCH_SAME_ARMS), diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 5e769c690a6..cd9b92efe58 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -93,6 +93,8 @@ pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"]; pub const RC: [&str; 3] = ["alloc", "rc", "Rc"]; pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"]; pub const RECEIVER: [&str; 4] = ["std", "sync", "mpsc", "Receiver"]; +pub const REFCELL_REF: [&str; 3] = ["core", "cell", "Ref"]; +pub const REFCELL_REFMUT: [&str; 3] = ["core", "cell", "RefMut"]; pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"]; pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"]; pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"]; diff --git a/tests/ui/await_holding_refcell_ref.rs b/tests/ui/await_holding_refcell_ref.rs new file mode 100644 index 00000000000..6e330f47dfc --- /dev/null +++ b/tests/ui/await_holding_refcell_ref.rs @@ -0,0 +1,71 @@ +// edition:2018 +#![warn(clippy::await_holding_refcell_ref)] + +use std::cell::RefCell; + +async fn bad(x: &RefCell) -> u32 { + let b = x.borrow(); + baz().await +} + +async fn bad_mut(x: &RefCell) -> u32 { + let b = x.borrow_mut(); + baz().await +} + +async fn good(x: &RefCell) -> u32 { + { + let b = x.borrow_mut(); + let y = *b + 1; + } + baz().await; + let b = x.borrow_mut(); + 47 +} + +async fn baz() -> u32 { + 42 +} + +async fn also_bad(x: &RefCell) -> u32 { + let first = baz().await; + + let b = x.borrow_mut(); + + let second = baz().await; + + let third = baz().await; + + first + second + third +} + +async fn not_good(x: &RefCell) -> u32 { + let first = baz().await; + + let second = { + let b = x.borrow_mut(); + baz().await + }; + + let third = baz().await; + + first + second + third +} + +#[allow(clippy::manual_async_fn)] +fn block_bad(x: &RefCell) -> impl std::future::Future + '_ { + async move { + let b = x.borrow_mut(); + baz().await + } +} + +fn main() { + let m = RefCell::new(100); + good(&m); + bad(&m); + bad_mut(&m); + also_bad(&m); + not_good(&m); + block_bad(&m); +} diff --git a/tests/ui/await_holding_refcell_ref.stderr b/tests/ui/await_holding_refcell_ref.stderr new file mode 100644 index 00000000000..b114945504a --- /dev/null +++ b/tests/ui/await_holding_refcell_ref.stderr @@ -0,0 +1,77 @@ +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_refcell_ref.rs:7:9 + | +LL | let b = x.borrow(); + | ^ + | + = note: `-D clippy::await-holding-refcell-ref` implied by `-D warnings` +note: these are all the await points this ref is held through + --> $DIR/await_holding_refcell_ref.rs:7:5 + | +LL | / let b = x.borrow(); +LL | | baz().await +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_refcell_ref.rs:12:9 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_refcell_ref.rs:12:5 + | +LL | / let b = x.borrow_mut(); +LL | | baz().await +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_refcell_ref.rs:33:9 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_refcell_ref.rs:33:5 + | +LL | / let b = x.borrow_mut(); +LL | | +LL | | let second = baz().await; +LL | | +... | +LL | | first + second + third +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_refcell_ref.rs:46:13 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_refcell_ref.rs:46:9 + | +LL | / let b = x.borrow_mut(); +LL | | baz().await +LL | | }; + | |_____^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_refcell_ref.rs:58:13 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_refcell_ref.rs:58:9 + | +LL | / let b = x.borrow_mut(); +LL | | baz().await +LL | | } + | |_____^ + +error: aborting due to 5 previous errors + -- cgit 1.4.1-3-g733a5 From 8727169f7243c87e3708d99e9602562370f01a1a Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Thu, 10 Sep 2020 16:01:27 -0400 Subject: fmt --- tests/ui/await_holding_refcell_ref.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/ui/await_holding_refcell_ref.rs b/tests/ui/await_holding_refcell_ref.rs index 6e330f47dfc..8e30da85d14 100644 --- a/tests/ui/await_holding_refcell_ref.rs +++ b/tests/ui/await_holding_refcell_ref.rs @@ -64,7 +64,7 @@ fn main() { let m = RefCell::new(100); good(&m); bad(&m); - bad_mut(&m); + bad_mut(&m); also_bad(&m); not_good(&m); block_bad(&m); -- cgit 1.4.1-3-g733a5 From 0f4abbf99a6f1ed783ea6935c83427c2aef95144 Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Mon, 28 Sep 2020 12:57:18 -0400 Subject: Better naming post copy/paste --- tests/ui/await_holding_refcell_ref.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/tests/ui/await_holding_refcell_ref.rs b/tests/ui/await_holding_refcell_ref.rs index 8e30da85d14..dd3adc494f1 100644 --- a/tests/ui/await_holding_refcell_ref.rs +++ b/tests/ui/await_holding_refcell_ref.rs @@ -61,11 +61,11 @@ fn block_bad(x: &RefCell) -> impl std::future::Future + '_ { } fn main() { - let m = RefCell::new(100); - good(&m); - bad(&m); - bad_mut(&m); - also_bad(&m); - not_good(&m); - block_bad(&m); + let rc = RefCell::new(100); + good(&rc); + bad(&rc); + bad_mut(&rc); + also_bad(&rc); + not_good(&rc); + block_bad(&rc); } -- cgit 1.4.1-3-g733a5 From b3a427d8733a549b11f9bc88eceb31c857851411 Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Mon, 28 Sep 2020 13:29:37 -0400 Subject: Add another test case --- tests/ui/await_holding_refcell_ref.rs | 15 +++++++++++++++ tests/ui/await_holding_refcell_ref.stderr | 28 +++++++++++++++++++++++----- 2 files changed, 38 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/tests/ui/await_holding_refcell_ref.rs b/tests/ui/await_holding_refcell_ref.rs index dd3adc494f1..88841597bb6 100644 --- a/tests/ui/await_holding_refcell_ref.rs +++ b/tests/ui/await_holding_refcell_ref.rs @@ -39,6 +39,20 @@ async fn also_bad(x: &RefCell) -> u32 { first + second + third } +async fn less_bad(x: &RefCell) -> u32 { + let first = baz().await; + + let b = x.borrow_mut(); + + let second = baz().await; + + drop(b); + + let third = baz().await; + + first + second + third +} + async fn not_good(x: &RefCell) -> u32 { let first = baz().await; @@ -66,6 +80,7 @@ fn main() { bad(&rc); bad_mut(&rc); also_bad(&rc); + less_bad(&rc); not_good(&rc); block_bad(&rc); } diff --git a/tests/ui/await_holding_refcell_ref.stderr b/tests/ui/await_holding_refcell_ref.stderr index b114945504a..b504f045491 100644 --- a/tests/ui/await_holding_refcell_ref.stderr +++ b/tests/ui/await_holding_refcell_ref.stderr @@ -46,13 +46,31 @@ LL | | } | |_^ error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_refcell_ref.rs:46:13 + --> $DIR/await_holding_refcell_ref.rs:45:9 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_refcell_ref.rs:45:5 + | +LL | / let b = x.borrow_mut(); +LL | | +LL | | let second = baz().await; +LL | | +... | +LL | | first + second + third +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_refcell_ref.rs:60:13 | LL | let b = x.borrow_mut(); | ^ | note: these are all the await points this ref is held through - --> $DIR/await_holding_refcell_ref.rs:46:9 + --> $DIR/await_holding_refcell_ref.rs:60:9 | LL | / let b = x.borrow_mut(); LL | | baz().await @@ -60,18 +78,18 @@ LL | | }; | |_____^ error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_refcell_ref.rs:58:13 + --> $DIR/await_holding_refcell_ref.rs:72:13 | LL | let b = x.borrow_mut(); | ^ | note: these are all the await points this ref is held through - --> $DIR/await_holding_refcell_ref.rs:58:9 + --> $DIR/await_holding_refcell_ref.rs:72:9 | LL | / let b = x.borrow_mut(); LL | | baz().await LL | | } | |_____^ -error: aborting due to 5 previous errors +error: aborting due to 6 previous errors -- cgit 1.4.1-3-g733a5 From 3ed69cdb13e5953467f9d849d7ad480479ca01d6 Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Tue, 13 Oct 2020 12:39:20 -0400 Subject: Move existing lint into shared file --- clippy_lints/src/await_holding_invalid.rs | 92 +++++++++++++++++++++++++++++++ clippy_lints/src/await_holding_lock.rs | 92 ------------------------------- clippy_lints/src/lib.rs | 8 +-- src/lintlist/mod.rs | 2 +- tests/ui/await_holding_invalid.rs | 65 ++++++++++++++++++++++ tests/ui/await_holding_invalid.stderr | 63 +++++++++++++++++++++ tests/ui/await_holding_lock.rs | 65 ---------------------- tests/ui/await_holding_lock.stderr | 63 --------------------- 8 files changed, 225 insertions(+), 225 deletions(-) create mode 100644 clippy_lints/src/await_holding_invalid.rs delete mode 100644 clippy_lints/src/await_holding_lock.rs create mode 100644 tests/ui/await_holding_invalid.rs create mode 100644 tests/ui/await_holding_invalid.stderr delete mode 100644 tests/ui/await_holding_lock.rs delete mode 100644 tests/ui/await_holding_lock.stderr (limited to 'tests') diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs new file mode 100644 index 00000000000..367534499fd --- /dev/null +++ b/clippy_lints/src/await_holding_invalid.rs @@ -0,0 +1,92 @@ +use crate::utils::{match_def_path, paths, span_lint_and_note}; +use rustc_hir::def_id::DefId; +use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::GeneratorInteriorTypeCause; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for calls to await while holding a + /// non-async-aware MutexGuard. + /// + /// **Why is this bad?** 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 + /// 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. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust,ignore + /// use std::sync::Mutex; + /// + /// async fn foo(x: &Mutex) { + /// let guard = x.lock().unwrap(); + /// *guard += 1; + /// bar.await; + /// } + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// use std::sync::Mutex; + /// + /// async fn foo(x: &Mutex) { + /// { + /// let guard = x.lock().unwrap(); + /// *guard += 1; + /// } + /// bar.await; + /// } + /// ``` + pub AWAIT_HOLDING_LOCK, + pedantic, + "Inside an async function, holding a MutexGuard while calling await" +} + +declare_lint_pass!(AwaitHoldingLock => [AWAIT_HOLDING_LOCK]); + +impl LateLintPass<'_> for AwaitHoldingLock { + fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) { + use AsyncGeneratorKind::{Block, Closure, Fn}; + if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind { + let body_id = BodyId { + hir_id: body.value.hir_id, + }; + let def_id = cx.tcx.hir().body_owner_def_id(body_id); + let typeck_results = cx.tcx.typeck(def_id); + check_interior_types(cx, &typeck_results.generator_interior_types, body.value.span); + } + } +} + +fn check_interior_types(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { + for ty_cause in ty_causes { + if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() { + if is_mutex_guard(cx, adt.did) { + span_lint_and_note( + cx, + AWAIT_HOLDING_LOCK, + ty_cause.span, + "this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await.", + ty_cause.scope_span.or(Some(span)), + "these are all the await points this lock is held through", + ); + } + } + } +} + +fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool { + match_def_path(cx, def_id, &paths::MUTEX_GUARD) + || match_def_path(cx, def_id, &paths::RWLOCK_READ_GUARD) + || match_def_path(cx, def_id, &paths::RWLOCK_WRITE_GUARD) + || match_def_path(cx, def_id, &paths::PARKING_LOT_MUTEX_GUARD) + || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_READ_GUARD) + || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD) +} diff --git a/clippy_lints/src/await_holding_lock.rs b/clippy_lints/src/await_holding_lock.rs deleted file mode 100644 index 367534499fd..00000000000 --- a/clippy_lints/src/await_holding_lock.rs +++ /dev/null @@ -1,92 +0,0 @@ -use crate::utils::{match_def_path, paths, span_lint_and_note}; -use rustc_hir::def_id::DefId; -use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::GeneratorInteriorTypeCause; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::Span; - -declare_clippy_lint! { - /// **What it does:** Checks for calls to await while holding a - /// non-async-aware MutexGuard. - /// - /// **Why is this bad?** 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 - /// 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. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// - /// ```rust,ignore - /// use std::sync::Mutex; - /// - /// async fn foo(x: &Mutex) { - /// let guard = x.lock().unwrap(); - /// *guard += 1; - /// bar.await; - /// } - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// use std::sync::Mutex; - /// - /// async fn foo(x: &Mutex) { - /// { - /// let guard = x.lock().unwrap(); - /// *guard += 1; - /// } - /// bar.await; - /// } - /// ``` - pub AWAIT_HOLDING_LOCK, - pedantic, - "Inside an async function, holding a MutexGuard while calling await" -} - -declare_lint_pass!(AwaitHoldingLock => [AWAIT_HOLDING_LOCK]); - -impl LateLintPass<'_> for AwaitHoldingLock { - fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) { - use AsyncGeneratorKind::{Block, Closure, Fn}; - if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind { - let body_id = BodyId { - hir_id: body.value.hir_id, - }; - let def_id = cx.tcx.hir().body_owner_def_id(body_id); - let typeck_results = cx.tcx.typeck(def_id); - check_interior_types(cx, &typeck_results.generator_interior_types, body.value.span); - } - } -} - -fn check_interior_types(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { - for ty_cause in ty_causes { - if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() { - if is_mutex_guard(cx, adt.did) { - span_lint_and_note( - cx, - AWAIT_HOLDING_LOCK, - ty_cause.span, - "this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await.", - ty_cause.scope_span.or(Some(span)), - "these are all the await points this lock is held through", - ); - } - } - } -} - -fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool { - match_def_path(cx, def_id, &paths::MUTEX_GUARD) - || match_def_path(cx, def_id, &paths::RWLOCK_READ_GUARD) - || match_def_path(cx, def_id, &paths::RWLOCK_WRITE_GUARD) - || match_def_path(cx, def_id, &paths::PARKING_LOT_MUTEX_GUARD) - || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_READ_GUARD) - || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD) -} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 69719986104..47cc0c90322 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -160,7 +160,7 @@ mod assign_ops; mod async_yields_async; mod atomic_ordering; mod attrs; -mod await_holding_lock; +mod await_holding_invalid; mod await_holding_refcell_ref; mod bit_mask; mod blacklisted_name; @@ -510,7 +510,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &attrs::MISMATCHED_TARGET_OS, &attrs::UNKNOWN_CLIPPY_LINTS, &attrs::USELESS_ATTRIBUTE, - &await_holding_lock::AWAIT_HOLDING_LOCK, + &await_holding_invalid::AWAIT_HOLDING_LOCK, &await_holding_refcell_ref::AWAIT_HOLDING_REFCELL_REF, &bit_mask::BAD_BIT_MASK, &bit_mask::INEFFECTIVE_BIT_MASK, @@ -907,7 +907,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ]); // end register lints, do not remove this comment, it’s used in `update_lints` - store.register_late_pass(|| box await_holding_lock::AwaitHoldingLock); + store.register_late_pass(|| box await_holding_invalid::AwaitHoldingLock); store.register_late_pass(|| box await_holding_refcell_ref::AwaitHoldingRefCellRef); store.register_late_pass(|| box serde_api::SerdeAPI); store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); @@ -1191,7 +1191,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(&attrs::INLINE_ALWAYS), - LintId::of(&await_holding_lock::AWAIT_HOLDING_LOCK), + LintId::of(&await_holding_invalid::AWAIT_HOLDING_LOCK), LintId::of(&await_holding_refcell_ref::AWAIT_HOLDING_REFCELL_REF), LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&checked_conversions::CHECKED_CONVERSIONS), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 21185a08d5c..63e9220ccd5 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -65,7 +65,7 @@ vec![ group: "pedantic", desc: "Inside an async function, holding a MutexGuard while calling await", deprecation: None, - module: "await_holding_lock", + module: "await_holding_invalid", }, Lint { name: "await_holding_refcell_ref", diff --git a/tests/ui/await_holding_invalid.rs b/tests/ui/await_holding_invalid.rs new file mode 100644 index 00000000000..0458950edee --- /dev/null +++ b/tests/ui/await_holding_invalid.rs @@ -0,0 +1,65 @@ +// edition:2018 +#![warn(clippy::await_holding_lock)] + +use std::sync::Mutex; + +async fn bad(x: &Mutex) -> u32 { + let guard = x.lock().unwrap(); + baz().await +} + +async fn good(x: &Mutex) -> u32 { + { + let guard = x.lock().unwrap(); + let y = *guard + 1; + } + baz().await; + let guard = x.lock().unwrap(); + 47 +} + +async fn baz() -> u32 { + 42 +} + +async fn also_bad(x: &Mutex) -> u32 { + let first = baz().await; + + let guard = x.lock().unwrap(); + + let second = baz().await; + + let third = baz().await; + + first + second + third +} + +async fn not_good(x: &Mutex) -> u32 { + let first = baz().await; + + let second = { + let guard = x.lock().unwrap(); + baz().await + }; + + let third = baz().await; + + first + second + third +} + +#[allow(clippy::manual_async_fn)] +fn block_bad(x: &Mutex) -> impl std::future::Future + '_ { + async move { + let guard = x.lock().unwrap(); + baz().await + } +} + +fn main() { + let m = Mutex::new(100); + good(&m); + bad(&m); + also_bad(&m); + not_good(&m); + block_bad(&m); +} diff --git a/tests/ui/await_holding_invalid.stderr b/tests/ui/await_holding_invalid.stderr new file mode 100644 index 00000000000..315d5731b96 --- /dev/null +++ b/tests/ui/await_holding_invalid.stderr @@ -0,0 +1,63 @@ +error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. + --> $DIR/await_holding_invalid.rs:7:9 + | +LL | let guard = x.lock().unwrap(); + | ^^^^^ + | + = note: `-D clippy::await-holding-lock` implied by `-D warnings` +note: these are all the await points this lock is held through + --> $DIR/await_holding_invalid.rs:7:5 + | +LL | / let guard = x.lock().unwrap(); +LL | | baz().await +LL | | } + | |_^ + +error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. + --> $DIR/await_holding_invalid.rs:28:9 + | +LL | let guard = x.lock().unwrap(); + | ^^^^^ + | +note: these are all the await points this lock is held through + --> $DIR/await_holding_invalid.rs:28:5 + | +LL | / let guard = x.lock().unwrap(); +LL | | +LL | | let second = baz().await; +LL | | +... | +LL | | first + second + third +LL | | } + | |_^ + +error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. + --> $DIR/await_holding_invalid.rs:41:13 + | +LL | let guard = x.lock().unwrap(); + | ^^^^^ + | +note: these are all the await points this lock is held through + --> $DIR/await_holding_invalid.rs:41:9 + | +LL | / let guard = x.lock().unwrap(); +LL | | baz().await +LL | | }; + | |_____^ + +error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. + --> $DIR/await_holding_invalid.rs:53:13 + | +LL | let guard = x.lock().unwrap(); + | ^^^^^ + | +note: these are all the await points this lock is held through + --> $DIR/await_holding_invalid.rs:53:9 + | +LL | / let guard = x.lock().unwrap(); +LL | | baz().await +LL | | } + | |_____^ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/await_holding_lock.rs b/tests/ui/await_holding_lock.rs deleted file mode 100644 index 0458950edee..00000000000 --- a/tests/ui/await_holding_lock.rs +++ /dev/null @@ -1,65 +0,0 @@ -// edition:2018 -#![warn(clippy::await_holding_lock)] - -use std::sync::Mutex; - -async fn bad(x: &Mutex) -> u32 { - let guard = x.lock().unwrap(); - baz().await -} - -async fn good(x: &Mutex) -> u32 { - { - let guard = x.lock().unwrap(); - let y = *guard + 1; - } - baz().await; - let guard = x.lock().unwrap(); - 47 -} - -async fn baz() -> u32 { - 42 -} - -async fn also_bad(x: &Mutex) -> u32 { - let first = baz().await; - - let guard = x.lock().unwrap(); - - let second = baz().await; - - let third = baz().await; - - first + second + third -} - -async fn not_good(x: &Mutex) -> u32 { - let first = baz().await; - - let second = { - let guard = x.lock().unwrap(); - baz().await - }; - - let third = baz().await; - - first + second + third -} - -#[allow(clippy::manual_async_fn)] -fn block_bad(x: &Mutex) -> impl std::future::Future + '_ { - async move { - let guard = x.lock().unwrap(); - baz().await - } -} - -fn main() { - let m = Mutex::new(100); - good(&m); - bad(&m); - also_bad(&m); - not_good(&m); - block_bad(&m); -} diff --git a/tests/ui/await_holding_lock.stderr b/tests/ui/await_holding_lock.stderr deleted file mode 100644 index 21bf49d16f0..00000000000 --- a/tests/ui/await_holding_lock.stderr +++ /dev/null @@ -1,63 +0,0 @@ -error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_lock.rs:7:9 - | -LL | let guard = x.lock().unwrap(); - | ^^^^^ - | - = note: `-D clippy::await-holding-lock` implied by `-D warnings` -note: these are all the await points this lock is held through - --> $DIR/await_holding_lock.rs:7:5 - | -LL | / let guard = x.lock().unwrap(); -LL | | baz().await -LL | | } - | |_^ - -error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_lock.rs:28:9 - | -LL | let guard = x.lock().unwrap(); - | ^^^^^ - | -note: these are all the await points this lock is held through - --> $DIR/await_holding_lock.rs:28:5 - | -LL | / let guard = x.lock().unwrap(); -LL | | -LL | | let second = baz().await; -LL | | -... | -LL | | first + second + third -LL | | } - | |_^ - -error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_lock.rs:41:13 - | -LL | let guard = x.lock().unwrap(); - | ^^^^^ - | -note: these are all the await points this lock is held through - --> $DIR/await_holding_lock.rs:41:9 - | -LL | / let guard = x.lock().unwrap(); -LL | | baz().await -LL | | }; - | |_____^ - -error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_lock.rs:53:13 - | -LL | let guard = x.lock().unwrap(); - | ^^^^^ - | -note: these are all the await points this lock is held through - --> $DIR/await_holding_lock.rs:53:9 - | -LL | / let guard = x.lock().unwrap(); -LL | | baz().await -LL | | } - | |_____^ - -error: aborting due to 4 previous errors - -- cgit 1.4.1-3-g733a5 From ee20ebadafc9b2e4995015097e376c0a866d84af Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Tue, 13 Oct 2020 13:15:45 -0400 Subject: Move refcell lint into shared module --- clippy_lints/src/await_holding_invalid.rs | 80 ++++++++++++++++++- clippy_lints/src/await_holding_refcell_ref.rs | 83 ------------------- clippy_lints/src/lib.rs | 7 +- src/lintlist/mod.rs | 2 +- tests/ui/await_holding_invalid.rs | 104 +++++++++++++++++++++--- tests/ui/await_holding_invalid.stderr | 111 +++++++++++++++++++++++--- tests/ui/await_holding_refcell_ref.rs | 86 -------------------- tests/ui/await_holding_refcell_ref.stderr | 95 ---------------------- 8 files changed, 276 insertions(+), 292 deletions(-) delete mode 100644 clippy_lints/src/await_holding_refcell_ref.rs delete mode 100644 tests/ui/await_holding_refcell_ref.rs delete mode 100644 tests/ui/await_holding_refcell_ref.stderr (limited to 'tests') diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 367534499fd..2000bdae363 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -60,12 +60,12 @@ impl LateLintPass<'_> for AwaitHoldingLock { }; let def_id = cx.tcx.hir().body_owner_def_id(body_id); let typeck_results = cx.tcx.typeck(def_id); - check_interior_types(cx, &typeck_results.generator_interior_types, body.value.span); + check_interior_types_lock(cx, &typeck_results.generator_interior_types, body.value.span); } } } -fn check_interior_types(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { +fn check_interior_types_lock(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { for ty_cause in ty_causes { if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() { if is_mutex_guard(cx, adt.did) { @@ -90,3 +90,79 @@ fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool { || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_READ_GUARD) || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD) } + +declare_clippy_lint! { + /// **What it does:** Checks for calls to await while holding a + /// `RefCell` `Ref` or `RefMut`. + /// + /// **Why is this bad?** `RefCell` refs only check for exclusive mutable access + /// at runtime. Holding onto a `RefCell` ref across an `await` suspension point + /// risks panics from a mutable ref shared while other refs are outstanding. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust,ignore + /// use std::cell::RefCell; + /// + /// async fn foo(x: &RefCell) { + /// let b = x.borrow_mut()(); + /// *ref += 1; + /// bar.await; + /// } + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// use std::cell::RefCell; + /// + /// async fn foo(x: &RefCell) { + /// { + /// let b = x.borrow_mut(); + /// *ref += 1; + /// } + /// bar.await; + /// } + /// ``` + pub AWAIT_HOLDING_REFCELL_REF, + pedantic, + "Inside an async function, holding a RefCell ref while calling await" +} + +declare_lint_pass!(AwaitHoldingRefCellRef => [AWAIT_HOLDING_REFCELL_REF]); + +impl LateLintPass<'_> for AwaitHoldingRefCellRef { + fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) { + use AsyncGeneratorKind::{Block, Closure, Fn}; + if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind { + let body_id = BodyId { + hir_id: body.value.hir_id, + }; + let def_id = cx.tcx.hir().body_owner_def_id(body_id); + let typeck_results = cx.tcx.typeck(def_id); + check_interior_types_refcell(cx, &typeck_results.generator_interior_types, body.value.span); + } + } +} + +fn check_interior_types_refcell(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { + for ty_cause in ty_causes { + if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() { + if is_refcell_ref(cx, adt.did) { + span_lint_and_note( + cx, + AWAIT_HOLDING_REFCELL_REF, + ty_cause.span, + "this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await.", + ty_cause.scope_span.or(Some(span)), + "these are all the await points this ref is held through", + ); + } + } + } +} + +fn is_refcell_ref(cx: &LateContext<'_>, def_id: DefId) -> bool { + match_def_path(cx, def_id, &paths::REFCELL_REF) || match_def_path(cx, def_id, &paths::REFCELL_REFMUT) +} diff --git a/clippy_lints/src/await_holding_refcell_ref.rs b/clippy_lints/src/await_holding_refcell_ref.rs deleted file mode 100644 index 9a75911acbe..00000000000 --- a/clippy_lints/src/await_holding_refcell_ref.rs +++ /dev/null @@ -1,83 +0,0 @@ -use crate::utils::{match_def_path, paths, span_lint_and_note}; -use rustc_hir::def_id::DefId; -use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::GeneratorInteriorTypeCause; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::Span; - -declare_clippy_lint! { - /// **What it does:** Checks for calls to await while holding a - /// `RefCell` `Ref` or `RefMut`. - /// - /// **Why is this bad?** `RefCell` refs only check for exclusive mutable access - /// at runtime. Holding onto a `RefCell` ref across an `await` suspension point - /// risks panics from a mutable ref shared while other refs are outstanding. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// - /// ```rust,ignore - /// use std::cell::RefCell; - /// - /// async fn foo(x: &RefCell) { - /// let b = x.borrow_mut()(); - /// *ref += 1; - /// bar.await; - /// } - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// use std::cell::RefCell; - /// - /// async fn foo(x: &RefCell) { - /// { - /// let b = x.borrow_mut(); - /// *ref += 1; - /// } - /// bar.await; - /// } - /// ``` - pub AWAIT_HOLDING_REFCELL_REF, - pedantic, - "Inside an async function, holding a RefCell ref while calling await" -} - -declare_lint_pass!(AwaitHoldingRefCellRef => [AWAIT_HOLDING_REFCELL_REF]); - -impl LateLintPass<'_> for AwaitHoldingRefCellRef { - fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) { - use AsyncGeneratorKind::{Block, Closure, Fn}; - if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind { - let body_id = BodyId { - hir_id: body.value.hir_id, - }; - let def_id = cx.tcx.hir().body_owner_def_id(body_id); - let typeck_results = cx.tcx.typeck(def_id); - check_interior_types(cx, &typeck_results.generator_interior_types, body.value.span); - } - } -} - -fn check_interior_types(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { - for ty_cause in ty_causes { - if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() { - if is_refcell_ref(cx, adt.did) { - span_lint_and_note( - cx, - AWAIT_HOLDING_REFCELL_REF, - ty_cause.span, - "this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await.", - ty_cause.scope_span.or(Some(span)), - "these are all the await points this ref is held through", - ); - } - } - } -} - -fn is_refcell_ref(cx: &LateContext<'_>, def_id: DefId) -> bool { - match_def_path(cx, def_id, &paths::REFCELL_REF) || match_def_path(cx, def_id, &paths::REFCELL_REFMUT) -} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 47cc0c90322..0c5baf96970 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -161,7 +161,6 @@ mod async_yields_async; mod atomic_ordering; mod attrs; mod await_holding_invalid; -mod await_holding_refcell_ref; mod bit_mask; mod blacklisted_name; mod blocks_in_if_conditions; @@ -511,7 +510,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &attrs::UNKNOWN_CLIPPY_LINTS, &attrs::USELESS_ATTRIBUTE, &await_holding_invalid::AWAIT_HOLDING_LOCK, - &await_holding_refcell_ref::AWAIT_HOLDING_REFCELL_REF, + &await_holding_invalid::AWAIT_HOLDING_REFCELL_REF, &bit_mask::BAD_BIT_MASK, &bit_mask::INEFFECTIVE_BIT_MASK, &bit_mask::VERBOSE_BIT_MASK, @@ -908,7 +907,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: // end register lints, do not remove this comment, it’s used in `update_lints` store.register_late_pass(|| box await_holding_invalid::AwaitHoldingLock); - store.register_late_pass(|| box await_holding_refcell_ref::AwaitHoldingRefCellRef); + store.register_late_pass(|| box await_holding_invalid::AwaitHoldingRefCellRef); store.register_late_pass(|| box serde_api::SerdeAPI); store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default()); @@ -1192,7 +1191,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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_refcell_ref::AWAIT_HOLDING_REFCELL_REF), + LintId::of(&await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&checked_conversions::CHECKED_CONVERSIONS), LintId::of(&copies::MATCH_SAME_ARMS), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 63e9220ccd5..c955f37b83a 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -72,7 +72,7 @@ vec![ group: "pedantic", desc: "Inside an async function, holding a RefCell ref while calling await", deprecation: None, - module: "await_holding_refcell_ref", + module: "await_holding_invalid", }, Lint { name: "bad_bit_mask", diff --git a/tests/ui/await_holding_invalid.rs b/tests/ui/await_holding_invalid.rs index 0458950edee..45aa3e64292 100644 --- a/tests/ui/await_holding_invalid.rs +++ b/tests/ui/await_holding_invalid.rs @@ -1,14 +1,15 @@ // edition:2018 -#![warn(clippy::await_holding_lock)] +#![warn(clippy::await_holding_lock, clippy::await_holding_refcell_ref)] +use std::cell::RefCell; use std::sync::Mutex; -async fn bad(x: &Mutex) -> u32 { +async fn bad_lock(x: &Mutex) -> u32 { let guard = x.lock().unwrap(); baz().await } -async fn good(x: &Mutex) -> u32 { +async fn good_lock(x: &Mutex) -> u32 { { let guard = x.lock().unwrap(); let y = *guard + 1; @@ -22,7 +23,7 @@ async fn baz() -> u32 { 42 } -async fn also_bad(x: &Mutex) -> u32 { +async fn also_bad_lock(x: &Mutex) -> u32 { let first = baz().await; let guard = x.lock().unwrap(); @@ -34,7 +35,7 @@ async fn also_bad(x: &Mutex) -> u32 { first + second + third } -async fn not_good(x: &Mutex) -> u32 { +async fn not_good_lock(x: &Mutex) -> u32 { let first = baz().await; let second = { @@ -48,18 +49,97 @@ async fn not_good(x: &Mutex) -> u32 { } #[allow(clippy::manual_async_fn)] -fn block_bad(x: &Mutex) -> impl std::future::Future + '_ { +fn block_bad_lock(x: &Mutex) -> impl std::future::Future + '_ { async move { let guard = x.lock().unwrap(); baz().await } } +async fn bad_refcell(x: &RefCell) -> u32 { + let b = x.borrow(); + baz().await +} + +async fn bad_mut_refcell(x: &RefCell) -> u32 { + let b = x.borrow_mut(); + baz().await +} + +async fn good_refcell(x: &RefCell) -> u32 { + { + let b = x.borrow_mut(); + let y = *b + 1; + } + baz().await; + let b = x.borrow_mut(); + 47 +} + +async fn also_bad_refcell(x: &RefCell) -> u32 { + let first = baz().await; + + let b = x.borrow_mut(); + + let second = baz().await; + + let third = baz().await; + + first + second + third +} + +async fn less_bad_refcell(x: &RefCell) -> u32 { + let first = baz().await; + + let b = x.borrow_mut(); + + let second = baz().await; + + drop(b); + + let third = baz().await; + + first + second + third +} + +async fn not_good_refcell(x: &RefCell) -> u32 { + let first = baz().await; + + let second = { + let b = x.borrow_mut(); + baz().await + }; + + let third = baz().await; + + first + second + third +} + +#[allow(clippy::manual_async_fn)] +fn block_bad_refcell(x: &RefCell) -> impl std::future::Future + '_ { + async move { + let b = x.borrow_mut(); + baz().await + } +} + fn main() { - let m = Mutex::new(100); - good(&m); - bad(&m); - also_bad(&m); - not_good(&m); - block_bad(&m); + { + let m = Mutex::new(100); + good_lock(&m); + bad_lock(&m); + also_bad_lock(&m); + not_good_lock(&m); + block_bad_lock(&m); + } + { + let rc = RefCell::new(100); + good_refcell(&rc); + bad_refcell(&rc); + bad_mut_refcell(&rc); + also_bad_refcell(&rc); + less_bad_refcell(&rc); + not_good_refcell(&rc); + block_bad_refcell(&rc); + } } diff --git a/tests/ui/await_holding_invalid.stderr b/tests/ui/await_holding_invalid.stderr index 315d5731b96..c8d49820c02 100644 --- a/tests/ui/await_holding_invalid.stderr +++ b/tests/ui/await_holding_invalid.stderr @@ -1,12 +1,12 @@ error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_invalid.rs:7:9 + --> $DIR/await_holding_invalid.rs:8:9 | LL | let guard = x.lock().unwrap(); | ^^^^^ | = note: `-D clippy::await-holding-lock` implied by `-D warnings` note: these are all the await points this lock is held through - --> $DIR/await_holding_invalid.rs:7:5 + --> $DIR/await_holding_invalid.rs:8:5 | LL | / let guard = x.lock().unwrap(); LL | | baz().await @@ -14,13 +14,13 @@ LL | | } | |_^ error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_invalid.rs:28:9 + --> $DIR/await_holding_invalid.rs:29:9 | LL | let guard = x.lock().unwrap(); | ^^^^^ | note: these are all the await points this lock is held through - --> $DIR/await_holding_invalid.rs:28:5 + --> $DIR/await_holding_invalid.rs:29:5 | LL | / let guard = x.lock().unwrap(); LL | | @@ -32,13 +32,13 @@ LL | | } | |_^ error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_invalid.rs:41:13 + --> $DIR/await_holding_invalid.rs:42:13 | LL | let guard = x.lock().unwrap(); | ^^^^^ | note: these are all the await points this lock is held through - --> $DIR/await_holding_invalid.rs:41:9 + --> $DIR/await_holding_invalid.rs:42:9 | LL | / let guard = x.lock().unwrap(); LL | | baz().await @@ -46,18 +46,111 @@ LL | | }; | |_____^ error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_invalid.rs:53:13 + --> $DIR/await_holding_invalid.rs:54:13 | LL | let guard = x.lock().unwrap(); | ^^^^^ | note: these are all the await points this lock is held through - --> $DIR/await_holding_invalid.rs:53:9 + --> $DIR/await_holding_invalid.rs:54:9 | LL | / let guard = x.lock().unwrap(); LL | | baz().await LL | | } | |_____^ -error: aborting due to 4 previous errors +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_invalid.rs:60:9 + | +LL | let b = x.borrow(); + | ^ + | + = note: `-D clippy::await-holding-refcell-ref` implied by `-D warnings` +note: these are all the await points this ref is held through + --> $DIR/await_holding_invalid.rs:60:5 + | +LL | / let b = x.borrow(); +LL | | baz().await +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_invalid.rs:65:9 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_invalid.rs:65:5 + | +LL | / let b = x.borrow_mut(); +LL | | baz().await +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_invalid.rs:82:9 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_invalid.rs:82:5 + | +LL | / let b = x.borrow_mut(); +LL | | +LL | | let second = baz().await; +LL | | +... | +LL | | first + second + third +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_invalid.rs:94:9 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_invalid.rs:94:5 + | +LL | / let b = x.borrow_mut(); +LL | | +LL | | let second = baz().await; +LL | | +... | +LL | | first + second + third +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_invalid.rs:109:13 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_invalid.rs:109:9 + | +LL | / let b = x.borrow_mut(); +LL | | baz().await +LL | | }; + | |_____^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_invalid.rs:121:13 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_invalid.rs:121:9 + | +LL | / let b = x.borrow_mut(); +LL | | baz().await +LL | | } + | |_____^ + +error: aborting due to 10 previous errors diff --git a/tests/ui/await_holding_refcell_ref.rs b/tests/ui/await_holding_refcell_ref.rs deleted file mode 100644 index 88841597bb6..00000000000 --- a/tests/ui/await_holding_refcell_ref.rs +++ /dev/null @@ -1,86 +0,0 @@ -// edition:2018 -#![warn(clippy::await_holding_refcell_ref)] - -use std::cell::RefCell; - -async fn bad(x: &RefCell) -> u32 { - let b = x.borrow(); - baz().await -} - -async fn bad_mut(x: &RefCell) -> u32 { - let b = x.borrow_mut(); - baz().await -} - -async fn good(x: &RefCell) -> u32 { - { - let b = x.borrow_mut(); - let y = *b + 1; - } - baz().await; - let b = x.borrow_mut(); - 47 -} - -async fn baz() -> u32 { - 42 -} - -async fn also_bad(x: &RefCell) -> u32 { - let first = baz().await; - - let b = x.borrow_mut(); - - let second = baz().await; - - let third = baz().await; - - first + second + third -} - -async fn less_bad(x: &RefCell) -> u32 { - let first = baz().await; - - let b = x.borrow_mut(); - - let second = baz().await; - - drop(b); - - let third = baz().await; - - first + second + third -} - -async fn not_good(x: &RefCell) -> u32 { - let first = baz().await; - - let second = { - let b = x.borrow_mut(); - baz().await - }; - - let third = baz().await; - - first + second + third -} - -#[allow(clippy::manual_async_fn)] -fn block_bad(x: &RefCell) -> impl std::future::Future + '_ { - async move { - let b = x.borrow_mut(); - baz().await - } -} - -fn main() { - let rc = RefCell::new(100); - good(&rc); - bad(&rc); - bad_mut(&rc); - also_bad(&rc); - less_bad(&rc); - not_good(&rc); - block_bad(&rc); -} diff --git a/tests/ui/await_holding_refcell_ref.stderr b/tests/ui/await_holding_refcell_ref.stderr deleted file mode 100644 index b504f045491..00000000000 --- a/tests/ui/await_holding_refcell_ref.stderr +++ /dev/null @@ -1,95 +0,0 @@ -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_refcell_ref.rs:7:9 - | -LL | let b = x.borrow(); - | ^ - | - = note: `-D clippy::await-holding-refcell-ref` implied by `-D warnings` -note: these are all the await points this ref is held through - --> $DIR/await_holding_refcell_ref.rs:7:5 - | -LL | / let b = x.borrow(); -LL | | baz().await -LL | | } - | |_^ - -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_refcell_ref.rs:12:9 - | -LL | let b = x.borrow_mut(); - | ^ - | -note: these are all the await points this ref is held through - --> $DIR/await_holding_refcell_ref.rs:12:5 - | -LL | / let b = x.borrow_mut(); -LL | | baz().await -LL | | } - | |_^ - -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_refcell_ref.rs:33:9 - | -LL | let b = x.borrow_mut(); - | ^ - | -note: these are all the await points this ref is held through - --> $DIR/await_holding_refcell_ref.rs:33:5 - | -LL | / let b = x.borrow_mut(); -LL | | -LL | | let second = baz().await; -LL | | -... | -LL | | first + second + third -LL | | } - | |_^ - -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_refcell_ref.rs:45:9 - | -LL | let b = x.borrow_mut(); - | ^ - | -note: these are all the await points this ref is held through - --> $DIR/await_holding_refcell_ref.rs:45:5 - | -LL | / let b = x.borrow_mut(); -LL | | -LL | | let second = baz().await; -LL | | -... | -LL | | first + second + third -LL | | } - | |_^ - -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_refcell_ref.rs:60:13 - | -LL | let b = x.borrow_mut(); - | ^ - | -note: these are all the await points this ref is held through - --> $DIR/await_holding_refcell_ref.rs:60:9 - | -LL | / let b = x.borrow_mut(); -LL | | baz().await -LL | | }; - | |_____^ - -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_refcell_ref.rs:72:13 - | -LL | let b = x.borrow_mut(); - | ^ - | -note: these are all the await points this ref is held through - --> $DIR/await_holding_refcell_ref.rs:72:9 - | -LL | / let b = x.borrow_mut(); -LL | | baz().await -LL | | } - | |_____^ - -error: aborting due to 6 previous errors - -- cgit 1.4.1-3-g733a5 From 4d3322525d9b65ce4c6fc183bc1cd3cfc9477300 Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Mon, 19 Oct 2020 12:07:04 -0400 Subject: Separate tests for each lint --- tests/ui/await_holding_invalid.rs | 145 --------------------------- tests/ui/await_holding_invalid.stderr | 156 ------------------------------ tests/ui/await_holding_lock.rs | 65 +++++++++++++ tests/ui/await_holding_lock.stderr | 63 ++++++++++++ tests/ui/await_holding_refcell_ref.rs | 86 ++++++++++++++++ tests/ui/await_holding_refcell_ref.stderr | 95 ++++++++++++++++++ 6 files changed, 309 insertions(+), 301 deletions(-) delete mode 100644 tests/ui/await_holding_invalid.rs delete mode 100644 tests/ui/await_holding_invalid.stderr create mode 100644 tests/ui/await_holding_lock.rs create mode 100644 tests/ui/await_holding_lock.stderr create mode 100644 tests/ui/await_holding_refcell_ref.rs create mode 100644 tests/ui/await_holding_refcell_ref.stderr (limited to 'tests') diff --git a/tests/ui/await_holding_invalid.rs b/tests/ui/await_holding_invalid.rs deleted file mode 100644 index 45aa3e64292..00000000000 --- a/tests/ui/await_holding_invalid.rs +++ /dev/null @@ -1,145 +0,0 @@ -// edition:2018 -#![warn(clippy::await_holding_lock, clippy::await_holding_refcell_ref)] - -use std::cell::RefCell; -use std::sync::Mutex; - -async fn bad_lock(x: &Mutex) -> u32 { - let guard = x.lock().unwrap(); - baz().await -} - -async fn good_lock(x: &Mutex) -> u32 { - { - let guard = x.lock().unwrap(); - let y = *guard + 1; - } - baz().await; - let guard = x.lock().unwrap(); - 47 -} - -async fn baz() -> u32 { - 42 -} - -async fn also_bad_lock(x: &Mutex) -> u32 { - let first = baz().await; - - let guard = x.lock().unwrap(); - - let second = baz().await; - - let third = baz().await; - - first + second + third -} - -async fn not_good_lock(x: &Mutex) -> u32 { - let first = baz().await; - - let second = { - let guard = x.lock().unwrap(); - baz().await - }; - - let third = baz().await; - - first + second + third -} - -#[allow(clippy::manual_async_fn)] -fn block_bad_lock(x: &Mutex) -> impl std::future::Future + '_ { - async move { - let guard = x.lock().unwrap(); - baz().await - } -} - -async fn bad_refcell(x: &RefCell) -> u32 { - let b = x.borrow(); - baz().await -} - -async fn bad_mut_refcell(x: &RefCell) -> u32 { - let b = x.borrow_mut(); - baz().await -} - -async fn good_refcell(x: &RefCell) -> u32 { - { - let b = x.borrow_mut(); - let y = *b + 1; - } - baz().await; - let b = x.borrow_mut(); - 47 -} - -async fn also_bad_refcell(x: &RefCell) -> u32 { - let first = baz().await; - - let b = x.borrow_mut(); - - let second = baz().await; - - let third = baz().await; - - first + second + third -} - -async fn less_bad_refcell(x: &RefCell) -> u32 { - let first = baz().await; - - let b = x.borrow_mut(); - - let second = baz().await; - - drop(b); - - let third = baz().await; - - first + second + third -} - -async fn not_good_refcell(x: &RefCell) -> u32 { - let first = baz().await; - - let second = { - let b = x.borrow_mut(); - baz().await - }; - - let third = baz().await; - - first + second + third -} - -#[allow(clippy::manual_async_fn)] -fn block_bad_refcell(x: &RefCell) -> impl std::future::Future + '_ { - async move { - let b = x.borrow_mut(); - baz().await - } -} - -fn main() { - { - let m = Mutex::new(100); - good_lock(&m); - bad_lock(&m); - also_bad_lock(&m); - not_good_lock(&m); - block_bad_lock(&m); - } - { - let rc = RefCell::new(100); - good_refcell(&rc); - bad_refcell(&rc); - bad_mut_refcell(&rc); - also_bad_refcell(&rc); - less_bad_refcell(&rc); - not_good_refcell(&rc); - block_bad_refcell(&rc); - } -} diff --git a/tests/ui/await_holding_invalid.stderr b/tests/ui/await_holding_invalid.stderr deleted file mode 100644 index c8d49820c02..00000000000 --- a/tests/ui/await_holding_invalid.stderr +++ /dev/null @@ -1,156 +0,0 @@ -error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_invalid.rs:8:9 - | -LL | let guard = x.lock().unwrap(); - | ^^^^^ - | - = note: `-D clippy::await-holding-lock` implied by `-D warnings` -note: these are all the await points this lock is held through - --> $DIR/await_holding_invalid.rs:8:5 - | -LL | / let guard = x.lock().unwrap(); -LL | | baz().await -LL | | } - | |_^ - -error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_invalid.rs:29:9 - | -LL | let guard = x.lock().unwrap(); - | ^^^^^ - | -note: these are all the await points this lock is held through - --> $DIR/await_holding_invalid.rs:29:5 - | -LL | / let guard = x.lock().unwrap(); -LL | | -LL | | let second = baz().await; -LL | | -... | -LL | | first + second + third -LL | | } - | |_^ - -error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_invalid.rs:42:13 - | -LL | let guard = x.lock().unwrap(); - | ^^^^^ - | -note: these are all the await points this lock is held through - --> $DIR/await_holding_invalid.rs:42:9 - | -LL | / let guard = x.lock().unwrap(); -LL | | baz().await -LL | | }; - | |_____^ - -error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. - --> $DIR/await_holding_invalid.rs:54:13 - | -LL | let guard = x.lock().unwrap(); - | ^^^^^ - | -note: these are all the await points this lock is held through - --> $DIR/await_holding_invalid.rs:54:9 - | -LL | / let guard = x.lock().unwrap(); -LL | | baz().await -LL | | } - | |_____^ - -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_invalid.rs:60:9 - | -LL | let b = x.borrow(); - | ^ - | - = note: `-D clippy::await-holding-refcell-ref` implied by `-D warnings` -note: these are all the await points this ref is held through - --> $DIR/await_holding_invalid.rs:60:5 - | -LL | / let b = x.borrow(); -LL | | baz().await -LL | | } - | |_^ - -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_invalid.rs:65:9 - | -LL | let b = x.borrow_mut(); - | ^ - | -note: these are all the await points this ref is held through - --> $DIR/await_holding_invalid.rs:65:5 - | -LL | / let b = x.borrow_mut(); -LL | | baz().await -LL | | } - | |_^ - -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_invalid.rs:82:9 - | -LL | let b = x.borrow_mut(); - | ^ - | -note: these are all the await points this ref is held through - --> $DIR/await_holding_invalid.rs:82:5 - | -LL | / let b = x.borrow_mut(); -LL | | -LL | | let second = baz().await; -LL | | -... | -LL | | first + second + third -LL | | } - | |_^ - -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_invalid.rs:94:9 - | -LL | let b = x.borrow_mut(); - | ^ - | -note: these are all the await points this ref is held through - --> $DIR/await_holding_invalid.rs:94:5 - | -LL | / let b = x.borrow_mut(); -LL | | -LL | | let second = baz().await; -LL | | -... | -LL | | first + second + third -LL | | } - | |_^ - -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_invalid.rs:109:13 - | -LL | let b = x.borrow_mut(); - | ^ - | -note: these are all the await points this ref is held through - --> $DIR/await_holding_invalid.rs:109:9 - | -LL | / let b = x.borrow_mut(); -LL | | baz().await -LL | | }; - | |_____^ - -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. - --> $DIR/await_holding_invalid.rs:121:13 - | -LL | let b = x.borrow_mut(); - | ^ - | -note: these are all the await points this ref is held through - --> $DIR/await_holding_invalid.rs:121:9 - | -LL | / let b = x.borrow_mut(); -LL | | baz().await -LL | | } - | |_____^ - -error: aborting due to 10 previous errors - diff --git a/tests/ui/await_holding_lock.rs b/tests/ui/await_holding_lock.rs new file mode 100644 index 00000000000..0458950edee --- /dev/null +++ b/tests/ui/await_holding_lock.rs @@ -0,0 +1,65 @@ +// edition:2018 +#![warn(clippy::await_holding_lock)] + +use std::sync::Mutex; + +async fn bad(x: &Mutex) -> u32 { + let guard = x.lock().unwrap(); + baz().await +} + +async fn good(x: &Mutex) -> u32 { + { + let guard = x.lock().unwrap(); + let y = *guard + 1; + } + baz().await; + let guard = x.lock().unwrap(); + 47 +} + +async fn baz() -> u32 { + 42 +} + +async fn also_bad(x: &Mutex) -> u32 { + let first = baz().await; + + let guard = x.lock().unwrap(); + + let second = baz().await; + + let third = baz().await; + + first + second + third +} + +async fn not_good(x: &Mutex) -> u32 { + let first = baz().await; + + let second = { + let guard = x.lock().unwrap(); + baz().await + }; + + let third = baz().await; + + first + second + third +} + +#[allow(clippy::manual_async_fn)] +fn block_bad(x: &Mutex) -> impl std::future::Future + '_ { + async move { + let guard = x.lock().unwrap(); + baz().await + } +} + +fn main() { + let m = Mutex::new(100); + good(&m); + bad(&m); + also_bad(&m); + not_good(&m); + block_bad(&m); +} diff --git a/tests/ui/await_holding_lock.stderr b/tests/ui/await_holding_lock.stderr new file mode 100644 index 00000000000..21bf49d16f0 --- /dev/null +++ b/tests/ui/await_holding_lock.stderr @@ -0,0 +1,63 @@ +error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. + --> $DIR/await_holding_lock.rs:7:9 + | +LL | let guard = x.lock().unwrap(); + | ^^^^^ + | + = note: `-D clippy::await-holding-lock` implied by `-D warnings` +note: these are all the await points this lock is held through + --> $DIR/await_holding_lock.rs:7:5 + | +LL | / let guard = x.lock().unwrap(); +LL | | baz().await +LL | | } + | |_^ + +error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. + --> $DIR/await_holding_lock.rs:28:9 + | +LL | let guard = x.lock().unwrap(); + | ^^^^^ + | +note: these are all the await points this lock is held through + --> $DIR/await_holding_lock.rs:28:5 + | +LL | / let guard = x.lock().unwrap(); +LL | | +LL | | let second = baz().await; +LL | | +... | +LL | | first + second + third +LL | | } + | |_^ + +error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. + --> $DIR/await_holding_lock.rs:41:13 + | +LL | let guard = x.lock().unwrap(); + | ^^^^^ + | +note: these are all the await points this lock is held through + --> $DIR/await_holding_lock.rs:41:9 + | +LL | / let guard = x.lock().unwrap(); +LL | | baz().await +LL | | }; + | |_____^ + +error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. + --> $DIR/await_holding_lock.rs:53:13 + | +LL | let guard = x.lock().unwrap(); + | ^^^^^ + | +note: these are all the await points this lock is held through + --> $DIR/await_holding_lock.rs:53:9 + | +LL | / let guard = x.lock().unwrap(); +LL | | baz().await +LL | | } + | |_____^ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/await_holding_refcell_ref.rs b/tests/ui/await_holding_refcell_ref.rs new file mode 100644 index 00000000000..88841597bb6 --- /dev/null +++ b/tests/ui/await_holding_refcell_ref.rs @@ -0,0 +1,86 @@ +// edition:2018 +#![warn(clippy::await_holding_refcell_ref)] + +use std::cell::RefCell; + +async fn bad(x: &RefCell) -> u32 { + let b = x.borrow(); + baz().await +} + +async fn bad_mut(x: &RefCell) -> u32 { + let b = x.borrow_mut(); + baz().await +} + +async fn good(x: &RefCell) -> u32 { + { + let b = x.borrow_mut(); + let y = *b + 1; + } + baz().await; + let b = x.borrow_mut(); + 47 +} + +async fn baz() -> u32 { + 42 +} + +async fn also_bad(x: &RefCell) -> u32 { + let first = baz().await; + + let b = x.borrow_mut(); + + let second = baz().await; + + let third = baz().await; + + first + second + third +} + +async fn less_bad(x: &RefCell) -> u32 { + let first = baz().await; + + let b = x.borrow_mut(); + + let second = baz().await; + + drop(b); + + let third = baz().await; + + first + second + third +} + +async fn not_good(x: &RefCell) -> u32 { + let first = baz().await; + + let second = { + let b = x.borrow_mut(); + baz().await + }; + + let third = baz().await; + + first + second + third +} + +#[allow(clippy::manual_async_fn)] +fn block_bad(x: &RefCell) -> impl std::future::Future + '_ { + async move { + let b = x.borrow_mut(); + baz().await + } +} + +fn main() { + let rc = RefCell::new(100); + good(&rc); + bad(&rc); + bad_mut(&rc); + also_bad(&rc); + less_bad(&rc); + not_good(&rc); + block_bad(&rc); +} diff --git a/tests/ui/await_holding_refcell_ref.stderr b/tests/ui/await_holding_refcell_ref.stderr new file mode 100644 index 00000000000..b504f045491 --- /dev/null +++ b/tests/ui/await_holding_refcell_ref.stderr @@ -0,0 +1,95 @@ +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_refcell_ref.rs:7:9 + | +LL | let b = x.borrow(); + | ^ + | + = note: `-D clippy::await-holding-refcell-ref` implied by `-D warnings` +note: these are all the await points this ref is held through + --> $DIR/await_holding_refcell_ref.rs:7:5 + | +LL | / let b = x.borrow(); +LL | | baz().await +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_refcell_ref.rs:12:9 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_refcell_ref.rs:12:5 + | +LL | / let b = x.borrow_mut(); +LL | | baz().await +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_refcell_ref.rs:33:9 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_refcell_ref.rs:33:5 + | +LL | / let b = x.borrow_mut(); +LL | | +LL | | let second = baz().await; +LL | | +... | +LL | | first + second + third +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_refcell_ref.rs:45:9 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_refcell_ref.rs:45:5 + | +LL | / let b = x.borrow_mut(); +LL | | +LL | | let second = baz().await; +LL | | +... | +LL | | first + second + third +LL | | } + | |_^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_refcell_ref.rs:60:13 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_refcell_ref.rs:60:9 + | +LL | / let b = x.borrow_mut(); +LL | | baz().await +LL | | }; + | |_____^ + +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. + --> $DIR/await_holding_refcell_ref.rs:72:13 + | +LL | let b = x.borrow_mut(); + | ^ + | +note: these are all the await points this ref is held through + --> $DIR/await_holding_refcell_ref.rs:72:9 + | +LL | / let b = x.borrow_mut(); +LL | | baz().await +LL | | } + | |_____^ + +error: aborting due to 6 previous errors + -- cgit 1.4.1-3-g733a5 From 4a4f998c39b1b7e06c34a5fc8ed90e8752142e20 Mon Sep 17 00:00:00 2001 From: cgm616 Date: Fri, 16 Oct 2020 11:12:37 -0400 Subject: Add new lint for undropped ManuallyDrop values --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 4 +++ clippy_lints/src/undropped_manually_drops.rs | 49 ++++++++++++++++++++++++++++ src/lintlist/mod.rs | 7 ++++ tests/ui/undropped_manually_drops.rs | 18 ++++++++++ 5 files changed, 79 insertions(+) create mode 100644 clippy_lints/src/undropped_manually_drops.rs create mode 100644 tests/ui/undropped_manually_drops.rs (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index d82f970b8bf..697e6166fdf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1979,6 +1979,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 +[`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 [`uninit_assumed_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_assumed_init diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d4d2f92a6a6..49ff8ad366e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -314,6 +314,7 @@ mod transmuting_null; mod trivially_copy_pass_by_ref; mod try_err; mod types; +mod undropped_manually_drops; mod unicode; mod unit_return_expecting_ord; mod unnamed_address; @@ -862,6 +863,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &types::UNIT_CMP, &types::UNNECESSARY_CAST, &types::VEC_BOX, + &undropped_manually_drops::UNDROPPED_MANUALLY_DROPS, &unicode::INVISIBLE_CHARACTERS, &unicode::NON_ASCII_LITERAL, &unicode::UNICODE_NOT_NFC, @@ -1137,6 +1139,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods)); store.register_early_pass(|| box asm_syntax::InlineAsmX86AttSyntax); store.register_early_pass(|| box asm_syntax::InlineAsmX86IntelSyntax); + store.register_late_pass(|| box undropped_manually_drops::UndroppedManuallyDrops); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ @@ -1790,6 +1793,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::ABSURD_EXTREME_COMPARISONS), LintId::of(&types::CAST_REF_TO_MUT), LintId::of(&types::UNIT_CMP), + 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(&unnamed_address::FN_ADDRESS_COMPARISONS), diff --git a/clippy_lints/src/undropped_manually_drops.rs b/clippy_lints/src/undropped_manually_drops.rs new file mode 100644 index 00000000000..48a050777b7 --- /dev/null +++ b/clippy_lints/src/undropped_manually_drops.rs @@ -0,0 +1,49 @@ +use rustc_lint::{LateLintPass, LateContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_hir::*; +use crate::utils::{match_function_call, is_type_lang_item, paths, span_lint_and_help}; + +declare_clippy_lint! { + /// **What it does:** Prevents the safe `std::mem::drop` function from being called on `std::mem::ManuallyDrop`. + /// + /// **Why is this bad?** The safe `drop` function does not drop the inner value of a `ManuallyDrop`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// struct S; + /// drop(std::mem::ManuallyDrop::new(S)); + /// ``` + /// Use instead: + /// ```rust + /// struct S; + /// unsafe { + /// std::mem::ManuallyDrop::drop(std::mem::ManuallyDrop::new(S)); + /// } + /// ``` + pub UNDROPPED_MANUALLY_DROPS, + correctness, + "use of safe `std::mem::drop` function to drop a std::mem::ManuallyDrop, which will not drop the inner value" +} + +declare_lint_pass!(UndroppedManuallyDrops => [UNDROPPED_MANUALLY_DROPS]); + +impl LateLintPass<'tcx> for UndroppedManuallyDrops { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let Some(ref args) = match_function_call(cx, expr, &paths::DROP) { + let ty = cx.typeck_results().expr_ty(&args[0]); + if is_type_lang_item(cx, ty, lang_items::LangItem::ManuallyDrop) { + span_lint_and_help( + cx, + UNDROPPED_MANUALLY_DROPS, + expr.span, + "the inner value of this ManuallyDrop will not be dropped", + None, + "to drop a `ManuallyDrop`, use std::mem::ManuallyDrop::drop" + ); + } + } + } +} \ No newline at end of file diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 6301d623a2b..25a69e78c8f 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2412,6 +2412,13 @@ vec![ deprecation: None, module: "trait_bounds", }, + Lint { + name: "undropped_manually_drops", + group: "correctness", + desc: "use of safe `std::mem::drop` function to drop a std::mem::ManuallyDrop, which will not drop the inner value", + deprecation: None, + module: "undropped_manually_drops", + }, Lint { name: "unicode_not_nfc", group: "pedantic", diff --git a/tests/ui/undropped_manually_drops.rs b/tests/ui/undropped_manually_drops.rs new file mode 100644 index 00000000000..bea62e1751e --- /dev/null +++ b/tests/ui/undropped_manually_drops.rs @@ -0,0 +1,18 @@ +#![warn(clippy::undropped_manually_drops)] + +struct S; + +fn main() { + let f = drop; + let manual = std::mem::ManuallyDrop::new(S); + + // These lines will not drop `S` + drop(std::mem::ManuallyDrop::new(S)); + f(manual); + + // These lines will + unsafe { + std::mem::ManuallyDrop::drop(std::mem::ManuallyDrop::new(S)); + std::mem::ManuallyDrop::drop(manual); + } +} -- cgit 1.4.1-3-g733a5 From e70817e712fd4d4e930ead0d587031e2b4a97a2e Mon Sep 17 00:00:00 2001 From: cgm616 Date: Fri, 16 Oct 2020 16:20:03 -0400 Subject: Update tests and add known problems to docs --- clippy_lints/src/lib.rs | 1 + clippy_lints/src/undropped_manually_drops.rs | 15 ++++++++------- tests/ui/undropped_manually_drops.rs | 22 +++++++++++++++------- tests/ui/undropped_manually_drops.stderr | 19 +++++++++++++++++++ 4 files changed, 43 insertions(+), 14 deletions(-) create mode 100644 tests/ui/undropped_manually_drops.stderr (limited to 'tests') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 49ff8ad366e..97e7cfd1bb2 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1524,6 +1524,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::UNIT_CMP), LintId::of(&types::UNNECESSARY_CAST), 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(&unnamed_address::FN_ADDRESS_COMPARISONS), diff --git a/clippy_lints/src/undropped_manually_drops.rs b/clippy_lints/src/undropped_manually_drops.rs index 48a050777b7..5443f1601fc 100644 --- a/clippy_lints/src/undropped_manually_drops.rs +++ b/clippy_lints/src/undropped_manually_drops.rs @@ -1,14 +1,15 @@ -use rustc_lint::{LateLintPass, LateContext}; +use crate::utils::{is_type_lang_item, match_function_call, paths, span_lint_and_help}; +use rustc_hir::{lang_items, Expr}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_hir::*; -use crate::utils::{match_function_call, is_type_lang_item, paths, span_lint_and_help}; declare_clippy_lint! { /// **What it does:** Prevents the safe `std::mem::drop` function from being called on `std::mem::ManuallyDrop`. /// /// **Why is this bad?** The safe `drop` function does not drop the inner value of a `ManuallyDrop`. /// - /// **Known problems:** None. + /// **Known problems:** Does not catch cases if the user binds `std::mem::drop` + /// to a different name and calls it that way. /// /// **Example:** /// @@ -20,7 +21,7 @@ declare_clippy_lint! { /// ```rust /// struct S; /// unsafe { - /// std::mem::ManuallyDrop::drop(std::mem::ManuallyDrop::new(S)); + /// std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S)); /// } /// ``` pub UNDROPPED_MANUALLY_DROPS, @@ -41,9 +42,9 @@ impl LateLintPass<'tcx> for UndroppedManuallyDrops { expr.span, "the inner value of this ManuallyDrop will not be dropped", None, - "to drop a `ManuallyDrop`, use std::mem::ManuallyDrop::drop" + "to drop a `ManuallyDrop`, use std::mem::ManuallyDrop::drop", ); } } } -} \ No newline at end of file +} diff --git a/tests/ui/undropped_manually_drops.rs b/tests/ui/undropped_manually_drops.rs index bea62e1751e..f4cfc92e1cd 100644 --- a/tests/ui/undropped_manually_drops.rs +++ b/tests/ui/undropped_manually_drops.rs @@ -3,16 +3,24 @@ struct S; fn main() { - let f = drop; - let manual = std::mem::ManuallyDrop::new(S); + let f = std::mem::drop; + let g = std::mem::ManuallyDrop::drop; + let mut manual1 = std::mem::ManuallyDrop::new(S); + let mut manual2 = std::mem::ManuallyDrop::new(S); + let mut manual3 = std::mem::ManuallyDrop::new(S); + let mut manual4 = std::mem::ManuallyDrop::new(S); - // These lines will not drop `S` + // These lines will not drop `S` and should be linted drop(std::mem::ManuallyDrop::new(S)); - f(manual); + drop(manual1); - // These lines will + // FIXME: this line is not linted, though it should be + f(manual2); + + // These lines will drop `S` and should be okay. unsafe { - std::mem::ManuallyDrop::drop(std::mem::ManuallyDrop::new(S)); - std::mem::ManuallyDrop::drop(manual); + std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S)); + std::mem::ManuallyDrop::drop(&mut manual3); + g(&mut manual4); } } diff --git a/tests/ui/undropped_manually_drops.stderr b/tests/ui/undropped_manually_drops.stderr new file mode 100644 index 00000000000..2ac0fe98697 --- /dev/null +++ b/tests/ui/undropped_manually_drops.stderr @@ -0,0 +1,19 @@ +error: the inner value of this ManuallyDrop will not be dropped + --> $DIR/undropped_manually_drops.rs:14:5 + | +LL | drop(std::mem::ManuallyDrop::new(S)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::undropped-manually-drops` implied by `-D warnings` + = help: to drop a `ManuallyDrop`, use std::mem::ManuallyDrop::drop + +error: the inner value of this ManuallyDrop will not be dropped + --> $DIR/undropped_manually_drops.rs:15:5 + | +LL | drop(manual1); + | ^^^^^^^^^^^^^ + | + = help: to drop a `ManuallyDrop`, use std::mem::ManuallyDrop::drop + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From c693de350aff4a3930e66bccf65caf79b390dca2 Mon Sep 17 00:00:00 2001 From: Andre Bogus Date: Thu, 15 Oct 2020 22:05:51 +0200 Subject: New lint: manual-range-contains --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 3 + clippy_lints/src/ranges.rs | 216 +++++++++++++++++++++++++++++++++++------ src/lintlist/mod.rs | 7 ++ tests/ui/range_contains.fixed | 41 ++++++++ tests/ui/range_contains.rs | 41 ++++++++ tests/ui/range_contains.stderr | 76 +++++++++++++++ 7 files changed, 354 insertions(+), 31 deletions(-) create mode 100644 tests/ui/range_contains.fixed create mode 100644 tests/ui/range_contains.rs create mode 100644 tests/ui/range_contains.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index d82f970b8bf..c9f0406a806 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1793,6 +1793,7 @@ Released 2018-09-13 [`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn [`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy [`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive +[`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains [`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic [`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip [`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d4d2f92a6a6..cc50655cb00 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -785,6 +785,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &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, @@ -1469,6 +1470,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), @@ -1624,6 +1626,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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(®ex::TRIVIAL_REGEX), diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index cc492917b9d..de54711d851 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -2,15 +2,19 @@ use crate::consts::{constant, Constant}; use if_chain::if_chain; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; +use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Spanned; +use rustc_span::source_map::{Span, Spanned}; +use rustc_span::symbol::Ident; use std::cmp::Ordering; use crate::utils::sugg::Sugg; -use crate::utils::{get_parent_expr, is_integer_const, snippet, snippet_opt, span_lint, span_lint_and_then}; +use crate::utils::{ + get_parent_expr, is_integer_const, single_segment_path, snippet, snippet_opt, snippet_with_applicability, + span_lint, span_lint_and_sugg, span_lint_and_then, +}; use crate::utils::{higher, SpanlessEq}; declare_clippy_lint! { @@ -128,43 +132,51 @@ declare_clippy_lint! { "reversing the limits of range expressions, resulting in empty ranges" } +declare_clippy_lint! { + /// **What it does:** Checks for expressions like `x >= 3 && x < 8` that could + /// be more readably expressed as `(3..8).contains(x)`. + /// + /// **Why is this bad?** `contains` expresses the intent better and has less + /// failure modes (such as fencepost errors or using `||` instead of `&&`). + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // given + /// let x = 6; + /// + /// assert!(x >= 3 && x < 8); + /// ``` + /// Use instead: + /// ```rust + ///# let x = 6; + /// assert!((3..8).contains(&x)); + /// ``` + pub MANUAL_RANGE_CONTAINS, + style, + "manually reimplementing {`Range`, `RangeInclusive`}`::contains`" +} + declare_lint_pass!(Ranges => [ RANGE_ZIP_WITH_LEN, RANGE_PLUS_ONE, RANGE_MINUS_ONE, REVERSED_EMPTY_RANGES, + MANUAL_RANGE_CONTAINS, ]); impl<'tcx> LateLintPass<'tcx> for Ranges { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind { - let name = path.ident.as_str(); - if name == "zip" && args.len() == 2 { - let iter = &args[0].kind; - let zip_arg = &args[1]; - if_chain! { - // `.iter()` call - if let ExprKind::MethodCall(ref iter_path, _, ref iter_args , _) = *iter; - if iter_path.ident.name == sym!(iter); - // range expression in `.zip()` call: `0..x.len()` - if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::range(zip_arg); - if is_integer_const(cx, start, 0); - // `.len()` call - if let ExprKind::MethodCall(ref len_path, _, ref len_args, _) = end.kind; - if len_path.ident.name == sym!(len) && len_args.len() == 1; - // `.iter()` and `.len()` called on same `Path` - if let ExprKind::Path(QPath::Resolved(_, ref iter_path)) = iter_args[0].kind; - if let ExprKind::Path(QPath::Resolved(_, ref len_path)) = len_args[0].kind; - if SpanlessEq::new(cx).eq_path_segments(&iter_path.segments, &len_path.segments); - then { - span_lint(cx, - RANGE_ZIP_WITH_LEN, - expr.span, - &format!("it is more idiomatic to use `{}.iter().enumerate()`", - snippet(cx, iter_args[0].span, "_"))); - } - } - } + match expr.kind { + ExprKind::MethodCall(ref path, _, ref args, _) => { + check_range_zip_with_len(cx, path, args, expr.span); + }, + ExprKind::Binary(ref op, ref l, ref r) => { + check_possible_range_contains(cx, op.node, l, r, expr.span); + }, + _ => {}, } check_exclusive_range_plus_one(cx, expr); @@ -173,6 +185,148 @@ impl<'tcx> LateLintPass<'tcx> for Ranges { } } +fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'_>, r: &Expr<'_>, span: Span) { + let combine_and = match op { + BinOpKind::And | BinOpKind::BitAnd => true, + BinOpKind::Or | BinOpKind::BitOr => false, + _ => return, + }; + // value, name, order (higher/lower), inclusiveness + if let (Some((lval, lname, name_span, lval_span, lord, linc)), Some((rval, rname, _, rval_span, rord, rinc))) = + (check_range_bounds(cx, l), check_range_bounds(cx, r)) + { + // we only lint comparisons on the same name and with different + // direction + if lname != rname || lord == rord { + return; + } + let ord = Constant::partial_cmp(cx.tcx, cx.typeck_results().expr_ty(l), &lval, &rval); + if combine_and && ord == Some(rord) { + // order lower bound and upper bound + let (l_span, u_span, l_inc, u_inc) = if rord == Ordering::Less { + (lval_span, rval_span, linc, rinc) + } else { + (rval_span, lval_span, rinc, linc) + }; + // we only lint inclusive lower bounds + if !l_inc { + return; + } + let (range_type, range_op) = if u_inc { + ("RangeInclusive", "..=") + } else { + ("Range", "..") + }; + let mut applicability = Applicability::MachineApplicable; + let name = snippet_with_applicability(cx, name_span, "_", &mut applicability); + let lo = snippet_with_applicability(cx, l_span, "_", &mut applicability); + let hi = snippet_with_applicability(cx, u_span, "_", &mut applicability); + span_lint_and_sugg( + cx, + MANUAL_RANGE_CONTAINS, + span, + &format!("manual `{}::contains` implementation", range_type), + "use", + format!("({}{}{}).contains(&{})", lo, range_op, hi, name), + applicability, + ); + } else if !combine_and && ord == Some(lord) { + // `!_.contains(_)` + // order lower bound and upper bound + let (l_span, u_span, l_inc, u_inc) = if lord == Ordering::Less { + (lval_span, rval_span, linc, rinc) + } else { + (rval_span, lval_span, rinc, linc) + }; + if l_inc { + return; + } + let (range_type, range_op) = if u_inc { + ("Range", "..") + } else { + ("RangeInclusive", "..=") + }; + let mut applicability = Applicability::MachineApplicable; + let name = snippet_with_applicability(cx, name_span, "_", &mut applicability); + let lo = snippet_with_applicability(cx, l_span, "_", &mut applicability); + let hi = snippet_with_applicability(cx, u_span, "_", &mut applicability); + span_lint_and_sugg( + cx, + MANUAL_RANGE_CONTAINS, + span, + &format!("manual `!{}::contains` implementation", range_type), + "use", + format!("!({}{}{}).contains(&{})", lo, range_op, hi, name), + applicability, + ); + } + } +} + +fn check_range_bounds(cx: &LateContext<'_>, ex: &Expr<'_>) -> Option<(Constant, Ident, Span, Span, Ordering, bool)> { + if let ExprKind::Binary(ref op, ref l, ref r) = ex.kind { + let (inclusive, ordering) = match op.node { + BinOpKind::Gt => (false, Ordering::Greater), + BinOpKind::Ge => (true, Ordering::Greater), + BinOpKind::Lt => (false, Ordering::Less), + BinOpKind::Le => (true, Ordering::Less), + _ => return None, + }; + if let Some(id) = match_ident(l) { + if let Some((c, _)) = constant(cx, cx.typeck_results(), r) { + return Some((c, id, l.span, r.span, ordering, inclusive)); + } + } else if let Some(id) = match_ident(r) { + if let Some((c, _)) = constant(cx, cx.typeck_results(), l) { + return Some((c, id, r.span, l.span, ordering.reverse(), inclusive)); + } + } + } + None +} + +fn match_ident(e: &Expr<'_>) -> Option { + if let ExprKind::Path(ref qpath) = e.kind { + if let Some(seg) = single_segment_path(qpath) { + if seg.args.is_none() { + return Some(seg.ident); + } + } + } + None +} + +fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args: &[Expr<'_>], span: Span) { + let name = path.ident.as_str(); + if name == "zip" && args.len() == 2 { + let iter = &args[0].kind; + let zip_arg = &args[1]; + if_chain! { + // `.iter()` call + if let ExprKind::MethodCall(ref iter_path, _, ref iter_args, _) = *iter; + if iter_path.ident.name == sym!(iter); + // range expression in `.zip()` call: `0..x.len()` + if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::range(zip_arg); + if is_integer_const(cx, start, 0); + // `.len()` call + if let ExprKind::MethodCall(ref len_path, _, ref len_args, _) = end.kind; + if len_path.ident.name == sym!(len) && len_args.len() == 1; + // `.iter()` and `.len()` called on same `Path` + if let ExprKind::Path(QPath::Resolved(_, ref iter_path)) = iter_args[0].kind; + if let ExprKind::Path(QPath::Resolved(_, ref len_path)) = len_args[0].kind; + if SpanlessEq::new(cx).eq_path_segments(&iter_path.segments, &len_path.segments); + then { + span_lint(cx, + RANGE_ZIP_WITH_LEN, + span, + &format!("it is more idiomatic to use `{}.iter().enumerate()`", + snippet(cx, iter_args[0].span, "_")) + ); + } + } + } +} + // exclusive range plus one: `x..(y+1)` fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 6301d623a2b..7bb68acc062 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1159,6 +1159,13 @@ vec![ deprecation: None, module: "manual_non_exhaustive", }, + Lint { + name: "manual_range_contains", + group: "style", + desc: "manually reimplementing {`Range`, `RangeInclusive`}`::contains`", + deprecation: None, + module: "ranges", + }, Lint { name: "manual_saturating_arithmetic", group: "style", diff --git a/tests/ui/range_contains.fixed b/tests/ui/range_contains.fixed new file mode 100644 index 00000000000..632a6592a28 --- /dev/null +++ b/tests/ui/range_contains.fixed @@ -0,0 +1,41 @@ +// run-rustfix + +#[warn(clippy::manual_range_contains)] +#[allow(unused)] +#[allow(clippy::no_effect)] +#[allow(clippy::short_circuit_statement)] +#[allow(clippy::unnecessary_operation)] +fn main() { + let x = 9_u32; + + // order shouldn't matter + (8..12).contains(&x); + (21..42).contains(&x); + (1..100).contains(&x); + + // also with inclusive ranges + (9..=99).contains(&x); + (1..=33).contains(&x); + (1..=999).contains(&x); + + // and the outside + !(8..12).contains(&x); + !(21..42).contains(&x); + !(1..100).contains(&x); + + // also with the outside of inclusive ranges + !(9..=99).contains(&x); + !(1..=33).contains(&x); + !(1..=999).contains(&x); + + // not a range.contains + x > 8 && x < 12; // lower bound not inclusive + x < 8 && x <= 12; // same direction + x >= 12 && 12 >= x; // same bounds + x < 8 && x > 12; // wrong direction + + x <= 8 || x >= 12; + x >= 8 || x >= 12; + x < 12 || 12 < x; + x >= 8 || x <= 12; +} diff --git a/tests/ui/range_contains.rs b/tests/ui/range_contains.rs new file mode 100644 index 00000000000..6af0d034ef6 --- /dev/null +++ b/tests/ui/range_contains.rs @@ -0,0 +1,41 @@ +// run-rustfix + +#[warn(clippy::manual_range_contains)] +#[allow(unused)] +#[allow(clippy::no_effect)] +#[allow(clippy::short_circuit_statement)] +#[allow(clippy::unnecessary_operation)] +fn main() { + let x = 9_u32; + + // order shouldn't matter + x >= 8 && x < 12; + x < 42 && x >= 21; + 100 > x && 1 <= x; + + // also with inclusive ranges + x >= 9 && x <= 99; + x <= 33 && x >= 1; + 999 >= x && 1 <= x; + + // and the outside + x < 8 || x >= 12; + x >= 42 || x < 21; + 100 <= x || 1 > x; + + // also with the outside of inclusive ranges + x < 9 || x > 99; + x > 33 || x < 1; + 999 < x || 1 > x; + + // not a range.contains + x > 8 && x < 12; // lower bound not inclusive + x < 8 && x <= 12; // same direction + x >= 12 && 12 >= x; // same bounds + x < 8 && x > 12; // wrong direction + + x <= 8 || x >= 12; + x >= 8 || x >= 12; + x < 12 || 12 < x; + x >= 8 || x <= 12; +} diff --git a/tests/ui/range_contains.stderr b/tests/ui/range_contains.stderr new file mode 100644 index 00000000000..69b009eafc3 --- /dev/null +++ b/tests/ui/range_contains.stderr @@ -0,0 +1,76 @@ +error: manual `Range::contains` implementation + --> $DIR/range_contains.rs:12:5 + | +LL | x >= 8 && x < 12; + | ^^^^^^^^^^^^^^^^ help: use: `(8..12).contains(&x)` + | + = note: `-D clippy::manual-range-contains` implied by `-D warnings` + +error: manual `Range::contains` implementation + --> $DIR/range_contains.rs:13:5 + | +LL | x < 42 && x >= 21; + | ^^^^^^^^^^^^^^^^^ help: use: `(21..42).contains(&x)` + +error: manual `Range::contains` implementation + --> $DIR/range_contains.rs:14:5 + | +LL | 100 > x && 1 <= x; + | ^^^^^^^^^^^^^^^^^ help: use: `(1..100).contains(&x)` + +error: manual `RangeInclusive::contains` implementation + --> $DIR/range_contains.rs:17:5 + | +LL | x >= 9 && x <= 99; + | ^^^^^^^^^^^^^^^^^ help: use: `(9..=99).contains(&x)` + +error: manual `RangeInclusive::contains` implementation + --> $DIR/range_contains.rs:18:5 + | +LL | x <= 33 && x >= 1; + | ^^^^^^^^^^^^^^^^^ help: use: `(1..=33).contains(&x)` + +error: manual `RangeInclusive::contains` implementation + --> $DIR/range_contains.rs:19:5 + | +LL | 999 >= x && 1 <= x; + | ^^^^^^^^^^^^^^^^^^ help: use: `(1..=999).contains(&x)` + +error: manual `!Range::contains` implementation + --> $DIR/range_contains.rs:22:5 + | +LL | x < 8 || x >= 12; + | ^^^^^^^^^^^^^^^^ help: use: `!(8..12).contains(&x)` + +error: manual `!Range::contains` implementation + --> $DIR/range_contains.rs:23:5 + | +LL | x >= 42 || x < 21; + | ^^^^^^^^^^^^^^^^^ help: use: `!(21..42).contains(&x)` + +error: manual `!Range::contains` implementation + --> $DIR/range_contains.rs:24:5 + | +LL | 100 <= x || 1 > x; + | ^^^^^^^^^^^^^^^^^ help: use: `!(1..100).contains(&x)` + +error: manual `!RangeInclusive::contains` implementation + --> $DIR/range_contains.rs:27:5 + | +LL | x < 9 || x > 99; + | ^^^^^^^^^^^^^^^ help: use: `!(9..=99).contains(&x)` + +error: manual `!RangeInclusive::contains` implementation + --> $DIR/range_contains.rs:28:5 + | +LL | x > 33 || x < 1; + | ^^^^^^^^^^^^^^^ help: use: `!(1..=33).contains(&x)` + +error: manual `!RangeInclusive::contains` implementation + --> $DIR/range_contains.rs:29:5 + | +LL | 999 < x || 1 > x; + | ^^^^^^^^^^^^^^^^ help: use: `!(1..=999).contains(&x)` + +error: aborting due to 12 previous errors + -- cgit 1.4.1-3-g733a5 From f2da0c701edef601b16b512b3a244977bf4b3afe Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Thu, 22 Oct 2020 22:46:10 +0200 Subject: manual-unwrap-or / pr remarks --- tests/ui/manual_unwrap_or.fixed | 31 +++++++++++++++++++------- tests/ui/manual_unwrap_or.rs | 34 ++++++++++++++++++++++------- tests/ui/manual_unwrap_or.stderr | 47 ++++++++++++++++++++++++---------------- 3 files changed, 77 insertions(+), 35 deletions(-) (limited to 'tests') diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index ceb8985d3d5..c784de0f604 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -1,5 +1,6 @@ // run-rustfix #![allow(dead_code)] +#![allow(unused_variables)] fn option_unwrap_or() { // int case @@ -67,44 +68,58 @@ fn option_unwrap_or() { fn result_unwrap_or() { // int case + Ok::(1).unwrap_or(42); + + // int case, suggestion must surround with parenthesis (Ok(1) as Result).unwrap_or(42); // int case reversed - (Ok(1) as Result).unwrap_or(42); + Ok::(1).unwrap_or(42); // richer none expr - (Ok(1) as Result).unwrap_or(1 + 42); + Ok::(1).unwrap_or(1 + 42); // multiline case #[rustfmt::skip] - (Ok(1) as Result).unwrap_or({ + Ok::(1).unwrap_or({ 42 + 42 + 42 + 42 + 42 + 42 + 42 + 42 }); // string case - (Ok("Bob") as Result<&str, &str>).unwrap_or("Alice"); + Ok::<&str, &str>("Bob").unwrap_or("Alice"); // don't lint - match Ok(1) as Result { + match Ok::(1) { Ok(i) => i + 2, Err(_) => 42, }; - match Ok(1) as Result { + match Ok::(1) { Ok(i) => i, Err(_) => return, }; for j in 0..4 { - match Ok(j) as Result { + match Ok::(j) { Ok(i) => i, Err(_) => continue, }; - match Ok(j) as Result { + match Ok::(j) { Ok(i) => i, Err(_) => break, }; } + + // don't lint, Err value is used + match Ok::<&str, &str>("Alice") { + Ok(s) => s, + Err(s) => s, + }; + // could lint, but unused_variables takes care of it + match Ok::<&str, &str>("Alice") { + Ok(s) => s, + Err(s) => "Bob", + }; } fn main() {} diff --git a/tests/ui/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs index beca1de0ed1..df5f237c3fb 100644 --- a/tests/ui/manual_unwrap_or.rs +++ b/tests/ui/manual_unwrap_or.rs @@ -1,5 +1,6 @@ // run-rustfix #![allow(dead_code)] +#![allow(unused_variables)] fn option_unwrap_or() { // int case @@ -82,26 +83,32 @@ fn option_unwrap_or() { fn result_unwrap_or() { // int case + match Ok::(1) { + Ok(i) => i, + Err(_) => 42, + }; + + // int case, suggestion must surround with parenthesis match Ok(1) as Result { Ok(i) => i, Err(_) => 42, }; // int case reversed - match Ok(1) as Result { + match Ok::(1) { Err(_) => 42, Ok(i) => i, }; // richer none expr - match Ok(1) as Result { + match Ok::(1) { Ok(i) => i, Err(_) => 1 + 42, }; // multiline case #[rustfmt::skip] - match Ok(1) as Result { + match Ok::(1) { Ok(i) => i, Err(_) => { 42 + 42 @@ -111,30 +118,41 @@ fn result_unwrap_or() { }; // string case - match Ok("Bob") as Result<&str, &str> { + match Ok::<&str, &str>("Bob") { Ok(i) => i, Err(_) => "Alice", }; // don't lint - match Ok(1) as Result { + match Ok::(1) { Ok(i) => i + 2, Err(_) => 42, }; - match Ok(1) as Result { + match Ok::(1) { Ok(i) => i, Err(_) => return, }; for j in 0..4 { - match Ok(j) as Result { + match Ok::(j) { Ok(i) => i, Err(_) => continue, }; - match Ok(j) as Result { + match Ok::(j) { Ok(i) => i, Err(_) => break, }; } + + // don't lint, Err value is used + match Ok::<&str, &str>("Alice") { + Ok(s) => s, + Err(s) => s, + }; + // could lint, but unused_variables takes care of it + match Ok::<&str, &str>("Alice") { + Ok(s) => s, + Err(s) => "Bob", + }; } fn main() {} diff --git a/tests/ui/manual_unwrap_or.stderr b/tests/ui/manual_unwrap_or.stderr index 5d465666caf..5bc01bf4e68 100644 --- a/tests/ui/manual_unwrap_or.stderr +++ b/tests/ui/manual_unwrap_or.stderr @@ -1,5 +1,5 @@ error: this pattern reimplements `Option::unwrap_or` - --> $DIR/manual_unwrap_or.rs:6:5 + --> $DIR/manual_unwrap_or.rs:7:5 | LL | / match Some(1) { LL | | Some(i) => i, @@ -10,7 +10,7 @@ LL | | }; = note: `-D clippy::manual-unwrap-or` implied by `-D warnings` error: this pattern reimplements `Option::unwrap_or` - --> $DIR/manual_unwrap_or.rs:12:5 + --> $DIR/manual_unwrap_or.rs:13:5 | LL | / match Some(1) { LL | | None => 42, @@ -19,7 +19,7 @@ LL | | }; | |_____^ help: replace with: `Some(1).unwrap_or(42)` error: this pattern reimplements `Option::unwrap_or` - --> $DIR/manual_unwrap_or.rs:18:5 + --> $DIR/manual_unwrap_or.rs:19:5 | LL | / match Some(1) { LL | | Some(i) => i, @@ -28,7 +28,7 @@ LL | | }; | |_____^ help: replace with: `Some(1).unwrap_or(1 + 42)` error: this pattern reimplements `Option::unwrap_or` - --> $DIR/manual_unwrap_or.rs:25:5 + --> $DIR/manual_unwrap_or.rs:26:5 | LL | / match Some(1) { LL | | Some(i) => i, @@ -49,7 +49,7 @@ LL | }); | error: this pattern reimplements `Option::unwrap_or` - --> $DIR/manual_unwrap_or.rs:35:5 + --> $DIR/manual_unwrap_or.rs:36:5 | LL | / match Some("Bob") { LL | | Some(i) => i, @@ -58,7 +58,16 @@ LL | | }; | |_____^ help: replace with: `Some("Bob").unwrap_or("Alice")` error: this pattern reimplements `Result::unwrap_or` - --> $DIR/manual_unwrap_or.rs:85:5 + --> $DIR/manual_unwrap_or.rs:86:5 + | +LL | / match Ok::(1) { +LL | | Ok(i) => i, +LL | | Err(_) => 42, +LL | | }; + | |_____^ help: replace with: `Ok::(1).unwrap_or(42)` + +error: this pattern reimplements `Result::unwrap_or` + --> $DIR/manual_unwrap_or.rs:92:5 | LL | / match Ok(1) as Result { LL | | Ok(i) => i, @@ -67,27 +76,27 @@ LL | | }; | |_____^ help: replace with: `(Ok(1) as Result).unwrap_or(42)` error: this pattern reimplements `Result::unwrap_or` - --> $DIR/manual_unwrap_or.rs:91:5 + --> $DIR/manual_unwrap_or.rs:98:5 | -LL | / match Ok(1) as Result { +LL | / match Ok::(1) { LL | | Err(_) => 42, LL | | Ok(i) => i, LL | | }; - | |_____^ help: replace with: `(Ok(1) as Result).unwrap_or(42)` + | |_____^ help: replace with: `Ok::(1).unwrap_or(42)` error: this pattern reimplements `Result::unwrap_or` - --> $DIR/manual_unwrap_or.rs:97:5 + --> $DIR/manual_unwrap_or.rs:104:5 | -LL | / match Ok(1) as Result { +LL | / match Ok::(1) { LL | | Ok(i) => i, LL | | Err(_) => 1 + 42, LL | | }; - | |_____^ help: replace with: `(Ok(1) as Result).unwrap_or(1 + 42)` + | |_____^ help: replace with: `Ok::(1).unwrap_or(1 + 42)` error: this pattern reimplements `Result::unwrap_or` - --> $DIR/manual_unwrap_or.rs:104:5 + --> $DIR/manual_unwrap_or.rs:111:5 | -LL | / match Ok(1) as Result { +LL | / match Ok::(1) { LL | | Ok(i) => i, LL | | Err(_) => { LL | | 42 + 42 @@ -98,7 +107,7 @@ LL | | }; | help: replace with | -LL | (Ok(1) as Result).unwrap_or({ +LL | Ok::(1).unwrap_or({ LL | 42 + 42 LL | + 42 + 42 + 42 LL | + 42 + 42 + 42 @@ -106,13 +115,13 @@ LL | }); | error: this pattern reimplements `Result::unwrap_or` - --> $DIR/manual_unwrap_or.rs:114:5 + --> $DIR/manual_unwrap_or.rs:121:5 | -LL | / match Ok("Bob") as Result<&str, &str> { +LL | / match Ok::<&str, &str>("Bob") { LL | | Ok(i) => i, LL | | Err(_) => "Alice", LL | | }; - | |_____^ help: replace with: `(Ok("Bob") as Result<&str, &str>).unwrap_or("Alice")` + | |_____^ help: replace with: `Ok::<&str, &str>("Bob").unwrap_or("Alice")` -error: aborting due to 10 previous errors +error: aborting due to 11 previous errors -- cgit 1.4.1-3-g733a5 From 6533d8becfd198299d0bd38550dd6c574cbd194f Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Thu, 22 Oct 2020 23:39:59 +0200 Subject: manual-unwrap-or / pr remarks, round 2 --- clippy_lints/src/manual_unwrap_or.rs | 4 +++- tests/ui/manual_unwrap_or.fixed | 4 ++++ tests/ui/manual_unwrap_or.rs | 7 +++++++ tests/ui/manual_unwrap_or.stderr | 21 +++++++++++++++------ 4 files changed, 29 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index f3f1e31abde..cc9ee28d027 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -111,7 +111,9 @@ fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { then { let reindented_or_body = utils::reindent_multiline(or_body_snippet.into(), true, Some(indent)); - let wrap_in_parens = !matches!(scrutinee, Expr { kind: ExprKind::Call(..), .. }); + let wrap_in_parens = !matches!(scrutinee, Expr { + kind: ExprKind::Call(..) | ExprKind::Path(_), .. + }); let l_paren = if wrap_in_parens { "(" } else { "" }; let r_paren = if wrap_in_parens { ")" } else { "" }; utils::span_lint_and_sugg( diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index c784de0f604..582f5c6f7a8 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -70,6 +70,10 @@ fn result_unwrap_or() { // int case Ok::(1).unwrap_or(42); + // int case, scrutinee is a binding + let a = Ok::(1); + a.unwrap_or(42); + // int case, suggestion must surround with parenthesis (Ok(1) as Result).unwrap_or(42); diff --git a/tests/ui/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs index df5f237c3fb..0e2b7ecadb3 100644 --- a/tests/ui/manual_unwrap_or.rs +++ b/tests/ui/manual_unwrap_or.rs @@ -88,6 +88,13 @@ fn result_unwrap_or() { Err(_) => 42, }; + // int case, scrutinee is a binding + let a = Ok::(1); + match a { + Ok(i) => i, + Err(_) => 42, + }; + // int case, suggestion must surround with parenthesis match Ok(1) as Result { Ok(i) => i, diff --git a/tests/ui/manual_unwrap_or.stderr b/tests/ui/manual_unwrap_or.stderr index 5bc01bf4e68..6603ab43437 100644 --- a/tests/ui/manual_unwrap_or.stderr +++ b/tests/ui/manual_unwrap_or.stderr @@ -67,7 +67,16 @@ LL | | }; | |_____^ help: replace with: `Ok::(1).unwrap_or(42)` error: this pattern reimplements `Result::unwrap_or` - --> $DIR/manual_unwrap_or.rs:92:5 + --> $DIR/manual_unwrap_or.rs:93:5 + | +LL | / match a { +LL | | Ok(i) => i, +LL | | Err(_) => 42, +LL | | }; + | |_____^ help: replace with: `a.unwrap_or(42)` + +error: this pattern reimplements `Result::unwrap_or` + --> $DIR/manual_unwrap_or.rs:99:5 | LL | / match Ok(1) as Result { LL | | Ok(i) => i, @@ -76,7 +85,7 @@ LL | | }; | |_____^ help: replace with: `(Ok(1) as Result).unwrap_or(42)` error: this pattern reimplements `Result::unwrap_or` - --> $DIR/manual_unwrap_or.rs:98:5 + --> $DIR/manual_unwrap_or.rs:105:5 | LL | / match Ok::(1) { LL | | Err(_) => 42, @@ -85,7 +94,7 @@ LL | | }; | |_____^ help: replace with: `Ok::(1).unwrap_or(42)` error: this pattern reimplements `Result::unwrap_or` - --> $DIR/manual_unwrap_or.rs:104:5 + --> $DIR/manual_unwrap_or.rs:111:5 | LL | / match Ok::(1) { LL | | Ok(i) => i, @@ -94,7 +103,7 @@ LL | | }; | |_____^ help: replace with: `Ok::(1).unwrap_or(1 + 42)` error: this pattern reimplements `Result::unwrap_or` - --> $DIR/manual_unwrap_or.rs:111:5 + --> $DIR/manual_unwrap_or.rs:118:5 | LL | / match Ok::(1) { LL | | Ok(i) => i, @@ -115,7 +124,7 @@ LL | }); | error: this pattern reimplements `Result::unwrap_or` - --> $DIR/manual_unwrap_or.rs:121:5 + --> $DIR/manual_unwrap_or.rs:128:5 | LL | / match Ok::<&str, &str>("Bob") { LL | | Ok(i) => i, @@ -123,5 +132,5 @@ LL | | Err(_) => "Alice", LL | | }; | |_____^ help: replace with: `Ok::<&str, &str>("Bob").unwrap_or("Alice")` -error: aborting due to 11 previous errors +error: aborting due to 12 previous errors -- cgit 1.4.1-3-g733a5 From e8f12d2f02644834282dec0c27710886f1e85ae6 Mon Sep 17 00:00:00 2001 From: Geoffrey Copin Date: Thu, 22 Oct 2020 23:53:50 +0200 Subject: Address review comments --- clippy_lints/src/types.rs | 53 +++++++++++++++++--------------- tests/ui/unnecessary_cast_fixable.fixed | 6 ++-- tests/ui/unnecessary_cast_fixable.rs | 2 ++ tests/ui/unnecessary_cast_fixable.stderr | 22 ++++++++++--- 4 files changed, 52 insertions(+), 31 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 716d027e434..f4bb648d15a 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -3,7 +3,6 @@ use std::borrow::Cow; use std::cmp::Ordering; use std::collections::BTreeMap; -use std::fmt::Display; use if_chain::if_chain; use rustc_ast::{FloatTy, IntTy, LitFloatType, LitIntType, LitKind, UintTy}; @@ -12,7 +11,7 @@ use rustc_hir as hir; use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::{ BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericParamKind, HirId, ImplItem, - ImplItemKind, Item, ItemKind, Lifetime, Local, MatchSource, MutTy, Mutability, Node, QPath, Stmt, StmtKind, + ImplItemKind, Item, ItemKind, Lifetime, Lit, Local, MatchSource, MutTy, Mutability, Node, QPath, Stmt, StmtKind, TraitFn, TraitItem, TraitItemKind, TyKind, UnOp, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -1225,7 +1224,8 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for casts to the same type. + /// **What it does:** Checks for casts to the same type, casts of int literals to integer types + /// and casts of float literals to float types. /// /// **Why is this bad?** It's just unnecessary. /// @@ -1234,6 +1234,7 @@ declare_clippy_lint! { /// **Example:** /// ```rust /// let _ = 2i32 as i32; + /// let _ = 0.5 as f32; /// ``` pub UNNECESSARY_CAST, complexity, @@ -1599,7 +1600,9 @@ impl<'tcx> LateLintPass<'tcx> for Casts { if let ExprKind::Cast(ref ex, _) = expr.kind { let (cast_from, cast_to) = (cx.typeck_results().expr_ty(ex), cx.typeck_results().expr_ty(expr)); lint_fn_to_numeric_cast(cx, expr, ex, cast_from, cast_to); - if let ExprKind::Lit(ref lit) = ex.kind { + if let Some(lit) = get_numeric_literal(ex) { + let literal_str = snippet_opt(cx, lit.span).unwrap_or_default(); + if_chain! { if let LitKind::Int(n, _) = lit.node; if let Some(src) = snippet_opt(cx, lit.span); @@ -1609,25 +1612,19 @@ impl<'tcx> LateLintPass<'tcx> for Casts { let to_nbits = fp_ty_mantissa_nbits(cast_to); if from_nbits != 0 && to_nbits != 0 && from_nbits <= to_nbits && num_lit.is_decimal(); then { - show_unnecessary_cast(cx, expr, n , cast_from, cast_to); + show_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to); return; } } match lit.node { - LitKind::Int(num, LitIntType::Unsuffixed) if cast_to.is_integral() => { - show_unnecessary_cast(cx, expr, num, cast_from, cast_to); - return; + LitKind::Int(_, LitIntType::Unsuffixed) if cast_to.is_integral() => { + show_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to); }, - LitKind::Float(num, LitFloatType::Unsuffixed) if cast_to.is_floating_point() => { - show_unnecessary_cast(cx, expr, num, cast_from, cast_to); - return; + LitKind::Float(_, LitFloatType::Unsuffixed) if cast_to.is_floating_point() => { + show_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to); }, - _ => (), - }; - - match lit.node { - LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => {}, + LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => (), _ => { if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) { span_lint( @@ -1652,13 +1649,21 @@ impl<'tcx> LateLintPass<'tcx> for Casts { } } -fn show_unnecessary_cast( - cx: &LateContext<'_>, - expr: &Expr<'_>, - num: Num, - cast_from: Ty<'_>, - cast_to: Ty<'_>, -) { +fn get_numeric_literal<'e>(expr: &'e Expr<'e>) -> Option<&'e Lit> { + match expr.kind { + ExprKind::Lit(ref lit) => Some(lit), + ExprKind::Unary(UnOp::UnNeg, e) => { + if let ExprKind::Lit(ref lit) = e.kind { + Some(lit) + } else { + None + } + }, + _ => None, + } +} + +fn show_unnecessary_cast(cx: &LateContext<'_>, expr: &Expr<'_>, literal_str: &str, cast_from: Ty<'_>, cast_to: Ty<'_>) { let literal_kind_name = if cast_from.is_integral() { "integer" } else { "float" }; span_lint_and_sugg( cx, @@ -1666,7 +1671,7 @@ fn show_unnecessary_cast( expr.span, &format!("casting {} literal to `{}` is unnecessary", literal_kind_name, cast_to), "try", - format!("{}_{}", num, cast_to), + format!("{}_{}", literal_str, cast_to), Applicability::MachineApplicable, ); } diff --git a/tests/ui/unnecessary_cast_fixable.fixed b/tests/ui/unnecessary_cast_fixable.fixed index ba52fc2703f..54853f4b8a2 100644 --- a/tests/ui/unnecessary_cast_fixable.fixed +++ b/tests/ui/unnecessary_cast_fixable.fixed @@ -20,8 +20,10 @@ fn main() { 0b11 as f64; 1_u32; - 16_i32; - 2_usize; + 0x10_i32; + 0b10_usize; + 0o73_u16; + 1_000_000_000_u32; 1.0_f64; 0.5_f32; diff --git a/tests/ui/unnecessary_cast_fixable.rs b/tests/ui/unnecessary_cast_fixable.rs index 0d2115548fd..8da3d947702 100644 --- a/tests/ui/unnecessary_cast_fixable.rs +++ b/tests/ui/unnecessary_cast_fixable.rs @@ -22,6 +22,8 @@ fn main() { 1 as u32; 0x10 as i32; 0b10 as usize; + 0o73 as u16; + 1_000_000_000 as u32; 1.0 as f64; 0.5 as f32; diff --git a/tests/ui/unnecessary_cast_fixable.stderr b/tests/ui/unnecessary_cast_fixable.stderr index 474e62c30d5..28fb9540afc 100644 --- a/tests/ui/unnecessary_cast_fixable.stderr +++ b/tests/ui/unnecessary_cast_fixable.stderr @@ -28,25 +28,37 @@ error: casting integer literal to `i32` is unnecessary --> $DIR/unnecessary_cast_fixable.rs:23:5 | LL | 0x10 as i32; - | ^^^^^^^^^^^ help: try: `16_i32` + | ^^^^^^^^^^^ help: try: `0x10_i32` error: casting integer literal to `usize` is unnecessary --> $DIR/unnecessary_cast_fixable.rs:24:5 | LL | 0b10 as usize; - | ^^^^^^^^^^^^^ help: try: `2_usize` + | ^^^^^^^^^^^^^ help: try: `0b10_usize` -error: casting float literal to `f64` is unnecessary +error: casting integer literal to `u16` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:25:5 + | +LL | 0o73 as u16; + | ^^^^^^^^^^^ help: try: `0o73_u16` + +error: casting integer literal to `u32` is unnecessary --> $DIR/unnecessary_cast_fixable.rs:26:5 | +LL | 1_000_000_000 as u32; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `1_000_000_000_u32` + +error: casting float literal to `f64` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:28:5 + | LL | 1.0 as f64; | ^^^^^^^^^^ help: try: `1.0_f64` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:27:5 + --> $DIR/unnecessary_cast_fixable.rs:29:5 | LL | 0.5 as f32; | ^^^^^^^^^^ help: try: `0.5_f32` -error: aborting due to 8 previous errors +error: aborting due to 10 previous errors -- cgit 1.4.1-3-g733a5 From 02f01104bfbb935ee1c3c3971ccf055173e4f82b Mon Sep 17 00:00:00 2001 From: Geoffrey Copin Date: Fri, 23 Oct 2020 00:04:27 +0200 Subject: Add test case for negative literals --- clippy_lints/src/types.rs | 2 +- tests/ui/unnecessary_cast_fixable.fixed | 3 +++ tests/ui/unnecessary_cast_fixable.rs | 3 +++ tests/ui/unnecessary_cast_fixable.stderr | 14 +++++++++++++- 4 files changed, 20 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index f4bb648d15a..3a088709a7e 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -1601,7 +1601,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { let (cast_from, cast_to) = (cx.typeck_results().expr_ty(ex), cx.typeck_results().expr_ty(expr)); lint_fn_to_numeric_cast(cx, expr, ex, cast_from, cast_to); if let Some(lit) = get_numeric_literal(ex) { - let literal_str = snippet_opt(cx, lit.span).unwrap_or_default(); + let literal_str = snippet_opt(cx, ex.span).unwrap_or_default(); if_chain! { if let LitKind::Int(n, _) = lit.node; diff --git a/tests/ui/unnecessary_cast_fixable.fixed b/tests/ui/unnecessary_cast_fixable.fixed index 54853f4b8a2..2a13469b146 100644 --- a/tests/ui/unnecessary_cast_fixable.fixed +++ b/tests/ui/unnecessary_cast_fixable.fixed @@ -29,4 +29,7 @@ fn main() { 0.5_f32; 1.0 as u16; + + -1_i32; + -1.0_f32; } diff --git a/tests/ui/unnecessary_cast_fixable.rs b/tests/ui/unnecessary_cast_fixable.rs index 8da3d947702..65ddd3c7fbf 100644 --- a/tests/ui/unnecessary_cast_fixable.rs +++ b/tests/ui/unnecessary_cast_fixable.rs @@ -29,4 +29,7 @@ fn main() { 0.5 as f32; 1.0 as u16; + + -1 as i32; + -1.0 as f32; } diff --git a/tests/ui/unnecessary_cast_fixable.stderr b/tests/ui/unnecessary_cast_fixable.stderr index 28fb9540afc..26b23e315e3 100644 --- a/tests/ui/unnecessary_cast_fixable.stderr +++ b/tests/ui/unnecessary_cast_fixable.stderr @@ -60,5 +60,17 @@ error: casting float literal to `f32` is unnecessary LL | 0.5 as f32; | ^^^^^^^^^^ help: try: `0.5_f32` -error: aborting due to 10 previous errors +error: casting integer literal to `i32` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:33:5 + | +LL | -1 as i32; + | ^^^^^^^^^ help: try: `-1_i32` + +error: casting float literal to `f32` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:34:5 + | +LL | -1.0 as f32; + | ^^^^^^^^^^^ help: try: `-1.0_f32` + +error: aborting due to 12 previous errors -- cgit 1.4.1-3-g733a5 From 30f80c3b8c4fcb5d0db37b84a77a58303322cf4e Mon Sep 17 00:00:00 2001 From: Geoffrey Copin Date: Fri, 23 Oct 2020 00:04:27 +0200 Subject: Fix test file --- tests/ui/unnecessary_cast_fixable.fixed | 4 ++-- tests/ui/unnecessary_cast_fixable.rs | 4 ++-- tests/ui/unnecessary_cast_fixable.stderr | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) (limited to 'tests') diff --git a/tests/ui/unnecessary_cast_fixable.fixed b/tests/ui/unnecessary_cast_fixable.fixed index 2a13469b146..5aeb0340b26 100644 --- a/tests/ui/unnecessary_cast_fixable.fixed +++ b/tests/ui/unnecessary_cast_fixable.fixed @@ -30,6 +30,6 @@ fn main() { 1.0 as u16; - -1_i32; - -1.0_f32; + let _ = -1_i32; + let _ = -1.0_f32; } diff --git a/tests/ui/unnecessary_cast_fixable.rs b/tests/ui/unnecessary_cast_fixable.rs index 65ddd3c7fbf..0f249c23055 100644 --- a/tests/ui/unnecessary_cast_fixable.rs +++ b/tests/ui/unnecessary_cast_fixable.rs @@ -30,6 +30,6 @@ fn main() { 1.0 as u16; - -1 as i32; - -1.0 as f32; + let _ = -1 as i32; + let _ = -1.0 as f32; } diff --git a/tests/ui/unnecessary_cast_fixable.stderr b/tests/ui/unnecessary_cast_fixable.stderr index 26b23e315e3..5100e9798c4 100644 --- a/tests/ui/unnecessary_cast_fixable.stderr +++ b/tests/ui/unnecessary_cast_fixable.stderr @@ -61,16 +61,16 @@ LL | 0.5 as f32; | ^^^^^^^^^^ help: try: `0.5_f32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:33:5 + --> $DIR/unnecessary_cast_fixable.rs:33:13 | -LL | -1 as i32; - | ^^^^^^^^^ help: try: `-1_i32` +LL | let _ = -1 as i32; + | ^^^^^^^^^ help: try: `-1_i32` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:34:5 + --> $DIR/unnecessary_cast_fixable.rs:34:13 | -LL | -1.0 as f32; - | ^^^^^^^^^^^ help: try: `-1.0_f32` +LL | let _ = -1.0 as f32; + | ^^^^^^^^^^^ help: try: `-1.0_f32` error: aborting due to 12 previous errors -- cgit 1.4.1-3-g733a5 From 3807634a470b572303d95feb8a5db273c7cea4af Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Sun, 11 Oct 2020 16:04:33 -0700 Subject: clippy_lints: Update empty_loop lint We also update the documentation to note that the remediations are different for `std` and `no_std` crates. Signed-off-by: Joe Richey --- clippy_lints/src/loops.rs | 29 +++++++++++++++++++++++------ tests/ui/crashes/ice-360.stderr | 3 ++- tests/ui/empty_loop.stderr | 11 ++++++++--- tests/ui/empty_loop_no_std.rs | 22 ++++++++++++++++++++++ tests/ui/issue-3746.rs | 22 ---------------------- 5 files changed, 55 insertions(+), 32 deletions(-) create mode 100644 tests/ui/empty_loop_no_std.rs delete mode 100644 tests/ui/issue-3746.rs (limited to 'tests') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 63d7e3176b1..bae12943869 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -293,9 +293,24 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** Checks for empty `loop` expressions. /// - /// **Why is this bad?** Those busy loops burn CPU cycles without doing - /// anything. Think of the environment and either block on something or at least - /// make the thread sleep for some microseconds. + /// **Why is this bad?** These busy loops burn CPU cycles without doing + /// anything. It is _almost always_ a better idea to `panic!` than to have + /// a busy loop. + /// + /// If panicking isn't possible, think of the environment and either: + /// - block on something + /// - sleep the thread for some microseconds + /// - yield or pause the thread + /// + /// For `std` targets, this can be done with + /// [`std::thread::sleep`](https://doc.rust-lang.org/std/thread/fn.sleep.html) + /// or [`std::thread::yield_now`](https://doc.rust-lang.org/std/thread/fn.yield_now.html). + /// + /// For `no_std` targets, doing this is more complicated, especially because + /// `#[panic_handler]`s can't panic. To stop/pause the thread, you will + /// probably need to invoke some target-specific intrinsic. Examples include: + /// - [`x86_64::instructions::hlt`](https://docs.rs/x86_64/0.12.2/x86_64/instructions/fn.hlt.html) + /// - [`cortex_m::asm::wfi`](https://docs.rs/cortex-m/0.6.3/cortex_m/asm/fn.wfi.html) /// /// **Known problems:** None. /// @@ -502,13 +517,15 @@ impl<'tcx> LateLintPass<'tcx> for Loops { // (even if the "match" or "if let" is used for declaration) if let ExprKind::Loop(ref block, _, LoopSource::Loop) = expr.kind { // also check for empty `loop {}` statements + // TODO(issue #6161): Enable for no_std crates (outside of #[panic_handler]) if block.stmts.is_empty() && block.expr.is_none() && !is_no_std_crate(cx.tcx.hir().krate()) { - span_lint( + span_lint_and_help( cx, EMPTY_LOOP, expr.span, - "empty `loop {}` detected. You may want to either use `panic!()` or add \ - `std::thread::sleep(..);` to the loop body.", + "empty `loop {}` wastes CPU cycles", + None, + "You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body.", ); } diff --git a/tests/ui/crashes/ice-360.stderr b/tests/ui/crashes/ice-360.stderr index 84e31eaf2e9..bb03ce40355 100644 --- a/tests/ui/crashes/ice-360.stderr +++ b/tests/ui/crashes/ice-360.stderr @@ -12,13 +12,14 @@ LL | | } | = note: `-D clippy::while-let-loop` implied by `-D warnings` -error: empty `loop {}` detected. You may want to either use `panic!()` or add `std::thread::sleep(..);` to the loop body. +error: empty `loop {}` wastes CPU cycles --> $DIR/ice-360.rs:10:9 | LL | loop {} | ^^^^^^^ | = note: `-D clippy::empty-loop` implied by `-D warnings` + = help: You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body. error: aborting due to 2 previous errors diff --git a/tests/ui/empty_loop.stderr b/tests/ui/empty_loop.stderr index e44c58ea770..fd3979f259a 100644 --- a/tests/ui/empty_loop.stderr +++ b/tests/ui/empty_loop.stderr @@ -1,22 +1,27 @@ -error: empty `loop {}` detected. You may want to either use `panic!()` or add `std::thread::sleep(..);` to the loop body. +error: empty `loop {}` wastes CPU cycles --> $DIR/empty_loop.rs:9:5 | LL | loop {} | ^^^^^^^ | = note: `-D clippy::empty-loop` implied by `-D warnings` + = help: You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body. -error: empty `loop {}` detected. You may want to either use `panic!()` or add `std::thread::sleep(..);` to the loop body. +error: empty `loop {}` wastes CPU cycles --> $DIR/empty_loop.rs:11:9 | LL | loop {} | ^^^^^^^ + | + = help: You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body. -error: empty `loop {}` detected. You may want to either use `panic!()` or add `std::thread::sleep(..);` to the loop body. +error: empty `loop {}` wastes CPU cycles --> $DIR/empty_loop.rs:15:9 | LL | 'inner: loop {} | ^^^^^^^^^^^^^^^ + | + = help: You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body. error: aborting due to 3 previous errors diff --git a/tests/ui/empty_loop_no_std.rs b/tests/ui/empty_loop_no_std.rs new file mode 100644 index 00000000000..879d1d5d916 --- /dev/null +++ b/tests/ui/empty_loop_no_std.rs @@ -0,0 +1,22 @@ +// ignore-macos +// ignore-windows + +#![warn(clippy::empty_loop)] +#![feature(lang_items, link_args, start, libc)] +#![link_args = "-nostartfiles"] +#![no_std] + +use core::panic::PanicInfo; + +#[start] +fn main(argc: isize, argv: *const *const u8) -> isize { + loop {} +} + +#[panic_handler] +fn panic(_info: &PanicInfo) -> ! { + loop {} +} + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} diff --git a/tests/ui/issue-3746.rs b/tests/ui/issue-3746.rs deleted file mode 100644 index 879d1d5d916..00000000000 --- a/tests/ui/issue-3746.rs +++ /dev/null @@ -1,22 +0,0 @@ -// ignore-macos -// ignore-windows - -#![warn(clippy::empty_loop)] -#![feature(lang_items, link_args, start, libc)] -#![link_args = "-nostartfiles"] -#![no_std] - -use core::panic::PanicInfo; - -#[start] -fn main(argc: isize, argv: *const *const u8) -> isize { - loop {} -} - -#[panic_handler] -fn panic(_info: &PanicInfo) -> ! { - loop {} -} - -#[lang = "eh_personality"] -extern "C" fn eh_personality() {} -- cgit 1.4.1-3-g733a5 From d46edd99667ad342e6118b2216a0c24ee009e86c Mon Sep 17 00:00:00 2001 From: Geoffrey Copin Date: Fri, 23 Oct 2020 23:40:57 +0200 Subject: Keep sign in int-to-float casts --- clippy_lints/src/types.rs | 16 ++++++++++++-- tests/ui/unnecessary_cast_fixable.fixed | 3 +++ tests/ui/unnecessary_cast_fixable.rs | 3 +++ tests/ui/unnecessary_cast_fixable.stderr | 38 +++++++++++++++++++++++--------- 4 files changed, 48 insertions(+), 12 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 3a088709a7e..6a33aaaaab2 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -1236,6 +1236,13 @@ declare_clippy_lint! { /// let _ = 2i32 as i32; /// let _ = 0.5 as f32; /// ``` + /// + /// Better: + /// + /// ```rust + /// let _ = 2_i32; + /// let _ = 0.5_f32; + /// ``` pub UNNECESSARY_CAST, complexity, "cast to the same type, e.g., `x as i32` where `x: i32`" @@ -1612,7 +1619,8 @@ impl<'tcx> LateLintPass<'tcx> for Casts { let to_nbits = fp_ty_mantissa_nbits(cast_to); if from_nbits != 0 && to_nbits != 0 && from_nbits <= to_nbits && num_lit.is_decimal(); then { - show_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to); + let literal_str = if is_unary_neg(ex) { format!("-{}", num_lit.integer) } else { num_lit.integer.into() }; + show_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to); return; } } @@ -1624,7 +1632,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { LitKind::Float(_, LitFloatType::Unsuffixed) if cast_to.is_floating_point() => { show_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to); }, - LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => (), + LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => {}, _ => { if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) { span_lint( @@ -1649,6 +1657,10 @@ impl<'tcx> LateLintPass<'tcx> for Casts { } } +fn is_unary_neg(expr: &Expr<'_>) -> bool { + matches!(expr.kind, ExprKind::Unary(UnOp::UnNeg, _)) +} + fn get_numeric_literal<'e>(expr: &'e Expr<'e>) -> Option<&'e Lit> { match expr.kind { ExprKind::Lit(ref lit) => Some(lit), diff --git a/tests/ui/unnecessary_cast_fixable.fixed b/tests/ui/unnecessary_cast_fixable.fixed index 5aeb0340b26..350da4965d1 100644 --- a/tests/ui/unnecessary_cast_fixable.fixed +++ b/tests/ui/unnecessary_cast_fixable.fixed @@ -8,6 +8,9 @@ fn main() { 100_f32; 100_f64; 100_f64; + let _ = -100_f32; + let _ = -100_f64; + let _ = -100_f64; // Should not trigger #[rustfmt::skip] let v = vec!(1); diff --git a/tests/ui/unnecessary_cast_fixable.rs b/tests/ui/unnecessary_cast_fixable.rs index 0f249c23055..ad2fb2e6289 100644 --- a/tests/ui/unnecessary_cast_fixable.rs +++ b/tests/ui/unnecessary_cast_fixable.rs @@ -8,6 +8,9 @@ fn main() { 100 as f32; 100 as f64; 100_i32 as f64; + let _ = -100 as f32; + let _ = -100 as f64; + let _ = -100_i32 as f64; // Should not trigger #[rustfmt::skip] let v = vec!(1); diff --git a/tests/ui/unnecessary_cast_fixable.stderr b/tests/ui/unnecessary_cast_fixable.stderr index 5100e9798c4..5a210fc8909 100644 --- a/tests/ui/unnecessary_cast_fixable.stderr +++ b/tests/ui/unnecessary_cast_fixable.stderr @@ -18,59 +18,77 @@ error: casting integer literal to `f64` is unnecessary LL | 100_i32 as f64; | ^^^^^^^^^^^^^^ help: try: `100_f64` +error: casting integer literal to `f32` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:11:13 + | +LL | let _ = -100 as f32; + | ^^^^^^^^^^^ help: try: `-100_f32` + +error: casting integer literal to `f64` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:12:13 + | +LL | let _ = -100 as f64; + | ^^^^^^^^^^^ help: try: `-100_f64` + +error: casting integer literal to `f64` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:13:13 + | +LL | let _ = -100_i32 as f64; + | ^^^^^^^^^^^^^^^ help: try: `-100_f64` + error: casting integer literal to `u32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:22:5 + --> $DIR/unnecessary_cast_fixable.rs:25:5 | LL | 1 as u32; | ^^^^^^^^ help: try: `1_u32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:23:5 + --> $DIR/unnecessary_cast_fixable.rs:26:5 | LL | 0x10 as i32; | ^^^^^^^^^^^ help: try: `0x10_i32` error: casting integer literal to `usize` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:24:5 + --> $DIR/unnecessary_cast_fixable.rs:27:5 | LL | 0b10 as usize; | ^^^^^^^^^^^^^ help: try: `0b10_usize` error: casting integer literal to `u16` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:25:5 + --> $DIR/unnecessary_cast_fixable.rs:28:5 | LL | 0o73 as u16; | ^^^^^^^^^^^ help: try: `0o73_u16` error: casting integer literal to `u32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:26:5 + --> $DIR/unnecessary_cast_fixable.rs:29:5 | LL | 1_000_000_000 as u32; | ^^^^^^^^^^^^^^^^^^^^ help: try: `1_000_000_000_u32` error: casting float literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:28:5 + --> $DIR/unnecessary_cast_fixable.rs:31:5 | LL | 1.0 as f64; | ^^^^^^^^^^ help: try: `1.0_f64` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:29:5 + --> $DIR/unnecessary_cast_fixable.rs:32:5 | LL | 0.5 as f32; | ^^^^^^^^^^ help: try: `0.5_f32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:33:13 + --> $DIR/unnecessary_cast_fixable.rs:36:13 | LL | let _ = -1 as i32; | ^^^^^^^^^ help: try: `-1_i32` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:34:13 + --> $DIR/unnecessary_cast_fixable.rs:37:13 | LL | let _ = -1.0 as f32; | ^^^^^^^^^^^ help: try: `-1.0_f32` -error: aborting due to 12 previous errors +error: aborting due to 15 previous errors -- cgit 1.4.1-3-g733a5 From 62f60e1ae5bd2287497746bf90a302903e0ae462 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sat, 24 Oct 2020 09:31:32 +0200 Subject: No lint with `cfg!` and fix sugg for macro in `needless_bool` lint --- clippy_lints/src/needless_bool.rs | 16 ++++++++-- tests/ui/bool_comparison.fixed | 40 ++++++++++++++++++++++++- tests/ui/bool_comparison.rs | 40 ++++++++++++++++++++++++- tests/ui/bool_comparison.stderr | 62 +++++++++++++++++++++++++++------------ 4 files changed, 135 insertions(+), 23 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs index dc5aa669139..a799a644e97 100644 --- a/clippy_lints/src/needless_bool.rs +++ b/clippy_lints/src/needless_bool.rs @@ -3,7 +3,9 @@ //! This lint is **warn** by default use crate::utils::sugg::Sugg; -use crate::utils::{higher, parent_node_is_if_expr, snippet_with_applicability, span_lint, span_lint_and_sugg}; +use crate::utils::{ + higher, is_expn_of, parent_node_is_if_expr, snippet_with_applicability, span_lint, span_lint_and_sugg, +}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -233,6 +235,9 @@ fn check_comparison<'a, 'tcx>( cx.typeck_results().expr_ty(left_side), cx.typeck_results().expr_ty(right_side), ); + if is_expn_of(left_side.span, "cfg").is_some() || is_expn_of(right_side.span, "cfg").is_some() { + return; + } if l_ty.is_bool() && r_ty.is_bool() { let mut applicability = Applicability::MachineApplicable; @@ -295,7 +300,14 @@ fn suggest_bool_comparison<'a, 'tcx>( message: &str, conv_hint: impl FnOnce(Sugg<'a>) -> Sugg<'a>, ) { - let hint = Sugg::hir_with_applicability(cx, expr, "..", &mut applicability); + let hint = if expr.span.from_expansion() { + if applicability != Applicability::Unspecified { + applicability = Applicability::MaybeIncorrect; + } + Sugg::hir_with_macro_callsite(cx, expr, "..") + } else { + Sugg::hir_with_applicability(cx, expr, "..", &mut applicability) + }; span_lint_and_sugg( cx, BOOL_COMPARISON, diff --git a/tests/ui/bool_comparison.fixed b/tests/ui/bool_comparison.fixed index 91211764759..5a012ff4d27 100644 --- a/tests/ui/bool_comparison.fixed +++ b/tests/ui/bool_comparison.fixed @@ -1,6 +1,7 @@ // run-rustfix -#[warn(clippy::bool_comparison)] +#![warn(clippy::bool_comparison)] + fn main() { let x = true; if x { @@ -127,3 +128,40 @@ fn issue4983() { if b == a {}; if !b == !a {}; } + +macro_rules! m { + ($func:ident) => { + $func() + }; +} + +fn func() -> bool { + true +} + +#[allow(dead_code)] +fn issue3973() { + // ok, don't lint on `cfg` invocation + if false == cfg!(feature = "debugging") {} + if cfg!(feature = "debugging") == false {} + if true == cfg!(feature = "debugging") {} + if cfg!(feature = "debugging") == true {} + + // lint, could be simplified + if !m!(func) {} + if !m!(func) {} + if m!(func) {} + if m!(func) {} + + // no lint with a variable + let is_debug = false; + if is_debug == cfg!(feature = "debugging") {} + if cfg!(feature = "debugging") == is_debug {} + if is_debug == m!(func) {} + if m!(func) == is_debug {} + let is_debug = true; + if is_debug == cfg!(feature = "debugging") {} + if cfg!(feature = "debugging") == is_debug {} + if is_debug == m!(func) {} + if m!(func) == is_debug {} +} diff --git a/tests/ui/bool_comparison.rs b/tests/ui/bool_comparison.rs index 01ee35859f0..c534bc25c20 100644 --- a/tests/ui/bool_comparison.rs +++ b/tests/ui/bool_comparison.rs @@ -1,6 +1,7 @@ // run-rustfix -#[warn(clippy::bool_comparison)] +#![warn(clippy::bool_comparison)] + fn main() { let x = true; if x == true { @@ -127,3 +128,40 @@ fn issue4983() { if b == a {}; if !b == !a {}; } + +macro_rules! m { + ($func:ident) => { + $func() + }; +} + +fn func() -> bool { + true +} + +#[allow(dead_code)] +fn issue3973() { + // ok, don't lint on `cfg` invocation + if false == cfg!(feature = "debugging") {} + if cfg!(feature = "debugging") == false {} + if true == cfg!(feature = "debugging") {} + if cfg!(feature = "debugging") == true {} + + // lint, could be simplified + if false == m!(func) {} + if m!(func) == false {} + if true == m!(func) {} + if m!(func) == true {} + + // no lint with a variable + let is_debug = false; + if is_debug == cfg!(feature = "debugging") {} + if cfg!(feature = "debugging") == is_debug {} + if is_debug == m!(func) {} + if m!(func) == is_debug {} + let is_debug = true; + if is_debug == cfg!(feature = "debugging") {} + if cfg!(feature = "debugging") == is_debug {} + if is_debug == m!(func) {} + if m!(func) == is_debug {} +} diff --git a/tests/ui/bool_comparison.stderr b/tests/ui/bool_comparison.stderr index 55d94b8257d..31522d4a525 100644 --- a/tests/ui/bool_comparison.stderr +++ b/tests/ui/bool_comparison.stderr @@ -1,5 +1,5 @@ error: equality checks against true are unnecessary - --> $DIR/bool_comparison.rs:6:8 + --> $DIR/bool_comparison.rs:7:8 | LL | if x == true { | ^^^^^^^^^ help: try simplifying it as shown: `x` @@ -7,106 +7,130 @@ 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/bool_comparison.rs:11:8 + --> $DIR/bool_comparison.rs:12:8 | LL | if x == false { | ^^^^^^^^^^ help: try simplifying it as shown: `!x` error: equality checks against true are unnecessary - --> $DIR/bool_comparison.rs:16:8 + --> $DIR/bool_comparison.rs:17:8 | LL | if true == x { | ^^^^^^^^^ help: try simplifying it as shown: `x` error: equality checks against false can be replaced by a negation - --> $DIR/bool_comparison.rs:21:8 + --> $DIR/bool_comparison.rs:22:8 | LL | if false == x { | ^^^^^^^^^^ help: try simplifying it as shown: `!x` error: inequality checks against true can be replaced by a negation - --> $DIR/bool_comparison.rs:26:8 + --> $DIR/bool_comparison.rs:27:8 | LL | if x != true { | ^^^^^^^^^ help: try simplifying it as shown: `!x` error: inequality checks against false are unnecessary - --> $DIR/bool_comparison.rs:31:8 + --> $DIR/bool_comparison.rs:32:8 | LL | if x != false { | ^^^^^^^^^^ help: try simplifying it as shown: `x` error: inequality checks against true can be replaced by a negation - --> $DIR/bool_comparison.rs:36:8 + --> $DIR/bool_comparison.rs:37:8 | LL | if true != x { | ^^^^^^^^^ help: try simplifying it as shown: `!x` error: inequality checks against false are unnecessary - --> $DIR/bool_comparison.rs:41:8 + --> $DIR/bool_comparison.rs:42:8 | LL | if false != x { | ^^^^^^^^^^ help: try simplifying it as shown: `x` error: less than comparison against true can be replaced by a negation - --> $DIR/bool_comparison.rs:46:8 + --> $DIR/bool_comparison.rs:47:8 | LL | if x < true { | ^^^^^^^^ help: try simplifying it as shown: `!x` error: greater than checks against false are unnecessary - --> $DIR/bool_comparison.rs:51:8 + --> $DIR/bool_comparison.rs:52:8 | LL | if false < x { | ^^^^^^^^^ help: try simplifying it as shown: `x` error: greater than checks against false are unnecessary - --> $DIR/bool_comparison.rs:56:8 + --> $DIR/bool_comparison.rs:57:8 | LL | if x > false { | ^^^^^^^^^ help: try simplifying it as shown: `x` error: less than comparison against true can be replaced by a negation - --> $DIR/bool_comparison.rs:61:8 + --> $DIR/bool_comparison.rs:62:8 | LL | if true > x { | ^^^^^^^^ help: try simplifying it as shown: `!x` error: order comparisons between booleans can be simplified - --> $DIR/bool_comparison.rs:67:8 + --> $DIR/bool_comparison.rs:68:8 | LL | if x < y { | ^^^^^ help: try simplifying it as shown: `!x & y` error: order comparisons between booleans can be simplified - --> $DIR/bool_comparison.rs:72:8 + --> $DIR/bool_comparison.rs:73:8 | LL | if x > y { | ^^^^^ help: try simplifying it as shown: `x & !y` error: this comparison might be written more concisely - --> $DIR/bool_comparison.rs:120:8 + --> $DIR/bool_comparison.rs:121:8 | LL | if a == !b {}; | ^^^^^^^ help: try simplifying it as shown: `a != b` error: this comparison might be written more concisely - --> $DIR/bool_comparison.rs:121:8 + --> $DIR/bool_comparison.rs:122:8 | LL | if !a == b {}; | ^^^^^^^ help: try simplifying it as shown: `a != b` error: this comparison might be written more concisely - --> $DIR/bool_comparison.rs:125:8 + --> $DIR/bool_comparison.rs:126:8 | LL | if b == !a {}; | ^^^^^^^ help: try simplifying it as shown: `b != a` error: this comparison might be written more concisely - --> $DIR/bool_comparison.rs:126:8 + --> $DIR/bool_comparison.rs:127:8 | LL | if !b == a {}; | ^^^^^^^ help: try simplifying it as shown: `b != a` -error: aborting due to 18 previous errors +error: equality checks against false can be replaced by a negation + --> $DIR/bool_comparison.rs:151:8 + | +LL | if false == m!(func) {} + | ^^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `!m!(func)` + +error: equality checks against false can be replaced by a negation + --> $DIR/bool_comparison.rs:152:8 + | +LL | if m!(func) == false {} + | ^^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `!m!(func)` + +error: equality checks against true are unnecessary + --> $DIR/bool_comparison.rs:153:8 + | +LL | if true == m!(func) {} + | ^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `m!(func)` + +error: equality checks against true are unnecessary + --> $DIR/bool_comparison.rs:154:8 + | +LL | if m!(func) == true {} + | ^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `m!(func)` + +error: aborting due to 22 previous errors -- cgit 1.4.1-3-g733a5 From 0d21ae0e194fd8f7f1f67bf1921910e0ca21a32c Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Sat, 24 Oct 2020 11:35:05 +0200 Subject: manual-unwrap-or / pr remarks, round 3 --- clippy_lints/src/manual_unwrap_or.rs | 13 +++---------- tests/ui/manual_unwrap_or.fixed | 12 +++++++++++- tests/ui/manual_unwrap_or.rs | 15 ++++++++++++++- tests/ui/manual_unwrap_or.stderr | 19 ++++++++++++++----- 4 files changed, 42 insertions(+), 17 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index cc9ee28d027..22aa37e41fe 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -1,5 +1,6 @@ use crate::consts::constant_simple; use crate::utils; +use crate::utils::sugg; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{def, Arm, Expr, ExprKind, Pat, PatKind, QPath}; @@ -104,28 +105,20 @@ fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { None }; if let Some(or_arm) = applicable_or_arm(match_arms); - if let Some(scrutinee_snippet) = utils::snippet_opt(cx, scrutinee.span); if let Some(or_body_snippet) = utils::snippet_opt(cx, or_arm.body.span); if let Some(indent) = utils::indent_of(cx, expr.span); if constant_simple(cx, cx.typeck_results(), or_arm.body).is_some(); then { let reindented_or_body = utils::reindent_multiline(or_body_snippet.into(), true, Some(indent)); - let wrap_in_parens = !matches!(scrutinee, Expr { - kind: ExprKind::Call(..) | ExprKind::Path(_), .. - }); - let l_paren = if wrap_in_parens { "(" } else { "" }; - let r_paren = if wrap_in_parens { ")" } else { "" }; utils::span_lint_and_sugg( cx, MANUAL_UNWRAP_OR, expr.span, &format!("this pattern reimplements `{}`", case.unwrap_fn_path()), "replace with", format!( - "{}{}{}.unwrap_or({})", - l_paren, - scrutinee_snippet, - r_paren, + "{}.unwrap_or({})", + sugg::Sugg::hir(cx, scrutinee, "..").maybe_par(), reindented_or_body, ), Applicability::MachineApplicable, diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index 582f5c6f7a8..5aa5a43cb92 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -74,9 +74,19 @@ fn result_unwrap_or() { let a = Ok::(1); a.unwrap_or(42); - // int case, suggestion must surround with parenthesis + // int case, suggestion must surround Result expr with parenthesis (Ok(1) as Result).unwrap_or(42); + // method call case, suggestion must not surround Result expr `s.method()` with parenthesis + struct S {} + impl S { + fn method(self) -> Option { + Some(42) + } + } + let s = S {}; + s.method().unwrap_or(42); + // int case reversed Ok::(1).unwrap_or(42); diff --git a/tests/ui/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs index 0e2b7ecadb3..df534031f54 100644 --- a/tests/ui/manual_unwrap_or.rs +++ b/tests/ui/manual_unwrap_or.rs @@ -95,12 +95,25 @@ fn result_unwrap_or() { Err(_) => 42, }; - // int case, suggestion must surround with parenthesis + // int case, suggestion must surround Result expr with parenthesis match Ok(1) as Result { Ok(i) => i, Err(_) => 42, }; + // method call case, suggestion must not surround Result expr `s.method()` with parenthesis + struct S {} + impl S { + fn method(self) -> Option { + Some(42) + } + } + let s = S {}; + match s.method() { + Some(i) => i, + None => 42, + }; + // int case reversed match Ok::(1) { Err(_) => 42, diff --git a/tests/ui/manual_unwrap_or.stderr b/tests/ui/manual_unwrap_or.stderr index 6603ab43437..fc174c4c270 100644 --- a/tests/ui/manual_unwrap_or.stderr +++ b/tests/ui/manual_unwrap_or.stderr @@ -84,8 +84,17 @@ LL | | Err(_) => 42, LL | | }; | |_____^ help: replace with: `(Ok(1) as Result).unwrap_or(42)` +error: this pattern reimplements `Option::unwrap_or` + --> $DIR/manual_unwrap_or.rs:112:5 + | +LL | / match s.method() { +LL | | Some(i) => i, +LL | | None => 42, +LL | | }; + | |_____^ help: replace with: `s.method().unwrap_or(42)` + error: this pattern reimplements `Result::unwrap_or` - --> $DIR/manual_unwrap_or.rs:105:5 + --> $DIR/manual_unwrap_or.rs:118:5 | LL | / match Ok::(1) { LL | | Err(_) => 42, @@ -94,7 +103,7 @@ LL | | }; | |_____^ help: replace with: `Ok::(1).unwrap_or(42)` error: this pattern reimplements `Result::unwrap_or` - --> $DIR/manual_unwrap_or.rs:111:5 + --> $DIR/manual_unwrap_or.rs:124:5 | LL | / match Ok::(1) { LL | | Ok(i) => i, @@ -103,7 +112,7 @@ LL | | }; | |_____^ help: replace with: `Ok::(1).unwrap_or(1 + 42)` error: this pattern reimplements `Result::unwrap_or` - --> $DIR/manual_unwrap_or.rs:118:5 + --> $DIR/manual_unwrap_or.rs:131:5 | LL | / match Ok::(1) { LL | | Ok(i) => i, @@ -124,7 +133,7 @@ LL | }); | error: this pattern reimplements `Result::unwrap_or` - --> $DIR/manual_unwrap_or.rs:128:5 + --> $DIR/manual_unwrap_or.rs:141:5 | LL | / match Ok::<&str, &str>("Bob") { LL | | Ok(i) => i, @@ -132,5 +141,5 @@ LL | | Err(_) => "Alice", LL | | }; | |_____^ help: replace with: `Ok::<&str, &str>("Bob").unwrap_or("Alice")` -error: aborting due to 12 previous errors +error: aborting due to 13 previous errors -- cgit 1.4.1-3-g733a5 From 71ac0c0be81019eb81b55eb75ae6318b3d0482ea Mon Sep 17 00:00:00 2001 From: Geoffrey Copin Date: Sat, 24 Oct 2020 14:07:34 +0200 Subject: Keep separators in cast_size_32bits stderr --- tests/ui/cast_size_32bit.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/ui/cast_size_32bit.stderr b/tests/ui/cast_size_32bit.stderr index 2eec51895f5..140676a5ffc 100644 --- a/tests/ui/cast_size_32bit.stderr +++ b/tests/ui/cast_size_32bit.stderr @@ -124,7 +124,7 @@ error: casting integer literal to `f64` is unnecessary --> $DIR/cast_size_32bit.rs:34:5 | LL | 3_999_999_999usize as f64; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `3999999999_f64` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `3_999_999_999_f64` | = note: `-D clippy::unnecessary-cast` implied by `-D warnings` -- cgit 1.4.1-3-g733a5 From e8731a926c9a642ca1ddf5b52baf40e0a8873d53 Mon Sep 17 00:00:00 2001 From: Cauê Baasch de Souza Date: Thu, 8 Oct 2020 02:17:32 -0300 Subject: Add large_types_passed_by_value lint Refactor trivially_copy_pass_by_ref and the new lint into pass_by_ref_or_value module Update stderr of conf_unknown_key test Rename lint to large_types_passed_by_value Increase `pass_by_value_size_limit` default value to 256 Improve rules for `large_types_passed_by_value` Improve tests for `large_types_passed_by_value` Improve documentation for `large_types_passed_by_value` Make minor corrections to pass_by_ref_or_value.rs suggested by clippy itself Fix `large_types_passed_by_value` example and improve docs pass_by_ref_or_value: Tweak check for mut annotation in params large_types_passed_by_value: add tests for pub trait, trait impl and inline attributes --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 13 +- clippy_lints/src/pass_by_ref_or_value.rs | 256 +++++++++++++++++++++ clippy_lints/src/trivially_copy_pass_by_ref.rs | 183 --------------- clippy_lints/src/utils/conf.rs | 2 + src/lintlist/mod.rs | 9 +- .../toml_unknown_key/conf_unknown_key.stderr | 2 +- tests/ui/large_types_passed_by_value.rs | 66 ++++++ tests/ui/large_types_passed_by_value.stderr | 52 +++++ 9 files changed, 394 insertions(+), 190 deletions(-) create mode 100644 clippy_lints/src/pass_by_ref_or_value.rs delete mode 100644 clippy_lints/src/trivially_copy_pass_by_ref.rs create mode 100644 tests/ui/large_types_passed_by_value.rs create mode 100644 tests/ui/large_types_passed_by_value.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index d82f970b8bf..22f96398153 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1779,6 +1779,7 @@ Released 2018-09-13 [`large_digit_groups`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_digit_groups [`large_enum_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant [`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays +[`large_types_passed_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value [`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty [`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero [`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d4d2f92a6a6..1a950a7c334 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -278,6 +278,7 @@ mod overflow_check_conditional; mod panic_in_result_fn; mod panic_unimplemented; mod partialeq_ne_impl; +mod pass_by_ref_or_value; mod path_buf_push_overwrite; mod pattern_type_mismatch; mod precedence; @@ -311,7 +312,6 @@ mod to_string_in_display; mod trait_bounds; mod transmute; mod transmuting_null; -mod trivially_copy_pass_by_ref; mod try_err; mod types; mod unicode; @@ -776,6 +776,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &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, @@ -835,7 +837,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &transmute::USELESS_TRANSMUTE, &transmute::WRONG_TRANSMUTE, &transmuting_null::TRANSMUTING_NULL, - &trivially_copy_pass_by_ref::TRIVIALLY_COPY_PASS_BY_REF, &try_err::TRY_ERR, &types::ABSURD_EXTREME_COMPARISONS, &types::BORROWED_BOX, @@ -1009,11 +1010,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box large_enum_variant::LargeEnumVariant::new(enum_variant_size_threshold)); store.register_late_pass(|| box explicit_write::ExplicitWrite); store.register_late_pass(|| box needless_pass_by_value::NeedlessPassByValue); - let trivially_copy_pass_by_ref = trivially_copy_pass_by_ref::TriviallyCopyPassByRef::new( + let pass_by_ref_or_value = pass_by_ref_or_value::PassByRefOrValue::new( conf.trivial_copy_size_limit, + conf.pass_by_value_size_limit, &sess.target, ); - store.register_late_pass(move || box trivially_copy_pass_by_ref); + store.register_late_pass(move || box pass_by_ref_or_value); store.register_late_pass(|| box try_err::TryErr); store.register_late_pass(|| box use_self::UseSelf); store.register_late_pass(|| box bytecount::ByteCount); @@ -1237,13 +1239,14 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&needless_pass_by_value::NEEDLESS_PASS_BY_VALUE), LintId::of(&non_expressive_names::SIMILAR_NAMES), LintId::of(&option_if_let_else::OPTION_IF_LET_ELSE), + 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(&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(&trivially_copy_pass_by_ref::TRIVIALLY_COPY_PASS_BY_REF), LintId::of(&types::CAST_LOSSLESS), LintId::of(&types::CAST_POSSIBLE_TRUNCATION), LintId::of(&types::CAST_POSSIBLE_WRAP), diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs new file mode 100644 index 00000000000..28816c3076d --- /dev/null +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -0,0 +1,256 @@ +use std::cmp; + +use crate::utils::{is_copy, is_self_ty, snippet, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_ast::attr; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{BindingAnnotation, Body, FnDecl, HirId, ItemKind, MutTy, Mutability, Node, PatKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::Span; +use rustc_target::abi::LayoutOf; +use rustc_target::spec::abi::Abi; +use rustc_target::spec::Target; + +declare_clippy_lint! { + /// **What it does:** Checks for functions taking arguments by reference, where + /// the argument type is `Copy` and small enough to be more efficient to always + /// pass by value. + /// + /// **Why is this bad?** In many calling conventions instances of structs will + /// be passed through registers if they fit into two or less general purpose + /// registers. + /// + /// **Known problems:** This lint is target register size dependent, it is + /// limited to 32-bit to try and reduce portability problems between 32 and + /// 64-bit, but if you are compiling for 8 or 16-bit targets then the limit + /// will be different. + /// + /// The configuration option `trivial_copy_size_limit` can be set to override + /// this limit for a project. + /// + /// This lint attempts to allow passing arguments by reference if a reference + /// to that argument is returned. This is implemented by comparing the lifetime + /// of the argument and return value for equality. However, this can cause + /// false positives in cases involving multiple lifetimes that are bounded by + /// each other. + /// + /// **Example:** + /// + /// ```rust + /// // Bad + /// fn foo(v: &u32) {} + /// ``` + /// + /// ```rust + /// // Better + /// fn foo(v: u32) {} + /// ``` + pub TRIVIALLY_COPY_PASS_BY_REF, + pedantic, + "functions taking small copyable arguments by reference" +} + +declare_clippy_lint! { + /// **What it does:** Checks for functions taking arguments by value, where + /// the argument type is `Copy` and large enough to be worth considering + /// passing by reference. Does not trigger if the function is being exported, + /// because that might induce API breakage, if the parameter is declared as mutable, + /// or if the argument is a `self`. + /// + /// **Why is this bad?** Arguments passed by value might result in an unnecessary + /// shallow copy, taking up more space in the stack and requiring a call to + /// `memcpy`, which which can be expensive. + /// + /// **Example:** + /// + /// ```rust + /// #[derive(Clone, Copy)] + /// struct TooLarge([u8; 2048]); + /// + /// // Bad + /// fn foo(v: TooLarge) {} + /// ``` + /// ```rust + /// #[derive(Clone, Copy)] + /// struct TooLarge([u8; 2048]); + /// + /// // Good + /// fn foo(v: &TooLarge) {} + /// ``` + pub LARGE_TYPES_PASSED_BY_VALUE, + pedantic, + "functions taking large arguments by value" +} + +#[derive(Copy, Clone)] +pub struct PassByRefOrValue { + ref_min_size: u64, + value_max_size: u64, +} + +impl<'tcx> PassByRefOrValue { + pub fn new(ref_min_size: Option, value_max_size: u64, target: &Target) -> Self { + let ref_min_size = ref_min_size.unwrap_or_else(|| { + let bit_width = u64::from(target.pointer_width); + // Cap the calculated bit width at 32-bits to reduce + // portability problems between 32 and 64-bit targets + let bit_width = cmp::min(bit_width, 32); + #[allow(clippy::integer_division)] + let byte_width = bit_width / 8; + // Use a limit of 2 times the register byte width + byte_width * 2 + }); + + Self { + ref_min_size, + value_max_size, + } + } + + fn check_poly_fn(&mut self, cx: &LateContext<'tcx>, hir_id: HirId, decl: &FnDecl<'_>, span: Option) { + let fn_def_id = cx.tcx.hir().local_def_id(hir_id); + + let fn_sig = cx.tcx.fn_sig(fn_def_id); + let fn_sig = cx.tcx.erase_late_bound_regions(&fn_sig); + + let fn_body = cx.enclosing_body.map(|id| cx.tcx.hir().body(id)); + + for (index, (input, &ty)) in decl.inputs.iter().zip(fn_sig.inputs()).enumerate() { + // All spans generated from a proc-macro invocation are the same... + match span { + Some(s) if s == input.span => return, + _ => (), + } + + match ty.kind() { + ty::Ref(input_lt, ty, Mutability::Not) => { + // Use lifetimes to determine if we're returning a reference to the + // argument. In that case we can't switch to pass-by-value as the + // argument will not live long enough. + let output_lts = match *fn_sig.output().kind() { + ty::Ref(output_lt, _, _) => vec![output_lt], + ty::Adt(_, substs) => substs.regions().collect(), + _ => vec![], + }; + + if_chain! { + if !output_lts.contains(&input_lt); + if is_copy(cx, ty); + if let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes()); + if size <= self.ref_min_size; + if let hir::TyKind::Rptr(_, MutTy { ty: ref decl_ty, .. }) = input.kind; + then { + let value_type = if is_self_ty(decl_ty) { + "self".into() + } else { + snippet(cx, decl_ty.span, "_").into() + }; + span_lint_and_sugg( + cx, + TRIVIALLY_COPY_PASS_BY_REF, + input.span, + &format!("this argument ({} byte) is passed by reference, but would be more efficient if passed by value (limit: {} byte)", size, self.ref_min_size), + "consider passing by value instead", + value_type, + Applicability::Unspecified, + ); + } + } + }, + + ty::Adt(_, _) | ty::Array(_, _) | ty::Tuple(_) => { + // if function has a body and parameter is annotated with mut, ignore + if let Some(param) = fn_body.and_then(|body| body.params.get(index)) { + match param.pat.kind { + PatKind::Binding(BindingAnnotation::Unannotated, _, _, _) => {}, + _ => continue, + } + } + + if_chain! { + if !cx.access_levels.is_exported(hir_id); + if is_copy(cx, ty); + if !is_self_ty(input); + if let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes()); + if size > self.value_max_size; + then { + span_lint_and_sugg( + cx, + LARGE_TYPES_PASSED_BY_VALUE, + input.span, + &format!("this argument ({} byte) is passed by value, but might be more efficient if passed by reference (limit: {} byte)", size, self.value_max_size), + "consider passing by reference instead", + format!("&{}", snippet(cx, input.span, "_")), + Applicability::MaybeIncorrect, + ); + } + } + }, + + _ => {}, + } + } + } +} + +impl_lint_pass!(PassByRefOrValue => [TRIVIALLY_COPY_PASS_BY_REF, LARGE_TYPES_PASSED_BY_VALUE]); + +impl<'tcx> LateLintPass<'tcx> for PassByRefOrValue { + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { + if item.span.from_expansion() { + return; + } + + if let hir::TraitItemKind::Fn(method_sig, _) = &item.kind { + self.check_poly_fn(cx, item.hir_id, &*method_sig.decl, None); + } + } + + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + kind: FnKind<'tcx>, + decl: &'tcx FnDecl<'_>, + _body: &'tcx Body<'_>, + span: Span, + hir_id: HirId, + ) { + if span.from_expansion() { + return; + } + + match kind { + FnKind::ItemFn(.., header, _, attrs) => { + if header.abi != Abi::Rust { + return; + } + for a in attrs { + if let Some(meta_items) = a.meta_item_list() { + if a.has_name(sym!(proc_macro_derive)) + || (a.has_name(sym!(inline)) && attr::list_contains_name(&meta_items, sym!(always))) + { + return; + } + } + } + }, + FnKind::Method(..) => (), + FnKind::Closure(..) => return, + } + + // Exclude non-inherent impls + if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { + if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), .. } | + ItemKind::Trait(..)) + { + return; + } + } + + self.check_poly_fn(cx, hir_id, decl, Some(span)); + } +} diff --git a/clippy_lints/src/trivially_copy_pass_by_ref.rs b/clippy_lints/src/trivially_copy_pass_by_ref.rs deleted file mode 100644 index e90ea0fc200..00000000000 --- a/clippy_lints/src/trivially_copy_pass_by_ref.rs +++ /dev/null @@ -1,183 +0,0 @@ -use std::cmp; - -use crate::utils::{is_copy, is_self_ty, snippet, span_lint_and_sugg}; -use if_chain::if_chain; -use rustc_ast::attr; -use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_hir::intravisit::FnKind; -use rustc_hir::{Body, FnDecl, HirId, ItemKind, MutTy, Mutability, Node}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; -use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::Span; -use rustc_target::abi::LayoutOf; -use rustc_target::spec::abi::Abi; -use rustc_target::spec::Target; - -declare_clippy_lint! { - /// **What it does:** Checks for functions taking arguments by reference, where - /// the argument type is `Copy` and small enough to be more efficient to always - /// pass by value. - /// - /// **Why is this bad?** In many calling conventions instances of structs will - /// be passed through registers if they fit into two or less general purpose - /// registers. - /// - /// **Known problems:** This lint is target register size dependent, it is - /// limited to 32-bit to try and reduce portability problems between 32 and - /// 64-bit, but if you are compiling for 8 or 16-bit targets then the limit - /// will be different. - /// - /// The configuration option `trivial_copy_size_limit` can be set to override - /// this limit for a project. - /// - /// This lint attempts to allow passing arguments by reference if a reference - /// to that argument is returned. This is implemented by comparing the lifetime - /// of the argument and return value for equality. However, this can cause - /// false positives in cases involving multiple lifetimes that are bounded by - /// each other. - /// - /// **Example:** - /// - /// ```rust - /// // Bad - /// fn foo(v: &u32) {} - /// ``` - /// - /// ```rust - /// // Better - /// fn foo(v: u32) {} - /// ``` - pub TRIVIALLY_COPY_PASS_BY_REF, - pedantic, - "functions taking small copyable arguments by reference" -} - -#[derive(Copy, Clone)] -pub struct TriviallyCopyPassByRef { - limit: u64, -} - -impl<'tcx> TriviallyCopyPassByRef { - pub fn new(limit: Option, target: &Target) -> Self { - let limit = limit.unwrap_or_else(|| { - let bit_width = u64::from(target.pointer_width); - // Cap the calculated bit width at 32-bits to reduce - // portability problems between 32 and 64-bit targets - let bit_width = cmp::min(bit_width, 32); - #[allow(clippy::integer_division)] - let byte_width = bit_width / 8; - // Use a limit of 2 times the register byte width - byte_width * 2 - }); - Self { limit } - } - - fn check_poly_fn(&mut self, cx: &LateContext<'tcx>, hir_id: HirId, decl: &FnDecl<'_>, span: Option) { - let fn_def_id = cx.tcx.hir().local_def_id(hir_id); - - let fn_sig = cx.tcx.fn_sig(fn_def_id); - let fn_sig = cx.tcx.erase_late_bound_regions(&fn_sig); - - // Use lifetimes to determine if we're returning a reference to the - // argument. In that case we can't switch to pass-by-value as the - // argument will not live long enough. - let output_lts = match *fn_sig.output().kind() { - ty::Ref(output_lt, _, _) => vec![output_lt], - ty::Adt(_, substs) => substs.regions().collect(), - _ => vec![], - }; - - for (input, &ty) in decl.inputs.iter().zip(fn_sig.inputs()) { - // All spans generated from a proc-macro invocation are the same... - match span { - Some(s) if s == input.span => return, - _ => (), - } - - if_chain! { - if let ty::Ref(input_lt, ty, Mutability::Not) = ty.kind(); - if !output_lts.contains(&input_lt); - if is_copy(cx, ty); - if let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes()); - if size <= self.limit; - if let hir::TyKind::Rptr(_, MutTy { ty: ref decl_ty, .. }) = input.kind; - then { - let value_type = if is_self_ty(decl_ty) { - "self".into() - } else { - snippet(cx, decl_ty.span, "_").into() - }; - span_lint_and_sugg( - cx, - TRIVIALLY_COPY_PASS_BY_REF, - input.span, - &format!("this argument ({} byte) is passed by reference, but would be more efficient if passed by value (limit: {} byte)", size, self.limit), - "consider passing by value instead", - value_type, - Applicability::Unspecified, - ); - } - } - } - } -} - -impl_lint_pass!(TriviallyCopyPassByRef => [TRIVIALLY_COPY_PASS_BY_REF]); - -impl<'tcx> LateLintPass<'tcx> for TriviallyCopyPassByRef { - fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { - if item.span.from_expansion() { - return; - } - - if let hir::TraitItemKind::Fn(method_sig, _) = &item.kind { - self.check_poly_fn(cx, item.hir_id, &*method_sig.decl, None); - } - } - - fn check_fn( - &mut self, - cx: &LateContext<'tcx>, - kind: FnKind<'tcx>, - decl: &'tcx FnDecl<'_>, - _body: &'tcx Body<'_>, - span: Span, - hir_id: HirId, - ) { - if span.from_expansion() { - return; - } - - match kind { - FnKind::ItemFn(.., header, _, attrs) => { - if header.abi != Abi::Rust { - return; - } - for a in attrs { - if let Some(meta_items) = a.meta_item_list() { - if a.has_name(sym!(proc_macro_derive)) - || (a.has_name(sym!(inline)) && attr::list_contains_name(&meta_items, sym!(always))) - { - return; - } - } - } - }, - FnKind::Method(..) => (), - FnKind::Closure(..) => return, - } - - // Exclude non-inherent impls - if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { - if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), .. } | - ItemKind::Trait(..)) - { - return; - } - } - - self.check_poly_fn(cx, hir_id, decl, Some(span)); - } -} diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index dd2fd0bb445..0ac8fff69f0 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -150,6 +150,8 @@ define_Conf! { (literal_representation_threshold, "literal_representation_threshold": u64, 16384), /// Lint: TRIVIALLY_COPY_PASS_BY_REF. The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference. (trivial_copy_size_limit, "trivial_copy_size_limit": Option, None), + /// Lint: LARGE_TYPE_PASS_BY_MOVE. The minimum size (in bytes) to consider a type for passing by reference instead of by value. + (pass_by_value_size_limit, "pass_by_value_size_limit": u64, 256), /// Lint: TOO_MANY_LINES. The maximum number of lines a function or method can have (too_many_lines_threshold, "too_many_lines_threshold": u64, 100), /// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS. The maximum allowed size for arrays on the stack diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 6301d623a2b..f3536f26339 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1061,6 +1061,13 @@ vec![ deprecation: None, module: "large_stack_arrays", }, + Lint { + name: "large_types_passed_by_value", + group: "pedantic", + desc: "functions taking large arguments by value", + deprecation: None, + module: "pass_by_ref_or_value", + }, Lint { name: "len_without_is_empty", group: "style", @@ -2389,7 +2396,7 @@ vec![ group: "pedantic", desc: "functions taking small copyable arguments by reference", deprecation: None, - module: "trivially_copy_pass_by_ref", + module: "pass_by_ref_or_value", }, Lint { name: "try_err", 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 103ec27e7d7..a58e7e918e2 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 `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`, `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`, `third-party` at line 5 column 1 +error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `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`, `third-party` at line 5 column 1 error: aborting due to previous error diff --git a/tests/ui/large_types_passed_by_value.rs b/tests/ui/large_types_passed_by_value.rs new file mode 100644 index 00000000000..e4a2e9df4d7 --- /dev/null +++ b/tests/ui/large_types_passed_by_value.rs @@ -0,0 +1,66 @@ +// normalize-stderr-test "\(\d+ byte\)" -> "(N byte)" +// normalize-stderr-test "\(limit: \d+ byte\)" -> "(limit: N byte)" + +#![warn(clippy::large_types_passed_by_value)] + +pub struct Large([u8; 2048]); + +#[derive(Clone, Copy)] +pub struct LargeAndCopy([u8; 2048]); + +pub struct Small([u8; 4]); + +#[derive(Clone, Copy)] +pub struct SmallAndCopy([u8; 4]); + +fn small(a: Small, b: SmallAndCopy) {} +fn not_copy(a: Large) {} +fn by_ref(a: &Large, b: &LargeAndCopy) {} +fn mutable(mut a: LargeAndCopy) {} +fn bad(a: LargeAndCopy) {} +pub fn bad_but_pub(a: LargeAndCopy) {} + +impl LargeAndCopy { + fn self_is_ok(self) {} + fn other_is_not_ok(self, other: LargeAndCopy) {} + fn unless_other_can_change(self, mut other: LargeAndCopy) {} + pub fn or_were_in_public(self, other: LargeAndCopy) {} +} + +trait LargeTypeDevourer { + fn devoure_array(&self, array: [u8; 6666]); + fn devoure_tuple(&self, tup: (LargeAndCopy, LargeAndCopy)); + fn devoure_array_and_tuple_wow(&self, array: [u8; 6666], tup: (LargeAndCopy, LargeAndCopy)); +} + +pub trait PubLargeTypeDevourer { + fn devoure_array_in_public(&self, array: [u8; 6666]); +} + +struct S {} +impl LargeTypeDevourer for S { + fn devoure_array(&self, array: [u8; 6666]) { + todo!(); + } + fn devoure_tuple(&self, tup: (LargeAndCopy, LargeAndCopy)) { + todo!(); + } + fn devoure_array_and_tuple_wow(&self, array: [u8; 6666], tup: (LargeAndCopy, LargeAndCopy)) { + todo!(); + } +} + +#[inline(always)] +fn foo_always(x: LargeAndCopy) { + todo!(); +} +#[inline(never)] +fn foo_never(x: LargeAndCopy) { + todo!(); +} +#[inline] +fn foo(x: LargeAndCopy) { + todo!(); +} + +fn main() {} diff --git a/tests/ui/large_types_passed_by_value.stderr b/tests/ui/large_types_passed_by_value.stderr new file mode 100644 index 00000000000..5f42dcfb9b5 --- /dev/null +++ b/tests/ui/large_types_passed_by_value.stderr @@ -0,0 +1,52 @@ +error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte) + --> $DIR/large_types_passed_by_value.rs:20:11 + | +LL | fn bad(a: LargeAndCopy) {} + | ^^^^^^^^^^^^ help: consider passing by reference instead: `&LargeAndCopy` + | + = note: `-D clippy::large-types-passed-by-value` implied by `-D warnings` + +error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte) + --> $DIR/large_types_passed_by_value.rs:25:37 + | +LL | fn other_is_not_ok(self, other: LargeAndCopy) {} + | ^^^^^^^^^^^^ help: consider passing by reference instead: `&LargeAndCopy` + +error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte) + --> $DIR/large_types_passed_by_value.rs:31:36 + | +LL | fn devoure_array(&self, array: [u8; 6666]); + | ^^^^^^^^^^ help: consider passing by reference instead: `&[u8; 6666]` + +error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte) + --> $DIR/large_types_passed_by_value.rs:32:34 + | +LL | fn devoure_tuple(&self, tup: (LargeAndCopy, LargeAndCopy)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider passing by reference instead: `&(LargeAndCopy, LargeAndCopy)` + +error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte) + --> $DIR/large_types_passed_by_value.rs:33:50 + | +LL | fn devoure_array_and_tuple_wow(&self, array: [u8; 6666], tup: (LargeAndCopy, LargeAndCopy)); + | ^^^^^^^^^^ help: consider passing by reference instead: `&[u8; 6666]` + +error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte) + --> $DIR/large_types_passed_by_value.rs:33:67 + | +LL | fn devoure_array_and_tuple_wow(&self, array: [u8; 6666], tup: (LargeAndCopy, LargeAndCopy)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider passing by reference instead: `&(LargeAndCopy, LargeAndCopy)` + +error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte) + --> $DIR/large_types_passed_by_value.rs:58:17 + | +LL | fn foo_never(x: LargeAndCopy) { + | ^^^^^^^^^^^^ help: consider passing by reference instead: `&LargeAndCopy` + +error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte) + --> $DIR/large_types_passed_by_value.rs:62:11 + | +LL | fn foo(x: LargeAndCopy) { + | ^^^^^^^^^^^^ help: consider passing by reference instead: `&LargeAndCopy` + +error: aborting due to 8 previous errors + -- cgit 1.4.1-3-g733a5 From f82f9c2c55392ef9d7649bf2e485f7e509fd0038 Mon Sep 17 00:00:00 2001 From: Francis Murillo Date: Fri, 2 Oct 2020 09:05:30 +0800 Subject: Add lint for `&mut Mutex::lock` --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 7 ++++ clippy_lints/src/mut_mutex_lock.rs | 75 ++++++++++++++++++++++++++++++++++++++ src/lintlist/mod.rs | 7 ++++ tests/ui/mut_mutex_lock.rs | 19 ++++++++++ tests/ui/mut_mutex_lock.stderr | 19 ++++++++++ 6 files changed, 128 insertions(+) create mode 100644 clippy_lints/src/mut_mutex_lock.rs create mode 100644 tests/ui/mut_mutex_lock.rs create mode 100644 tests/ui/mut_mutex_lock.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d995cc9701..ba080835f5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1843,6 +1843,7 @@ Released 2018-09-13 [`must_use_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_unit [`mut_from_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_from_ref [`mut_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_mut +[`mut_mutex_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_mutex_lock [`mut_range_bound`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_range_bound [`mutable_key_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type [`mutex_atomic`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_atomic diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 8d53b9799f0..3a108bcfe6a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -255,6 +255,7 @@ mod modulo_arithmetic; mod multiple_crate_versions; mod mut_key; mod mut_mut; +mod mut_mutex_lock; mod mut_reference; mod mutable_debug_assertion; mod mutex_atomic; @@ -744,6 +745,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &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, @@ -1112,6 +1114,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box future_not_send::FutureNotSend); store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls); store.register_late_pass(|| box if_let_mutex::IfLetMutex); + store.register_late_pass(|| box mut_mutex_lock::MutMutexLock); store.register_late_pass(|| box match_on_vec_items::MatchOnVecItems); store.register_early_pass(|| box manual_non_exhaustive::ManualNonExhaustive); store.register_late_pass(|| box manual_async_fn::ManualAsyncFn); @@ -1446,6 +1449,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), @@ -1780,6 +1784,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&misc::FLOAT_CMP), LintId::of(&misc::MODULO_ONE), LintId::of(&mut_key::MUTABLE_KEY_TYPE), + LintId::of(&mut_mutex_lock::MUT_MUTEX_LOCK), + LintId::of(&non_copy_const::BORROW_INTERIOR_MUTABLE_CONST), + LintId::of(&non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST), LintId::of(&open_options::NONSENSICAL_OPEN_OPTIONS), LintId::of(&option_env_unwrap::OPTION_ENV_UNWRAP), LintId::of(&ptr::MUT_FROM_REF), diff --git a/clippy_lints/src/mut_mutex_lock.rs b/clippy_lints/src/mut_mutex_lock.rs new file mode 100644 index 00000000000..4f3108355ca --- /dev/null +++ b/clippy_lints/src/mut_mutex_lock.rs @@ -0,0 +1,75 @@ +use crate::utils::{is_type_diagnostic_item, span_lint_and_help}; +use if_chain::if_chain; +use rustc_hir::{Expr, ExprKind, Mutability}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for `&mut Mutex::lock` calls + /// + /// **Why is this bad?** `Mutex::lock` is less efficient than + /// calling `Mutex::get_mut` + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// use std::sync::{Arc, Mutex}; + /// + /// let mut value_rc = Arc::new(Mutex::new(42_u8)); + /// let value_mutex = Arc::get_mut(&mut value_rc).unwrap(); + /// + /// let value = value_mutex.lock().unwrap(); + /// do_stuff(value); + /// ``` + /// Use instead: + /// ```rust + /// use std::sync::{Arc, Mutex}; + /// + /// let mut value_rc = Arc::new(Mutex::new(42_u8)); + /// let value_mutex = Arc::get_mut(&mut value_rc).unwrap(); + /// + /// let value = value_mutex.get_mut().unwrap(); + /// do_stuff(value); + /// ``` + pub MUT_MUTEX_LOCK, + correctness, + "`&mut Mutex::lock` does unnecessary locking" +} + +declare_lint_pass!(MutMutexLock => [MUT_MUTEX_LOCK]); + +impl<'tcx> LateLintPass<'tcx> for MutMutexLock { + fn check_expr(&mut self, cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>) { + if_chain! { + if is_mut_mutex_lock_call(cx, ex).is_some(); + then { + span_lint_and_help( + cx, + MUT_MUTEX_LOCK, + ex.span, + "calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference", + None, + "use `&mut Mutex::get_mut` instead", + ); + } + } + } +} + +fn is_mut_mutex_lock_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { + if_chain! { + if let ExprKind::MethodCall(path, _span, args, _) = &expr.kind; + if path.ident.name == sym!(lock); + let ty = cx.typeck_results().expr_ty(&args[0]); + if let ty::Ref(_, inner_ty, Mutability::Mut) = ty.kind(); + if is_type_diagnostic_item(cx, inner_ty, sym!(mutex_type)); + then { + Some(&args[0]) + } else { + None + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 98ad994ea7b..5e48757a4c5 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1502,6 +1502,13 @@ vec![ deprecation: None, module: "mut_mut", }, + Lint { + name: "mut_mutex_lock", + group: "correctness", + desc: "`&mut Mutex::lock` does unnecessary locking", + deprecation: None, + module: "mut_mutex_lock", + }, Lint { name: "mut_range_bound", group: "complexity", diff --git a/tests/ui/mut_mutex_lock.rs b/tests/ui/mut_mutex_lock.rs new file mode 100644 index 00000000000..516d44bb7a9 --- /dev/null +++ b/tests/ui/mut_mutex_lock.rs @@ -0,0 +1,19 @@ +#![warn(clippy::mut_mutex_lock)] + +use std::sync::{Arc, Mutex}; + +fn mut_mutex_lock() { + let mut value_rc = Arc::new(Mutex::new(42_u8)); + let value_mutex = Arc::get_mut(&mut value_rc).unwrap(); + + let value = value_mutex.lock().unwrap(); + *value += 1; +} + +fn no_owned_mutex_lock() { + let mut value_rc = Arc::new(Mutex::new(42_u8)); + let value = value_rc.lock().unwrap(); + *value += 1; +} + +fn main() {} diff --git a/tests/ui/mut_mutex_lock.stderr b/tests/ui/mut_mutex_lock.stderr new file mode 100644 index 00000000000..426e0240830 --- /dev/null +++ b/tests/ui/mut_mutex_lock.stderr @@ -0,0 +1,19 @@ +error[E0596]: cannot borrow `value` as mutable, as it is not declared as mutable + --> $DIR/mut_mutex_lock.rs:10:6 + | +LL | let value = value_mutex.lock().unwrap(); + | ----- help: consider changing this to be mutable: `mut value` +LL | *value += 1; + | ^^^^^ cannot borrow as mutable + +error[E0596]: cannot borrow `value` as mutable, as it is not declared as mutable + --> $DIR/mut_mutex_lock.rs:16:6 + | +LL | let value = value_rc.lock().unwrap(); + | ----- help: consider changing this to be mutable: `mut value` +LL | *value += 1; + | ^^^^^ cannot borrow as mutable + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0596`. -- cgit 1.4.1-3-g733a5 From fb8a9cb38d73f3876b57f250502895c7cc60d421 Mon Sep 17 00:00:00 2001 From: Francis Murillo Date: Fri, 2 Oct 2020 10:54:44 +0800 Subject: Change lint doc test --- clippy_lints/src/mut_mutex_lock.rs | 6 +++--- tests/ui/mut_mutex_lock.rs | 4 ++-- tests/ui/mut_mutex_lock.stderr | 22 +++++++--------------- 3 files changed, 12 insertions(+), 20 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/mut_mutex_lock.rs b/clippy_lints/src/mut_mutex_lock.rs index 4f3108355ca..0680e578537 100644 --- a/clippy_lints/src/mut_mutex_lock.rs +++ b/clippy_lints/src/mut_mutex_lock.rs @@ -21,8 +21,8 @@ declare_clippy_lint! { /// let mut value_rc = Arc::new(Mutex::new(42_u8)); /// let value_mutex = Arc::get_mut(&mut value_rc).unwrap(); /// - /// let value = value_mutex.lock().unwrap(); - /// do_stuff(value); + /// let mut value = value_mutex.lock().unwrap(); + /// *value += 1; /// ``` /// Use instead: /// ```rust @@ -32,7 +32,7 @@ declare_clippy_lint! { /// let value_mutex = Arc::get_mut(&mut value_rc).unwrap(); /// /// let value = value_mutex.get_mut().unwrap(); - /// do_stuff(value); + /// *value += 1; /// ``` pub MUT_MUTEX_LOCK, correctness, diff --git a/tests/ui/mut_mutex_lock.rs b/tests/ui/mut_mutex_lock.rs index 516d44bb7a9..9cd98e90c29 100644 --- a/tests/ui/mut_mutex_lock.rs +++ b/tests/ui/mut_mutex_lock.rs @@ -6,13 +6,13 @@ fn mut_mutex_lock() { let mut value_rc = Arc::new(Mutex::new(42_u8)); let value_mutex = Arc::get_mut(&mut value_rc).unwrap(); - let value = value_mutex.lock().unwrap(); + let mut value = value_mutex.lock().unwrap(); *value += 1; } fn no_owned_mutex_lock() { let mut value_rc = Arc::new(Mutex::new(42_u8)); - let value = value_rc.lock().unwrap(); + let mut value = value_rc.lock().unwrap(); *value += 1; } diff --git a/tests/ui/mut_mutex_lock.stderr b/tests/ui/mut_mutex_lock.stderr index 426e0240830..d521ebb56c4 100644 --- a/tests/ui/mut_mutex_lock.stderr +++ b/tests/ui/mut_mutex_lock.stderr @@ -1,19 +1,11 @@ -error[E0596]: cannot borrow `value` as mutable, as it is not declared as mutable - --> $DIR/mut_mutex_lock.rs:10:6 +error: calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference + --> $DIR/mut_mutex_lock.rs:9:21 | -LL | let value = value_mutex.lock().unwrap(); - | ----- help: consider changing this to be mutable: `mut value` -LL | *value += 1; - | ^^^^^ cannot borrow as mutable - -error[E0596]: cannot borrow `value` as mutable, as it is not declared as mutable - --> $DIR/mut_mutex_lock.rs:16:6 +LL | let mut value = value_mutex.lock().unwrap(); + | ^^^^^^^^^^^^^^^^^^ | -LL | let value = value_rc.lock().unwrap(); - | ----- help: consider changing this to be mutable: `mut value` -LL | *value += 1; - | ^^^^^ cannot borrow as mutable + = note: `-D clippy::mut-mutex-lock` implied by `-D warnings` + = help: use `&mut Mutex::get_mut` instead -error: aborting due to 2 previous errors +error: aborting due to previous error -For more information about this error, try `rustc --explain E0596`. -- cgit 1.4.1-3-g733a5 From 292cb9bfb6bc51595404425b0ada90f21e6d9661 Mon Sep 17 00:00:00 2001 From: Francis Murillo Date: Sat, 10 Oct 2020 18:07:47 +0800 Subject: Use `sugg_lint_and_help` --- clippy_lints/src/mut_mutex_lock.rs | 18 +++++++++++------- tests/ui/mut_mutex_lock.fixed | 21 +++++++++++++++++++++ tests/ui/mut_mutex_lock.rs | 2 ++ tests/ui/mut_mutex_lock.stderr | 5 ++--- 4 files changed, 36 insertions(+), 10 deletions(-) create mode 100644 tests/ui/mut_mutex_lock.fixed (limited to 'tests') diff --git a/clippy_lints/src/mut_mutex_lock.rs b/clippy_lints/src/mut_mutex_lock.rs index ca3371a5d75..82ed2d6d69c 100644 --- a/clippy_lints/src/mut_mutex_lock.rs +++ b/clippy_lints/src/mut_mutex_lock.rs @@ -1,5 +1,6 @@ -use crate::utils::{is_type_diagnostic_item, span_lint_and_help}; +use crate::utils::{is_type_diagnostic_item, span_lint_and_sugg}; use if_chain::if_chain; +use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Mutability}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; @@ -9,7 +10,9 @@ declare_clippy_lint! { /// **What it does:** Checks for `&mut Mutex::lock` calls /// /// **Why is this bad?** `Mutex::lock` is less efficient than - /// calling `Mutex::get_mut` + /// calling `Mutex::get_mut`. In addition you also have a statically + /// guarantee that the mutex isn't locked, instead of just a runtime + /// guarantee. /// /// **Known problems:** None. /// @@ -44,19 +47,20 @@ declare_lint_pass!(MutMutexLock => [MUT_MUTEX_LOCK]); impl<'tcx> LateLintPass<'tcx> for MutMutexLock { fn check_expr(&mut self, cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>) { if_chain! { - if let ExprKind::MethodCall(path, _span, args, _) = &ex.kind; + if let ExprKind::MethodCall(path, method_span, args, _) = &ex.kind; if path.ident.name == sym!(lock); let ty = cx.typeck_results().expr_ty(&args[0]); if let ty::Ref(_, inner_ty, Mutability::Mut) = ty.kind(); if is_type_diagnostic_item(cx, inner_ty, sym!(mutex_type)); then { - span_lint_and_help( + span_lint_and_sugg( cx, MUT_MUTEX_LOCK, - ex.span, + *method_span, "calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference", - None, - "use `&mut Mutex::get_mut` instead", + "change this to", + "get_mut".to_owned(), + Applicability::MachineApplicable, ); } } diff --git a/tests/ui/mut_mutex_lock.fixed b/tests/ui/mut_mutex_lock.fixed new file mode 100644 index 00000000000..36bc52e3374 --- /dev/null +++ b/tests/ui/mut_mutex_lock.fixed @@ -0,0 +1,21 @@ +// run-rustfix +#![allow(dead_code, unused_mut)] +#![warn(clippy::mut_mutex_lock)] + +use std::sync::{Arc, Mutex}; + +fn mut_mutex_lock() { + let mut value_rc = Arc::new(Mutex::new(42_u8)); + let value_mutex = Arc::get_mut(&mut value_rc).unwrap(); + + let mut value = value_mutex.get_mut().unwrap(); + *value += 1; +} + +fn no_owned_mutex_lock() { + let mut value_rc = Arc::new(Mutex::new(42_u8)); + let mut value = value_rc.lock().unwrap(); + *value += 1; +} + +fn main() {} diff --git a/tests/ui/mut_mutex_lock.rs b/tests/ui/mut_mutex_lock.rs index 9cd98e90c29..ea60df5ae1b 100644 --- a/tests/ui/mut_mutex_lock.rs +++ b/tests/ui/mut_mutex_lock.rs @@ -1,3 +1,5 @@ +// run-rustfix +#![allow(dead_code, unused_mut)] #![warn(clippy::mut_mutex_lock)] use std::sync::{Arc, Mutex}; diff --git a/tests/ui/mut_mutex_lock.stderr b/tests/ui/mut_mutex_lock.stderr index d521ebb56c4..21c1b3486ca 100644 --- a/tests/ui/mut_mutex_lock.stderr +++ b/tests/ui/mut_mutex_lock.stderr @@ -1,11 +1,10 @@ error: calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference - --> $DIR/mut_mutex_lock.rs:9:21 + --> $DIR/mut_mutex_lock.rs:11:33 | LL | let mut value = value_mutex.lock().unwrap(); - | ^^^^^^^^^^^^^^^^^^ + | ^^^^ help: change this to: `get_mut` | = note: `-D clippy::mut-mutex-lock` implied by `-D warnings` - = help: use `&mut Mutex::get_mut` instead error: aborting due to previous error -- cgit 1.4.1-3-g733a5 From db8380c4a0e7f707112bbce19a6074a5fac2de59 Mon Sep 17 00:00:00 2001 From: Florian Hartwig Date: Wed, 31 Oct 2018 17:14:55 +0100 Subject: Add lint for unusually-grouped hex/binary literals --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 3 +++ clippy_lints/src/literal_representation.rs | 42 +++++++++++++++++++++++++++--- clippy_lints/src/utils/numeric_literal.rs | 6 ++--- src/lintlist/mod.rs | 7 +++++ tests/ui/large_digit_groups.fixed | 2 +- tests/ui/large_digit_groups.stderr | 20 +++++++++----- tests/ui/literals.rs | 13 ++++++--- tests/ui/literals.stderr | 22 +++++++++++++++- tests/ui/unreadable_literal.fixed | 2 +- tests/ui/unreadable_literal.stderr | 10 ++++++- 11 files changed, 107 insertions(+), 21 deletions(-) (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fd79deb56c..c0dd7b352ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2017,6 +2017,7 @@ Released 2018-09-13 [`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label [`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self [`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit +[`unusual_byte_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_grouping [`unwrap_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_in_result [`unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used [`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index b330b66776c..5d6900f6b96 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -623,6 +623,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &literal_representation::LARGE_DIGIT_GROUPS, &literal_representation::MISTYPED_LITERAL_SUFFIXES, &literal_representation::UNREADABLE_LITERAL, + &literal_representation::UNUSUAL_BYTE_GROUPING, &loops::EMPTY_LOOP, &loops::EXPLICIT_COUNTER_LOOP, &loops::EXPLICIT_INTO_ITER_LOOP, @@ -1365,6 +1366,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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_GROUPING), LintId::of(&loops::EMPTY_LOOP), LintId::of(&loops::EXPLICIT_COUNTER_LOOP), LintId::of(&loops::FOR_KV_MAP), @@ -1587,6 +1589,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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_GROUPING), LintId::of(&loops::EMPTY_LOOP), LintId::of(&loops::FOR_KV_MAP), LintId::of(&loops::NEEDLESS_RANGE_LOOP), diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index c54103b25c2..b41cfe32cfe 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -82,6 +82,25 @@ declare_clippy_lint! { "integer literals with digits grouped inconsistently" } +declare_clippy_lint! { + /// **What it does:** Warns if hexadecimal or binary literals are not grouped + /// by nibble or byte. + /// + /// **Why is this bad?** Negatively impacts readability. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let x: u32 = 0xFFF_FFF; + /// let y: u8 = 0b01_011_101; + /// ``` + pub UNUSUAL_BYTE_GROUPING, + style, + "binary or hex literals that aren't grouped by four" +} + declare_clippy_lint! { /// **What it does:** Warns if the digits of an integral or floating-point /// constant are grouped into groups that @@ -125,6 +144,7 @@ enum WarningType { LargeDigitGroups, DecimalRepresentation, MistypedLiteralSuffix, + UnusualByteGrouping, } impl WarningType { @@ -175,6 +195,15 @@ impl WarningType { suggested_format, Applicability::MachineApplicable, ), + Self::UnusualByteGrouping => span_lint_and_sugg( + cx, + UNUSUAL_BYTE_GROUPING, + span, + "digits of hex or binary literal not grouped by four", + "consider", + suggested_format, + Applicability::MachineApplicable, + ), }; } } @@ -184,6 +213,7 @@ declare_lint_pass!(LiteralDigitGrouping => [ INCONSISTENT_DIGIT_GROUPING, LARGE_DIGIT_GROUPS, MISTYPED_LITERAL_SUFFIXES, + UNUSUAL_BYTE_GROUPING, ]); impl EarlyLintPass for LiteralDigitGrouping { @@ -217,9 +247,9 @@ impl LiteralDigitGrouping { let result = (|| { - let integral_group_size = Self::get_group_size(num_lit.integer.split('_'))?; + let integral_group_size = Self::get_group_size(num_lit.integer.split('_'), num_lit.radix)?; if let Some(fraction) = num_lit.fraction { - let fractional_group_size = Self::get_group_size(fraction.rsplit('_'))?; + let fractional_group_size = Self::get_group_size(fraction.rsplit('_'), num_lit.radix)?; let consistent = Self::parts_consistent(integral_group_size, fractional_group_size, @@ -229,6 +259,7 @@ impl LiteralDigitGrouping { return Err(WarningType::InconsistentDigitGrouping); }; } + Ok(()) })(); @@ -237,6 +268,7 @@ impl LiteralDigitGrouping { let should_warn = match warning_type { | WarningType::UnreadableLiteral | WarningType::InconsistentDigitGrouping + | WarningType::UnusualByteGrouping | WarningType::LargeDigitGroups => { !in_macro(lit.span) } @@ -331,11 +363,15 @@ impl LiteralDigitGrouping { /// Returns the size of the digit groups (or None if ungrouped) if successful, /// otherwise returns a `WarningType` for linting. - fn get_group_size<'a>(groups: impl Iterator) -> Result, WarningType> { + fn get_group_size<'a>(groups: impl Iterator, radix: Radix) -> Result, WarningType> { let mut groups = groups.map(str::len); let first = groups.next().expect("At least one group"); + if (radix == Radix::Binary || radix == Radix::Hexadecimal) && groups.any(|i| i % 4 != 0) { + return Err(WarningType::UnusualByteGrouping); + } + if let Some(second) = groups.next() { if !groups.all(|x| x == second) || first > second { Err(WarningType::InconsistentDigitGrouping) diff --git a/clippy_lints/src/utils/numeric_literal.rs b/clippy_lints/src/utils/numeric_literal.rs index 52d3c2c1daf..d02603d7702 100644 --- a/clippy_lints/src/utils/numeric_literal.rs +++ b/clippy_lints/src/utils/numeric_literal.rs @@ -1,6 +1,6 @@ use rustc_ast::ast::{Lit, LitFloatType, LitIntType, LitKind}; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Copy, Clone)] pub enum Radix { Binary, Octal, @@ -11,8 +11,8 @@ pub enum Radix { impl Radix { /// Returns a reasonable digit group size for this radix. #[must_use] - fn suggest_grouping(&self) -> usize { - match *self { + fn suggest_grouping(self) -> usize { + match self { Self::Binary | Self::Hexadecimal => 4, Self::Octal | Self::Decimal => 3, } diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 4bf77dae637..fc8efb81cfc 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2643,6 +2643,13 @@ vec![ deprecation: None, module: "unused_unit", }, + Lint { + name: "unusual_byte_grouping", + group: "style", + desc: "binary or hex literals that aren\'t grouped by four", + deprecation: None, + module: "literal_representation", + }, Lint { name: "unwrap_in_result", group: "restriction", diff --git a/tests/ui/large_digit_groups.fixed b/tests/ui/large_digit_groups.fixed index 859fad2f54d..3430c137ec2 100644 --- a/tests/ui/large_digit_groups.fixed +++ b/tests/ui/large_digit_groups.fixed @@ -11,7 +11,7 @@ fn main() { let _good = ( 0b1011_i64, 0o1_234_u32, - 0x1_234_567, + 0x0123_4567, 1_2345_6789, 1234_f32, 1_234.12_f32, diff --git a/tests/ui/large_digit_groups.stderr b/tests/ui/large_digit_groups.stderr index b6d9672a78e..fe472e66949 100644 --- a/tests/ui/large_digit_groups.stderr +++ b/tests/ui/large_digit_groups.stderr @@ -1,24 +1,30 @@ -error: digit groups should be smaller +error: digits of hex or binary literal not grouped by four + --> $DIR/large_digit_groups.rs:14:9 + | +LL | 0x1_234_567, + | ^^^^^^^^^^^ help: consider: `0x0123_4567` + | + = note: `-D clippy::unusual-byte-grouping` implied by `-D warnings` + +error: digits of hex or binary literal not grouped by four --> $DIR/large_digit_groups.rs:22:9 | LL | 0b1_10110_i64, | ^^^^^^^^^^^^^ help: consider: `0b11_0110_i64` - | - = note: `-D clippy::large-digit-groups` implied by `-D warnings` -error: digits grouped inconsistently by underscores +error: digits of hex or binary literal not grouped by four --> $DIR/large_digit_groups.rs:23:9 | LL | 0xd_e_adbee_f_usize, | ^^^^^^^^^^^^^^^^^^^ help: consider: `0xdead_beef_usize` - | - = note: `-D clippy::inconsistent-digit-grouping` implied by `-D warnings` error: digit groups should be smaller --> $DIR/large_digit_groups.rs:24:9 | LL | 1_23456_f32, | ^^^^^^^^^^^ help: consider: `123_456_f32` + | + = note: `-D clippy::large-digit-groups` implied by `-D warnings` error: digit groups should be smaller --> $DIR/large_digit_groups.rs:25:9 @@ -38,5 +44,5 @@ error: digit groups should be smaller LL | 1_23456.12345_6_f64, | ^^^^^^^^^^^^^^^^^^^ help: consider: `123_456.123_456_f64` -error: aborting due to 6 previous errors +error: aborting due to 7 previous errors diff --git a/tests/ui/literals.rs b/tests/ui/literals.rs index c299b16c8ce..2608638ff80 100644 --- a/tests/ui/literals.rs +++ b/tests/ui/literals.rs @@ -7,10 +7,10 @@ fn main() { let ok1 = 0xABCD; - let ok3 = 0xab_cd; - let ok4 = 0xab_cd_i32; - let ok5 = 0xAB_CD_u32; - let ok5 = 0xAB_CD_isize; + let ok3 = 0xabcd; + let ok4 = 0xabcd_i32; + let ok5 = 0xABCD_u32; + let ok5 = 0xABCD_isize; let fail1 = 0xabCD; let fail2 = 0xabCD_u32; let fail2 = 0xabCD_isize; @@ -33,4 +33,9 @@ fn main() { let fail19 = 12_3456_21; let fail22 = 3__4___23; let fail23 = 3__16___23; + + let fail24 = 0xAB_ABC_AB; + let fail25 = 0b01_100_101; + let ok26 = 0x6_A0_BF; + let ok27 = 0b1_0010_0101; } diff --git a/tests/ui/literals.stderr b/tests/ui/literals.stderr index 0b3af2d8bc3..c88848a40fc 100644 --- a/tests/ui/literals.stderr +++ b/tests/ui/literals.stderr @@ -69,5 +69,25 @@ error: digits grouped inconsistently by underscores LL | let fail23 = 3__16___23; | ^^^^^^^^^^ help: consider: `31_623` -error: aborting due to 8 previous errors +error: digits of hex or binary literal not grouped by four + --> $DIR/literals.rs:37:18 + | +LL | let fail24 = 0xAB_ABC_AB; + | ^^^^^^^^^^^ help: consider: `0x0ABA_BCAB` + | + = note: `-D clippy::unusual-byte-grouping` implied by `-D warnings` + +error: digits of hex or binary literal not grouped by four + --> $DIR/literals.rs:38:18 + | +LL | let fail25 = 0b01_100_101; + | ^^^^^^^^^^^^ help: consider: `0b0110_0101` + +error: digits of hex or binary literal not grouped by four + --> $DIR/literals.rs:39:16 + | +LL | let ok26 = 0x6_A0_BF; + | ^^^^^^^^^ help: consider: `0x0006_A0BF` + +error: aborting due to 11 previous errors diff --git a/tests/ui/unreadable_literal.fixed b/tests/ui/unreadable_literal.fixed index 3f358d9ecaa..4043d53299f 100644 --- a/tests/ui/unreadable_literal.fixed +++ b/tests/ui/unreadable_literal.fixed @@ -14,7 +14,7 @@ fn main() { let _good = ( 0b1011_i64, 0o1_234_u32, - 0x1_234_567, + 0x0123_4567, 65536, 1_2345_6789, 1234_f32, diff --git a/tests/ui/unreadable_literal.stderr b/tests/ui/unreadable_literal.stderr index 1b2ff6bff04..fa4c3fe13e3 100644 --- a/tests/ui/unreadable_literal.stderr +++ b/tests/ui/unreadable_literal.stderr @@ -1,3 +1,11 @@ +error: digits of hex or binary literal not grouped by four + --> $DIR/unreadable_literal.rs:17:9 + | +LL | 0x1_234_567, + | ^^^^^^^^^^^ help: consider: `0x0123_4567` + | + = note: `-D clippy::unusual-byte-grouping` implied by `-D warnings` + error: long literal lacking separators --> $DIR/unreadable_literal.rs:25:17 | @@ -54,5 +62,5 @@ error: long literal lacking separators LL | let _fail12: i128 = 0xabcabcabcabcabcabc; | ^^^^^^^^^^^^^^^^^^^^ help: consider: `0x00ab_cabc_abca_bcab_cabc` -error: aborting due to 9 previous errors +error: aborting due to 10 previous errors -- cgit 1.4.1-3-g733a5 From 0c0f8db347e406682875fcd08d2bc483e93f710f Mon Sep 17 00:00:00 2001 From: cgm616 Date: Sun, 25 Oct 2020 09:19:58 -0400 Subject: Remove accidental test inclusion --- tests/ui/literals.stderr | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'tests') diff --git a/tests/ui/literals.stderr b/tests/ui/literals.stderr index c88848a40fc..e321f2a1cef 100644 --- a/tests/ui/literals.stderr +++ b/tests/ui/literals.stderr @@ -83,11 +83,5 @@ error: digits of hex or binary literal not grouped by four LL | let fail25 = 0b01_100_101; | ^^^^^^^^^^^^ help: consider: `0b0110_0101` -error: digits of hex or binary literal not grouped by four - --> $DIR/literals.rs:39:16 - | -LL | let ok26 = 0x6_A0_BF; - | ^^^^^^^^^ help: consider: `0x0006_A0BF` - -error: aborting due to 11 previous errors +error: aborting due to 10 previous errors -- cgit 1.4.1-3-g733a5 From 3ce820bf83657879493aa7b107634f1951e7c219 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Wed, 21 Oct 2020 22:33:41 +0900 Subject: Fix an invalid suggestion in `needless_collect` test --- clippy_lints/src/loops.rs | 9 ++++++++- tests/ui/needless_collect_indirect.stderr | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 23ca35fffaa..c3f75f283f4 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -3001,7 +3001,14 @@ impl IterFunction { IterFunctionKind::IntoIter => String::new(), IterFunctionKind::Len => String::from(".count()"), IterFunctionKind::IsEmpty => String::from(".next().is_none()"), - IterFunctionKind::Contains(span) => format!(".any(|x| x == {})", snippet(cx, *span, "..")), + IterFunctionKind::Contains(span) => { + let s = snippet(cx, *span, ".."); + if let Some(stripped) = s.strip_prefix('&') { + format!(".any(|x| x == {})", stripped) + } else { + format!(".any(|x| x == *{})", s) + } + }, } } fn get_suggestion_text(&self) -> &'static str { diff --git a/tests/ui/needless_collect_indirect.stderr b/tests/ui/needless_collect_indirect.stderr index 0c1e61d7496..7b8e227f304 100644 --- a/tests/ui/needless_collect_indirect.stderr +++ b/tests/ui/needless_collect_indirect.stderr @@ -48,7 +48,7 @@ LL | | indirect_contains.contains(&&5); help: Check if the original Iterator contains an element instead of collecting then checking | LL | -LL | sample.iter().any(|x| x == &&5); +LL | sample.iter().any(|x| x == &5); | error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From 2f5d418011f80d99e0e4c8ddea3980b6cdc8332d Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Sun, 25 Oct 2020 23:55:41 +0900 Subject: Add test case --- tests/ui/needless_collect_indirect.rs | 6 ++++++ tests/ui/needless_collect_indirect.stderr | 15 ++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/ui/needless_collect_indirect.rs b/tests/ui/needless_collect_indirect.rs index 4cf03e82035..4f6e5357727 100644 --- a/tests/ui/needless_collect_indirect.rs +++ b/tests/ui/needless_collect_indirect.rs @@ -16,4 +16,10 @@ fn main() { .into_iter() .map(|x| (*x, *x + 1)) .collect::>(); + + // #6202 + let a = "a".to_string(); + let sample = vec![a.clone(), "b".to_string(), "c".to_string()]; + let non_copy_contains = sample.into_iter().collect::>(); + non_copy_contains.contains(&a); } diff --git a/tests/ui/needless_collect_indirect.stderr b/tests/ui/needless_collect_indirect.stderr index 7b8e227f304..fb807da5f8a 100644 --- a/tests/ui/needless_collect_indirect.stderr +++ b/tests/ui/needless_collect_indirect.stderr @@ -51,5 +51,18 @@ LL | LL | sample.iter().any(|x| x == &5); | -error: aborting due to 4 previous errors +error: avoid using `collect()` when not needed + --> $DIR/needless_collect_indirect.rs:23:5 + | +LL | / let non_copy_contains = sample.into_iter().collect::>(); +LL | | non_copy_contains.contains(&a); + | |____^ + | +help: Check if the original Iterator contains an element instead of collecting then checking + | +LL | +LL | sample.into_iter().any(|x| x == a); + | + +error: aborting due to 5 previous errors -- cgit 1.4.1-3-g733a5 From 312bbff6968dbebd367ca90677c676e2cf5198d2 Mon Sep 17 00:00:00 2001 From: cgm616 Date: Sun, 25 Oct 2020 11:31:24 -0400 Subject: Integrate suggestions from code review --- CHANGELOG.md | 2 +- clippy_lints/src/lib.rs | 6 +++--- clippy_lints/src/literal_representation.rs | 14 +++++++------- src/lintlist/mod.rs | 2 +- tests/ui/large_digit_groups.stderr | 2 +- tests/ui/literals.rs | 8 ++++---- tests/ui/literals.stderr | 2 +- tests/ui/unreadable_literal.stderr | 2 +- 8 files changed, 19 insertions(+), 19 deletions(-) (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index c0dd7b352ad..25f3b5da198 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2017,7 +2017,7 @@ Released 2018-09-13 [`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label [`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self [`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit -[`unusual_byte_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_grouping +[`unusual_byte_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_groupings [`unwrap_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_in_result [`unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used [`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 5d6900f6b96..3be8bc0e36d 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -623,7 +623,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &literal_representation::LARGE_DIGIT_GROUPS, &literal_representation::MISTYPED_LITERAL_SUFFIXES, &literal_representation::UNREADABLE_LITERAL, - &literal_representation::UNUSUAL_BYTE_GROUPING, + &literal_representation::UNUSUAL_BYTE_GROUPINGS, &loops::EMPTY_LOOP, &loops::EXPLICIT_COUNTER_LOOP, &loops::EXPLICIT_INTO_ITER_LOOP, @@ -1366,7 +1366,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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_GROUPING), + LintId::of(&literal_representation::UNUSUAL_BYTE_GROUPINGS), LintId::of(&loops::EMPTY_LOOP), LintId::of(&loops::EXPLICIT_COUNTER_LOOP), LintId::of(&loops::FOR_KV_MAP), @@ -1589,7 +1589,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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_GROUPING), + LintId::of(&literal_representation::UNUSUAL_BYTE_GROUPINGS), LintId::of(&loops::EMPTY_LOOP), LintId::of(&loops::FOR_KV_MAP), LintId::of(&loops::NEEDLESS_RANGE_LOOP), diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index f51a0edc9c5..e8a741683da 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -96,7 +96,7 @@ declare_clippy_lint! { /// let x: u32 = 0xFFF_FFF; /// let y: u8 = 0b01_011_101; /// ``` - pub UNUSUAL_BYTE_GROUPING, + pub UNUSUAL_BYTE_GROUPINGS, style, "binary or hex literals that aren't grouped by four" } @@ -144,7 +144,7 @@ enum WarningType { LargeDigitGroups, DecimalRepresentation, MistypedLiteralSuffix, - UnusualByteGrouping, + UnusualByteGroupings, } impl WarningType { @@ -195,9 +195,9 @@ impl WarningType { suggested_format, Applicability::MachineApplicable, ), - Self::UnusualByteGrouping => span_lint_and_sugg( + Self::UnusualByteGroupings => span_lint_and_sugg( cx, - UNUSUAL_BYTE_GROUPING, + UNUSUAL_BYTE_GROUPINGS, span, "digits of hex or binary literal not grouped by four", "consider", @@ -213,7 +213,7 @@ declare_lint_pass!(LiteralDigitGrouping => [ INCONSISTENT_DIGIT_GROUPING, LARGE_DIGIT_GROUPS, MISTYPED_LITERAL_SUFFIXES, - UNUSUAL_BYTE_GROUPING, + UNUSUAL_BYTE_GROUPINGS, ]); impl EarlyLintPass for LiteralDigitGrouping { @@ -268,7 +268,7 @@ impl LiteralDigitGrouping { let should_warn = match warning_type { | WarningType::UnreadableLiteral | WarningType::InconsistentDigitGrouping - | WarningType::UnusualByteGrouping + | WarningType::UnusualByteGroupings | WarningType::LargeDigitGroups => { !in_macro(lit.span) } @@ -369,7 +369,7 @@ impl LiteralDigitGrouping { let first = groups.next().expect("At least one group"); if (radix == Radix::Binary || radix == Radix::Hexadecimal) && groups.any(|i| i != 4 && i != 2) { - return Err(WarningType::UnusualByteGrouping); + return Err(WarningType::UnusualByteGroupings); } if let Some(second) = groups.next() { diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index fc8efb81cfc..6272ce45efb 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2644,7 +2644,7 @@ vec![ module: "unused_unit", }, Lint { - name: "unusual_byte_grouping", + name: "unusual_byte_groupings", group: "style", desc: "binary or hex literals that aren\'t grouped by four", deprecation: None, diff --git a/tests/ui/large_digit_groups.stderr b/tests/ui/large_digit_groups.stderr index fe472e66949..13d108b56e0 100644 --- a/tests/ui/large_digit_groups.stderr +++ b/tests/ui/large_digit_groups.stderr @@ -4,7 +4,7 @@ error: digits of hex or binary literal not grouped by four LL | 0x1_234_567, | ^^^^^^^^^^^ help: consider: `0x0123_4567` | - = note: `-D clippy::unusual-byte-grouping` implied by `-D warnings` + = note: `-D clippy::unusual-byte-groupings` implied by `-D warnings` error: digits of hex or binary literal not grouped by four --> $DIR/large_digit_groups.rs:22:9 diff --git a/tests/ui/literals.rs b/tests/ui/literals.rs index 2608638ff80..a72a74b9131 100644 --- a/tests/ui/literals.rs +++ b/tests/ui/literals.rs @@ -7,10 +7,10 @@ fn main() { let ok1 = 0xABCD; - let ok3 = 0xabcd; - let ok4 = 0xabcd_i32; - let ok5 = 0xABCD_u32; - let ok5 = 0xABCD_isize; + let ok3 = 0xab_cd; + let ok4 = 0xab_cd_i32; + let ok5 = 0xAB_CD_u32; + let ok5 = 0xAB_CD_isize; let fail1 = 0xabCD; let fail2 = 0xabCD_u32; let fail2 = 0xabCD_isize; diff --git a/tests/ui/literals.stderr b/tests/ui/literals.stderr index e321f2a1cef..64ceeb316d8 100644 --- a/tests/ui/literals.stderr +++ b/tests/ui/literals.stderr @@ -75,7 +75,7 @@ error: digits of hex or binary literal not grouped by four LL | let fail24 = 0xAB_ABC_AB; | ^^^^^^^^^^^ help: consider: `0x0ABA_BCAB` | - = note: `-D clippy::unusual-byte-grouping` implied by `-D warnings` + = note: `-D clippy::unusual-byte-groupings` implied by `-D warnings` error: digits of hex or binary literal not grouped by four --> $DIR/literals.rs:38:18 diff --git a/tests/ui/unreadable_literal.stderr b/tests/ui/unreadable_literal.stderr index fa4c3fe13e3..8645cabeabb 100644 --- a/tests/ui/unreadable_literal.stderr +++ b/tests/ui/unreadable_literal.stderr @@ -4,7 +4,7 @@ error: digits of hex or binary literal not grouped by four LL | 0x1_234_567, | ^^^^^^^^^^^ help: consider: `0x0123_4567` | - = note: `-D clippy::unusual-byte-grouping` implied by `-D warnings` + = note: `-D clippy::unusual-byte-groupings` implied by `-D warnings` error: long literal lacking separators --> $DIR/unreadable_literal.rs:25:17 -- cgit 1.4.1-3-g733a5 From de5a6d3420d9f2f2c4c995e325412e862a9dc583 Mon Sep 17 00:00:00 2001 From: Urcra Date: Mon, 26 Oct 2020 00:31:25 +0100 Subject: Initial implementation of comparison_to_empty --- CHANGELOG.md | 1 + Cargo.toml | 7 ++ clippy_lints/Cargo.toml | 22 +++++ clippy_lints/src/comparison_to_empty.rs | 155 ++++++++++++++++++++++++++++++++ clippy_lints/src/lib.rs | 5 ++ src/lintlist/mod.rs | 7 ++ tests/ui/comparison_to_empty.fixed | 23 +++++ tests/ui/comparison_to_empty.rs | 23 +++++ tests/ui/comparison_to_empty.stderr | 28 ++++++ 9 files changed, 271 insertions(+) create mode 100644 clippy_lints/src/comparison_to_empty.rs create mode 100644 tests/ui/comparison_to_empty.fixed create mode 100644 tests/ui/comparison_to_empty.rs create mode 100644 tests/ui/comparison_to_empty.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 22f96398153..869e9949167 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1664,6 +1664,7 @@ Released 2018-09-13 [`cognitive_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity [`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if [`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain +[`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty [`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator [`create_dir`]: https://rust-lang.github.io/rust-clippy/master/index.html#create_dir [`crosspointer_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#crosspointer_transmute diff --git a/Cargo.toml b/Cargo.toml index 1ddcd18598d..dae3e03074f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,13 @@ path = "src/main.rs" name = "clippy-driver" path = "src/driver.rs" +[target.'cfg(NOT_A_PLATFORM)'.dependencies] +rustc_data_structures = { path = "/home/urcra/rust/compiler/rustc_data_structures" } +rustc_driver = { path = "/home/urcra/rust/compiler/rustc_driver" } +rustc_errors = { path = "/home/urcra/rust/compiler/rustc_errors" } +rustc_interface = { path = "/home/urcra/rust/compiler/rustc_interface" } +rustc_middle = { path = "/home/urcra/rust/compiler/rustc_middle" } + [dependencies] # begin automatic update clippy_lints = { version = "0.0.212", path = "clippy_lints" } diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index d9471d25197..167db15be20 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -16,6 +16,28 @@ license = "MIT OR Apache-2.0" keywords = ["clippy", "lint", "plugin"] edition = "2018" +[target.'cfg(NOT_A_PLATFORM)'.dependencies] +rustc_ast = { path = "/home/urcra/rust/compiler/rustc_ast" } +rustc_ast_pretty = { path = "/home/urcra/rust/compiler/rustc_ast_pretty" } +rustc_attr = { path = "/home/urcra/rust/compiler/rustc_attr" } +rustc_data_structures = { path = "/home/urcra/rust/compiler/rustc_data_structures" } +rustc_errors = { path = "/home/urcra/rust/compiler/rustc_errors" } +rustc_hir = { path = "/home/urcra/rust/compiler/rustc_hir" } +rustc_hir_pretty = { path = "/home/urcra/rust/compiler/rustc_hir_pretty" } +rustc_index = { path = "/home/urcra/rust/compiler/rustc_index" } +rustc_infer = { path = "/home/urcra/rust/compiler/rustc_infer" } +rustc_lexer = { path = "/home/urcra/rust/compiler/rustc_lexer" } +rustc_lint = { path = "/home/urcra/rust/compiler/rustc_lint" } +rustc_middle = { path = "/home/urcra/rust/compiler/rustc_middle" } +rustc_mir = { path = "/home/urcra/rust/compiler/rustc_mir" } +rustc_parse = { path = "/home/urcra/rust/compiler/rustc_parse" } +rustc_parse_format = { path = "/home/urcra/rust/compiler/rustc_parse_format" } +rustc_session = { path = "/home/urcra/rust/compiler/rustc_session" } +rustc_span = { path = "/home/urcra/rust/compiler/rustc_span" } +rustc_target = { path = "/home/urcra/rust/compiler/rustc_target" } +rustc_trait_selection = { path = "/home/urcra/rust/compiler/rustc_trait_selection" } +rustc_typeck = { path = "/home/urcra/rust/compiler/rustc_typeck" } + [dependencies] cargo_metadata = "0.12" if_chain = "1.0.0" diff --git a/clippy_lints/src/comparison_to_empty.rs b/clippy_lints/src/comparison_to_empty.rs new file mode 100644 index 00000000000..3b55336722c --- /dev/null +++ b/clippy_lints/src/comparison_to_empty.rs @@ -0,0 +1,155 @@ +use crate::utils::{snippet_with_applicability, span_lint_and_sugg}; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::def_id::DefId; +use rustc_hir::{BinOpKind, Expr, ExprKind, ItemKind, TraitItemRef}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::{Span, Spanned}; + +declare_clippy_lint! { + /// **What it does:** + /// + /// **Why is this bad?** + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // example code where clippy issues a warning + /// ``` + /// Use instead: + /// ```rust + /// // example code which does not raise clippy warning + /// ``` + pub COMPARISON_TO_EMPTY, + style, + "default lint description" +} + +declare_lint_pass!(ComparisonToEmpty => [COMPARISON_TO_EMPTY]); + +impl LateLintPass<'_> for ComparisonToEmpty { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if expr.span.from_expansion() { + return; + } + + if let ExprKind::Binary(Spanned { node: cmp, .. }, ref left, ref right) = expr.kind { + match cmp { + BinOpKind::Eq => { + check_cmp(cx, expr.span, left, right, "", 0); // len == 0 + check_cmp(cx, expr.span, right, left, "", 0); // 0 == len + }, + BinOpKind::Ne => { + check_cmp(cx, expr.span, left, right, "!", 0); // len != 0 + check_cmp(cx, expr.span, right, left, "!", 0); // 0 != len + }, + BinOpKind::Gt => { + check_cmp(cx, expr.span, left, right, "!", 0); // len > 0 + check_cmp(cx, expr.span, right, left, "", 1); // 1 > len + }, + BinOpKind::Lt => { + check_cmp(cx, expr.span, left, right, "", 1); // len < 1 + check_cmp(cx, expr.span, right, left, "!", 0); // 0 < len + }, + BinOpKind::Ge => check_cmp(cx, expr.span, left, right, "!", 1), // len >= 1 + BinOpKind::Le => check_cmp(cx, expr.span, right, left, "!", 1), // 1 <= len + _ => (), + } + } + } + +} + + +fn check_cmp(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Expr<'_>, op: &str, compare_to: u32) { + check_empty_expr(cx, span, lit1, lit2, op) +} + +fn check_empty_expr( + cx: &LateContext<'_>, + span: Span, + lit1: &Expr<'_>, + lit2: &Expr<'_>, + op: &str +) { + if (is_empty_array(lit2) || is_empty_string(lit2)) && has_is_empty(cx, lit1) { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + COMPARISON_TO_EMPTY, + span, + &format!("comparison to empty slice"), + &format!("using `{}is_empty` is clearer and more explicit", op), + format!( + "{}{}.is_empty()", + op, + snippet_with_applicability(cx, lit1.span, "_", &mut applicability) + ), + applicability, + ); + } +} + +fn is_empty_string(expr: &Expr<'_>) -> bool { + if let ExprKind::Lit(ref lit) = expr.kind { + if let LitKind::Str(lit, _) = lit.node { + let lit = lit.as_str(); + return lit == ""; + } + } + false +} + +fn is_empty_array(expr: &Expr<'_>) -> bool { + if let ExprKind::Array(ref arr) = expr.kind { + return arr.is_empty(); + } + false +} + + +/// Checks if this type has an `is_empty` method. +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 + } + } else { + false + } + } + + /// Checks the inherent impl's items for an `is_empty(self)` method. + fn has_is_empty_impl(cx: &LateContext<'_>, id: DefId) -> bool { + cx.tcx.inherent_impls(id).iter().any(|imp| { + cx.tcx + .associated_items(*imp) + .in_definition_order() + .any(|item| is_is_empty(cx, &item)) + }) + } + + let ty = &cx.typeck_results().expr_ty(expr).peel_refs(); + match ty.kind() { + ty::Dynamic(ref tt, ..) => tt.principal().map_or(false, |principal| { + cx.tcx + .associated_items(principal.def_id()) + .in_definition_order() + .any(|item| is_is_empty(cx, &item)) + }), + ty::Projection(ref proj) => has_is_empty_impl(cx, proj.item_def_id), + ty::Adt(id, _) => has_is_empty_impl(cx, id.did), + ty::Array(..) | ty::Slice(..) | ty::Str => true, + _ => false, + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 1a950a7c334..75629e13a8e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -171,6 +171,7 @@ mod checked_conversions; mod cognitive_complexity; mod collapsible_if; mod comparison_chain; +mod comparison_to_empty; mod copies; mod copy_iterator; mod create_dir; @@ -523,6 +524,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &cognitive_complexity::COGNITIVE_COMPLEXITY, &collapsible_if::COLLAPSIBLE_IF, &comparison_chain::COMPARISON_CHAIN, + &comparison_to_empty::COMPARISON_TO_EMPTY, &copies::IFS_SAME_COND, &copies::IF_SAME_THEN_ELSE, &copies::MATCH_SAME_ARMS, @@ -1139,6 +1141,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods)); store.register_early_pass(|| box asm_syntax::InlineAsmX86AttSyntax); store.register_early_pass(|| box asm_syntax::InlineAsmX86IntelSyntax); + store.register_late_pass(|| box comparison_to_empty::ComparisonToEmpty); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ @@ -1299,6 +1302,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&bytecount::NAIVE_BYTECOUNT), LintId::of(&collapsible_if::COLLAPSIBLE_IF), LintId::of(&comparison_chain::COMPARISON_CHAIN), + LintId::of(&comparison_to_empty::COMPARISON_TO_EMPTY), LintId::of(&copies::IFS_SAME_COND), LintId::of(&copies::IF_SAME_THEN_ELSE), LintId::of(&derive::DERIVE_HASH_XOR_EQ), @@ -1555,6 +1559,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), LintId::of(&collapsible_if::COLLAPSIBLE_IF), LintId::of(&comparison_chain::COMPARISON_CHAIN), + LintId::of(&comparison_to_empty::COMPARISON_TO_EMPTY), LintId::of(&doc::MISSING_SAFETY_DOC), LintId::of(&doc::NEEDLESS_DOCTEST_MAIN), LintId::of(&enum_variants::ENUM_VARIANT_NAMES), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index da865a66c45..c2b2236bc66 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -291,6 +291,13 @@ vec![ deprecation: None, module: "comparison_chain", }, + Lint { + name: "comparison_to_empty", + group: "style", + desc: "default lint description", + deprecation: None, + module: "comparison_to_empty", + }, Lint { name: "copy_iterator", group: "pedantic", diff --git a/tests/ui/comparison_to_empty.fixed b/tests/ui/comparison_to_empty.fixed new file mode 100644 index 00000000000..261024caca7 --- /dev/null +++ b/tests/ui/comparison_to_empty.fixed @@ -0,0 +1,23 @@ +// run-rustfix + +#![warn(clippy::comparison_to_empty)] + +fn main() { + // Disallow comparisons to empty + let s = String::new(); + let _ = s.is_empty(); + let _ = !s.is_empty(); + + let v = vec![0]; + let _ = v.is_empty(); + let _ = !v.is_empty(); + + // Allow comparisons to non-empty + let s = String::new(); + let _ = s == " "; + let _ = s != " "; + + let v = vec![0]; + let _ = v == [0]; + let _ = v != [0]; +} diff --git a/tests/ui/comparison_to_empty.rs b/tests/ui/comparison_to_empty.rs new file mode 100644 index 00000000000..98ddd974951 --- /dev/null +++ b/tests/ui/comparison_to_empty.rs @@ -0,0 +1,23 @@ +// run-rustfix + +#![warn(clippy::comparison_to_empty)] + +fn main() { + // Disallow comparisons to empty + let s = String::new(); + let _ = s == ""; + let _ = s != ""; + + let v = vec![0]; + let _ = v == []; + let _ = v != []; + + // Allow comparisons to non-empty + let s = String::new(); + let _ = s == " "; + let _ = s != " "; + + let v = vec![0]; + let _ = v == [0]; + let _ = v != [0]; +} diff --git a/tests/ui/comparison_to_empty.stderr b/tests/ui/comparison_to_empty.stderr new file mode 100644 index 00000000000..f69d6bd5255 --- /dev/null +++ b/tests/ui/comparison_to_empty.stderr @@ -0,0 +1,28 @@ +error: comparison to empty slice + --> $DIR/comparison_to_empty.rs:8:13 + | +LL | let _ = s == ""; + | ^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()` + | + = note: `-D clippy::comparison-to-empty` implied by `-D warnings` + +error: comparison to empty slice + --> $DIR/comparison_to_empty.rs:9:13 + | +LL | let _ = s != ""; + | ^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!s.is_empty()` + +error: comparison to empty slice + --> $DIR/comparison_to_empty.rs:12:13 + | +LL | let _ = v == []; + | ^^^^^^^ help: using `is_empty` is clearer and more explicit: `v.is_empty()` + +error: comparison to empty slice + --> $DIR/comparison_to_empty.rs:13:13 + | +LL | let _ = v != []; + | ^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!v.is_empty()` + +error: aborting due to 4 previous errors + -- cgit 1.4.1-3-g733a5 From f0cf3bdca198ead0e1d76115bf30a2eef72e8c58 Mon Sep 17 00:00:00 2001 From: HMPerson1 Date: Sun, 25 Oct 2020 21:20:57 -0400 Subject: Add lint for replacing `.map().collect()` with `.try_for_each()` --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 3 ++ clippy_lints/src/methods/mod.rs | 59 +++++++++++++++++++++++++++++++++ src/lintlist/mod.rs | 7 ++++ tests/ui/map_collect_result_unit.fixed | 16 +++++++++ tests/ui/map_collect_result_unit.rs | 16 +++++++++ tests/ui/map_collect_result_unit.stderr | 16 +++++++++ 7 files changed, 118 insertions(+) create mode 100644 tests/ui/map_collect_result_unit.fixed create mode 100644 tests/ui/map_collect_result_unit.rs create mode 100644 tests/ui/map_collect_result_unit.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 25f3b5da198..287cf6bb725 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1802,6 +1802,7 @@ Released 2018-09-13 [`manual_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or [`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names [`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone +[`map_collect_result_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_collect_result_unit [`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry [`map_err_ignore`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_err_ignore [`map_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3be8bc0e36d..138d1ab9b50 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -697,6 +697,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::ITER_NTH_ZERO, &methods::ITER_SKIP_NEXT, &methods::MANUAL_SATURATING_ARITHMETIC, + &methods::MAP_COLLECT_RESULT_UNIT, &methods::MAP_FLATTEN, &methods::MAP_UNWRAP_OR, &methods::NEW_RET_NO_SELF, @@ -1420,6 +1421,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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_AS_REF_DEREF), @@ -1615,6 +1617,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index c0824bacbc7..5f9bed90845 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1383,6 +1383,27 @@ declare_clippy_lint! { "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation" } +declare_clippy_lint! { + /// **What it does:** Checks for usage of `_.map(_).collect::()`. + /// + /// **Why is this bad?** Using `try_for_each` instead is more readable and idiomatic. + /// + /// **Known problems:** None + /// + /// **Example:** + /// + /// ```rust + /// (0..3).map(|t| Err(t)).collect::>(); + /// ``` + /// Use instead: + /// ```rust + /// (0..3).try_for_each(|t| Err(t)); + /// ``` + pub MAP_COLLECT_RESULT_UNIT, + style, + "using `.map(_).collect::()`, which can be replaced with `try_for_each`" +} + declare_lint_pass!(Methods => [ UNWRAP_USED, EXPECT_USED, @@ -1433,6 +1454,7 @@ declare_lint_pass!(Methods => [ FILETYPE_IS_FILE, OPTION_AS_REF_DEREF, UNNECESSARY_LAZY_EVALUATIONS, + MAP_COLLECT_RESULT_UNIT, ]); impl<'tcx> LateLintPass<'tcx> for Methods { @@ -1515,6 +1537,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["unwrap_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "unwrap_or"), ["get_or_insert_with", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "get_or_insert"), ["ok_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "ok_or"), + ["collect", "map"] => lint_map_collect(cx, expr, arg_lists[1], arg_lists[0]), _ => {}, } @@ -3501,6 +3524,42 @@ fn lint_option_as_ref_deref<'tcx>( } } +fn lint_map_collect( + cx: &LateContext<'_>, + expr: &hir::Expr<'_>, + map_args: &[hir::Expr<'_>], + collect_args: &[hir::Expr<'_>], +) { + if_chain! { + // called on Iterator + if let [map_expr] = collect_args; + if match_trait_method(cx, map_expr, &paths::ITERATOR); + // return of collect `Result<(),_>` + let collect_ret_ty = cx.typeck_results().expr_ty(expr); + if is_type_diagnostic_item(cx, collect_ret_ty, sym!(result_type)); + if let ty::Adt(_, substs) = collect_ret_ty.kind(); + if let Some(result_t) = substs.types().next(); + if result_t.is_unit(); + // get parts for snippet + if let [iter, map_fn] = map_args; + then { + span_lint_and_sugg( + cx, + MAP_COLLECT_RESULT_UNIT, + expr.span, + "`.map().collect()` can be replaced with `.try_for_each()`", + "try this", + format!( + "{}.try_for_each({})", + snippet(cx, iter.span, ".."), + snippet(cx, map_fn.span, "..") + ), + Applicability::MachineApplicable, + ); + } + } +} + /// Given a `Result` type, return its error type (`E`). fn get_error_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option> { match ty.kind() { diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 6272ce45efb..d9b3e5c17f4 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1222,6 +1222,13 @@ vec![ deprecation: None, module: "map_clone", }, + Lint { + name: "map_collect_result_unit", + group: "style", + desc: "using `.map(_).collect::()`, which can be replaced with `try_for_each`", + deprecation: None, + module: "methods", + }, Lint { name: "map_entry", group: "perf", diff --git a/tests/ui/map_collect_result_unit.fixed b/tests/ui/map_collect_result_unit.fixed new file mode 100644 index 00000000000..e66c9cc2420 --- /dev/null +++ b/tests/ui/map_collect_result_unit.fixed @@ -0,0 +1,16 @@ +// run-rustfix +#![warn(clippy::map_collect_result_unit)] + +fn main() { + { + let _ = (0..3).try_for_each(|t| Err(t + 1)); + let _: Result<(), _> = (0..3).try_for_each(|t| Err(t + 1)); + + let _ = (0..3).try_for_each(|t| Err(t + 1)); + } +} + +fn _ignore() { + let _ = (0..3).map(|t| Err(t + 1)).collect::, _>>(); + let _ = (0..3).map(|t| Err(t + 1)).collect::>>(); +} diff --git a/tests/ui/map_collect_result_unit.rs b/tests/ui/map_collect_result_unit.rs new file mode 100644 index 00000000000..6f08f4c3c53 --- /dev/null +++ b/tests/ui/map_collect_result_unit.rs @@ -0,0 +1,16 @@ +// run-rustfix +#![warn(clippy::map_collect_result_unit)] + +fn main() { + { + let _ = (0..3).map(|t| Err(t + 1)).collect::>(); + let _: Result<(), _> = (0..3).map(|t| Err(t + 1)).collect(); + + let _ = (0..3).try_for_each(|t| Err(t + 1)); + } +} + +fn _ignore() { + let _ = (0..3).map(|t| Err(t + 1)).collect::, _>>(); + let _ = (0..3).map(|t| Err(t + 1)).collect::>>(); +} diff --git a/tests/ui/map_collect_result_unit.stderr b/tests/ui/map_collect_result_unit.stderr new file mode 100644 index 00000000000..8b06e13baa6 --- /dev/null +++ b/tests/ui/map_collect_result_unit.stderr @@ -0,0 +1,16 @@ +error: `.map().collect()` can be replaced with `.try_for_each()` + --> $DIR/map_collect_result_unit.rs:6:17 + | +LL | let _ = (0..3).map(|t| Err(t + 1)).collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(0..3).try_for_each(|t| Err(t + 1))` + | + = note: `-D clippy::map-collect-result-unit` implied by `-D warnings` + +error: `.map().collect()` can be replaced with `.try_for_each()` + --> $DIR/map_collect_result_unit.rs:7:32 + | +LL | let _: Result<(), _> = (0..3).map(|t| Err(t + 1)).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(0..3).try_for_each(|t| Err(t + 1))` + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From bab338685f42e4b184c0f645c3a1a24a79890e17 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sat, 24 Oct 2020 10:50:11 +0200 Subject: No lint in external macro for `toplevel_ref_arg` --- clippy_lints/src/misc.rs | 15 +++++++++++---- tests/ui/auxiliary/macro_rules.rs | 14 ++++++++++++++ tests/ui/toplevel_ref_arg.fixed | 21 +++++++++++++++++++++ tests/ui/toplevel_ref_arg.rs | 21 +++++++++++++++++++++ tests/ui/toplevel_ref_arg.stderr | 23 +++++++++++++++++------ tests/ui/toplevel_ref_arg_non_rustfix.rs | 22 ++++++++++++++++++++++ tests/ui/toplevel_ref_arg_non_rustfix.stderr | 15 +++++++++++++-- 7 files changed, 119 insertions(+), 12 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 909e79f661a..308e92057b7 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -7,6 +7,7 @@ use rustc_hir::{ StmtKind, TyKind, UnOp, }; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::hygiene::DesugaringKind; @@ -271,13 +272,16 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { k: FnKind<'tcx>, decl: &'tcx FnDecl<'_>, body: &'tcx Body<'_>, - _: Span, + span: Span, _: HirId, ) { if let FnKind::Closure(_) = k { // Does not apply to closures return; } + if in_external_macro(cx.tcx.sess, span) { + return; + } for arg in iter_input_pats(decl, body) { if let PatKind::Binding(BindingAnnotation::Ref | BindingAnnotation::RefMut, ..) = arg.pat.kind { span_lint( @@ -293,13 +297,16 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { if_chain! { + if !in_external_macro(cx.tcx.sess, stmt.span); if let StmtKind::Local(ref local) = stmt.kind; if let PatKind::Binding(an, .., name, None) = local.pat.kind; if let Some(ref init) = local.init; if !higher::is_from_for_desugar(local); then { if an == BindingAnnotation::Ref || an == BindingAnnotation::RefMut { - let sugg_init = if init.span.from_expansion() { + // use the macro callsite when the init span (but not the whole local span) + // comes from an expansion like `vec![1, 2, 3]` in `let ref _ = vec![1, 2, 3];` + let sugg_init = if init.span.from_expansion() && !local.span.from_expansion() { Sugg::hir_with_macro_callsite(cx, init, "..") } else { Sugg::hir(cx, init, "..") @@ -310,7 +317,7 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { ("", sugg_init.addr()) }; let tyopt = if let Some(ref ty) = local.ty { - format!(": &{mutopt}{ty}", mutopt=mutopt, ty=snippet(cx, ty.span, "_")) + format!(": &{mutopt}{ty}", mutopt=mutopt, ty=snippet(cx, ty.span, "..")) } else { String::new() }; @@ -326,7 +333,7 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { "try", format!( "let {name}{tyopt} = {initref};", - name=snippet(cx, name.span, "_"), + name=snippet(cx, name.span, ".."), tyopt=tyopt, initref=initref, ), diff --git a/tests/ui/auxiliary/macro_rules.rs b/tests/ui/auxiliary/macro_rules.rs index 0bbb9534928..93303865e17 100644 --- a/tests/ui/auxiliary/macro_rules.rs +++ b/tests/ui/auxiliary/macro_rules.rs @@ -56,3 +56,17 @@ macro_rules! option_env_unwrap_external { option_env!($env).expect($message) }; } + +#[macro_export] +macro_rules! ref_arg_binding { + () => { + let ref _y = 42; + }; +} + +#[macro_export] +macro_rules! ref_arg_function { + () => { + fn fun_example(ref _x: usize) {} + }; +} diff --git a/tests/ui/toplevel_ref_arg.fixed b/tests/ui/toplevel_ref_arg.fixed index 33605aca019..b129d95c560 100644 --- a/tests/ui/toplevel_ref_arg.fixed +++ b/tests/ui/toplevel_ref_arg.fixed @@ -1,7 +1,17 @@ // run-rustfix +// aux-build:macro_rules.rs #![warn(clippy::toplevel_ref_arg)] +#[macro_use] +extern crate macro_rules; + +macro_rules! gen_binding { + () => { + let _y = &42; + }; +} + fn main() { // Closures should not warn let y = |ref x| println!("{:?}", x); @@ -26,4 +36,15 @@ fn main() { // ok for ref _x in 0..10 {} + + // lint in macro + #[allow(unused)] + { + gen_binding!(); + } + + // do not lint in external macro + { + ref_arg_binding!(); + } } diff --git a/tests/ui/toplevel_ref_arg.rs b/tests/ui/toplevel_ref_arg.rs index 59759f11893..73eb4ff7306 100644 --- a/tests/ui/toplevel_ref_arg.rs +++ b/tests/ui/toplevel_ref_arg.rs @@ -1,7 +1,17 @@ // run-rustfix +// aux-build:macro_rules.rs #![warn(clippy::toplevel_ref_arg)] +#[macro_use] +extern crate macro_rules; + +macro_rules! gen_binding { + () => { + let ref _y = 42; + }; +} + fn main() { // Closures should not warn let y = |ref x| println!("{:?}", x); @@ -26,4 +36,15 @@ fn main() { // ok for ref _x in 0..10 {} + + // lint in macro + #[allow(unused)] + { + gen_binding!(); + } + + // do not lint in external macro + { + ref_arg_binding!(); + } } diff --git a/tests/ui/toplevel_ref_arg.stderr b/tests/ui/toplevel_ref_arg.stderr index 19d69496709..15cb933fedc 100644 --- a/tests/ui/toplevel_ref_arg.stderr +++ b/tests/ui/toplevel_ref_arg.stderr @@ -1,5 +1,5 @@ error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead - --> $DIR/toplevel_ref_arg.rs:10:9 + --> $DIR/toplevel_ref_arg.rs:20:9 | LL | let ref _x = 1; | ----^^^^^^----- help: try: `let _x = &1;` @@ -7,28 +7,39 @@ LL | let ref _x = 1; = note: `-D clippy::toplevel-ref-arg` implied by `-D warnings` error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead - --> $DIR/toplevel_ref_arg.rs:12:9 + --> $DIR/toplevel_ref_arg.rs:22:9 | LL | let ref _y: (&_, u8) = (&1, 2); | ----^^^^^^--------------------- help: try: `let _y: &(&_, u8) = &(&1, 2);` error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead - --> $DIR/toplevel_ref_arg.rs:14:9 + --> $DIR/toplevel_ref_arg.rs:24:9 | LL | let ref _z = 1 + 2; | ----^^^^^^--------- help: try: `let _z = &(1 + 2);` error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead - --> $DIR/toplevel_ref_arg.rs:16:9 + --> $DIR/toplevel_ref_arg.rs:26:9 | LL | let ref mut _z = 1 + 2; | ----^^^^^^^^^^--------- help: try: `let _z = &mut (1 + 2);` error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead - --> $DIR/toplevel_ref_arg.rs:21:9 + --> $DIR/toplevel_ref_arg.rs:31:9 | LL | let ref _x = vec![1, 2, 3]; | ----^^^^^^----------------- help: try: `let _x = &vec![1, 2, 3];` -error: aborting due to 5 previous errors +error: `ref` on an entire `let` pattern is discouraged, take a reference with `&` instead + --> $DIR/toplevel_ref_arg.rs:11:13 + | +LL | let ref _y = 42; + | ----^^^^^^------ help: try: `let _y = &42;` +... +LL | gen_binding!(); + | --------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 6 previous errors diff --git a/tests/ui/toplevel_ref_arg_non_rustfix.rs b/tests/ui/toplevel_ref_arg_non_rustfix.rs index 42cac2ba4de..1a493fbce0e 100644 --- a/tests/ui/toplevel_ref_arg_non_rustfix.rs +++ b/tests/ui/toplevel_ref_arg_non_rustfix.rs @@ -1,11 +1,33 @@ +// aux-build:macro_rules.rs + #![warn(clippy::toplevel_ref_arg)] #![allow(unused)] +#[macro_use] +extern crate macro_rules; + fn the_answer(ref mut x: u8) { *x = 42; } +macro_rules! gen_function { + () => { + fn fun_example(ref _x: usize) {} + }; +} + fn main() { let mut x = 0; the_answer(x); + + // lint in macro + #[allow(unused)] + { + gen_function!(); + } + + // do not lint in external macro + { + ref_arg_function!(); + } } diff --git a/tests/ui/toplevel_ref_arg_non_rustfix.stderr b/tests/ui/toplevel_ref_arg_non_rustfix.stderr index 295e2f35608..6c36141a58c 100644 --- a/tests/ui/toplevel_ref_arg_non_rustfix.stderr +++ b/tests/ui/toplevel_ref_arg_non_rustfix.stderr @@ -1,10 +1,21 @@ error: `ref` directly on a function argument is ignored. Consider using a reference type instead. - --> $DIR/toplevel_ref_arg_non_rustfix.rs:4:15 + --> $DIR/toplevel_ref_arg_non_rustfix.rs:9:15 | LL | fn the_answer(ref mut x: u8) { | ^^^^^^^^^ | = note: `-D clippy::toplevel-ref-arg` implied by `-D warnings` -error: aborting due to previous error +error: `ref` directly on a function argument is ignored. Consider using a reference type instead. + --> $DIR/toplevel_ref_arg_non_rustfix.rs:15:24 + | +LL | fn fun_example(ref _x: usize) {} + | ^^^^^^ +... +LL | gen_function!(); + | ---------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From 2911d9c7de4c0acf7bdd6a2d6983d1fccbeb6a69 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 18 Oct 2020 13:09:06 +0200 Subject: Use better placeholders for some methods lint messages --- clippy_lints/src/methods/mod.rs | 58 ++++++++++++------------ clippy_lints/src/methods/option_map_unwrap_or.rs | 8 ++-- tests/ui/filter_map_next.stderr | 4 +- tests/ui/filter_methods.stderr | 8 ++-- tests/ui/find_map.stderr | 4 +- tests/ui/iter_skip_next.stderr | 8 ++-- tests/ui/map_unwrap_or.stderr | 40 ++++++++-------- tests/ui/methods.stderr | 4 +- tests/ui/option_map_or_none.stderr | 4 +- tests/ui/skip_while_next.stderr | 8 ++-- 10 files changed, 73 insertions(+), 73 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index c0824bacbc7..fc77cd52b8f 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1748,7 +1748,7 @@ fn lint_or_fun_call<'tcx>( "try this", format!( "{}.unwrap_or_default()", - snippet_with_applicability(cx, self_expr.span, "_", &mut applicability) + snippet_with_applicability(cx, self_expr.span, "..", &mut applicability) ), applicability, ); @@ -2155,7 +2155,7 @@ fn lint_clone_on_ref_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir:: return; }; - let snippet = snippet_with_macro_callsite(cx, arg.span, "_"); + let snippet = snippet_with_macro_callsite(cx, arg.span, ".."); span_lint_and_sugg( cx, @@ -2191,9 +2191,9 @@ fn lint_string_extend(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::E "try this", format!( "{}.push_str({}{})", - snippet_with_applicability(cx, args[0].span, "_", &mut applicability), + snippet_with_applicability(cx, args[0].span, "..", &mut applicability), ref_str, - snippet_with_applicability(cx, target.span, "_", &mut applicability) + snippet_with_applicability(cx, target.span, "..", &mut applicability) ), applicability, ); @@ -2460,7 +2460,7 @@ fn lint_get_unwrap<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, get_args: let mut applicability = Applicability::MachineApplicable; let expr_ty = cx.typeck_results().expr_ty(&get_args[0]); let get_args_str = if get_args.len() > 1 { - snippet_with_applicability(cx, get_args[1].span, "_", &mut applicability) + snippet_with_applicability(cx, get_args[1].span, "..", &mut applicability) } else { return; // not linting on a .get().unwrap() chain or variant }; @@ -2520,7 +2520,7 @@ fn lint_get_unwrap<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, get_args: format!( "{}{}[{}]", borrow_str, - snippet_with_applicability(cx, get_args[0].span, "_", &mut applicability), + snippet_with_applicability(cx, get_args[0].span, "..", &mut applicability), get_args_str ), applicability, @@ -2536,7 +2536,7 @@ fn lint_iter_skip_next(cx: &LateContext<'_>, expr: &hir::Expr<'_>, skip_args: &[ cx, ITER_SKIP_NEXT, expr.span.trim_start(caller.span).unwrap(), - "called `skip(x).next()` on an iterator", + "called `skip(..).next()` on an iterator", "use `nth` instead", hint, Applicability::MachineApplicable, @@ -2739,11 +2739,11 @@ fn lint_map_unwrap_or_else<'tcx>( // lint message let msg = if is_option { - "called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling \ - `map_or_else(g, f)` instead" + "called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling \ + `map_or_else(, )` instead" } else { - "called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling \ - `.map_or_else(g, f)` instead" + "called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling \ + `.map_or_else(, )` instead" }; // get snippets for args to map() and unwrap_or_else() let map_snippet = snippet(cx, map_args[1].span, ".."); @@ -2809,8 +2809,8 @@ fn lint_map_or_none<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map if is_option { let self_snippet = snippet(cx, map_or_args[0].span, ".."); let func_snippet = snippet(cx, map_or_args[2].span, ".."); - let msg = "called `map_or(None, f)` on an `Option` value. This can be done more directly by calling \ - `and_then(f)` instead"; + let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \ + `and_then(..)` instead"; ( OPTION_MAP_OR_NONE, msg, @@ -2848,8 +2848,8 @@ fn lint_map_or_none<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map fn lint_filter_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, filter_args: &'tcx [hir::Expr<'_>]) { // lint if caller of `.filter().next()` is an Iterator if match_trait_method(cx, expr, &paths::ITERATOR) { - let msg = "called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling \ - `.find(p)` instead."; + let msg = "called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling \ + `.find(..)` instead."; let filter_snippet = snippet(cx, filter_args[1].span, ".."); if filter_snippet.lines().count() <= 1 { // add note if not multi-line @@ -2879,9 +2879,9 @@ fn lint_skip_while_next<'tcx>( cx, SKIP_WHILE_NEXT, expr.span, - "called `skip_while(p).next()` on an `Iterator`", + "called `skip_while(

).next()` on an `Iterator`", None, - "this is more succinctly expressed by calling `.find(!p)` instead", + "this is more succinctly expressed by calling `.find(!

)` instead", ); } } @@ -2895,7 +2895,7 @@ fn lint_filter_map<'tcx>( ) { // lint if caller of `.filter().map()` is an Iterator if match_trait_method(cx, expr, &paths::ITERATOR) { - let msg = "called `filter(p).map(q)` on an `Iterator`"; + let msg = "called `filter(..).map(..)` on an `Iterator`"; let hint = "this is more succinctly expressed by calling `.filter_map(..)` instead"; span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint); } @@ -2904,8 +2904,8 @@ fn lint_filter_map<'tcx>( /// lint use of `filter_map().next()` for `Iterators` fn lint_filter_map_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, filter_args: &'tcx [hir::Expr<'_>]) { if match_trait_method(cx, expr, &paths::ITERATOR) { - let msg = "called `filter_map(p).next()` on an `Iterator`. This is more succinctly expressed by calling \ - `.find_map(p)` instead."; + let msg = "called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling \ + `.find_map(..)` instead."; let filter_snippet = snippet(cx, filter_args[1].span, ".."); if filter_snippet.lines().count() <= 1 { span_lint_and_note( @@ -2931,7 +2931,7 @@ fn lint_find_map<'tcx>( ) { // lint if caller of `.filter().map()` is an Iterator if match_trait_method(cx, &map_args[0], &paths::ITERATOR) { - let msg = "called `find(p).map(q)` on an `Iterator`"; + let msg = "called `find(..).map(..)` on an `Iterator`"; let hint = "this is more succinctly expressed by calling `.find_map(..)` instead"; span_lint_and_help(cx, FIND_MAP, expr.span, msg, None, hint); } @@ -2946,7 +2946,7 @@ fn lint_filter_map_map<'tcx>( ) { // lint if caller of `.filter().map()` is an Iterator if match_trait_method(cx, expr, &paths::ITERATOR) { - let msg = "called `filter_map(p).map(q)` on an `Iterator`"; + let msg = "called `filter_map(..).map(..)` on an `Iterator`"; let hint = "this is more succinctly expressed by only calling `.filter_map(..)` instead"; span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint); } @@ -2961,7 +2961,7 @@ fn lint_filter_flat_map<'tcx>( ) { // lint if caller of `.filter().flat_map()` is an Iterator if match_trait_method(cx, expr, &paths::ITERATOR) { - let msg = "called `filter(p).flat_map(q)` on an `Iterator`"; + let msg = "called `filter(..).flat_map(..)` on an `Iterator`"; let hint = "this is more succinctly expressed by calling `.flat_map(..)` \ and filtering by returning `iter::empty()`"; span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint); @@ -2977,7 +2977,7 @@ fn lint_filter_map_flat_map<'tcx>( ) { // lint if caller of `.filter_map().flat_map()` is an Iterator if match_trait_method(cx, expr, &paths::ITERATOR) { - let msg = "called `filter_map(p).flat_map(q)` on an `Iterator`"; + let msg = "called `filter_map(..).flat_map(..)` on an `Iterator`"; let hint = "this is more succinctly expressed by calling `.flat_map(..)` \ and filtering by returning `iter::empty()`"; span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint); @@ -3148,9 +3148,9 @@ fn lint_chars_cmp( "like this", format!("{}{}.{}({})", if info.eq { "" } else { "!" }, - snippet_with_applicability(cx, args[0][0].span, "_", &mut applicability), + snippet_with_applicability(cx, args[0][0].span, "..", &mut applicability), suggest, - snippet_with_applicability(cx, arg_char[0].span, "_", &mut applicability)), + snippet_with_applicability(cx, arg_char[0].span, "..", &mut applicability)), applicability, ); @@ -3197,7 +3197,7 @@ fn lint_chars_cmp_with_unwrap<'tcx>( "like this", format!("{}{}.{}('{}')", if info.eq { "" } else { "!" }, - snippet_with_applicability(cx, args[0][0].span, "_", &mut applicability), + snippet_with_applicability(cx, args[0][0].span, "..", &mut applicability), suggest, c), applicability, @@ -3272,7 +3272,7 @@ fn lint_single_char_pattern(cx: &LateContext<'_>, _expr: &hir::Expr<'_>, arg: &h fn lint_single_char_push_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { let mut applicability = Applicability::MachineApplicable; if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[1], &mut applicability) { - let base_string_snippet = snippet_with_applicability(cx, args[0].span, "_", &mut applicability); + let base_string_snippet = snippet_with_applicability(cx, args[0].span, "..", &mut applicability); let sugg = format!("{}.push({})", base_string_snippet, extension_string); span_lint_and_sugg( cx, @@ -3315,7 +3315,7 @@ fn lint_asref(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, as_re expr.span, &format!("this call to `{}` does nothing", call_name), "try this", - snippet_with_applicability(cx, recvr.span, "_", &mut applicability).to_string(), + snippet_with_applicability(cx, recvr.span, "..", &mut applicability).to_string(), applicability, ); } diff --git a/clippy_lints/src/methods/option_map_unwrap_or.rs b/clippy_lints/src/methods/option_map_unwrap_or.rs index 95fa28e1c0f..d30b85d6a78 100644 --- a/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -53,15 +53,15 @@ pub(super) fn lint<'tcx>( // lint message // comparing the snippet from source to raw text ("None") below is safe // because we already have checked the type. - let arg = if unwrap_snippet == "None" { "None" } else { "a" }; + let arg = if unwrap_snippet == "None" { "None" } else { "" }; let unwrap_snippet_none = unwrap_snippet == "None"; let suggest = if unwrap_snippet_none { - "and_then(f)" + "and_then()" } else { - "map_or(a, f)" + "map_or(, )" }; let msg = &format!( - "called `map(f).unwrap_or({})` on an `Option` value. \ + "called `map().unwrap_or({})` on an `Option` value. \ This can be done more directly by calling `{}` instead", arg, suggest ); diff --git a/tests/ui/filter_map_next.stderr b/tests/ui/filter_map_next.stderr index d69ae212414..01281ebaf6a 100644 --- a/tests/ui/filter_map_next.stderr +++ b/tests/ui/filter_map_next.stderr @@ -1,4 +1,4 @@ -error: called `filter_map(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(p)` instead. +error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead. --> $DIR/filter_map_next.rs:6:32 | LL | let element: Option = a.iter().filter_map(|s| s.parse().ok()).next(); @@ -7,7 +7,7 @@ LL | let element: Option = a.iter().filter_map(|s| s.parse().ok()).next = note: `-D clippy::filter-map-next` implied by `-D warnings` = note: replace `filter_map(|s| s.parse().ok()).next()` with `find_map(|s| s.parse().ok())` -error: called `filter_map(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(p)` instead. +error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead. --> $DIR/filter_map_next.rs:10:26 | LL | let _: Option = vec![1, 2, 3, 4, 5, 6] diff --git a/tests/ui/filter_methods.stderr b/tests/ui/filter_methods.stderr index 84a957a374c..91718dd1175 100644 --- a/tests/ui/filter_methods.stderr +++ b/tests/ui/filter_methods.stderr @@ -1,4 +1,4 @@ -error: called `filter(p).map(q)` on an `Iterator` +error: called `filter(..).map(..)` on an `Iterator` --> $DIR/filter_methods.rs:5:21 | LL | let _: Vec<_> = vec![5; 6].into_iter().filter(|&x| x == 0).map(|x| x * 2).collect(); @@ -7,7 +7,7 @@ LL | let _: Vec<_> = vec![5; 6].into_iter().filter(|&x| x == 0).map(|x| x * = note: `-D clippy::filter-map` implied by `-D warnings` = help: this is more succinctly expressed by calling `.filter_map(..)` instead -error: called `filter(p).flat_map(q)` on an `Iterator` +error: called `filter(..).flat_map(..)` on an `Iterator` --> $DIR/filter_methods.rs:7:21 | LL | let _: Vec<_> = vec![5_i8; 6] @@ -19,7 +19,7 @@ LL | | .flat_map(|x| x.checked_mul(2)) | = help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()` -error: called `filter_map(p).flat_map(q)` on an `Iterator` +error: called `filter_map(..).flat_map(..)` on an `Iterator` --> $DIR/filter_methods.rs:13:21 | LL | let _: Vec<_> = vec![5_i8; 6] @@ -31,7 +31,7 @@ LL | | .flat_map(|x| x.checked_mul(2)) | = help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()` -error: called `filter_map(p).map(q)` on an `Iterator` +error: called `filter_map(..).map(..)` on an `Iterator` --> $DIR/filter_methods.rs:19:21 | LL | let _: Vec<_> = vec![5_i8; 6] diff --git a/tests/ui/find_map.stderr b/tests/ui/find_map.stderr index f279850fef8..aea3cc62afc 100644 --- a/tests/ui/find_map.stderr +++ b/tests/ui/find_map.stderr @@ -1,4 +1,4 @@ -error: called `find(p).map(q)` on an `Iterator` +error: called `find(..).map(..)` on an `Iterator` --> $DIR/find_map.rs:20:26 | LL | let _: Option = a.iter().find(|s| s.parse::().is_ok()).map(|s| s.parse().unwrap()); @@ -7,7 +7,7 @@ LL | let _: Option = a.iter().find(|s| s.parse::().is_ok()).map(|s = note: `-D clippy::find-map` implied by `-D warnings` = help: this is more succinctly expressed by calling `.find_map(..)` instead -error: called `find(p).map(q)` on an `Iterator` +error: called `find(..).map(..)` on an `Iterator` --> $DIR/find_map.rs:23:29 | LL | let _: Option = desserts_of_the_week diff --git a/tests/ui/iter_skip_next.stderr b/tests/ui/iter_skip_next.stderr index feedc2f288a..486de718bb5 100644 --- a/tests/ui/iter_skip_next.stderr +++ b/tests/ui/iter_skip_next.stderr @@ -1,4 +1,4 @@ -error: called `skip(x).next()` on an iterator +error: called `skip(..).next()` on an iterator --> $DIR/iter_skip_next.rs:15:28 | LL | let _ = some_vec.iter().skip(42).next(); @@ -6,19 +6,19 @@ LL | let _ = some_vec.iter().skip(42).next(); | = note: `-D clippy::iter-skip-next` implied by `-D warnings` -error: called `skip(x).next()` on an iterator +error: called `skip(..).next()` on an iterator --> $DIR/iter_skip_next.rs:16:36 | LL | let _ = some_vec.iter().cycle().skip(42).next(); | ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(42)` -error: called `skip(x).next()` on an iterator +error: called `skip(..).next()` on an iterator --> $DIR/iter_skip_next.rs:17:20 | LL | let _ = (1..10).skip(10).next(); | ^^^^^^^^^^^^^^^^ help: use `nth` instead: `.nth(10)` -error: called `skip(x).next()` on an iterator +error: called `skip(..).next()` on an iterator --> $DIR/iter_skip_next.rs:18:33 | LL | let _ = &some_vec[..].iter().skip(3).next(); diff --git a/tests/ui/map_unwrap_or.stderr b/tests/ui/map_unwrap_or.stderr index b62080a073f..6fa2800e9bd 100644 --- a/tests/ui/map_unwrap_or.stderr +++ b/tests/ui/map_unwrap_or.stderr @@ -1,4 +1,4 @@ -error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead +error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead --> $DIR/map_unwrap_or.rs:17:13 | LL | let _ = opt.map(|x| x + 1) @@ -8,12 +8,12 @@ LL | | .unwrap_or(0); | |_____________________^ | = note: `-D clippy::map-unwrap-or` implied by `-D warnings` -help: use `map_or(a, f)` instead +help: use `map_or(, )` instead | LL | let _ = opt.map_or(0, |x| x + 1); | ^^^^^^ ^^ -- -error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead +error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead --> $DIR/map_unwrap_or.rs:21:13 | LL | let _ = opt.map(|x| { @@ -23,7 +23,7 @@ LL | | } LL | | ).unwrap_or(0); | |__________________^ | -help: use `map_or(a, f)` instead +help: use `map_or(, )` instead | LL | let _ = opt.map_or(0, |x| { LL | x + 1 @@ -31,7 +31,7 @@ LL | } LL | ); | -error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead +error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead --> $DIR/map_unwrap_or.rs:25:13 | LL | let _ = opt.map(|x| x + 1) @@ -41,25 +41,25 @@ LL | | 0 LL | | }); | |__________^ | -help: use `map_or(a, f)` instead +help: use `map_or(, )` instead | LL | let _ = opt.map_or({ LL | 0 LL | }, |x| x + 1); | -error: called `map(f).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead +error: called `map().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead --> $DIR/map_unwrap_or.rs:30:13 | LL | let _ = opt.map(|x| Some(x + 1)).unwrap_or(None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: use `and_then(f)` instead +help: use `and_then()` instead | LL | let _ = opt.and_then(|x| Some(x + 1)); | ^^^^^^^^ -- -error: called `map(f).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead +error: called `map().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead --> $DIR/map_unwrap_or.rs:32:13 | LL | let _ = opt.map(|x| { @@ -69,7 +69,7 @@ LL | | } LL | | ).unwrap_or(None); | |_____________________^ | -help: use `and_then(f)` instead +help: use `and_then()` instead | LL | let _ = opt.and_then(|x| { LL | Some(x + 1) @@ -77,7 +77,7 @@ LL | } LL | ); | -error: called `map(f).unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead +error: called `map().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead --> $DIR/map_unwrap_or.rs:36:13 | LL | let _ = opt @@ -86,23 +86,23 @@ LL | | .map(|x| Some(x + 1)) LL | | .unwrap_or(None); | |________________________^ | -help: use `and_then(f)` instead +help: use `and_then()` instead | LL | .and_then(|x| Some(x + 1)); | ^^^^^^^^ -- -error: called `map(f).unwrap_or(a)` on an `Option` value. This can be done more directly by calling `map_or(a, f)` instead +error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead --> $DIR/map_unwrap_or.rs:47:13 | LL | let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -help: use `map_or(a, f)` instead +help: use `map_or(, )` instead | LL | let _ = Some("prefix").map_or(id, |p| format!("{}.", p)); | ^^^^^^ ^^^ -- -error: called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling `map_or_else(g, f)` instead +error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead --> $DIR/map_unwrap_or.rs:51:13 | LL | let _ = opt.map(|x| x + 1) @@ -113,7 +113,7 @@ LL | | .unwrap_or_else(|| 0); | = note: replace `map(|x| x + 1).unwrap_or_else(|| 0)` with `map_or_else(|| 0, |x| x + 1)` -error: called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling `map_or_else(g, f)` instead +error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead --> $DIR/map_unwrap_or.rs:55:13 | LL | let _ = opt.map(|x| { @@ -123,7 +123,7 @@ LL | | } LL | | ).unwrap_or_else(|| 0); | |__________________________^ -error: called `map(f).unwrap_or_else(g)` on an `Option` value. This can be done more directly by calling `map_or_else(g, f)` instead +error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead --> $DIR/map_unwrap_or.rs:59:13 | LL | let _ = opt.map(|x| x + 1) @@ -133,7 +133,7 @@ LL | | 0 LL | | ); | |_________^ -error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead +error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead --> $DIR/map_unwrap_or.rs:88:13 | LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); // should lint even though this call is on a separate line @@ -141,7 +141,7 @@ LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); // should lint even t | = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` -error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead +error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead --> $DIR/map_unwrap_or.rs:90:13 | LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); @@ -149,7 +149,7 @@ LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); | = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` -error: called `map(f).unwrap_or_else(g)` on a `Result` value. This can be done more directly by calling `.map_or_else(g, f)` instead +error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead --> $DIR/map_unwrap_or.rs:91:13 | LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); diff --git a/tests/ui/methods.stderr b/tests/ui/methods.stderr index 2a0a43e83a6..74d8533d4da 100644 --- a/tests/ui/methods.stderr +++ b/tests/ui/methods.stderr @@ -8,7 +8,7 @@ LL | | } | = note: `-D clippy::new-ret-no-self` implied by `-D warnings` -error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead. +error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead. --> $DIR/methods.rs:126:13 | LL | let _ = v.iter().filter(|&x| *x < 0).next(); @@ -17,7 +17,7 @@ LL | let _ = v.iter().filter(|&x| *x < 0).next(); = note: `-D clippy::filter-next` implied by `-D warnings` = note: replace `filter(|&x| *x < 0).next()` with `find(|&x| *x < 0)` -error: called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` instead. +error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead. --> $DIR/methods.rs:129:13 | LL | let _ = v.iter().filter(|&x| { diff --git a/tests/ui/option_map_or_none.stderr b/tests/ui/option_map_or_none.stderr index 6f707987dbc..1cba29412b8 100644 --- a/tests/ui/option_map_or_none.stderr +++ b/tests/ui/option_map_or_none.stderr @@ -1,4 +1,4 @@ -error: called `map_or(None, f)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead +error: called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling `and_then(..)` instead --> $DIR/option_map_or_none.rs:10:13 | LL | let _ = opt.map_or(None, |x| Some(x + 1)); @@ -6,7 +6,7 @@ LL | let _ = opt.map_or(None, |x| Some(x + 1)); | = note: `-D clippy::option-map-or-none` implied by `-D warnings` -error: called `map_or(None, f)` on an `Option` value. This can be done more directly by calling `and_then(f)` instead +error: called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling `and_then(..)` instead --> $DIR/option_map_or_none.rs:13:13 | LL | let _ = opt.map_or(None, |x| { diff --git a/tests/ui/skip_while_next.stderr b/tests/ui/skip_while_next.stderr index a6b7bcd63ff..269cc13468b 100644 --- a/tests/ui/skip_while_next.stderr +++ b/tests/ui/skip_while_next.stderr @@ -1,13 +1,13 @@ -error: called `skip_while(p).next()` on an `Iterator` +error: called `skip_while(

).next()` on an `Iterator` --> $DIR/skip_while_next.rs:14:13 | LL | let _ = v.iter().skip_while(|&x| *x < 0).next(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::skip-while-next` implied by `-D warnings` - = help: this is more succinctly expressed by calling `.find(!p)` instead + = help: this is more succinctly expressed by calling `.find(!

)` instead -error: called `skip_while(p).next()` on an `Iterator` +error: called `skip_while(

).next()` on an `Iterator` --> $DIR/skip_while_next.rs:17:13 | LL | let _ = v.iter().skip_while(|&x| { @@ -17,7 +17,7 @@ LL | | } LL | | ).next(); | |___________________________^ | - = help: this is more succinctly expressed by calling `.find(!p)` instead + = help: this is more succinctly expressed by calling `.find(!

)` instead error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From 3fec6f568daababf3520e2a818b4c1db79266b92 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Mon, 19 Oct 2020 18:47:02 +0200 Subject: Improve some suggestions for `filter_map_next`, `filter_next` and `map_unwrap_or` lints --- clippy_lints/src/methods/mod.rs | 30 ++++++++++++++++-------------- tests/ui/filter_map_next.stderr | 3 +-- tests/ui/map_unwrap_or.stderr | 16 ++++------------ tests/ui/methods.stderr | 3 +-- 4 files changed, 22 insertions(+), 30 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index fc77cd52b8f..d3e26a09ad4 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -32,8 +32,7 @@ use crate::utils::{ is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls, method_chain_args, paths, remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint, - span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty_depth, - SpanlessEq, + span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty_depth, SpanlessEq, }; declare_clippy_lint! { @@ -2753,16 +2752,15 @@ fn lint_map_unwrap_or_else<'tcx>( let multiline = map_snippet.lines().count() > 1 || unwrap_snippet.lines().count() > 1; let same_span = map_args[1].span.ctxt() == unwrap_args[1].span.ctxt(); if same_span && !multiline { - span_lint_and_note( + let var_snippet = snippet(cx, map_args[0].span, ".."); + span_lint_and_sugg( cx, MAP_UNWRAP_OR, expr.span, msg, - None, - &format!( - "replace `map({0}).unwrap_or_else({1})` with `map_or_else({1}, {0})`", - map_snippet, unwrap_snippet, - ), + "try this", + format!("{}.map_or_else({}, {})", var_snippet, unwrap_snippet, map_snippet), + Applicability::MachineApplicable, ); return true; } else if same_span && multiline { @@ -2852,14 +2850,16 @@ fn lint_filter_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, fil `.find(..)` instead."; let filter_snippet = snippet(cx, filter_args[1].span, ".."); if filter_snippet.lines().count() <= 1 { + let iter_snippet = snippet(cx, filter_args[0].span, ".."); // add note if not multi-line - span_lint_and_note( + span_lint_and_sugg( cx, FILTER_NEXT, expr.span, msg, - None, - &format!("replace `filter({0}).next()` with `find({0})`", filter_snippet), + "try this", + format!("{}.find({})", iter_snippet, filter_snippet), + Applicability::MachineApplicable, ); } else { span_lint(cx, FILTER_NEXT, expr.span, msg); @@ -2908,13 +2908,15 @@ fn lint_filter_map_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, `.find_map(..)` instead."; let filter_snippet = snippet(cx, filter_args[1].span, ".."); if filter_snippet.lines().count() <= 1 { - span_lint_and_note( + let iter_snippet = snippet(cx, filter_args[0].span, ".."); + span_lint_and_sugg( cx, FILTER_MAP_NEXT, expr.span, msg, - None, - &format!("replace `filter_map({0}).next()` with `find_map({0})`", filter_snippet), + "try this", + format!("{}.find_map({})", iter_snippet, filter_snippet), + Applicability::MachineApplicable, ); } else { span_lint(cx, FILTER_MAP_NEXT, expr.span, msg); diff --git a/tests/ui/filter_map_next.stderr b/tests/ui/filter_map_next.stderr index 01281ebaf6a..bcedf11e536 100644 --- a/tests/ui/filter_map_next.stderr +++ b/tests/ui/filter_map_next.stderr @@ -2,10 +2,9 @@ error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly --> $DIR/filter_map_next.rs:6:32 | LL | let element: Option = a.iter().filter_map(|s| s.parse().ok()).next(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `a.iter().find_map(|s| s.parse().ok())` | = note: `-D clippy::filter-map-next` implied by `-D warnings` - = note: replace `filter_map(|s| s.parse().ok()).next()` with `find_map(|s| s.parse().ok())` error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead. --> $DIR/filter_map_next.rs:10:26 diff --git a/tests/ui/map_unwrap_or.stderr b/tests/ui/map_unwrap_or.stderr index 6fa2800e9bd..1975abb3e9f 100644 --- a/tests/ui/map_unwrap_or.stderr +++ b/tests/ui/map_unwrap_or.stderr @@ -109,9 +109,7 @@ LL | let _ = opt.map(|x| x + 1) | _____________^ LL | | // Should lint even though this call is on a separate line. LL | | .unwrap_or_else(|| 0); - | |_____________________________^ - | - = note: replace `map(|x| x + 1).unwrap_or_else(|| 0)` with `map_or_else(|| 0, |x| x + 1)` + | |_____________________________^ help: try this: `opt.map_or_else(|| 0, |x| x + 1)` error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead --> $DIR/map_unwrap_or.rs:55:13 @@ -137,25 +135,19 @@ error: called `map().unwrap_or_else()` on a `Result` value. This can be do --> $DIR/map_unwrap_or.rs:88:13 | LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); // should lint even though this call is on a separate line - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `res.map_or_else(|e| 0, |x| x + 1)` error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead --> $DIR/map_unwrap_or.rs:90:13 | LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `res.map_or_else(|e| 0, |x| x + 1)` error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead --> $DIR/map_unwrap_or.rs:91:13 | LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: replace `map(|x| x + 1).unwrap_or_else(|e| 0)` with `map_or_else(|e| 0, |x| x + 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `res.map_or_else(|e| 0, |x| x + 1)` error: aborting due to 13 previous errors diff --git a/tests/ui/methods.stderr b/tests/ui/methods.stderr index 74d8533d4da..2df1941aaaa 100644 --- a/tests/ui/methods.stderr +++ b/tests/ui/methods.stderr @@ -12,10 +12,9 @@ error: called `filter(..).next()` on an `Iterator`. This is more succinctly expr --> $DIR/methods.rs:126:13 | LL | let _ = v.iter().filter(|&x| *x < 0).next(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `v.iter().find(|&x| *x < 0)` | = note: `-D clippy::filter-next` implied by `-D warnings` - = note: replace `filter(|&x| *x < 0).next()` with `find(|&x| *x < 0)` error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead. --> $DIR/methods.rs:129:13 -- cgit 1.4.1-3-g733a5 From 2a3ae114858ff971b4cc51b4a43cb1475bd39516 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Fri, 23 Oct 2020 09:24:25 +0200 Subject: Move fixable `map_unwrap_or` cases to rustfixed test --- tests/ui/map_unwrap_or.rs | 41 ------------------- tests/ui/map_unwrap_or.stderr | 61 +++++------------------------ tests/ui/map_unwrap_or_else_fixable.fixed | 59 ++++++++++++++++++++++++++++ tests/ui/map_unwrap_or_else_fixable.rs | 63 ++++++++++++++++++++++++++++++ tests/ui/map_unwrap_or_else_fixable.stderr | 40 +++++++++++++++++++ 5 files changed, 172 insertions(+), 92 deletions(-) create mode 100644 tests/ui/map_unwrap_or_else_fixable.fixed create mode 100644 tests/ui/map_unwrap_or_else_fixable.rs create mode 100644 tests/ui/map_unwrap_or_else_fixable.stderr (limited to 'tests') diff --git a/tests/ui/map_unwrap_or.rs b/tests/ui/map_unwrap_or.rs index 585944032e7..4e977051ab7 100644 --- a/tests/ui/map_unwrap_or.rs +++ b/tests/ui/map_unwrap_or.rs @@ -1,4 +1,3 @@ -// FIXME: Add "run-rustfix" once it's supported for multipart suggestions // aux-build:option_helpers.rs #![warn(clippy::map_unwrap_or)] @@ -13,10 +12,6 @@ fn option_methods() { let opt = Some(1); // Check for `option.map(_).unwrap_or(_)` use. - // Single line case. - let _ = opt.map(|x| x + 1) - // Should lint even though this call is on a separate line. - .unwrap_or(0); // Multi-line cases. let _ = opt.map(|x| { x + 1 @@ -47,10 +42,6 @@ fn option_methods() { let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id); // Check for `option.map(_).unwrap_or_else(_)` use. - // single line case - let _ = opt.map(|x| x + 1) - // Should lint even though this call is on a separate line. - .unwrap_or_else(|| 0); // Multi-line cases. let _ = opt.map(|x| { x + 1 @@ -60,40 +51,8 @@ fn option_methods() { .unwrap_or_else(|| 0 ); - // Macro case. - // Should not lint. - let _ = opt_map!(opt, |x| x + 1).unwrap_or_else(|| 0); - - // Issue #4144 - { - let mut frequencies = HashMap::new(); - let word = "foo"; - - frequencies - .get_mut(word) - .map(|count| { - *count += 1; - }) - .unwrap_or_else(|| { - frequencies.insert(word.to_owned(), 1); - }); - } -} - -fn result_methods() { - let res: Result = Ok(1); - - // Check for `result.map(_).unwrap_or_else(_)` use. - // single line case - let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); // should lint even though this call is on a separate line - // multi line cases - let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); - let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); - // macro case - let _ = opt_map!(res, |x| x + 1).unwrap_or_else(|e| 0); // should not lint } fn main() { option_methods(); - result_methods(); } diff --git a/tests/ui/map_unwrap_or.stderr b/tests/ui/map_unwrap_or.stderr index 1975abb3e9f..3fd4bdfd2b9 100644 --- a/tests/ui/map_unwrap_or.stderr +++ b/tests/ui/map_unwrap_or.stderr @@ -1,20 +1,5 @@ error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead - --> $DIR/map_unwrap_or.rs:17:13 - | -LL | let _ = opt.map(|x| x + 1) - | _____________^ -LL | | // Should lint even though this call is on a separate line. -LL | | .unwrap_or(0); - | |_____________________^ - | - = note: `-D clippy::map-unwrap-or` implied by `-D warnings` -help: use `map_or(, )` instead - | -LL | let _ = opt.map_or(0, |x| x + 1); - | ^^^^^^ ^^ -- - -error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead - --> $DIR/map_unwrap_or.rs:21:13 + --> $DIR/map_unwrap_or.rs:16:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -23,6 +8,7 @@ LL | | } LL | | ).unwrap_or(0); | |__________________^ | + = note: `-D clippy::map-unwrap-or` implied by `-D warnings` help: use `map_or(, )` instead | LL | let _ = opt.map_or(0, |x| { @@ -32,7 +18,7 @@ LL | ); | error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead - --> $DIR/map_unwrap_or.rs:25:13 + --> $DIR/map_unwrap_or.rs:20:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -49,7 +35,7 @@ LL | }, |x| x + 1); | error: called `map().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead - --> $DIR/map_unwrap_or.rs:30:13 + --> $DIR/map_unwrap_or.rs:25:13 | LL | let _ = opt.map(|x| Some(x + 1)).unwrap_or(None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -60,7 +46,7 @@ LL | let _ = opt.and_then(|x| Some(x + 1)); | ^^^^^^^^ -- error: called `map().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead - --> $DIR/map_unwrap_or.rs:32:13 + --> $DIR/map_unwrap_or.rs:27:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -78,7 +64,7 @@ LL | ); | error: called `map().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead - --> $DIR/map_unwrap_or.rs:36:13 + --> $DIR/map_unwrap_or.rs:31:13 | LL | let _ = opt | _____________^ @@ -92,7 +78,7 @@ LL | .and_then(|x| Some(x + 1)); | ^^^^^^^^ -- error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead - --> $DIR/map_unwrap_or.rs:47:13 + --> $DIR/map_unwrap_or.rs:42:13 | LL | let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -103,16 +89,7 @@ LL | let _ = Some("prefix").map_or(id, |p| format!("{}.", p)); | ^^^^^^ ^^^ -- error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:51:13 - | -LL | let _ = opt.map(|x| x + 1) - | _____________^ -LL | | // Should lint even though this call is on a separate line. -LL | | .unwrap_or_else(|| 0); - | |_____________________________^ help: try this: `opt.map_or_else(|| 0, |x| x + 1)` - -error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:55:13 + --> $DIR/map_unwrap_or.rs:46:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -122,7 +99,7 @@ LL | | ).unwrap_or_else(|| 0); | |__________________________^ error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:59:13 + --> $DIR/map_unwrap_or.rs:50:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -131,23 +108,5 @@ LL | | 0 LL | | ); | |_________^ -error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:88:13 - | -LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); // should lint even though this call is on a separate line - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `res.map_or_else(|e| 0, |x| x + 1)` - -error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:90:13 - | -LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `res.map_or_else(|e| 0, |x| x + 1)` - -error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:91:13 - | -LL | let _ = res.map(|x| x + 1).unwrap_or_else(|e| 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `res.map_or_else(|e| 0, |x| x + 1)` - -error: aborting due to 13 previous errors +error: aborting due to 8 previous errors diff --git a/tests/ui/map_unwrap_or_else_fixable.fixed b/tests/ui/map_unwrap_or_else_fixable.fixed new file mode 100644 index 00000000000..cb2492a3be0 --- /dev/null +++ b/tests/ui/map_unwrap_or_else_fixable.fixed @@ -0,0 +1,59 @@ +// run-rustfix +// aux-build:option_helpers.rs + +#![warn(clippy::map_unwrap_or)] + +#[macro_use] +extern crate option_helpers; + +use std::collections::HashMap; + +#[rustfmt::skip] +fn option_methods() { + let opt = Some(1); + + // Check for `option.map(_).unwrap_or_else(_)` use. + // single line case + let _ = opt.map_or_else(|| 0, |x| x + 1); + + // Macro case. + // Should not lint. + let _ = opt_map!(opt, |x| x + 1).unwrap_or_else(|| 0); + + // Check for `option.map(_).unwrap_or_else(_)` use. + // single line case + let _ = opt.map_or_else(|| 0, |x| x + 1); + + // Issue #4144 + { + let mut frequencies = HashMap::new(); + let word = "foo"; + + frequencies + .get_mut(word) + .map(|count| { + *count += 1; + }) + .unwrap_or_else(|| { + frequencies.insert(word.to_owned(), 1); + }); + } +} + +fn result_methods() { + let res: Result = Ok(1); + + // Check for `result.map(_).unwrap_or_else(_)` use. + // single line case + let _ = res.map_or_else(|_e| 0, |x| x + 1); // should lint even though this call is on a separate line + // multi line cases + let _ = res.map_or_else(|_e| 0, |x| x + 1); + let _ = res.map_or_else(|_e| 0, |x| x + 1); + // macro case + let _ = opt_map!(res, |x| x + 1).unwrap_or_else(|_e| 0); // should not lint +} + +fn main() { + option_methods(); + result_methods(); +} diff --git a/tests/ui/map_unwrap_or_else_fixable.rs b/tests/ui/map_unwrap_or_else_fixable.rs new file mode 100644 index 00000000000..ed762dacd87 --- /dev/null +++ b/tests/ui/map_unwrap_or_else_fixable.rs @@ -0,0 +1,63 @@ +// run-rustfix +// aux-build:option_helpers.rs + +#![warn(clippy::map_unwrap_or)] + +#[macro_use] +extern crate option_helpers; + +use std::collections::HashMap; + +#[rustfmt::skip] +fn option_methods() { + let opt = Some(1); + + // Check for `option.map(_).unwrap_or_else(_)` use. + // single line case + let _ = opt.map(|x| x + 1) + // Should lint even though this call is on a separate line. + .unwrap_or_else(|| 0); + + // Macro case. + // Should not lint. + let _ = opt_map!(opt, |x| x + 1).unwrap_or_else(|| 0); + + // Check for `option.map(_).unwrap_or_else(_)` use. + // single line case + let _ = opt.map(|x| x + 1) + // Should lint even though this call is on a separate line. + .unwrap_or_else(|| 0); + + // Issue #4144 + { + let mut frequencies = HashMap::new(); + let word = "foo"; + + frequencies + .get_mut(word) + .map(|count| { + *count += 1; + }) + .unwrap_or_else(|| { + frequencies.insert(word.to_owned(), 1); + }); + } +} + +fn result_methods() { + let res: Result = Ok(1); + + // Check for `result.map(_).unwrap_or_else(_)` use. + // single line case + let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); // should lint even though this call is on a separate line + // multi line cases + let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); + let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); + // macro case + let _ = opt_map!(res, |x| x + 1).unwrap_or_else(|_e| 0); // should not lint +} + +fn main() { + option_methods(); + result_methods(); +} diff --git a/tests/ui/map_unwrap_or_else_fixable.stderr b/tests/ui/map_unwrap_or_else_fixable.stderr new file mode 100644 index 00000000000..2cb76d70684 --- /dev/null +++ b/tests/ui/map_unwrap_or_else_fixable.stderr @@ -0,0 +1,40 @@ +error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead + --> $DIR/map_unwrap_or_else_fixable.rs:17:13 + | +LL | let _ = opt.map(|x| x + 1) + | _____________^ +LL | | // Should lint even though this call is on a separate line. +LL | | .unwrap_or_else(|| 0); + | |_____________________________^ help: try this: `opt.map_or_else(|| 0, |x| x + 1)` + | + = note: `-D clippy::map-unwrap-or` implied by `-D warnings` + +error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead + --> $DIR/map_unwrap_or_else_fixable.rs:27:13 + | +LL | let _ = opt.map(|x| x + 1) + | _____________^ +LL | | // Should lint even though this call is on a separate line. +LL | | .unwrap_or_else(|| 0); + | |_____________________________^ help: try this: `opt.map_or_else(|| 0, |x| x + 1)` + +error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead + --> $DIR/map_unwrap_or_else_fixable.rs:52:13 + | +LL | let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); // should lint even though this call is on a separate line + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `res.map_or_else(|_e| 0, |x| x + 1)` + +error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead + --> $DIR/map_unwrap_or_else_fixable.rs:54:13 + | +LL | let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `res.map_or_else(|_e| 0, |x| x + 1)` + +error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead + --> $DIR/map_unwrap_or_else_fixable.rs:55:13 + | +LL | let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `res.map_or_else(|_e| 0, |x| x + 1)` + +error: aborting due to 5 previous errors + -- cgit 1.4.1-3-g733a5 From e2d86b5b809584da55952f4150016fdbaf74e7f4 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Fri, 23 Oct 2020 09:49:47 +0200 Subject: Move fixable `filter_next` and `filter_map_next` cases to rustfixed tests --- tests/ui/filter_map_next.rs | 3 --- tests/ui/filter_map_next.stderr | 14 ++++---------- tests/ui/filter_map_next_fixable.fixed | 10 ++++++++++ tests/ui/filter_map_next_fixable.rs | 10 ++++++++++ tests/ui/filter_map_next_fixable.stderr | 10 ++++++++++ tests/ui/methods.rs | 5 +---- tests/ui/methods.stderr | 30 ++++++++++++------------------ tests/ui/methods_fixable.fixed | 11 +++++++++++ tests/ui/methods_fixable.rs | 11 +++++++++++ tests/ui/methods_fixable.stderr | 10 ++++++++++ 10 files changed, 79 insertions(+), 35 deletions(-) create mode 100644 tests/ui/filter_map_next_fixable.fixed create mode 100644 tests/ui/filter_map_next_fixable.rs create mode 100644 tests/ui/filter_map_next_fixable.stderr create mode 100644 tests/ui/methods_fixable.fixed create mode 100644 tests/ui/methods_fixable.rs create mode 100644 tests/ui/methods_fixable.stderr (limited to 'tests') diff --git a/tests/ui/filter_map_next.rs b/tests/ui/filter_map_next.rs index f5d051be198..dbeb2354309 100644 --- a/tests/ui/filter_map_next.rs +++ b/tests/ui/filter_map_next.rs @@ -3,9 +3,6 @@ fn main() { let a = ["1", "lol", "3", "NaN", "5"]; - let element: Option = a.iter().filter_map(|s| s.parse().ok()).next(); - assert_eq!(element, Some(1)); - #[rustfmt::skip] let _: Option = vec![1, 2, 3, 4, 5, 6] .into_iter() diff --git a/tests/ui/filter_map_next.stderr b/tests/ui/filter_map_next.stderr index bcedf11e536..45427684d96 100644 --- a/tests/ui/filter_map_next.stderr +++ b/tests/ui/filter_map_next.stderr @@ -1,13 +1,5 @@ error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead. - --> $DIR/filter_map_next.rs:6:32 - | -LL | let element: Option = a.iter().filter_map(|s| s.parse().ok()).next(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `a.iter().find_map(|s| s.parse().ok())` - | - = note: `-D clippy::filter-map-next` implied by `-D warnings` - -error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead. - --> $DIR/filter_map_next.rs:10:26 + --> $DIR/filter_map_next.rs:7:26 | LL | let _: Option = vec![1, 2, 3, 4, 5, 6] | __________________________^ @@ -18,6 +10,8 @@ LL | | if x == 2 { LL | | }) LL | | .next(); | |_______________^ + | + = note: `-D clippy::filter-map-next` implied by `-D warnings` -error: aborting due to 2 previous errors +error: aborting due to previous error diff --git a/tests/ui/filter_map_next_fixable.fixed b/tests/ui/filter_map_next_fixable.fixed new file mode 100644 index 00000000000..c3992d7e92c --- /dev/null +++ b/tests/ui/filter_map_next_fixable.fixed @@ -0,0 +1,10 @@ +// run-rustfix + +#![warn(clippy::all, clippy::pedantic)] + +fn main() { + let a = ["1", "lol", "3", "NaN", "5"]; + + let element: Option = a.iter().find_map(|s| s.parse().ok()); + assert_eq!(element, Some(1)); +} diff --git a/tests/ui/filter_map_next_fixable.rs b/tests/ui/filter_map_next_fixable.rs new file mode 100644 index 00000000000..447219a9683 --- /dev/null +++ b/tests/ui/filter_map_next_fixable.rs @@ -0,0 +1,10 @@ +// run-rustfix + +#![warn(clippy::all, clippy::pedantic)] + +fn main() { + let a = ["1", "lol", "3", "NaN", "5"]; + + let element: Option = a.iter().filter_map(|s| s.parse().ok()).next(); + assert_eq!(element, Some(1)); +} diff --git a/tests/ui/filter_map_next_fixable.stderr b/tests/ui/filter_map_next_fixable.stderr new file mode 100644 index 00000000000..6c2530e0379 --- /dev/null +++ b/tests/ui/filter_map_next_fixable.stderr @@ -0,0 +1,10 @@ +error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead. + --> $DIR/filter_map_next_fixable.rs:8:32 + | +LL | let element: Option = a.iter().filter_map(|s| s.parse().ok()).next(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `a.iter().find_map(|s| s.parse().ok())` + | + = note: `-D clippy::filter-map-next` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/methods.rs b/tests/ui/methods.rs index 80dd2f744b3..d93e5b114ec 100644 --- a/tests/ui/methods.rs +++ b/tests/ui/methods.rs @@ -122,16 +122,13 @@ impl Mul for T { fn filter_next() { let v = vec![3, 2, 1, 0, -1, -2, -3]; - // Single-line case. - let _ = v.iter().filter(|&x| *x < 0).next(); - // Multi-line case. let _ = v.iter().filter(|&x| { *x < 0 } ).next(); - // Check that hat we don't lint if the caller is not an `Iterator`. + // Check that we don't lint if the caller is not an `Iterator`. let foo = IteratorFalsePositives { foo: 0 }; let _ = foo.filter().next(); } diff --git a/tests/ui/methods.stderr b/tests/ui/methods.stderr index 2df1941aaaa..8a281c2dbd2 100644 --- a/tests/ui/methods.stderr +++ b/tests/ui/methods.stderr @@ -11,23 +11,17 @@ LL | | } error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead. --> $DIR/methods.rs:126:13 | -LL | let _ = v.iter().filter(|&x| *x < 0).next(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `v.iter().find(|&x| *x < 0)` - | - = note: `-D clippy::filter-next` implied by `-D warnings` - -error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead. - --> $DIR/methods.rs:129:13 - | LL | let _ = v.iter().filter(|&x| { | _____________^ LL | | *x < 0 LL | | } LL | | ).next(); | |___________________________^ + | + = note: `-D clippy::filter-next` implied by `-D warnings` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:146:22 + --> $DIR/methods.rs:143:22 | LL | let _ = v.iter().find(|&x| *x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x < 0)` @@ -35,25 +29,25 @@ LL | let _ = v.iter().find(|&x| *x < 0).is_some(); = note: `-D clippy::search-is-some` implied by `-D warnings` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:147:20 + --> $DIR/methods.rs:144:20 | LL | let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| **y == x)` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:148:20 + --> $DIR/methods.rs:145:20 | LL | let _ = (0..1).find(|x| *x == 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| x == 0)` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:149:22 + --> $DIR/methods.rs:146:22 | LL | let _ = v.iter().find(|x| **x == 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x == 0)` error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:152:13 + --> $DIR/methods.rs:149:13 | LL | let _ = v.iter().find(|&x| { | _____________^ @@ -63,13 +57,13 @@ LL | | ).is_some(); | |______________________________^ error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:158:22 + --> $DIR/methods.rs:155:22 | LL | let _ = v.iter().position(|&x| x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:161:13 + --> $DIR/methods.rs:158:13 | LL | let _ = v.iter().position(|&x| { | _____________^ @@ -79,13 +73,13 @@ LL | | ).is_some(); | |______________________________^ error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:167:22 + --> $DIR/methods.rs:164:22 | LL | let _ = v.iter().rposition(|&x| x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:170:13 + --> $DIR/methods.rs:167:13 | LL | let _ = v.iter().rposition(|&x| { | _____________^ @@ -94,5 +88,5 @@ LL | | } LL | | ).is_some(); | |______________________________^ -error: aborting due to 12 previous errors +error: aborting due to 11 previous errors diff --git a/tests/ui/methods_fixable.fixed b/tests/ui/methods_fixable.fixed new file mode 100644 index 00000000000..ee7c1b0da6d --- /dev/null +++ b/tests/ui/methods_fixable.fixed @@ -0,0 +1,11 @@ +// run-rustfix + +#![warn(clippy::filter_next)] + +/// Checks implementation of `FILTER_NEXT` lint. +fn main() { + let v = vec![3, 2, 1, 0, -1, -2, -3]; + + // Single-line case. + let _ = v.iter().find(|&x| *x < 0); +} diff --git a/tests/ui/methods_fixable.rs b/tests/ui/methods_fixable.rs new file mode 100644 index 00000000000..6d0f1b7bd51 --- /dev/null +++ b/tests/ui/methods_fixable.rs @@ -0,0 +1,11 @@ +// run-rustfix + +#![warn(clippy::filter_next)] + +/// Checks implementation of `FILTER_NEXT` lint. +fn main() { + let v = vec![3, 2, 1, 0, -1, -2, -3]; + + // Single-line case. + let _ = v.iter().filter(|&x| *x < 0).next(); +} diff --git a/tests/ui/methods_fixable.stderr b/tests/ui/methods_fixable.stderr new file mode 100644 index 00000000000..70e7c3dea54 --- /dev/null +++ b/tests/ui/methods_fixable.stderr @@ -0,0 +1,10 @@ +error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead. + --> $DIR/methods_fixable.rs:10:13 + | +LL | let _ = v.iter().filter(|&x| *x < 0).next(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `v.iter().find(|&x| *x < 0)` + | + = note: `-D clippy::filter-next` implied by `-D warnings` + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From c0dd1f9f7614d86c15fcccd4e0faabaa52c7c339 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Mon, 26 Oct 2020 11:02:01 +0100 Subject: Fix tests for `map_unwrap_or*` --- tests/ui/map_unwrap_or.rs | 23 +++++++++++ tests/ui/map_unwrap_or.stderr | 52 +++++++++++++++++++----- tests/ui/map_unwrap_or_else_fixable.fixed | 59 ---------------------------- tests/ui/map_unwrap_or_else_fixable.rs | 63 ------------------------------ tests/ui/map_unwrap_or_else_fixable.stderr | 40 ------------------- tests/ui/map_unwrap_or_fixable.fixed | 54 +++++++++++++++++++++++++ tests/ui/map_unwrap_or_fixable.rs | 58 +++++++++++++++++++++++++++ tests/ui/map_unwrap_or_fixable.stderr | 22 +++++++++++ 8 files changed, 200 insertions(+), 171 deletions(-) delete mode 100644 tests/ui/map_unwrap_or_else_fixable.fixed delete mode 100644 tests/ui/map_unwrap_or_else_fixable.rs delete mode 100644 tests/ui/map_unwrap_or_else_fixable.stderr create mode 100644 tests/ui/map_unwrap_or_fixable.fixed create mode 100644 tests/ui/map_unwrap_or_fixable.rs create mode 100644 tests/ui/map_unwrap_or_fixable.stderr (limited to 'tests') diff --git a/tests/ui/map_unwrap_or.rs b/tests/ui/map_unwrap_or.rs index 4e977051ab7..87e16f5d09b 100644 --- a/tests/ui/map_unwrap_or.rs +++ b/tests/ui/map_unwrap_or.rs @@ -12,6 +12,10 @@ fn option_methods() { let opt = Some(1); // Check for `option.map(_).unwrap_or(_)` use. + // Single line case. + let _ = opt.map(|x| x + 1) + // Should lint even though this call is on a separate line. + .unwrap_or(0); // Multi-line cases. let _ = opt.map(|x| { x + 1 @@ -53,6 +57,25 @@ fn option_methods() { ); } +#[rustfmt::skip] +fn result_methods() { + let res: Result = Ok(1); + + // Check for `result.map(_).unwrap_or_else(_)` use. + // multi line cases + let _ = res.map(|x| { + x + 1 + } + ).unwrap_or_else(|_e| 0); + let _ = res.map(|x| x + 1) + .unwrap_or_else(|_e| { + 0 + }); + // macro case + let _ = opt_map!(res, |x| x + 1).unwrap_or_else(|_e| 0); // should not lint +} + fn main() { option_methods(); + result_methods(); } diff --git a/tests/ui/map_unwrap_or.stderr b/tests/ui/map_unwrap_or.stderr index 3fd4bdfd2b9..96b9d6cc3c1 100644 --- a/tests/ui/map_unwrap_or.stderr +++ b/tests/ui/map_unwrap_or.stderr @@ -1,6 +1,21 @@ error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead --> $DIR/map_unwrap_or.rs:16:13 | +LL | let _ = opt.map(|x| x + 1) + | _____________^ +LL | | // Should lint even though this call is on a separate line. +LL | | .unwrap_or(0); + | |_____________________^ + | + = note: `-D clippy::map-unwrap-or` implied by `-D warnings` +help: use `map_or(, )` instead + | +LL | let _ = opt.map_or(0, |x| x + 1); + | ^^^^^^ ^^ -- + +error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead + --> $DIR/map_unwrap_or.rs:20:13 + | LL | let _ = opt.map(|x| { | _____________^ LL | | x + 1 @@ -8,7 +23,6 @@ LL | | } LL | | ).unwrap_or(0); | |__________________^ | - = note: `-D clippy::map-unwrap-or` implied by `-D warnings` help: use `map_or(, )` instead | LL | let _ = opt.map_or(0, |x| { @@ -18,7 +32,7 @@ LL | ); | error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead - --> $DIR/map_unwrap_or.rs:20:13 + --> $DIR/map_unwrap_or.rs:24:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -35,7 +49,7 @@ LL | }, |x| x + 1); | error: called `map().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead - --> $DIR/map_unwrap_or.rs:25:13 + --> $DIR/map_unwrap_or.rs:29:13 | LL | let _ = opt.map(|x| Some(x + 1)).unwrap_or(None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -46,7 +60,7 @@ LL | let _ = opt.and_then(|x| Some(x + 1)); | ^^^^^^^^ -- error: called `map().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead - --> $DIR/map_unwrap_or.rs:27:13 + --> $DIR/map_unwrap_or.rs:31:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -64,7 +78,7 @@ LL | ); | error: called `map().unwrap_or(None)` on an `Option` value. This can be done more directly by calling `and_then()` instead - --> $DIR/map_unwrap_or.rs:31:13 + --> $DIR/map_unwrap_or.rs:35:13 | LL | let _ = opt | _____________^ @@ -78,7 +92,7 @@ LL | .and_then(|x| Some(x + 1)); | ^^^^^^^^ -- error: called `map().unwrap_or()` on an `Option` value. This can be done more directly by calling `map_or(, )` instead - --> $DIR/map_unwrap_or.rs:42:13 + --> $DIR/map_unwrap_or.rs:46:13 | LL | let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -89,7 +103,7 @@ LL | let _ = Some("prefix").map_or(id, |p| format!("{}.", p)); | ^^^^^^ ^^^ -- error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:46:13 + --> $DIR/map_unwrap_or.rs:50:13 | LL | let _ = opt.map(|x| { | _____________^ @@ -99,7 +113,7 @@ LL | | ).unwrap_or_else(|| 0); | |__________________________^ error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead - --> $DIR/map_unwrap_or.rs:50:13 + --> $DIR/map_unwrap_or.rs:54:13 | LL | let _ = opt.map(|x| x + 1) | _____________^ @@ -108,5 +122,25 @@ LL | | 0 LL | | ); | |_________^ -error: aborting due to 8 previous errors +error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead + --> $DIR/map_unwrap_or.rs:66:13 + | +LL | let _ = res.map(|x| { + | _____________^ +LL | | x + 1 +LL | | } +LL | | ).unwrap_or_else(|_e| 0); + | |____________________________^ + +error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead + --> $DIR/map_unwrap_or.rs:70:13 + | +LL | let _ = res.map(|x| x + 1) + | _____________^ +LL | | .unwrap_or_else(|_e| { +LL | | 0 +LL | | }); + | |__________^ + +error: aborting due to 11 previous errors diff --git a/tests/ui/map_unwrap_or_else_fixable.fixed b/tests/ui/map_unwrap_or_else_fixable.fixed deleted file mode 100644 index cb2492a3be0..00000000000 --- a/tests/ui/map_unwrap_or_else_fixable.fixed +++ /dev/null @@ -1,59 +0,0 @@ -// run-rustfix -// aux-build:option_helpers.rs - -#![warn(clippy::map_unwrap_or)] - -#[macro_use] -extern crate option_helpers; - -use std::collections::HashMap; - -#[rustfmt::skip] -fn option_methods() { - let opt = Some(1); - - // Check for `option.map(_).unwrap_or_else(_)` use. - // single line case - let _ = opt.map_or_else(|| 0, |x| x + 1); - - // Macro case. - // Should not lint. - let _ = opt_map!(opt, |x| x + 1).unwrap_or_else(|| 0); - - // Check for `option.map(_).unwrap_or_else(_)` use. - // single line case - let _ = opt.map_or_else(|| 0, |x| x + 1); - - // Issue #4144 - { - let mut frequencies = HashMap::new(); - let word = "foo"; - - frequencies - .get_mut(word) - .map(|count| { - *count += 1; - }) - .unwrap_or_else(|| { - frequencies.insert(word.to_owned(), 1); - }); - } -} - -fn result_methods() { - let res: Result = Ok(1); - - // Check for `result.map(_).unwrap_or_else(_)` use. - // single line case - let _ = res.map_or_else(|_e| 0, |x| x + 1); // should lint even though this call is on a separate line - // multi line cases - let _ = res.map_or_else(|_e| 0, |x| x + 1); - let _ = res.map_or_else(|_e| 0, |x| x + 1); - // macro case - let _ = opt_map!(res, |x| x + 1).unwrap_or_else(|_e| 0); // should not lint -} - -fn main() { - option_methods(); - result_methods(); -} diff --git a/tests/ui/map_unwrap_or_else_fixable.rs b/tests/ui/map_unwrap_or_else_fixable.rs deleted file mode 100644 index ed762dacd87..00000000000 --- a/tests/ui/map_unwrap_or_else_fixable.rs +++ /dev/null @@ -1,63 +0,0 @@ -// run-rustfix -// aux-build:option_helpers.rs - -#![warn(clippy::map_unwrap_or)] - -#[macro_use] -extern crate option_helpers; - -use std::collections::HashMap; - -#[rustfmt::skip] -fn option_methods() { - let opt = Some(1); - - // Check for `option.map(_).unwrap_or_else(_)` use. - // single line case - let _ = opt.map(|x| x + 1) - // Should lint even though this call is on a separate line. - .unwrap_or_else(|| 0); - - // Macro case. - // Should not lint. - let _ = opt_map!(opt, |x| x + 1).unwrap_or_else(|| 0); - - // Check for `option.map(_).unwrap_or_else(_)` use. - // single line case - let _ = opt.map(|x| x + 1) - // Should lint even though this call is on a separate line. - .unwrap_or_else(|| 0); - - // Issue #4144 - { - let mut frequencies = HashMap::new(); - let word = "foo"; - - frequencies - .get_mut(word) - .map(|count| { - *count += 1; - }) - .unwrap_or_else(|| { - frequencies.insert(word.to_owned(), 1); - }); - } -} - -fn result_methods() { - let res: Result = Ok(1); - - // Check for `result.map(_).unwrap_or_else(_)` use. - // single line case - let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); // should lint even though this call is on a separate line - // multi line cases - let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); - let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); - // macro case - let _ = opt_map!(res, |x| x + 1).unwrap_or_else(|_e| 0); // should not lint -} - -fn main() { - option_methods(); - result_methods(); -} diff --git a/tests/ui/map_unwrap_or_else_fixable.stderr b/tests/ui/map_unwrap_or_else_fixable.stderr deleted file mode 100644 index 2cb76d70684..00000000000 --- a/tests/ui/map_unwrap_or_else_fixable.stderr +++ /dev/null @@ -1,40 +0,0 @@ -error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead - --> $DIR/map_unwrap_or_else_fixable.rs:17:13 - | -LL | let _ = opt.map(|x| x + 1) - | _____________^ -LL | | // Should lint even though this call is on a separate line. -LL | | .unwrap_or_else(|| 0); - | |_____________________________^ help: try this: `opt.map_or_else(|| 0, |x| x + 1)` - | - = note: `-D clippy::map-unwrap-or` implied by `-D warnings` - -error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead - --> $DIR/map_unwrap_or_else_fixable.rs:27:13 - | -LL | let _ = opt.map(|x| x + 1) - | _____________^ -LL | | // Should lint even though this call is on a separate line. -LL | | .unwrap_or_else(|| 0); - | |_____________________________^ help: try this: `opt.map_or_else(|| 0, |x| x + 1)` - -error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead - --> $DIR/map_unwrap_or_else_fixable.rs:52:13 - | -LL | let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); // should lint even though this call is on a separate line - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `res.map_or_else(|_e| 0, |x| x + 1)` - -error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead - --> $DIR/map_unwrap_or_else_fixable.rs:54:13 - | -LL | let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `res.map_or_else(|_e| 0, |x| x + 1)` - -error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead - --> $DIR/map_unwrap_or_else_fixable.rs:55:13 - | -LL | let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `res.map_or_else(|_e| 0, |x| x + 1)` - -error: aborting due to 5 previous errors - diff --git a/tests/ui/map_unwrap_or_fixable.fixed b/tests/ui/map_unwrap_or_fixable.fixed new file mode 100644 index 00000000000..bd5b4f7165a --- /dev/null +++ b/tests/ui/map_unwrap_or_fixable.fixed @@ -0,0 +1,54 @@ +// run-rustfix +// aux-build:option_helpers.rs + +#![warn(clippy::map_unwrap_or)] + +#[macro_use] +extern crate option_helpers; + +use std::collections::HashMap; + +#[rustfmt::skip] +fn option_methods() { + let opt = Some(1); + + // Check for `option.map(_).unwrap_or_else(_)` use. + // single line case + let _ = opt.map_or_else(|| 0, |x| x + 1); + + // Macro case. + // Should not lint. + let _ = opt_map!(opt, |x| x + 1).unwrap_or_else(|| 0); + + // Issue #4144 + { + let mut frequencies = HashMap::new(); + let word = "foo"; + + frequencies + .get_mut(word) + .map(|count| { + *count += 1; + }) + .unwrap_or_else(|| { + frequencies.insert(word.to_owned(), 1); + }); + } +} + +#[rustfmt::skip] +fn result_methods() { + let res: Result = Ok(1); + + // Check for `result.map(_).unwrap_or_else(_)` use. + // single line case + let _ = res.map_or_else(|_e| 0, |x| x + 1); + + // macro case + let _ = opt_map!(res, |x| x + 1).unwrap_or_else(|_e| 0); // should not lint +} + +fn main() { + option_methods(); + result_methods(); +} diff --git a/tests/ui/map_unwrap_or_fixable.rs b/tests/ui/map_unwrap_or_fixable.rs new file mode 100644 index 00000000000..0b892caf20e --- /dev/null +++ b/tests/ui/map_unwrap_or_fixable.rs @@ -0,0 +1,58 @@ +// run-rustfix +// aux-build:option_helpers.rs + +#![warn(clippy::map_unwrap_or)] + +#[macro_use] +extern crate option_helpers; + +use std::collections::HashMap; + +#[rustfmt::skip] +fn option_methods() { + let opt = Some(1); + + // Check for `option.map(_).unwrap_or_else(_)` use. + // single line case + let _ = opt.map(|x| x + 1) + // Should lint even though this call is on a separate line. + .unwrap_or_else(|| 0); + + // Macro case. + // Should not lint. + let _ = opt_map!(opt, |x| x + 1).unwrap_or_else(|| 0); + + // Issue #4144 + { + let mut frequencies = HashMap::new(); + let word = "foo"; + + frequencies + .get_mut(word) + .map(|count| { + *count += 1; + }) + .unwrap_or_else(|| { + frequencies.insert(word.to_owned(), 1); + }); + } +} + +#[rustfmt::skip] +fn result_methods() { + let res: Result = Ok(1); + + // Check for `result.map(_).unwrap_or_else(_)` use. + // single line case + let _ = res.map(|x| x + 1) + // should lint even though this call is on a separate line + .unwrap_or_else(|_e| 0); + + // macro case + let _ = opt_map!(res, |x| x + 1).unwrap_or_else(|_e| 0); // should not lint +} + +fn main() { + option_methods(); + result_methods(); +} diff --git a/tests/ui/map_unwrap_or_fixable.stderr b/tests/ui/map_unwrap_or_fixable.stderr new file mode 100644 index 00000000000..1837bc2ca3b --- /dev/null +++ b/tests/ui/map_unwrap_or_fixable.stderr @@ -0,0 +1,22 @@ +error: called `map().unwrap_or_else()` on an `Option` value. This can be done more directly by calling `map_or_else(, )` instead + --> $DIR/map_unwrap_or_fixable.rs:17:13 + | +LL | let _ = opt.map(|x| x + 1) + | _____________^ +LL | | // Should lint even though this call is on a separate line. +LL | | .unwrap_or_else(|| 0); + | |_____________________________^ help: try this: `opt.map_or_else(|| 0, |x| x + 1)` + | + = note: `-D clippy::map-unwrap-or` implied by `-D warnings` + +error: called `map().unwrap_or_else()` on a `Result` value. This can be done more directly by calling `.map_or_else(, )` instead + --> $DIR/map_unwrap_or_fixable.rs:47:13 + | +LL | let _ = res.map(|x| x + 1) + | _____________^ +LL | | // should lint even though this call is on a separate line +LL | | .unwrap_or_else(|_e| 0); + | |_______________________________^ help: try this: `res.map_or_else(|_e| 0, |x| x + 1)` + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From 213dbf7aacb01cc4d05d47f010833aa6e9c2a7d0 Mon Sep 17 00:00:00 2001 From: dvermd <315743+dvermd@users.noreply.github.com> Date: Mon, 12 Oct 2020 23:58:59 +0200 Subject: clippy_lint: Add 'ref_option_ref' --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 +++ clippy_lints/src/ref_option_ref.rs | 65 ++++++++++++++++++++++++++++++++++++++ src/lintlist/mod.rs | 7 ++++ tests/ui/ref_option_ref.rs | 5 +++ tests/ui/ref_option_ref.stderr | 10 ++++++ 6 files changed, 93 insertions(+) create mode 100644 clippy_lints/src/ref_option_ref.rs create mode 100644 tests/ui/ref_option_ref.rs create mode 100644 tests/ui/ref_option_ref.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 25f3b5da198..db834fe108b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1917,6 +1917,7 @@ Released 2018-09-13 [`redundant_pub_crate`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pub_crate [`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes [`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref +[`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref [`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro [`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once [`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3be8bc0e36d..1ab36231758 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -293,6 +293,7 @@ mod redundant_closure_call; mod redundant_field_names; mod redundant_pub_crate; mod redundant_static_lifetimes; +mod ref_option_ref; mod reference; mod regex; mod repeat_once; @@ -803,6 +804,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &redundant_field_names::REDUNDANT_FIELD_NAMES, &redundant_pub_crate::REDUNDANT_PUB_CRATE, &redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES, + &ref_option_ref::REF_OPTION_REF, &reference::DEREF_ADDROF, &reference::REF_IN_DEREF, ®ex::INVALID_REGEX, @@ -1024,6 +1026,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &sess.target, ); store.register_late_pass(move || box pass_by_ref_or_value); + store.register_late_pass(|| box ref_option_ref::RefOptionRef); store.register_late_pass(|| box try_err::TryErr); store.register_late_pass(|| box use_self::UseSelf); store.register_late_pass(|| box bytecount::ByteCount); @@ -1493,6 +1496,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&redundant_closure_call::REDUNDANT_CLOSURE_CALL), LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), + LintId::of(&ref_option_ref::REF_OPTION_REF), LintId::of(&reference::DEREF_ADDROF), LintId::of(&reference::REF_IN_DEREF), LintId::of(®ex::INVALID_REGEX), @@ -1648,6 +1652,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ranges::MANUAL_RANGE_CONTAINS), LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), + LintId::of(&ref_option_ref::REF_OPTION_REF), LintId::of(®ex::TRIVIAL_REGEX), LintId::of(&returns::LET_AND_RETURN), LintId::of(&returns::NEEDLESS_RETURN), diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs new file mode 100644 index 00000000000..fbee3263556 --- /dev/null +++ b/clippy_lints/src/ref_option_ref.rs @@ -0,0 +1,65 @@ +use crate::utils::{last_path_segment, match_def_path, paths, snippet, span_lint_and_sugg}; +use rustc_hir::{GenericArg, Local, Mutability, TyKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +use if_chain::if_chain; +use rustc_errors::Applicability; + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `&Option<&T>`. + /// + /// **Why is this bad?** Since `&` is Copy, it's useless to have a + /// reference on `Option<&T>`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust,ignore + /// // example code where clippy issues a warning + /// let x: &Option<&u32> = &Some(&0u32); + /// ``` + /// Use instead: + /// ```rust,ignore + /// // example code which does not raise clippy warning + /// let x: Option<&u32> = Some(&0u32); + /// ``` + pub REF_OPTION_REF, + style, + "use `Option<&T>` instead of `&Option<&T>`" +} + +declare_lint_pass!(RefOptionRef => [REF_OPTION_REF]); + +impl<'tcx> LateLintPass<'tcx> for RefOptionRef { + fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) { + if_chain! { + if let Some(ref ty) = local.ty; + if let TyKind::Rptr(_, ref mut_ty) = ty.kind; + if mut_ty.mutbl == Mutability::Not; + if let TyKind::Path(ref qpath) = &mut_ty.ty.kind ; + if let Some(def_id) = cx.typeck_results().qpath_res(qpath, local.hir_id).opt_def_id(); + if match_def_path(cx, def_id, &paths::OPTION); + if let Some(ref params) = last_path_segment(qpath).args ; + if !params.parenthesized; + if let Some(inner_ty) = params.args.iter().find_map(|arg| match arg { + GenericArg::Type(inner_ty) => Some(inner_ty), + _ => None, + }); + if let TyKind::Rptr(_, _) = inner_ty.kind; + + then { + span_lint_and_sugg( + cx, + REF_OPTION_REF, + ty.span, + "since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T>", + "try", + format!("Option<{}>", &snippet(cx, inner_ty.span, "..")), + Applicability::MachineApplicable, + ); + } + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 6272ce45efb..b3bd1923d4e 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2013,6 +2013,13 @@ vec![ deprecation: None, module: "reference", }, + Lint { + name: "ref_option_ref", + group: "style", + desc: "use `Option<&T>` instead of `&Option<&T>`", + deprecation: None, + module: "ref_option_ref", + }, Lint { name: "repeat_once", group: "complexity", diff --git a/tests/ui/ref_option_ref.rs b/tests/ui/ref_option_ref.rs new file mode 100644 index 00000000000..7f05990c0a0 --- /dev/null +++ b/tests/ui/ref_option_ref.rs @@ -0,0 +1,5 @@ +#![warn(clippy::ref_option_ref)] + +fn main() { + let x: &Option<&u32> = &None; +} diff --git a/tests/ui/ref_option_ref.stderr b/tests/ui/ref_option_ref.stderr new file mode 100644 index 00000000000..90bcaef7570 --- /dev/null +++ b/tests/ui/ref_option_ref.stderr @@ -0,0 +1,10 @@ +error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> + --> $DIR/ref_option_ref.rs:4:12 + | +LL | let x: &Option<&u32> = &None; + | ^^^^^^^^^^^^^ help: try: `Option<&u32>` + | + = note: `-D clippy::ref-option-ref` implied by `-D warnings` + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From 469b2fc7811c14fa645dceadfb976fb4f4ef248a Mon Sep 17 00:00:00 2001 From: dvermd <315743+dvermd@users.noreply.github.com> Date: Tue, 13 Oct 2020 22:13:54 +0200 Subject: clippy_lint: Add 'ref_option_ref' move to check_ty and add type alias test --- clippy_lints/src/ref_option_ref.rs | 16 ++++++++-------- tests/ui/ref_option_ref.rs | 4 ++++ tests/ui/ref_option_ref.stderr | 20 ++++++++++++++++---- 3 files changed, 28 insertions(+), 12 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs index 40ac2b3f4f1..4b5bc93d8a1 100644 --- a/clippy_lints/src/ref_option_ref.rs +++ b/clippy_lints/src/ref_option_ref.rs @@ -33,11 +33,8 @@ declare_clippy_lint! { declare_lint_pass!(RefOptionRef => [REF_OPTION_REF]); impl<'tcx> LateLintPass<'tcx> for RefOptionRef { - fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) { - - if let Some(ref ty) = local.ty { - self.check_ref_option_ref(cx, ty); - } + fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) { + self.check_ref_option_ref(cx, ty); } } @@ -46,8 +43,11 @@ impl RefOptionRef { if_chain! { if let TyKind::Rptr(_, ref mut_ty) = ty.kind; if mut_ty.mutbl == Mutability::Not; - if let TyKind::Path(ref qpath) = &mut_ty.ty.kind ; - if let Some(def_id) = cx.typeck_results().qpath_res(qpath, ty.hir_id).opt_def_id(); + if let TyKind::Path(ref qpath) = &mut_ty.ty.kind; + let last = last_path_segment(qpath); + if let Some(res) = last.res; + if let Some(def_id) = res.opt_def_id(); + if match_def_path(cx, def_id, &paths::OPTION); if let Some(ref params) = last_path_segment(qpath).args ; if !params.parenthesized; @@ -70,4 +70,4 @@ impl RefOptionRef { } } } -} \ No newline at end of file +} diff --git a/tests/ui/ref_option_ref.rs b/tests/ui/ref_option_ref.rs index 7f05990c0a0..24db42efb80 100644 --- a/tests/ui/ref_option_ref.rs +++ b/tests/ui/ref_option_ref.rs @@ -1,5 +1,9 @@ +#![allow(unused)] #![warn(clippy::ref_option_ref)] +type OptRefU32<'a> = &'a Option<&'a u32>; +type OptRef<'a, T> = &'a Option<&'a T>; + fn main() { let x: &Option<&u32> = &None; } diff --git a/tests/ui/ref_option_ref.stderr b/tests/ui/ref_option_ref.stderr index 90bcaef7570..26f4065d311 100644 --- a/tests/ui/ref_option_ref.stderr +++ b/tests/ui/ref_option_ref.stderr @@ -1,10 +1,22 @@ error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> - --> $DIR/ref_option_ref.rs:4:12 + --> $DIR/ref_option_ref.rs:4:22 | -LL | let x: &Option<&u32> = &None; - | ^^^^^^^^^^^^^ help: try: `Option<&u32>` +LL | type OptRefU32<'a> = &'a Option<&'a u32>; + | ^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a u32>` | = note: `-D clippy::ref-option-ref` implied by `-D warnings` -error: aborting due to previous error +error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> + --> $DIR/ref_option_ref.rs:5:22 + | +LL | type OptRef<'a, T> = &'a Option<&'a T>; + | ^^^^^^^^^^^^^^^^^ help: try: `Option<&'a T>` + +error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> + --> $DIR/ref_option_ref.rs:8:12 + | +LL | let x: &Option<&u32> = &None; + | ^^^^^^^^^^^^^ help: try: `Option<&u32>` + +error: aborting due to 3 previous errors -- cgit 1.4.1-3-g733a5 From bdd76a9d1c4140624d758e5b8727869db3f9207c Mon Sep 17 00:00:00 2001 From: dvermd <315743+dvermd@users.noreply.github.com> Date: Tue, 13 Oct 2020 22:47:46 +0200 Subject: clippy_lint: Allow 'ref_option_ref' for 'option_if_let_else' --- tests/ui/option_if_let_else.fixed | 1 + tests/ui/option_if_let_else.rs | 1 + tests/ui/option_if_let_else.stderr | 24 ++++++++++++------------ 3 files changed, 14 insertions(+), 12 deletions(-) (limited to 'tests') diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed index a7fb00a2705..47e7460fa7a 100644 --- a/tests/ui/option_if_let_else.fixed +++ b/tests/ui/option_if_let_else.fixed @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::option_if_let_else)] #![allow(clippy::redundant_closure)] +#![allow(clippy::ref_option_ref)] 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 895fd86321f..e2f8dec3b93 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::option_if_let_else)] #![allow(clippy::redundant_closure)] +#![allow(clippy::ref_option_ref)] 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 b69fe767682..7aab068800a 100644 --- a/tests/ui/option_if_let_else.stderr +++ b/tests/ui/option_if_let_else.stderr @@ -1,5 +1,5 @@ error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:6:5 + --> $DIR/option_if_let_else.rs:7:5 | LL | / if let Some(x) = string { LL | | (true, x) @@ -11,7 +11,7 @@ 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:16:12 + --> $DIR/option_if_let_else.rs:17:12 | LL | } else if let Some(x) = string { | ____________^ @@ -22,19 +22,19 @@ LL | | } | |_____^ help: try: `{ string.map_or(Some((false, "")), |x| Some((true, x))) }` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:24:13 + --> $DIR/option_if_let_else.rs: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:25: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:26:13 + --> $DIR/option_if_let_else.rs:27:13 | LL | let _ = if let Some(s) = &mut num { | _____________^ @@ -54,13 +54,13 @@ LL | }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:32:13 + --> $DIR/option_if_let_else.rs: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:33:13 + --> $DIR/option_if_let_else.rs:34:13 | LL | let _ = if let Some(mut s) = num { | _____________^ @@ -80,7 +80,7 @@ LL | }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:39:13 + --> $DIR/option_if_let_else.rs:40:13 | LL | let _ = if let Some(ref mut s) = num { | _____________^ @@ -100,7 +100,7 @@ LL | }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:48:5 + --> $DIR/option_if_let_else.rs:49:5 | LL | / if let Some(x) = arg { LL | | let y = x * x; @@ -119,7 +119,7 @@ LL | }) | error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:61:13 + --> $DIR/option_if_let_else.rs:62:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -131,7 +131,7 @@ LL | | }; | |_____^ help: try: `arg.map_or_else(|| side_effect(), |x| x)` error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:70:13 + --> $DIR/option_if_let_else.rs:71:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -154,7 +154,7 @@ LL | }, |x| x * x * x * x); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:99: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)` -- cgit 1.4.1-3-g733a5 From 2270ff4d160fc65de4401a0984d2957c6d7a8186 Mon Sep 17 00:00:00 2001 From: dvermd <315743+dvermd@users.noreply.github.com> Date: Thu, 15 Oct 2020 21:56:56 +0200 Subject: clippy_lint: Add test cases --- tests/ui/ref_option_ref.rs | 37 ++++++++++++++++++++++-- tests/ui/ref_option_ref.stderr | 64 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 91 insertions(+), 10 deletions(-) (limited to 'tests') diff --git a/tests/ui/ref_option_ref.rs b/tests/ui/ref_option_ref.rs index 24db42efb80..3c4def272f7 100644 --- a/tests/ui/ref_option_ref.rs +++ b/tests/ui/ref_option_ref.rs @@ -1,8 +1,41 @@ #![allow(unused)] #![warn(clippy::ref_option_ref)] -type OptRefU32<'a> = &'a Option<&'a u32>; -type OptRef<'a, T> = &'a Option<&'a T>; +static THRESHOLD: i32 = 10; +static REF_THRESHOLD: &Option<&i32> = &Some(&THRESHOLD); +const CONST_THRESHOLD: &i32 = &10; +const REF_CONST: &Option<&i32> = &Some(&CONST_THRESHOLD); + +type RefOptRefU32<'a> = &'a Option<&'a u32>; +type RefOptRef<'a, T> = &'a Option<&'a T>; + +fn foo(data: &Option<&u32>) {} + +fn bar(data: &u32) -> &Option<&u32> { + &None +} + +struct StructRef<'a> { + data: &'a Option<&'a u32>, +} + +struct StructTupleRef<'a>(u32, &'a Option<&'a u32>); + +enum EnumRef<'a> { + Variant1(u32), + Variant2(&'a Option<&'a u32>), +} + +trait RefOptTrait { + type A; + fn foo(&self, _: Self::A); +} + +impl RefOptTrait for u32 { + type A = &'static Option<&'static Self>; + + fn foo(&self, _: Self::A) {} +} fn main() { let x: &Option<&u32> = &None; diff --git a/tests/ui/ref_option_ref.stderr b/tests/ui/ref_option_ref.stderr index 26f4065d311..16c1de19bb6 100644 --- a/tests/ui/ref_option_ref.stderr +++ b/tests/ui/ref_option_ref.stderr @@ -1,22 +1,70 @@ error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> - --> $DIR/ref_option_ref.rs:4:22 + --> $DIR/ref_option_ref.rs:5:23 | -LL | type OptRefU32<'a> = &'a Option<&'a u32>; - | ^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a u32>` +LL | static REF_THRESHOLD: &Option<&i32> = &Some(&THRESHOLD); + | ^^^^^^^^^^^^^ help: try: `Option<&i32>` | = note: `-D clippy::ref-option-ref` implied by `-D warnings` error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> - --> $DIR/ref_option_ref.rs:5:22 + --> $DIR/ref_option_ref.rs:7:18 | -LL | type OptRef<'a, T> = &'a Option<&'a T>; - | ^^^^^^^^^^^^^^^^^ help: try: `Option<&'a T>` +LL | const REF_CONST: &Option<&i32> = &Some(&CONST_THRESHOLD); + | ^^^^^^^^^^^^^ help: try: `Option<&i32>` error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> - --> $DIR/ref_option_ref.rs:8:12 + --> $DIR/ref_option_ref.rs:9:25 + | +LL | type RefOptRefU32<'a> = &'a Option<&'a u32>; + | ^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a u32>` + +error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> + --> $DIR/ref_option_ref.rs:10:25 + | +LL | type RefOptRef<'a, T> = &'a Option<&'a T>; + | ^^^^^^^^^^^^^^^^^ help: try: `Option<&'a T>` + +error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> + --> $DIR/ref_option_ref.rs:12:14 + | +LL | fn foo(data: &Option<&u32>) {} + | ^^^^^^^^^^^^^ help: try: `Option<&u32>` + +error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> + --> $DIR/ref_option_ref.rs:14:23 + | +LL | fn bar(data: &u32) -> &Option<&u32> { + | ^^^^^^^^^^^^^ help: try: `Option<&u32>` + +error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> + --> $DIR/ref_option_ref.rs:19:11 + | +LL | data: &'a Option<&'a u32>, + | ^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a u32>` + +error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> + --> $DIR/ref_option_ref.rs:22:32 + | +LL | struct StructTupleRef<'a>(u32, &'a Option<&'a u32>); + | ^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a u32>` + +error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> + --> $DIR/ref_option_ref.rs:26:14 + | +LL | Variant2(&'a Option<&'a u32>), + | ^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a u32>` + +error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> + --> $DIR/ref_option_ref.rs:35:14 + | +LL | type A = &'static Option<&'static Self>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'static Self>` + +error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> + --> $DIR/ref_option_ref.rs:41:12 | LL | let x: &Option<&u32> = &None; | ^^^^^^^^^^^^^ help: try: `Option<&u32>` -error: aborting due to 3 previous errors +error: aborting due to 11 previous errors -- cgit 1.4.1-3-g733a5 From 6212950ceb9add443db7450f2e4790fbb2fbddd7 Mon Sep 17 00:00:00 2001 From: dvermd <315743+dvermd@users.noreply.github.com> Date: Mon, 26 Oct 2020 22:50:13 +0100 Subject: Explain why 'run-rustfix' is not defined --- tests/ui/ref_option_ref.rs | 5 +++++ tests/ui/ref_option_ref.stderr | 22 +++++++++++----------- 2 files changed, 16 insertions(+), 11 deletions(-) (limited to 'tests') diff --git a/tests/ui/ref_option_ref.rs b/tests/ui/ref_option_ref.rs index 3c4def272f7..b2c275d68af 100644 --- a/tests/ui/ref_option_ref.rs +++ b/tests/ui/ref_option_ref.rs @@ -1,6 +1,11 @@ #![allow(unused)] #![warn(clippy::ref_option_ref)] +// This lint is not tagged as run-rustfix because automatically +// changing the type of a variable would also means changing +// all usages of this variable to match and This is not handled +// by this lint. + static THRESHOLD: i32 = 10; static REF_THRESHOLD: &Option<&i32> = &Some(&THRESHOLD); const CONST_THRESHOLD: &i32 = &10; diff --git a/tests/ui/ref_option_ref.stderr b/tests/ui/ref_option_ref.stderr index 16c1de19bb6..10f19661268 100644 --- a/tests/ui/ref_option_ref.stderr +++ b/tests/ui/ref_option_ref.stderr @@ -1,5 +1,5 @@ error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> - --> $DIR/ref_option_ref.rs:5:23 + --> $DIR/ref_option_ref.rs:10:23 | LL | static REF_THRESHOLD: &Option<&i32> = &Some(&THRESHOLD); | ^^^^^^^^^^^^^ help: try: `Option<&i32>` @@ -7,61 +7,61 @@ LL | static REF_THRESHOLD: &Option<&i32> = &Some(&THRESHOLD); = note: `-D clippy::ref-option-ref` implied by `-D warnings` error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> - --> $DIR/ref_option_ref.rs:7:18 + --> $DIR/ref_option_ref.rs:12:18 | LL | const REF_CONST: &Option<&i32> = &Some(&CONST_THRESHOLD); | ^^^^^^^^^^^^^ help: try: `Option<&i32>` error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> - --> $DIR/ref_option_ref.rs:9:25 + --> $DIR/ref_option_ref.rs:14:25 | LL | type RefOptRefU32<'a> = &'a Option<&'a u32>; | ^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a u32>` error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> - --> $DIR/ref_option_ref.rs:10:25 + --> $DIR/ref_option_ref.rs:15:25 | LL | type RefOptRef<'a, T> = &'a Option<&'a T>; | ^^^^^^^^^^^^^^^^^ help: try: `Option<&'a T>` error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> - --> $DIR/ref_option_ref.rs:12:14 + --> $DIR/ref_option_ref.rs:17:14 | LL | fn foo(data: &Option<&u32>) {} | ^^^^^^^^^^^^^ help: try: `Option<&u32>` error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> - --> $DIR/ref_option_ref.rs:14:23 + --> $DIR/ref_option_ref.rs:19:23 | LL | fn bar(data: &u32) -> &Option<&u32> { | ^^^^^^^^^^^^^ help: try: `Option<&u32>` error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> - --> $DIR/ref_option_ref.rs:19:11 + --> $DIR/ref_option_ref.rs:24:11 | LL | data: &'a Option<&'a u32>, | ^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a u32>` error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> - --> $DIR/ref_option_ref.rs:22:32 + --> $DIR/ref_option_ref.rs:27:32 | LL | struct StructTupleRef<'a>(u32, &'a Option<&'a u32>); | ^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a u32>` error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> - --> $DIR/ref_option_ref.rs:26:14 + --> $DIR/ref_option_ref.rs:31:14 | LL | Variant2(&'a Option<&'a u32>), | ^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a u32>` error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> - --> $DIR/ref_option_ref.rs:35:14 + --> $DIR/ref_option_ref.rs:40:14 | LL | type A = &'static Option<&'static Self>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'static Self>` error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> - --> $DIR/ref_option_ref.rs:41:12 + --> $DIR/ref_option_ref.rs:46:12 | LL | let x: &Option<&u32> = &None; | ^^^^^^^^^^^^^ help: try: `Option<&u32>` -- cgit 1.4.1-3-g733a5 From e568a328f9eb528653351643b1482ccecada1480 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Wed, 21 Oct 2020 09:42:00 +1300 Subject: fix the error-causing suggestion of 'borrowed_box' fix the error-causing suggestion of 'borrowed_box', which missed parentheses and was ambiguous. --- clippy_lints/src/types.rs | 44 ++++++++++++++++++++++++++++++++-------- tests/ui/borrow_box.rs | 16 +++++++++++++++ tests/ui/borrow_box.stderr | 50 ++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 98 insertions(+), 12 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 6a33aaaaab2..45f3bc3ea85 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -10,9 +10,9 @@ use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::{ - BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericParamKind, HirId, ImplItem, - ImplItemKind, Item, ItemKind, Lifetime, Lit, Local, MatchSource, MutTy, Mutability, Node, QPath, Stmt, StmtKind, - TraitFn, TraitItem, TraitItemKind, TyKind, UnOp, + BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericBounds, GenericParamKind, HirId, + ImplItem, ImplItemKind, Item, ItemKind, Lifetime, Lit, Local, MatchSource, MutTy, Mutability, Node, QPath, Stmt, + StmtKind, SyntheticTyParamKind, TraitFn, TraitItem, TraitItemKind, TyKind, UnOp, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; @@ -678,17 +678,30 @@ impl Types { // details. return; } + + // When trait objects or opaque types have lifetime or auto-trait bounds, + // we need to add parentheses to avoid a syntax error due to its ambiguity. + // Originally reported as the issue #3128. + let inner_snippet = snippet(cx, inner.span, ".."); + let suggestion = match &inner.kind { + TyKind::TraitObject(bounds, lt_bound) if bounds.len() > 1 || !lt_bound.is_elided() => { + format!("&{}({})", ltopt, &inner_snippet) + }, + TyKind::Path(qpath) + if get_bounds_if_impl_trait(cx, qpath, inner.hir_id) + .map_or(false, |bounds| bounds.len() > 1) => + { + format!("&{}({})", ltopt, &inner_snippet) + }, + _ => format!("&{}{}", ltopt, &inner_snippet), + }; span_lint_and_sugg( cx, BORROWED_BOX, hir_ty.span, "you seem to be trying to use `&Box`. Consider using just `&T`", "try", - format!( - "&{}{}", - ltopt, - &snippet(cx, inner.span, "..") - ), + suggestion, // To make this `MachineApplicable`, at least one needs to check if it isn't a trait item // because the trait impls of it will break otherwise; // and there may be other cases that result in invalid code. @@ -721,6 +734,21 @@ fn is_any_trait(t: &hir::Ty<'_>) -> bool { false } +fn get_bounds_if_impl_trait<'tcx>(cx: &LateContext<'tcx>, qpath: &QPath<'_>, id: HirId) -> Option> { + if_chain! { + if let Some(did) = qpath_res(cx, qpath, id).opt_def_id(); + if let Some(node) = cx.tcx.hir().get_if_local(did); + if let Node::GenericParam(generic_param) = node; + if let GenericParamKind::Type { synthetic, .. } = generic_param.kind; + if synthetic == Some(SyntheticTyParamKind::ImplTrait); + then { + Some(generic_param.bounds) + } else { + None + } + } +} + declare_clippy_lint! { /// **What it does:** Checks for binding a unit value. /// diff --git a/tests/ui/borrow_box.rs b/tests/ui/borrow_box.rs index 1901de46ca8..b606f773cfb 100644 --- a/tests/ui/borrow_box.rs +++ b/tests/ui/borrow_box.rs @@ -3,6 +3,8 @@ #![allow(unused_variables)] #![allow(dead_code)] +use std::fmt::Display; + pub fn test1(foo: &mut Box) { // Although this function could be changed to "&mut bool", // avoiding the Box, mutable references to boxes are not @@ -89,6 +91,20 @@ pub fn test13(boxed_slice: &mut Box<[i32]>) { *boxed_slice = data.into_boxed_slice(); } +// The suggestion should include proper parentheses to avoid a syntax error. +pub fn test14(_display: &Box) {} +pub fn test15(_display: &Box) {} +pub fn test16<'a>(_display: &'a Box) {} + +pub fn test17(_display: &Box) {} +pub fn test18(_display: &Box) {} +pub fn test19<'a>(_display: &'a Box) {} + +// This exists only to check what happens when parentheses are already present. +// Even though the current implementation doesn't put extra parentheses, +// it's fine that unnecessary parentheses appear in the future for some reason. +pub fn test20(_display: &Box<(dyn Display + Send)>) {} + fn main() { test1(&mut Box::new(false)); test2(); diff --git a/tests/ui/borrow_box.stderr b/tests/ui/borrow_box.stderr index b5db691f89f..3eac32815be 100644 --- a/tests/ui/borrow_box.stderr +++ b/tests/ui/borrow_box.stderr @@ -1,5 +1,5 @@ error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:19:14 + --> $DIR/borrow_box.rs:21:14 | LL | let foo: &Box; | ^^^^^^^^^^ help: try: `&bool` @@ -11,16 +11,58 @@ LL | #![deny(clippy::borrowed_box)] | ^^^^^^^^^^^^^^^^^^^^ error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:23:10 + --> $DIR/borrow_box.rs:25:10 | LL | foo: &'a Box, | ^^^^^^^^^^^^^ help: try: `&'a bool` error: you seem to be trying to use `&Box`. Consider using just `&T` - --> $DIR/borrow_box.rs:27:17 + --> $DIR/borrow_box.rs:29:17 | LL | fn test4(a: &Box); | ^^^^^^^^^^ help: try: `&bool` -error: aborting due to 3 previous errors +error: you seem to be trying to use `&Box`. Consider using just `&T` + --> $DIR/borrow_box.rs:95:25 + | +LL | pub fn test14(_display: &Box) {} + | ^^^^^^^^^^^^^^^^^ help: try: `&dyn Display` + +error: you seem to be trying to use `&Box`. Consider using just `&T` + --> $DIR/borrow_box.rs:96:25 + | +LL | pub fn test15(_display: &Box) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(dyn Display + Send)` + +error: you seem to be trying to use `&Box`. Consider using just `&T` + --> $DIR/borrow_box.rs:97:29 + | +LL | pub fn test16<'a>(_display: &'a Box) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&'a (dyn Display + 'a)` + +error: you seem to be trying to use `&Box`. Consider using just `&T` + --> $DIR/borrow_box.rs:99:25 + | +LL | pub fn test17(_display: &Box) {} + | ^^^^^^^^^^^^^^^^^^ help: try: `&impl Display` + +error: you seem to be trying to use `&Box`. Consider using just `&T` + --> $DIR/borrow_box.rs:100:25 + | +LL | pub fn test18(_display: &Box) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(impl Display + Send)` + +error: you seem to be trying to use `&Box`. Consider using just `&T` + --> $DIR/borrow_box.rs:101:29 + | +LL | pub fn test19<'a>(_display: &'a Box) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&'a (impl Display + 'a)` + +error: you seem to be trying to use `&Box`. Consider using just `&T` + --> $DIR/borrow_box.rs:106:25 + | +LL | pub fn test20(_display: &Box<(dyn Display + Send)>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(dyn Display + Send)` + +error: aborting due to 10 previous errors -- cgit 1.4.1-3-g733a5 From 111b9023dad65721300a39c3cf337f6bfb96d5d3 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Tue, 27 Oct 2020 01:47:40 +0100 Subject: add manual_ok_or lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 4 ++ clippy_lints/src/manual_ok_or.rs | 97 ++++++++++++++++++++++++++++++++++++++++ src/lintlist/mod.rs | 7 +++ tests/ui/manual_ok_or.fixed | 40 +++++++++++++++++ tests/ui/manual_ok_or.rs | 44 ++++++++++++++++++ tests/ui/manual_ok_or.stderr | 41 +++++++++++++++++ 7 files changed, 234 insertions(+) create mode 100644 clippy_lints/src/manual_ok_or.rs create mode 100644 tests/ui/manual_ok_or.fixed create mode 100644 tests/ui/manual_ok_or.rs create mode 100644 tests/ui/manual_ok_or.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 25f3b5da198..cd884e0665d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1795,6 +1795,7 @@ Released 2018-09-13 [`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn [`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy [`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive +[`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or [`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains [`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic [`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3be8bc0e36d..9b6f8e73454 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -233,6 +233,7 @@ mod macro_use; mod main_recursion; mod manual_async_fn; mod manual_non_exhaustive; +mod manual_ok_or; mod manual_strip; mod manual_unwrap_or; mod map_clone; @@ -645,6 +646,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &main_recursion::MAIN_RECURSION, &manual_async_fn::MANUAL_ASYNC_FN, &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, @@ -1140,6 +1142,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box unwrap_in_result::UnwrapInResult); store.register_late_pass(|| box self_assignment::SelfAssignment); store.register_late_pass(|| box manual_unwrap_or::ManualUnwrapOr); + store.register_late_pass(|| box manual_ok_or::ManualOkOr); store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); store.register_late_pass(|| box manual_strip::ManualStrip); @@ -1229,6 +1232,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::EXPLICIT_INTO_ITER_LOOP), LintId::of(&loops::EXPLICIT_ITER_LOOP), LintId::of(¯o_use::MACRO_USE_IMPORTS), + LintId::of(&manual_ok_or::MANUAL_OK_OR), LintId::of(&map_err_ignore::MAP_ERR_IGNORE), LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS), LintId::of(&matches::MATCH_BOOL), diff --git a/clippy_lints/src/manual_ok_or.rs b/clippy_lints/src/manual_ok_or.rs new file mode 100644 index 00000000000..38298eb813a --- /dev/null +++ b/clippy_lints/src/manual_ok_or.rs @@ -0,0 +1,97 @@ +use crate::utils; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{def, Expr, ExprKind, PatKind, QPath}; +use rustc_lint::LintContext; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** + /// Finds patterns that reimplement `Option::ok_or`. + /// + /// **Why is this bad?** + /// Concise code helps focusing on behavior instead of boilerplate. + /// + /// **Known problems:** None. + /// + /// **Examples:** + /// ```rust + /// let foo: Option = None; + /// foo.map_or(Err("error"), |v| Ok(v)); + /// + /// let foo: Option = None; + /// foo.map_or(Err("error"), |v| Ok(v)); + /// ``` + /// + /// Use instead: + /// ```rust + /// let foo: Option = None; + /// foo.ok_or("error"); + /// ``` + pub MANUAL_OK_OR, + pedantic, + "finds patterns that can be encoded more concisely with `Option::ok_or`" +} + +declare_lint_pass!(ManualOkOr => [MANUAL_OK_OR]); + +impl LateLintPass<'_> for ManualOkOr { + fn check_expr(&mut self, cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'tcx>) { + if in_external_macro(cx.sess(), scrutinee.span) { + return; + } + + if_chain! { + if let ExprKind::MethodCall(method_segment, _, args, _) = scrutinee.kind; + if method_segment.ident.name == sym!(map_or); + if args.len() == 3; + let method_receiver = &args[0]; + let ty = cx.typeck_results().expr_ty(method_receiver); + if utils::is_type_diagnostic_item(cx, ty, sym!(option_type)); + let or_expr = &args[1]; + if is_ok_wrapping(cx, &args[2]); + if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, &[ref err_arg]) = or_expr.kind; + if utils::match_qpath(err_path, &utils::paths::RESULT_ERR); + if let Some(method_receiver_snippet) = utils::snippet_opt(cx, method_receiver.span); + if let Some(err_arg_snippet) = utils::snippet_opt(cx, err_arg.span); + if let Some(indent) = utils::indent_of(cx, scrutinee.span); + then { + let reindented_err_arg_snippet = + utils::reindent_multiline(err_arg_snippet.into(), true, Some(indent + 4)); + utils::span_lint_and_sugg( + cx, + MANUAL_OK_OR, + scrutinee.span, + "this pattern reimplements `Option::ok_or`", + "replace with", + format!( + "{}.ok_or({})", + method_receiver_snippet, + reindented_err_arg_snippet + ), + Applicability::MachineApplicable, + ); + } + } + } +} + +fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool { + if let ExprKind::Path(ref qpath) = map_expr.kind { + if utils::match_qpath(qpath, &utils::paths::RESULT_OK) { + return true; + } + } + if_chain! { + if let ExprKind::Closure(_, _, body_id, ..) = map_expr.kind; + let body = cx.tcx.hir().body(body_id); + if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind; + if let ExprKind::Call(Expr { kind: ExprKind::Path(ok_path), .. }, &[ref ok_arg]) = body.value.kind; + if utils::match_qpath(ok_path, &utils::paths::RESULT_OK); + if let ExprKind::Path(QPath::Resolved(_, ok_arg_path)) = ok_arg.kind; + if let def::Res::Local(ok_arg_path_id) = ok_arg_path.res; + then { param_id == ok_arg_path_id } else { false } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 6272ce45efb..f438da00720 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1173,6 +1173,13 @@ vec![ deprecation: None, module: "manual_non_exhaustive", }, + Lint { + name: "manual_ok_or", + group: "pedantic", + desc: "finds patterns that can be encoded more concisely with `Option::ok_or`", + deprecation: None, + module: "manual_ok_or", + }, Lint { name: "manual_range_contains", group: "style", diff --git a/tests/ui/manual_ok_or.fixed b/tests/ui/manual_ok_or.fixed new file mode 100644 index 00000000000..b42e94bd727 --- /dev/null +++ b/tests/ui/manual_ok_or.fixed @@ -0,0 +1,40 @@ +// run-rustfix +#![warn(clippy::manual_ok_or)] +#![allow(clippy::blacklisted_name)] +#![allow(clippy::redundant_closure)] +#![allow(dead_code)] +#![allow(unused_must_use)] + +fn main() { + // basic case + let foo: Option = None; + foo.ok_or("error"); + + // eta expansion case + foo.ok_or("error"); + + // turbo fish syntax + None::.ok_or("error"); + + // multiline case + #[rustfmt::skip] + foo.ok_or(&format!( + "{}{}{}{}{}{}{}", + "Alice", "Bob", "Sarah", "Marc", "Sandra", "Eric", "Jenifer")); + + // not applicable, closure isn't direct `Ok` wrapping + foo.map_or(Err("error"), |v| Ok(v + 1)); + + // not applicable, or side isn't `Result::Err` + foo.map_or(Ok::(1), |v| Ok(v)); + + // not applicatble, expr is not a `Result` value + foo.map_or(42, |v| v); + + // TODO patterns not covered yet + match foo { + Some(v) => Ok(v), + None => Err("error"), + }; + foo.map_or_else(|| Err("error"), |v| Ok(v)); +} diff --git a/tests/ui/manual_ok_or.rs b/tests/ui/manual_ok_or.rs new file mode 100644 index 00000000000..e5a6056fbf5 --- /dev/null +++ b/tests/ui/manual_ok_or.rs @@ -0,0 +1,44 @@ +// run-rustfix +#![warn(clippy::manual_ok_or)] +#![allow(clippy::blacklisted_name)] +#![allow(clippy::redundant_closure)] +#![allow(dead_code)] +#![allow(unused_must_use)] + +fn main() { + // basic case + let foo: Option = None; + foo.map_or(Err("error"), |v| Ok(v)); + + // eta expansion case + foo.map_or(Err("error"), Ok); + + // turbo fish syntax + None::.map_or(Err("error"), |v| Ok(v)); + + // multiline case + #[rustfmt::skip] + foo.map_or(Err::( + &format!( + "{}{}{}{}{}{}{}", + "Alice", "Bob", "Sarah", "Marc", "Sandra", "Eric", "Jenifer") + ), + |v| Ok(v), + ); + + // not applicable, closure isn't direct `Ok` wrapping + foo.map_or(Err("error"), |v| Ok(v + 1)); + + // not applicable, or side isn't `Result::Err` + foo.map_or(Ok::(1), |v| Ok(v)); + + // not applicatble, expr is not a `Result` value + foo.map_or(42, |v| v); + + // TODO patterns not covered yet + match foo { + Some(v) => Ok(v), + None => Err("error"), + }; + foo.map_or_else(|| Err("error"), |v| Ok(v)); +} diff --git a/tests/ui/manual_ok_or.stderr b/tests/ui/manual_ok_or.stderr new file mode 100644 index 00000000000..8ea10ac5436 --- /dev/null +++ b/tests/ui/manual_ok_or.stderr @@ -0,0 +1,41 @@ +error: this pattern reimplements `Option::ok_or` + --> $DIR/manual_ok_or.rs:11:5 + | +LL | foo.map_or(Err("error"), |v| Ok(v)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `foo.ok_or("error")` + | + = note: `-D clippy::manual-ok-or` implied by `-D warnings` + +error: this pattern reimplements `Option::ok_or` + --> $DIR/manual_ok_or.rs:14:5 + | +LL | foo.map_or(Err("error"), Ok); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `foo.ok_or("error")` + +error: this pattern reimplements `Option::ok_or` + --> $DIR/manual_ok_or.rs:17:5 + | +LL | None::.map_or(Err("error"), |v| Ok(v)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `None::.ok_or("error")` + +error: this pattern reimplements `Option::ok_or` + --> $DIR/manual_ok_or.rs:21:5 + | +LL | / foo.map_or(Err::( +LL | | &format!( +LL | | "{}{}{}{}{}{}{}", +LL | | "Alice", "Bob", "Sarah", "Marc", "Sandra", "Eric", "Jenifer") +LL | | ), +LL | | |v| Ok(v), +LL | | ); + | |_____^ + | +help: replace with + | +LL | foo.ok_or(&format!( +LL | "{}{}{}{}{}{}{}", +LL | "Alice", "Bob", "Sarah", "Marc", "Sandra", "Eric", "Jenifer")); + | + +error: aborting due to 4 previous errors + -- cgit 1.4.1-3-g733a5 From 66d56fefc5b7ce22d2db65ee9dc1a5f9f6bf2f09 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Tue, 27 Oct 2020 07:42:13 +0200 Subject: Add `invalid_paths` internal lint --- clippy_lints/src/lib.rs | 3 ++ clippy_lints/src/utils/internal_lints.rs | 79 ++++++++++++++++++++++++++++++++ clippy_lints/src/utils/mod.rs | 25 ++++++++++ tests/ui/invalid_paths.rs | 23 ++++++++++ tests/ui/invalid_paths.stderr | 16 +++++++ 5 files changed, 146 insertions(+) create mode 100644 tests/ui/invalid_paths.rs create mode 100644 tests/ui/invalid_paths.stderr (limited to 'tests') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3be8bc0e36d..7c8cb90fe1c 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -892,6 +892,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS, &utils::internal_lints::COMPILER_LINT_FUNCTIONS, &utils::internal_lints::DEFAULT_LINT, + &utils::internal_lints::INVALID_PATHS, &utils::internal_lints::LINT_WITHOUT_LINT_PASS, &utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM, &utils::internal_lints::OUTER_EXPN_EXPN_DATA, @@ -919,6 +920,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default()); store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass); + store.register_late_pass(|| box utils::internal_lints::InvalidPaths); store.register_late_pass(|| box utils::inspector::DeepCodeInspector); store.register_late_pass(|| box utils::author::Author); let vec_box_size_threshold = conf.vec_box_size_threshold; @@ -1280,6 +1282,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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::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), diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index bfe426a25eb..6ca72d895c8 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -1,3 +1,4 @@ +use crate::consts::{constant_simple, Constant}; use crate::utils::{ is_expn_of, match_def_path, match_qpath, match_type, method_calls, path_to_res, paths, qpath_res, run_lints, snippet, span_lint, span_lint_and_help, span_lint_and_sugg, SpanlessEq, @@ -14,9 +15,11 @@ use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; use rustc_hir::{Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Node, Path, StmtKind, Ty, TyKind}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_middle::hir::map::Map; +use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::{Span, Spanned}; use rustc_span::symbol::{Symbol, SymbolStr}; +use rustc_typeck::hir_ty_to_ty; use std::borrow::{Borrow, Cow}; @@ -229,6 +232,21 @@ declare_clippy_lint! { "using `utils::match_type()` instead of `utils::is_type_diagnostic_item()`" } +declare_clippy_lint! { + /// **What it does:** + /// Checks the paths module for invalid paths. + /// + /// **Why is this bad?** + /// It indicates a bug in the code. + /// + /// **Known problems:** None. + /// + /// **Example:** None. + pub INVALID_PATHS, + internal, + "invalid path" +} + declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]); impl EarlyLintPass for ClippyLintsInternal { @@ -761,3 +779,64 @@ fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option, path: &[&str]) -> bool { + if path_to_res(cx, path).is_some() { + return true; + } + + // Some implementations can't be found by `path_to_res`, particularly inherent + // implementations of native types. Check lang items. + let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect(); + let lang_items = cx.tcx.lang_items(); + for lang_item in lang_items.items() { + if let Some(def_id) = lang_item { + let lang_item_path = cx.get_def_path(*def_id); + if path_syms.starts_with(&lang_item_path) { + if let [item] = &path_syms[lang_item_path.len()..] { + for child in cx.tcx.item_children(*def_id) { + if child.ident.name == *item { + return true; + } + } + } + } + } + } + + false +} + +declare_lint_pass!(InvalidPaths => [INVALID_PATHS]); + +impl<'tcx> LateLintPass<'tcx> for InvalidPaths { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + let local_def_id = &cx.tcx.parent_module(item.hir_id); + let mod_name = &cx.tcx.item_name(local_def_id.to_def_id()); + if_chain! { + if mod_name.as_str() == "paths"; + if let hir::ItemKind::Const(ty, body_id) = item.kind; + let ty = hir_ty_to_ty(cx.tcx, ty); + if let ty::Array(el_ty, _) = &ty.kind(); + if let ty::Ref(_, el_ty, _) = &el_ty.kind(); + if el_ty.is_str(); + let body = cx.tcx.hir().body(body_id); + let typeck_results = cx.tcx.typeck_body(body_id); + if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, &body.value); + let path: Vec<&str> = path.iter().map(|x| { + if let Constant::Str(s) = x { + s.as_str() + } else { + // We checked the type of the constant above + unreachable!() + } + }).collect(); + if !check_path(cx, &path[..]); + then { + span_lint(cx, CLIPPY_LINTS_INTERNAL, item.span, "invalid path"); + } + } + } +} diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 8297b9d128d..a1ecca0961a 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -268,6 +268,7 @@ pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Option { krate: *krate, index: CRATE_DEF_INDEX, }; + let mut current_item = None; let mut items = cx.tcx.item_children(krate); let mut path_it = path.iter().skip(1).peekable(); @@ -277,6 +278,12 @@ pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Option { None => return None, }; + // `get_def_path` seems to generate these empty segments for extern blocks. + // We can just ignore them. + if segment.is_empty() { + continue; + } + let result = SmallVec::<[_; 8]>::new(); for item in mem::replace(&mut items, cx.tcx.arena.alloc_slice(&result)).iter() { if item.ident.name.as_str() == *segment { @@ -284,10 +291,28 @@ pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Option { return Some(item.res); } + current_item = Some(item); items = cx.tcx.item_children(item.res.def_id()); break; } } + + // The segment isn't a child_item. + // Try to find it under an inherent impl. + if_chain! { + if path_it.peek().is_none(); + if let Some(current_item) = current_item; + let item_def_id = current_item.res.def_id(); + if cx.tcx.def_kind(item_def_id) == DefKind::Struct; + then { + // Bad `find_map` suggestion. See #4193. + #[allow(clippy::find_map)] + return cx.tcx.inherent_impls(item_def_id).iter() + .flat_map(|&impl_def_id| cx.tcx.item_children(impl_def_id)) + .find(|item| item.ident.name.as_str() == *segment) + .map(|item| item.res); + } + } } } else { None diff --git a/tests/ui/invalid_paths.rs b/tests/ui/invalid_paths.rs new file mode 100644 index 00000000000..01e28ae5e9d --- /dev/null +++ b/tests/ui/invalid_paths.rs @@ -0,0 +1,23 @@ +#![warn(clippy::internal)] + +mod paths { + // Good path + pub const ANY_TRAIT: [&str; 3] = ["std", "any", "Any"]; + + // Path to method on inherent impl of a primitive type + pub const F32_EPSILON: [&str; 4] = ["core", "f32", "", "EPSILON"]; + + // Path to method on inherent impl + pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"]; + + // Path with empty segment + pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"]; + + // Path with bad crate + pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"]; + + // Path with bad module + pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"]; +} + +fn main() {} diff --git a/tests/ui/invalid_paths.stderr b/tests/ui/invalid_paths.stderr new file mode 100644 index 00000000000..bd69d661b71 --- /dev/null +++ b/tests/ui/invalid_paths.stderr @@ -0,0 +1,16 @@ +error: invalid path + --> $DIR/invalid_paths.rs:17:5 + | +LL | pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::clippy-lints-internal` implied by `-D warnings` + +error: invalid path + --> $DIR/invalid_paths.rs:20:5 + | +LL | pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From 2b7dd313685279fce06f86bd739d798792135331 Mon Sep 17 00:00:00 2001 From: Aleksei Latyshev Date: Sat, 24 Oct 2020 18:06:07 +0300 Subject: improve MATCH_LIKE_MATCHES_MACRO lint - add tests - refactor match_same_arms lint - prioritize match_expr_like_matches_macro over match_same_arms --- clippy_lints/src/copies.rs | 215 +------------------------ clippy_lints/src/lib.rs | 4 +- clippy_lints/src/matches.rs | 217 +++++++++++++++++++++++--- clippy_lints/src/utils/mod.rs | 38 +++++ src/lintlist/mod.rs | 2 +- tests/ui/match_expr_like_matches_macro.fixed | 68 +++++++- tests/ui/match_expr_like_matches_macro.rs | 76 ++++++++- tests/ui/match_expr_like_matches_macro.stderr | 24 ++- tests/ui/match_same_arms2.rs | 16 ++ tests/ui/match_same_arms2.stderr | 15 +- 10 files changed, 439 insertions(+), 236 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 6c969c3ead0..46ce92ea6d7 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -1,13 +1,8 @@ -use crate::utils::{eq_expr_value, in_macro, SpanlessEq, SpanlessHash}; -use crate::utils::{get_parent_expr, higher, if_sequence, snippet, span_lint_and_note, span_lint_and_then}; -use rustc_data_structures::fx::FxHashMap; -use rustc_hir::{Arm, Block, Expr, ExprKind, MatchSource, Pat, PatKind}; +use crate::utils::{eq_expr_value, in_macro, search_same, SpanlessEq, SpanlessHash}; +use crate::utils::{get_parent_expr, higher, if_sequence, span_lint_and_note}; +use rustc_hir::{Block, Expr}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{Ty, TyS}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::symbol::Symbol; -use std::collections::hash_map::Entry; -use std::hash::BuildHasherDefault; declare_clippy_lint! { /// **What it does:** Checks for consecutive `if`s with the same condition. @@ -108,48 +103,7 @@ declare_clippy_lint! { "`if` with the same `then` and `else` blocks" } -declare_clippy_lint! { - /// **What it does:** Checks for `match` with identical arm bodies. - /// - /// **Why is this bad?** This is probably a copy & paste error. If arm bodies - /// are the same on purpose, you can factor them - /// [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns). - /// - /// **Known problems:** False positive possible with order dependent `match` - /// (see issue - /// [#860](https://github.com/rust-lang/rust-clippy/issues/860)). - /// - /// **Example:** - /// ```rust,ignore - /// match foo { - /// Bar => bar(), - /// Quz => quz(), - /// Baz => bar(), // <= oops - /// } - /// ``` - /// - /// This should probably be - /// ```rust,ignore - /// match foo { - /// Bar => bar(), - /// Quz => quz(), - /// Baz => baz(), // <= fixed - /// } - /// ``` - /// - /// or if the original code was not a typo: - /// ```rust,ignore - /// match foo { - /// Bar | Baz => bar(), // <= shows the intent better - /// Quz => quz(), - /// } - /// ``` - pub MATCH_SAME_ARMS, - pedantic, - "`match` with identical arm bodies" -} - -declare_lint_pass!(CopyAndPaste => [IFS_SAME_COND, SAME_FUNCTIONS_IN_IF_CONDITION, IF_SAME_THEN_ELSE, MATCH_SAME_ARMS]); +declare_lint_pass!(CopyAndPaste => [IFS_SAME_COND, SAME_FUNCTIONS_IN_IF_CONDITION, IF_SAME_THEN_ELSE]); impl<'tcx> LateLintPass<'tcx> for CopyAndPaste { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { @@ -167,7 +121,6 @@ impl<'tcx> LateLintPass<'tcx> for CopyAndPaste { lint_same_then_else(cx, &blocks); lint_same_cond(cx, &conds); lint_same_fns_in_if_cond(cx, &conds); - lint_match_arms(cx, expr); } } } @@ -243,122 +196,6 @@ fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { } } -/// Implementation of `MATCH_SAME_ARMS`. -fn lint_match_arms<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) { - fn same_bindings<'tcx>(lhs: &FxHashMap>, rhs: &FxHashMap>) -> bool { - lhs.len() == rhs.len() - && lhs - .iter() - .all(|(name, l_ty)| rhs.get(name).map_or(false, |r_ty| TyS::same_type(l_ty, r_ty))) - } - - if let ExprKind::Match(_, ref arms, MatchSource::Normal) = expr.kind { - let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 { - let mut h = SpanlessHash::new(cx); - h.hash_expr(&arm.body); - h.finish() - }; - - let eq = |&(lindex, lhs): &(usize, &Arm<'_>), &(rindex, rhs): &(usize, &Arm<'_>)| -> bool { - let min_index = usize::min(lindex, rindex); - let max_index = usize::max(lindex, rindex); - - // Arms with a guard are ignored, those can’t always be merged together - // This is also the case for arms in-between each there is an arm with a guard - (min_index..=max_index).all(|index| arms[index].guard.is_none()) && - SpanlessEq::new(cx).eq_expr(&lhs.body, &rhs.body) && - // all patterns should have the same bindings - same_bindings(&bindings(cx, &lhs.pat), &bindings(cx, &rhs.pat)) - }; - - let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect(); - for (&(_, i), &(_, j)) in search_same(&indexed_arms, hash, eq) { - span_lint_and_then( - cx, - MATCH_SAME_ARMS, - j.body.span, - "this `match` has identical arm bodies", - |diag| { - diag.span_note(i.body.span, "same as this"); - - // Note: this does not use `span_suggestion` on purpose: - // there is no clean way - // to remove the other arm. Building a span and suggest to replace it to "" - // makes an even more confusing error message. Also in order not to make up a - // span for the whole pattern, the suggestion is only shown when there is only - // one pattern. The user should know about `|` if they are already using it… - - let lhs = snippet(cx, i.pat.span, ""); - let rhs = snippet(cx, j.pat.span, ""); - - if let PatKind::Wild = j.pat.kind { - // if the last arm is _, then i could be integrated into _ - // note that i.pat cannot be _, because that would mean that we're - // hiding all the subsequent arms, and rust won't compile - diag.span_note( - i.body.span, - &format!( - "`{}` has the same arm body as the `_` wildcard, consider removing it", - lhs - ), - ); - } else { - diag.span_help(i.pat.span, &format!("consider refactoring into `{} | {}`", lhs, rhs)); - } - }, - ); - } - } -} - -/// Returns the list of bindings in a pattern. -fn bindings<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>) -> FxHashMap> { - fn bindings_impl<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, map: &mut FxHashMap>) { - match pat.kind { - PatKind::Box(ref pat) | PatKind::Ref(ref pat, _) => bindings_impl(cx, pat, map), - PatKind::TupleStruct(_, pats, _) => { - for pat in pats { - bindings_impl(cx, pat, map); - } - }, - PatKind::Binding(.., ident, ref as_pat) => { - if let Entry::Vacant(v) = map.entry(ident.name) { - v.insert(cx.typeck_results().pat_ty(pat)); - } - if let Some(ref as_pat) = *as_pat { - bindings_impl(cx, as_pat, map); - } - }, - PatKind::Or(fields) | PatKind::Tuple(fields, _) => { - for pat in fields { - bindings_impl(cx, pat, map); - } - }, - PatKind::Struct(_, fields, _) => { - for pat in fields { - bindings_impl(cx, &pat.pat, map); - } - }, - PatKind::Slice(lhs, ref mid, rhs) => { - for pat in lhs { - bindings_impl(cx, pat, map); - } - if let Some(ref mid) = *mid { - bindings_impl(cx, mid, map); - } - for pat in rhs { - bindings_impl(cx, pat, map); - } - }, - PatKind::Lit(..) | PatKind::Range(..) | PatKind::Wild | PatKind::Path(..) => (), - } - } - - let mut result = FxHashMap::default(); - bindings_impl(cx, pat, &mut result); - result -} - fn search_same_sequenced(exprs: &[T], eq: Eq) -> Option<(&T, &T)> where Eq: Fn(&T, &T) -> bool, @@ -370,47 +207,3 @@ where } None } - -fn search_common_cases<'a, T, Eq>(exprs: &'a [T], eq: &Eq) -> Option<(&'a T, &'a T)> -where - Eq: Fn(&T, &T) -> bool, -{ - if exprs.len() == 2 && eq(&exprs[0], &exprs[1]) { - Some((&exprs[0], &exprs[1])) - } else { - None - } -} - -fn search_same(exprs: &[T], hash: Hash, eq: Eq) -> Vec<(&T, &T)> -where - Hash: Fn(&T) -> u64, - Eq: Fn(&T, &T) -> bool, -{ - if let Some(expr) = search_common_cases(&exprs, &eq) { - return vec![expr]; - } - - let mut match_expr_list: Vec<(&T, &T)> = Vec::new(); - - let mut map: FxHashMap<_, Vec<&_>> = - FxHashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default()); - - for expr in exprs { - match map.entry(hash(expr)) { - Entry::Occupied(mut o) => { - for o in o.get() { - if eq(o, expr) { - match_expr_list.push((o, expr)); - } - } - o.get_mut().push(expr); - }, - Entry::Vacant(v) => { - v.insert(vec![expr]); - }, - } - } - - match_expr_list -} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 7c8cb90fe1c..459fdd70443 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -528,7 +528,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &comparison_chain::COMPARISON_CHAIN, &copies::IFS_SAME_COND, &copies::IF_SAME_THEN_ELSE, - &copies::MATCH_SAME_ARMS, &copies::SAME_FUNCTIONS_IN_IF_CONDITION, ©_iterator::COPY_ITERATOR, &create_dir::CREATE_DIR, @@ -659,6 +658,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &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, @@ -1204,7 +1204,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&attrs::INLINE_ALWAYS), LintId::of(&bit_mask::VERBOSE_BIT_MASK), LintId::of(&checked_conversions::CHECKED_CONVERSIONS), - LintId::of(&copies::MATCH_SAME_ARMS), LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION), LintId::of(©_iterator::COPY_ITERATOR), LintId::of(&default_trait_access::DEFAULT_TRAIT_ACCESS), @@ -1234,6 +1233,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&map_err_ignore::MAP_ERR_IGNORE), 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), diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index d93433c607f..4bdfca1a292 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -1,5 +1,4 @@ use crate::consts::{constant, miri_to_const, Constant}; -use crate::utils::paths; use crate::utils::sugg::Sugg; use crate::utils::usage::is_unused; use crate::utils::{ @@ -8,8 +7,10 @@ use crate::utils::{ snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, }; +use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash}; use if_chain::if_chain; use rustc_ast::ast::LitKind; +use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::def::CtorKind; use rustc_hir::{ @@ -18,10 +19,12 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Ty, TyS}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::{Span, Spanned}; +use rustc_span::Symbol; use std::cmp::Ordering; +use std::collections::hash_map::Entry; use std::collections::Bound; declare_clippy_lint! { @@ -475,6 +478,47 @@ declare_clippy_lint! { "a match that could be written with the matches! macro" } +declare_clippy_lint! { + /// **What it does:** Checks for `match` with identical arm bodies. + /// + /// **Why is this bad?** This is probably a copy & paste error. If arm bodies + /// are the same on purpose, you can factor them + /// [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns). + /// + /// **Known problems:** False positive possible with order dependent `match` + /// (see issue + /// [#860](https://github.com/rust-lang/rust-clippy/issues/860)). + /// + /// **Example:** + /// ```rust,ignore + /// match foo { + /// Bar => bar(), + /// Quz => quz(), + /// Baz => bar(), // <= oops + /// } + /// ``` + /// + /// This should probably be + /// ```rust,ignore + /// match foo { + /// Bar => bar(), + /// Quz => quz(), + /// Baz => baz(), // <= fixed + /// } + /// ``` + /// + /// or if the original code was not a typo: + /// ```rust,ignore + /// match foo { + /// Bar | Baz => bar(), // <= shows the intent better + /// Quz => quz(), + /// } + /// ``` + pub MATCH_SAME_ARMS, + pedantic, + "`match` with identical arm bodies" +} + #[derive(Default)] pub struct Matches { infallible_destructuring_match_linted: bool, @@ -495,7 +539,8 @@ impl_lint_pass!(Matches => [ INFALLIBLE_DESTRUCTURING_MATCH, REST_PAT_IN_FULLY_BOUND_STRUCTS, REDUNDANT_PATTERN_MATCHING, - MATCH_LIKE_MATCHES_MACRO + MATCH_LIKE_MATCHES_MACRO, + MATCH_SAME_ARMS, ]); impl<'tcx> LateLintPass<'tcx> for Matches { @@ -505,7 +550,9 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } redundant_pattern_match::check(cx, expr); - check_match_like_matches(cx, expr); + if !check_match_like_matches(cx, expr) { + lint_match_arms(cx, expr); + } if let ExprKind::Match(ref ex, ref arms, MatchSource::Normal) = expr.kind { check_single_match(cx, ex, arms, expr); @@ -1063,32 +1110,47 @@ fn check_wild_in_or_pats(cx: &LateContext<'_>, arms: &[Arm<'_>]) { } /// Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!` -fn check_match_like_matches<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { +fn check_match_like_matches<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { if let ExprKind::Match(ex, arms, ref match_source) = &expr.kind { match match_source { MatchSource::Normal => find_matches_sugg(cx, ex, arms, expr, false), MatchSource::IfLetDesugar { .. } => find_matches_sugg(cx, ex, arms, expr, true), - _ => return, + _ => false, } + } else { + false } } /// Lint a `match` or desugared `if let` for replacement by `matches!` -fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>, desugared: bool) { +fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>, desugared: bool) -> bool { if_chain! { - if arms.len() == 2; + if arms.len() >= 2; if cx.typeck_results().expr_ty(expr).is_bool(); - if is_wild(&arms[1].pat); - if let Some(first) = find_bool_lit(&arms[0].body.kind, desugared); - if let Some(second) = find_bool_lit(&arms[1].body.kind, desugared); - if first != second; + if let Some((b1_arm, b0_arms)) = arms.split_last(); + if let Some(b0) = find_bool_lit(&b0_arms[0].body.kind, desugared); + if let Some(b1) = find_bool_lit(&b1_arm.body.kind, desugared); + if is_wild(&b1_arm.pat); + if b0 != b1; + let if_guard = &b0_arms[0].guard; + if if_guard.is_none() || b0_arms.len() == 1; + if b0_arms[1..].iter() + .all(|arm| { + find_bool_lit(&arm.body.kind, desugared).map_or(false, |b| b == b0) && + arm.guard.is_none() + }); then { let mut applicability = Applicability::MachineApplicable; - - let pat_and_guard = if let Some(Guard::If(g)) = arms[0].guard { - format!("{} if {}", snippet_with_applicability(cx, arms[0].pat.span, "..", &mut applicability), snippet_with_applicability(cx, g.span, "..", &mut applicability)) + let pat = { + use itertools::Itertools as _; + b0_arms.iter() + .map(|arm| snippet_with_applicability(cx, arm.pat.span, "..", &mut applicability)) + .join(" | ") + }; + let pat_and_guard = if let Some(Guard::If(g)) = if_guard { + format!("{} if {}", pat, snippet_with_applicability(cx, g.span, "..", &mut applicability)) } else { - format!("{}", snippet_with_applicability(cx, arms[0].pat.span, "..", &mut applicability)) + pat }; span_lint_and_sugg( cx, @@ -1098,12 +1160,15 @@ fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr "try this", format!( "{}matches!({}, {})", - if first { "" } else { "!" }, + if b0 { "" } else { "!" }, snippet_with_applicability(cx, ex.span, "..", &mut applicability), pat_and_guard, ), applicability, - ) + ); + true + } else { + false } } } @@ -1657,3 +1722,119 @@ fn test_overlapping() { ],) ); } + +/// Implementation of `MATCH_SAME_ARMS`. +fn lint_match_arms<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) { + fn same_bindings<'tcx>(lhs: &FxHashMap>, rhs: &FxHashMap>) -> bool { + lhs.len() == rhs.len() + && lhs + .iter() + .all(|(name, l_ty)| rhs.get(name).map_or(false, |r_ty| TyS::same_type(l_ty, r_ty))) + } + + if let ExprKind::Match(_, ref arms, MatchSource::Normal) = expr.kind { + let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 { + let mut h = SpanlessHash::new(cx); + h.hash_expr(&arm.body); + h.finish() + }; + + let eq = |&(lindex, lhs): &(usize, &Arm<'_>), &(rindex, rhs): &(usize, &Arm<'_>)| -> bool { + let min_index = usize::min(lindex, rindex); + let max_index = usize::max(lindex, rindex); + + // Arms with a guard are ignored, those can’t always be merged together + // This is also the case for arms in-between each there is an arm with a guard + (min_index..=max_index).all(|index| arms[index].guard.is_none()) && + SpanlessEq::new(cx).eq_expr(&lhs.body, &rhs.body) && + // all patterns should have the same bindings + same_bindings(&bindings(cx, &lhs.pat), &bindings(cx, &rhs.pat)) + }; + + let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect(); + for (&(_, i), &(_, j)) in search_same(&indexed_arms, hash, eq) { + span_lint_and_then( + cx, + MATCH_SAME_ARMS, + j.body.span, + "this `match` has identical arm bodies", + |diag| { + diag.span_note(i.body.span, "same as this"); + + // Note: this does not use `span_suggestion` on purpose: + // there is no clean way + // to remove the other arm. Building a span and suggest to replace it to "" + // makes an even more confusing error message. Also in order not to make up a + // span for the whole pattern, the suggestion is only shown when there is only + // one pattern. The user should know about `|` if they are already using it… + + let lhs = snippet(cx, i.pat.span, ""); + let rhs = snippet(cx, j.pat.span, ""); + + if let PatKind::Wild = j.pat.kind { + // if the last arm is _, then i could be integrated into _ + // note that i.pat cannot be _, because that would mean that we're + // hiding all the subsequent arms, and rust won't compile + diag.span_note( + i.body.span, + &format!( + "`{}` has the same arm body as the `_` wildcard, consider removing it", + lhs + ), + ); + } else { + diag.span_help(i.pat.span, &format!("consider refactoring into `{} | {}`", lhs, rhs)); + } + }, + ); + } + } +} + +/// Returns the list of bindings in a pattern. +fn bindings<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>) -> FxHashMap> { + fn bindings_impl<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, map: &mut FxHashMap>) { + match pat.kind { + PatKind::Box(ref pat) | PatKind::Ref(ref pat, _) => bindings_impl(cx, pat, map), + PatKind::TupleStruct(_, pats, _) => { + for pat in pats { + bindings_impl(cx, pat, map); + } + }, + PatKind::Binding(.., ident, ref as_pat) => { + if let Entry::Vacant(v) = map.entry(ident.name) { + v.insert(cx.typeck_results().pat_ty(pat)); + } + if let Some(ref as_pat) = *as_pat { + bindings_impl(cx, as_pat, map); + } + }, + PatKind::Or(fields) | PatKind::Tuple(fields, _) => { + for pat in fields { + bindings_impl(cx, pat, map); + } + }, + PatKind::Struct(_, fields, _) => { + for pat in fields { + bindings_impl(cx, &pat.pat, map); + } + }, + PatKind::Slice(lhs, ref mid, rhs) => { + for pat in lhs { + bindings_impl(cx, pat, map); + } + if let Some(ref mid) = *mid { + bindings_impl(cx, mid, map); + } + for pat in rhs { + bindings_impl(cx, pat, map); + } + }, + PatKind::Lit(..) | PatKind::Range(..) | PatKind::Wild | PatKind::Path(..) => (), + } + } + + let mut result = FxHashMap::default(); + bindings_impl(cx, pat, &mut result); + result +} diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index a1ecca0961a..0a8a4a5f9ae 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -27,11 +27,14 @@ pub use self::diagnostics::*; pub use self::hir_utils::{both, eq_expr_value, over, SpanlessEq, SpanlessHash}; use std::borrow::Cow; +use std::collections::hash_map::Entry; +use std::hash::BuildHasherDefault; use std::mem; use if_chain::if_chain; use rustc_ast::ast::{self, Attribute, LitKind}; use rustc_attr as attr; +use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; @@ -1465,6 +1468,41 @@ pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option(exprs: &[T], hash: Hash, eq: Eq) -> Vec<(&T, &T)> +where + Hash: Fn(&T) -> u64, + Eq: Fn(&T, &T) -> bool, +{ + if exprs.len() == 2 && eq(&exprs[0], &exprs[1]) { + return vec![(&exprs[0], &exprs[1])]; + } + + let mut match_expr_list: Vec<(&T, &T)> = Vec::new(); + + let mut map: FxHashMap<_, Vec<&_>> = + FxHashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default()); + + for expr in exprs { + match map.entry(hash(expr)) { + Entry::Occupied(mut o) => { + for o in o.get() { + if eq(o, expr) { + match_expr_list.push((o, expr)); + } + } + o.get_mut().push(expr); + }, + Entry::Vacant(v) => { + v.insert(vec![expr]); + }, + } + } + + match_expr_list +} + #[macro_export] macro_rules! unwrap_cargo_metadata { ($cx: ident, $lint: ident, $deps: expr) => {{ diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 6272ce45efb..aba436e5c02 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1304,7 +1304,7 @@ vec![ group: "pedantic", desc: "`match` with identical arm bodies", deprecation: None, - module: "copies", + module: "matches", }, Lint { name: "match_single_binding", diff --git a/tests/ui/match_expr_like_matches_macro.fixed b/tests/ui/match_expr_like_matches_macro.fixed index f3e19092480..7f4ebf56673 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)] +#![allow(unreachable_patterns, dead_code)] fn main() { let x = Some(5); @@ -33,4 +33,70 @@ fn main() { _ => true, None => false, }; + + enum E { + A(u32), + B(i32), + C, + D, + }; + let x = E::A(2); + { + // lint + let _ans = matches!(x, E::A(_) | E::B(_)); + } + { + // lint + let _ans = !matches!(x, E::B(_) | E::C); + } + { + // no lint + let _ans = match x { + E::A(_) => false, + E::B(_) => false, + E::C => true, + _ => true, + }; + } + { + // no lint + let _ans = match x { + E::A(_) => true, + E::B(_) => false, + E::C => false, + _ => true, + }; + } + { + // no lint + let _ans = match x { + E::A(a) if a < 10 => false, + E::B(a) if a < 10 => false, + _ => true, + }; + } + { + // no lint + let _ans = match x { + E::A(_) => false, + E::B(a) if a < 10 => false, + _ => true, + }; + } + { + // no lint + let _ans = match x { + E::A(a) => a == 10, + E::B(_) => false, + _ => true, + }; + } + { + // no lint + let _ans = match x { + E::A(_) => false, + E::B(_) => true, + _ => false, + }; + } } diff --git a/tests/ui/match_expr_like_matches_macro.rs b/tests/ui/match_expr_like_matches_macro.rs index fbae7c18b92..aee56dd4a5e 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)] +#![allow(unreachable_patterns, dead_code)] fn main() { let x = Some(5); @@ -45,4 +45,78 @@ fn main() { _ => true, None => false, }; + + enum E { + A(u32), + B(i32), + C, + D, + }; + let x = E::A(2); + { + // lint + let _ans = match x { + E::A(_) => true, + E::B(_) => true, + _ => false, + }; + } + { + // lint + let _ans = match x { + E::B(_) => false, + E::C => false, + _ => true, + }; + } + { + // no lint + let _ans = match x { + E::A(_) => false, + E::B(_) => false, + E::C => true, + _ => true, + }; + } + { + // no lint + let _ans = match x { + E::A(_) => true, + E::B(_) => false, + E::C => false, + _ => true, + }; + } + { + // no lint + let _ans = match x { + E::A(a) if a < 10 => false, + E::B(a) if a < 10 => false, + _ => true, + }; + } + { + // no lint + let _ans = match x { + E::A(_) => false, + E::B(a) if a < 10 => false, + _ => true, + }; + } + { + // no lint + let _ans = match x { + E::A(a) => a == 10, + E::B(_) => false, + _ => true, + }; + } + { + // no lint + let _ans = match x { + E::A(_) => false, + E::B(_) => true, + _ => false, + }; + } } diff --git a/tests/ui/match_expr_like_matches_macro.stderr b/tests/ui/match_expr_like_matches_macro.stderr index 4668f8565a6..c52e41c7889 100644 --- a/tests/ui/match_expr_like_matches_macro.stderr +++ b/tests/ui/match_expr_like_matches_macro.stderr @@ -48,5 +48,27 @@ error: if let .. else expression looks like `matches!` macro LL | let _zzz = if let Some(5) = x { true } else { false }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `matches!(x, Some(5))` -error: aborting due to 5 previous errors +error: match expression looks like `matches!` macro + --> $DIR/match_expr_like_matches_macro.rs:58:20 + | +LL | let _ans = match x { + | ____________________^ +LL | | E::A(_) => true, +LL | | E::B(_) => true, +LL | | _ => false, +LL | | }; + | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))` + +error: match expression looks like `matches!` macro + --> $DIR/match_expr_like_matches_macro.rs:66:20 + | +LL | let _ans = match x { + | ____________________^ +LL | | E::B(_) => false, +LL | | E::C => false, +LL | | _ => true, +LL | | }; + | |_________^ help: try this: `!matches!(x, E::B(_) | E::C)` + +error: aborting due to 7 previous errors diff --git a/tests/ui/match_same_arms2.rs b/tests/ui/match_same_arms2.rs index e1401d2796a..06d91497242 100644 --- a/tests/ui/match_same_arms2.rs +++ b/tests/ui/match_same_arms2.rs @@ -119,6 +119,22 @@ fn match_same_arms() { unreachable!(); }, } + + match_expr_like_matches_macro_priority(); +} + +fn match_expr_like_matches_macro_priority() { + enum E { + A, + B, + C, + } + let x = E::A; + let _ans = match x { + E::A => false, + E::B => false, + _ => true, + }; } fn main() {} diff --git a/tests/ui/match_same_arms2.stderr b/tests/ui/match_same_arms2.stderr index 26c65f32ad7..fccaf805616 100644 --- a/tests/ui/match_same_arms2.stderr +++ b/tests/ui/match_same_arms2.stderr @@ -141,5 +141,18 @@ LL | Ok(3) => println!("ok"), | ^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 7 previous errors +error: match expression looks like `matches!` macro + --> $DIR/match_same_arms2.rs:133:16 + | +LL | let _ans = match x { + | ________________^ +LL | | E::A => false, +LL | | E::B => false, +LL | | _ => true, +LL | | }; + | |_____^ help: try this: `!matches!(x, E::A | E::B)` + | + = note: `-D clippy::match-like-matches-macro` implied by `-D warnings` + +error: aborting due to 8 previous errors -- cgit 1.4.1-3-g733a5 From 4c58860cbfe51049d48f4f31f76dcc18b39cfe98 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 27 Oct 2020 23:18:08 +0900 Subject: Fix suggestion to add unneeded space in `unused_unit` --- clippy_lints/src/unused_unit.rs | 11 +++++++ tests/ui/unused_unit.fixed | 31 ++++++++++++------- tests/ui/unused_unit.rs | 9 ++++++ tests/ui/unused_unit.stderr | 68 ++++++++++++++++++++++++++--------------- 4 files changed, 83 insertions(+), 36 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/unused_unit.rs b/clippy_lints/src/unused_unit.rs index 7548c6afa97..b1339c3d639 100644 --- a/clippy_lints/src/unused_unit.rs +++ b/clippy_lints/src/unused_unit.rs @@ -123,6 +123,17 @@ fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) { fn_source .rfind("->") .map_or((ty.span, Applicability::MaybeIncorrect), |rpos| { + let mut rpos = rpos; + let chars: Vec = fn_source.chars().collect(); + while rpos > 1 { + if let Some(c) = chars.get(rpos - 1) { + if c.is_whitespace() { + rpos -= 1; + continue; + } + } + break; + } ( #[allow(clippy::cast_possible_truncation)] ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), diff --git a/tests/ui/unused_unit.fixed b/tests/ui/unused_unit.fixed index 07f2791786d..7afc5361356 100644 --- a/tests/ui/unused_unit.fixed +++ b/tests/ui/unused_unit.fixed @@ -15,35 +15,35 @@ struct Unitter; impl Unitter { #[allow(clippy::no_effect)] - pub fn get_unit(&self, f: F, _g: G) - where G: Fn() { - let _y: &dyn Fn() = &f; + pub fn get_unit(&self, f: F, _g: G) + where G: Fn() { + let _y: &dyn Fn() = &f; (); // this should not lint, as it's not in return type position } } impl Into<()> for Unitter { #[rustfmt::skip] - fn into(self) { + fn into(self) { } } trait Trait { - fn redundant(&self, _f: F, _g: G, _h: H) + fn redundant(&self, _f: F, _g: G, _h: H) where - G: FnMut() , - H: Fn() ; + G: FnMut(), + H: Fn(); } impl Trait for Unitter { - fn redundant(&self, _f: F, _g: G, _h: H) + fn redundant(&self, _f: F, _g: G, _h: H) where - G: FnMut() , - H: Fn() {} + G: FnMut(), + H: Fn() {} } -fn return_unit() { } +fn return_unit() { } #[allow(clippy::needless_return)] #[allow(clippy::never_loop)] @@ -70,3 +70,12 @@ fn foo() { recv(rx) -> _x => () } } + +#[rustfmt::skip] +fn test(){} + +#[rustfmt::skip] +fn test2(){} + +#[rustfmt::skip] +fn test3(){} diff --git a/tests/ui/unused_unit.rs b/tests/ui/unused_unit.rs index e2c6afb020f..96cef1ed5a5 100644 --- a/tests/ui/unused_unit.rs +++ b/tests/ui/unused_unit.rs @@ -70,3 +70,12 @@ fn foo() { recv(rx) -> _x => () } } + +#[rustfmt::skip] +fn test()->(){} + +#[rustfmt::skip] +fn test2() ->(){} + +#[rustfmt::skip] +fn test3()-> (){} diff --git a/tests/ui/unused_unit.stderr b/tests/ui/unused_unit.stderr index 81e6738e6bf..c45634c2b6d 100644 --- a/tests/ui/unused_unit.stderr +++ b/tests/ui/unused_unit.stderr @@ -1,8 +1,8 @@ error: unneeded unit return type - --> $DIR/unused_unit.rs:18:29 + --> $DIR/unused_unit.rs:18:28 | LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` | note: the lint level is defined here --> $DIR/unused_unit.rs:12:9 @@ -11,28 +11,28 @@ LL | #![deny(clippy::unused_unit)] | ^^^^^^^^^^^^^^^^^^^ error: unneeded unit return type - --> $DIR/unused_unit.rs:19:19 + --> $DIR/unused_unit.rs:19:18 | LL | where G: Fn() -> () { - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:18:59 + --> $DIR/unused_unit.rs:18:58 | LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:20:27 + --> $DIR/unused_unit.rs:20:26 | LL | let _y: &dyn Fn() -> () = &f; - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:27:19 + --> $DIR/unused_unit.rs:27:18 | LL | fn into(self) -> () { - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit expression --> $DIR/unused_unit.rs:28:9 @@ -41,46 +41,46 @@ LL | () | ^^ help: remove the final `()` error: unneeded unit return type - --> $DIR/unused_unit.rs:33:30 + --> $DIR/unused_unit.rs:33:29 | LL | fn redundant (), G, H>(&self, _f: F, _g: G, _h: H) - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:35:20 + --> $DIR/unused_unit.rs:35:19 | LL | G: FnMut() -> (), - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:36:17 + --> $DIR/unused_unit.rs:36:16 | LL | H: Fn() -> (); - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:40:30 + --> $DIR/unused_unit.rs:40:29 | LL | fn redundant (), G, H>(&self, _f: F, _g: G, _h: H) - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:42:20 + --> $DIR/unused_unit.rs:42:19 | LL | G: FnMut() -> (), - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:43:17 + --> $DIR/unused_unit.rs:43:16 | LL | H: Fn() -> () {} - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:46:18 + --> $DIR/unused_unit.rs:46:17 | LL | fn return_unit() -> () { () } - | ^^^^^ help: remove the `-> ()` + | ^^^^^^ help: remove the `-> ()` error: unneeded unit expression --> $DIR/unused_unit.rs:46:26 @@ -100,5 +100,23 @@ error: unneeded `()` LL | return(); | ^^ help: remove the `()` -error: aborting due to 16 previous errors +error: unneeded unit return type + --> $DIR/unused_unit.rs:75:10 + | +LL | fn test()->(){} + | ^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:78:11 + | +LL | fn test2() ->(){} + | ^^^^^ help: remove the `-> ()` + +error: unneeded unit return type + --> $DIR/unused_unit.rs:81:11 + | +LL | fn test3()-> (){} + | ^^^^^ help: remove the `-> ()` + +error: aborting due to 19 previous errors -- cgit 1.4.1-3-g733a5 From a50d9e7af641d52e066b111014993b042b0be5a7 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 28 Oct 2020 22:32:13 +0100 Subject: Deprecate temporary_cstr_as_ptr --- CHANGELOG.md | 1 + clippy_lints/src/deprecated_lints.rs | 9 +++++++++ clippy_lints/src/lib.rs | 4 ++++ tests/ui/deprecated.rs | 1 + tests/ui/deprecated.stderr | 8 +++++++- 5 files changed, 22 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index d10aefa2042..aa47930b520 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1962,6 +1962,7 @@ Released 2018-09-13 [`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting [`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments [`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment +[`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr [`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 [`todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index c5884361dff..461c6e31d3e 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -172,3 +172,12 @@ declare_deprecated_lint! { pub DROP_BOUNDS, "this lint has been uplifted to rustc and is now called `drop_bounds`" } + +declare_deprecated_lint! { + /// **What it does:** Nothing. This lint has been deprecated. + /// + /// **Deprecation reason:** This lint has been uplifted to rustc and is now called + /// `temporary_cstring_as_ptr`. + pub TEMPORARY_CSTRING_AS_PTR, + "this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr`" +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index fb949345945..2d374969846 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -488,6 +488,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: "clippy::drop_bounds", "this lint has been uplifted to rustc and is now called `drop_bounds`", ); + store.register_removed( + "clippy::temporary_cstring_as_ptr", + "this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr`", + ); // end deprecated lints, do not remove this comment, it’s used in `update_lints` // begin register lints, do not remove this comment, it’s used in `update_lints` diff --git a/tests/ui/deprecated.rs b/tests/ui/deprecated.rs index 9e32fe36ece..56755596c97 100644 --- a/tests/ui/deprecated.rs +++ b/tests/ui/deprecated.rs @@ -9,5 +9,6 @@ #[warn(clippy::unused_label)] #[warn(clippy::regex_macro)] #[warn(clippy::drop_bounds)] +#[warn(clippy::temporary_cstring_as_ptr)] fn main() {} diff --git a/tests/ui/deprecated.stderr b/tests/ui/deprecated.stderr index d3400a7be09..37b726fc00f 100644 --- a/tests/ui/deprecated.stderr +++ b/tests/ui/deprecated.stderr @@ -66,11 +66,17 @@ error: lint `clippy::drop_bounds` has been removed: `this lint has been uplifted LL | #[warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ +error: lint `clippy::temporary_cstring_as_ptr` has been removed: `this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr`` + --> $DIR/deprecated.rs:12:8 + | +LL | #[warn(clippy::temporary_cstring_as_ptr)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: lint `clippy::str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon` --> $DIR/deprecated.rs:1:8 | LL | #[warn(clippy::str_to_string)] | ^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 12 previous errors +error: aborting due to 13 previous errors -- cgit 1.4.1-3-g733a5 From abd64d7c054ece769abff0cd90374789f2ecf639 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Wed, 28 Oct 2020 14:45:21 +0100 Subject: add a couple of ICE testcases Fixes #6250 Fixes #6251 Fixes #6252 Fixes #6255 Fixes #6256 --- tests/ui/crashes/ice-6250.rs | 16 ++++++++++++++ tests/ui/crashes/ice-6250.stderr | 27 +++++++++++++++++++++++ tests/ui/crashes/ice-6251.rs | 6 ++++++ tests/ui/crashes/ice-6251.stderr | 43 +++++++++++++++++++++++++++++++++++++ tests/ui/crashes/ice-6252.rs | 15 +++++++++++++ tests/ui/crashes/ice-6252.stderr | 46 ++++++++++++++++++++++++++++++++++++++++ tests/ui/crashes/ice-6254.rs | 15 +++++++++++++ tests/ui/crashes/ice-6254.stderr | 12 +++++++++++ tests/ui/crashes/ice-6255.rs | 15 +++++++++++++ tests/ui/crashes/ice-6255.stderr | 13 ++++++++++++ tests/ui/crashes/ice-6256.rs | 13 ++++++++++++ tests/ui/crashes/ice-6256.stderr | 18 ++++++++++++++++ 12 files changed, 239 insertions(+) create mode 100644 tests/ui/crashes/ice-6250.rs create mode 100644 tests/ui/crashes/ice-6250.stderr create mode 100644 tests/ui/crashes/ice-6251.rs create mode 100644 tests/ui/crashes/ice-6251.stderr create mode 100644 tests/ui/crashes/ice-6252.rs create mode 100644 tests/ui/crashes/ice-6252.stderr create mode 100644 tests/ui/crashes/ice-6254.rs create mode 100644 tests/ui/crashes/ice-6254.stderr create mode 100644 tests/ui/crashes/ice-6255.rs create mode 100644 tests/ui/crashes/ice-6255.stderr create mode 100644 tests/ui/crashes/ice-6256.rs create mode 100644 tests/ui/crashes/ice-6256.stderr (limited to 'tests') diff --git a/tests/ui/crashes/ice-6250.rs b/tests/ui/crashes/ice-6250.rs new file mode 100644 index 00000000000..c33580ff6ab --- /dev/null +++ b/tests/ui/crashes/ice-6250.rs @@ -0,0 +1,16 @@ +// originally from glacier/fixed/77218.rs +// ice while adjusting... + +pub struct Cache { + data: Vec, +} + +pub fn list_data(cache: &Cache, key: usize) { + for reference in vec![1, 2, 3] { + if + /* let */ + Some(reference) = cache.data.get(key) { + unimplemented!() + } + } +} diff --git a/tests/ui/crashes/ice-6250.stderr b/tests/ui/crashes/ice-6250.stderr new file mode 100644 index 00000000000..8241dcd8feb --- /dev/null +++ b/tests/ui/crashes/ice-6250.stderr @@ -0,0 +1,27 @@ +error[E0601]: `main` function not found in crate `ice_6250` + --> $DIR/ice-6250.rs:4:1 + | +LL | / pub struct Cache { +LL | | data: Vec, +LL | | } +LL | | +... | +LL | | } +LL | | } + | |_^ consider adding a `main` function to `$DIR/ice-6250.rs` + +error[E0308]: mismatched types + --> $DIR/ice-6250.rs:12:9 + | +LL | Some(reference) = cache.data.get(key) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `()` + | +help: you might have meant to use pattern matching + | +LL | let Some(reference) = cache.data.get(key) { + | ^^^ + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0308, E0601. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/crashes/ice-6251.rs b/tests/ui/crashes/ice-6251.rs new file mode 100644 index 00000000000..6aa779aaeb3 --- /dev/null +++ b/tests/ui/crashes/ice-6251.rs @@ -0,0 +1,6 @@ +// originally from glacier/fixed/77329.rs +// assertion failed: `(left == right) ; different DefIds + +fn bug() -> impl Iterator { + std::iter::empty() +} diff --git a/tests/ui/crashes/ice-6251.stderr b/tests/ui/crashes/ice-6251.stderr new file mode 100644 index 00000000000..9a7cf4b0919 --- /dev/null +++ b/tests/ui/crashes/ice-6251.stderr @@ -0,0 +1,43 @@ +error[E0601]: `main` function not found in crate `ice_6251` + --> $DIR/ice-6251.rs:4:1 + | +LL | / fn bug() -> impl Iterator { +LL | | std::iter::empty() +LL | | } + | |_^ consider adding a `main` function to `$DIR/ice-6251.rs` + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/ice-6251.rs:4:45 + | +LL | fn bug() -> impl Iterator { + | ^ doesn't have a size known at compile-time + | + = help: the trait `std::marker::Sized` is not implemented for `[u8]` + = help: unsized fn params are gated as an unstable feature +help: function arguments must have a statically known size, borrowed types always have a known size + | +LL | fn bug() -> impl Iterator { + | ^ + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/ice-6251.rs:4:54 + | +LL | fn bug() -> impl Iterator { + | ^ doesn't have a size known at compile-time + | + = help: the trait `std::marker::Sized` is not implemented for `[u8]` + = note: the return type of a function must have a statically known size + +error[E0308]: mismatched types + --> $DIR/ice-6251.rs:4:44 + | +LL | fn bug() -> impl Iterator { + | ^^^^^^^^^^^ expected `usize`, found closure + | + = note: expected type `usize` + found closure `[closure@$DIR/ice-6251.rs:4:44: 4:55]` + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0277, E0308, E0601. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/crashes/ice-6252.rs b/tests/ui/crashes/ice-6252.rs new file mode 100644 index 00000000000..2e3d9fd1e92 --- /dev/null +++ b/tests/ui/crashes/ice-6252.rs @@ -0,0 +1,15 @@ +// originally from glacier fixed/77919.rs +// encountered errors resolving bounds after type-checking + +trait TypeVal { + const VAL: T; +} +struct Five; +struct Multiply { + _n: PhantomData, +} +impl TypeVal for Multiply where N: TypeVal {} + +fn main() { + [1; >::VAL]; +} diff --git a/tests/ui/crashes/ice-6252.stderr b/tests/ui/crashes/ice-6252.stderr new file mode 100644 index 00000000000..440973e2439 --- /dev/null +++ b/tests/ui/crashes/ice-6252.stderr @@ -0,0 +1,46 @@ +error[E0412]: cannot find type `PhantomData` in this scope + --> $DIR/ice-6252.rs:9:9 + | +LL | _n: PhantomData, + | ^^^^^^^^^^^ not found in this scope + | +help: consider importing this struct + | +LL | use std::marker::PhantomData; + | + +error[E0412]: cannot find type `VAL` in this scope + --> $DIR/ice-6252.rs:11:63 + | +LL | impl TypeVal for Multiply where N: TypeVal {} + | - ^^^ not found in this scope + | | + | help: you might be missing a type parameter: `, VAL` + +error[E0046]: not all trait items implemented, missing: `VAL` + --> $DIR/ice-6252.rs:11:1 + | +LL | const VAL: T; + | ------------- `VAL` from trait +... +LL | impl TypeVal for Multiply where N: TypeVal {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `VAL` in implementation + +error: any use of this value will cause an error + --> $DIR/ice-6252.rs:5:5 + | +LL | const VAL: T; + | ^^^^^^^^^^^^^ no MIR body is available for DefId(0:5 ~ ice_6252[317d]::TypeVal::VAL) + | + = note: `#[deny(const_err)]` on by default + +error[E0080]: evaluation of constant value failed + --> $DIR/ice-6252.rs:14:9 + | +LL | [1; >::VAL]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0046, E0080, E0412. +For more information about an error, try `rustc --explain E0046`. diff --git a/tests/ui/crashes/ice-6254.rs b/tests/ui/crashes/ice-6254.rs new file mode 100644 index 00000000000..c19eca43884 --- /dev/null +++ b/tests/ui/crashes/ice-6254.rs @@ -0,0 +1,15 @@ +// originally from ./src/test/ui/pattern/usefulness/consts-opaque.rs +// panicked at 'assertion failed: rows.iter().all(|r| r.len() == v.len())', +// compiler/rustc_mir_build/src/thir/pattern/_match.rs:2030:5 + +#[derive(PartialEq)] +struct Foo(i32); +const FOO_REF_REF: &&Foo = &&Foo(42); + +fn main() { + // This used to cause an ICE (https://github.com/rust-lang/rust/issues/78071) + match FOO_REF_REF { + FOO_REF_REF => {}, + Foo(_) => {}, + } +} diff --git a/tests/ui/crashes/ice-6254.stderr b/tests/ui/crashes/ice-6254.stderr new file mode 100644 index 00000000000..95ebf23d818 --- /dev/null +++ b/tests/ui/crashes/ice-6254.stderr @@ -0,0 +1,12 @@ +error: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]` + --> $DIR/ice-6254.rs:12:9 + | +LL | FOO_REF_REF => {}, + | ^^^^^^^^^^^ + | + = note: `-D indirect-structural-match` implied by `-D warnings` + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #62411 + +error: aborting due to previous error + diff --git a/tests/ui/crashes/ice-6255.rs b/tests/ui/crashes/ice-6255.rs new file mode 100644 index 00000000000..bd4a81d98e2 --- /dev/null +++ b/tests/ui/crashes/ice-6255.rs @@ -0,0 +1,15 @@ +// originally from rustc ./src/test/ui/macros/issue-78325-inconsistent-resolution.rs +// inconsistent resolution for a macro + +macro_rules! define_other_core { + ( ) => { + extern crate std as core; + //~^ ERROR macro-expanded `extern crate` items cannot shadow names passed with `--extern` + }; +} + +fn main() { + core::panic!(); +} + +define_other_core!(); diff --git a/tests/ui/crashes/ice-6255.stderr b/tests/ui/crashes/ice-6255.stderr new file mode 100644 index 00000000000..d973ea1e23a --- /dev/null +++ b/tests/ui/crashes/ice-6255.stderr @@ -0,0 +1,13 @@ +error: macro-expanded `extern crate` items cannot shadow names passed with `--extern` + --> $DIR/ice-6255.rs:6:9 + | +LL | extern crate std as core; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | define_other_core!(); + | --------------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error + diff --git a/tests/ui/crashes/ice-6256.rs b/tests/ui/crashes/ice-6256.rs new file mode 100644 index 00000000000..6f60d45d68a --- /dev/null +++ b/tests/ui/crashes/ice-6256.rs @@ -0,0 +1,13 @@ +// originally from rustc ./src/test/ui/regions/issue-78262.rs +// ICE: to get the signature of a closure, use substs.as_closure().sig() not fn_sig() + +trait TT {} + +impl dyn TT { + fn func(&self) {} +} + +fn main() { + let f = |x: &dyn TT| x.func(); //[default]~ ERROR: mismatched types + //[nll]~^ ERROR: borrowed data escapes outside of closure +} diff --git a/tests/ui/crashes/ice-6256.stderr b/tests/ui/crashes/ice-6256.stderr new file mode 100644 index 00000000000..0e8353a418a --- /dev/null +++ b/tests/ui/crashes/ice-6256.stderr @@ -0,0 +1,18 @@ +error[E0308]: mismatched types + --> $DIR/ice-6256.rs:11:28 + | +LL | let f = |x: &dyn TT| x.func(); //[default]~ ERROR: mismatched types + | ^^^^ lifetime mismatch + | + = note: expected reference `&(dyn TT + 'static)` + found reference `&dyn TT` +note: the anonymous lifetime #1 defined on the body at 11:13... + --> $DIR/ice-6256.rs:11:13 + | +LL | let f = |x: &dyn TT| x.func(); //[default]~ ERROR: mismatched types + | ^^^^^^^^^^^^^^^^^^^^^ + = note: ...does not necessarily outlive the static lifetime + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. -- cgit 1.4.1-3-g733a5 From e97602e4825fd5a01834d7b0e218703dcc82c091 Mon Sep 17 00:00:00 2001 From: henil Date: Mon, 26 Oct 2020 19:28:22 +0530 Subject: Update existing arithmetic lint and add new tests related to it. --- clippy_lints/src/arithmetic.rs | 30 +++++++++-- tests/ui/integer_arithmetic.rs | 10 ++++ tests/ui/integer_arithmetic.stderr | 100 +++++++++++++++++++++++++++---------- 3 files changed, 112 insertions(+), 28 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/arithmetic.rs b/clippy_lints/src/arithmetic.rs index e84481f9b53..b1cdbab6de9 100644 --- a/clippy_lints/src/arithmetic.rs +++ b/clippy_lints/src/arithmetic.rs @@ -88,9 +88,33 @@ impl<'tcx> LateLintPass<'tcx> for Arithmetic { let (l_ty, r_ty) = (cx.typeck_results().expr_ty(l), cx.typeck_results().expr_ty(r)); if l_ty.peel_refs().is_integral() && r_ty.peel_refs().is_integral() { - span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected"); - self.expr_span = Some(expr.span); - } else if l_ty.peel_refs().is_floating_point() && r_ty.peel_refs().is_floating_point() { + match op.node { + hir::BinOpKind::Div | hir::BinOpKind::Rem => match &r.kind { + hir::ExprKind::Lit(lit) => { + if let rustc_ast::ast::LitKind::Int(0, _) = lit.node { + span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected"); + self.expr_span = Some(expr.span); + } + }, + hir::ExprKind::Unary(hir::UnOp::UnNeg, expr) => { + if let hir::ExprKind::Lit(lit) = &expr.kind { + if let rustc_ast::ast::LitKind::Int(1, _) = lit.node { + span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected"); + self.expr_span = Some(expr.span); + } + } + }, + _ => { + span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected"); + self.expr_span = Some(expr.span); + }, + }, + _ => { + span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected"); + self.expr_span = Some(expr.span); + }, + } + } else if r_ty.peel_refs().is_floating_point() && r_ty.peel_refs().is_floating_point() { span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected"); self.expr_span = Some(expr.span); } diff --git a/tests/ui/integer_arithmetic.rs b/tests/ui/integer_arithmetic.rs index 7b1b64f390a..b74c93dc4a6 100644 --- a/tests/ui/integer_arithmetic.rs +++ b/tests/ui/integer_arithmetic.rs @@ -11,6 +11,8 @@ #[rustfmt::skip] fn main() { let mut i = 1i32; + let mut var1 = 0i32; + let mut var2 = -1i32; 1 + i; i * 2; 1 % @@ -32,7 +34,15 @@ fn main() { i -= 1; i *= 2; i /= 2; + i /= 0; + i /= -1; + i /= var1; + i /= var2; i %= 2; + i %= 0; + i %= -1; + i %= var1; + i %= var2; i <<= 3; i >>= 2; diff --git a/tests/ui/integer_arithmetic.stderr b/tests/ui/integer_arithmetic.stderr index 83e8a9cde3f..20356afc358 100644 --- a/tests/ui/integer_arithmetic.stderr +++ b/tests/ui/integer_arithmetic.stderr @@ -1,5 +1,19 @@ +error: this operation will panic at runtime + --> $DIR/integer_arithmetic.rs:37:5 + | +LL | i /= 0; + | ^^^^^^ attempt to divide `_` by zero + | + = note: `#[deny(unconditional_panic)]` on by default + +error: this operation will panic at runtime + --> $DIR/integer_arithmetic.rs:42:5 + | +LL | i %= 0; + | ^^^^^^ attempt to calculate the remainder of `_` with a divisor of zero + error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:14:5 + --> $DIR/integer_arithmetic.rs:16:5 | LL | 1 + i; | ^^^^^ @@ -7,125 +21,161 @@ LL | 1 + i; = note: `-D clippy::integer-arithmetic` implied by `-D warnings` error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:15:5 + --> $DIR/integer_arithmetic.rs:17:5 | LL | i * 2; | ^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:16:5 + --> $DIR/integer_arithmetic.rs:18: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:18:5 + --> $DIR/integer_arithmetic.rs:20:5 | LL | i - 2 + 2 - i; | ^^^^^^^^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:19:5 + --> $DIR/integer_arithmetic.rs:21:5 | LL | -i; | ^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:20:5 + --> $DIR/integer_arithmetic.rs:22:5 | LL | i >> 1; | ^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:21:5 + --> $DIR/integer_arithmetic.rs:23:5 | LL | i << 1; | ^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:31:5 + --> $DIR/integer_arithmetic.rs:33:5 | LL | i += 1; | ^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:32:5 + --> $DIR/integer_arithmetic.rs:34:5 | LL | i -= 1; | ^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:33:5 + --> $DIR/integer_arithmetic.rs:35:5 | LL | i *= 2; | ^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:34:5 + --> $DIR/integer_arithmetic.rs:37:5 | -LL | i /= 2; +LL | i /= 0; | ^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:35:5 + --> $DIR/integer_arithmetic.rs:38:11 + | +LL | i /= -1; + | ^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:39:5 + | +LL | i /= var1; + | ^^^^^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:40:5 + | +LL | i /= var2; + | ^^^^^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:42:5 | -LL | i %= 2; +LL | i %= 0; | ^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:36:5 + --> $DIR/integer_arithmetic.rs:43:11 + | +LL | i %= -1; + | ^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:44:5 + | +LL | i %= var1; + | ^^^^^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:45:5 + | +LL | i %= var2; + | ^^^^^^^^^ + +error: integer arithmetic detected + --> $DIR/integer_arithmetic.rs:46:5 | LL | i <<= 3; | ^^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:37:5 + --> $DIR/integer_arithmetic.rs:47:5 | LL | i >>= 2; | ^^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:79:5 + --> $DIR/integer_arithmetic.rs:89:5 | LL | 3 + &1; | ^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:80:5 + --> $DIR/integer_arithmetic.rs:90:5 | LL | &3 + 1; | ^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:81:5 + --> $DIR/integer_arithmetic.rs:91:5 | LL | &3 + &1; | ^^^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:86:5 + --> $DIR/integer_arithmetic.rs:96:5 | LL | a + x | ^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:90:5 + --> $DIR/integer_arithmetic.rs:100:5 | LL | x + y | ^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:94:5 + --> $DIR/integer_arithmetic.rs:104:5 | LL | x + y | ^^^^^ error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:98:5 + --> $DIR/integer_arithmetic.rs:108:5 | LL | (&x + &y) | ^^^^^^^^^ -error: aborting due to 21 previous errors +error: aborting due to 29 previous errors -- cgit 1.4.1-3-g733a5 From 7b065dba84362ce90a7d9a920e193dc719198a9b Mon Sep 17 00:00:00 2001 From: dvermd <315743+dvermd@users.noreply.github.com> Date: Thu, 29 Oct 2020 17:38:46 +0100 Subject: clippy: update reference file to match suggested change --- tests/ui/ref_option_ref.stderr | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'tests') diff --git a/tests/ui/ref_option_ref.stderr b/tests/ui/ref_option_ref.stderr index 10f19661268..4e7fc800061 100644 --- a/tests/ui/ref_option_ref.stderr +++ b/tests/ui/ref_option_ref.stderr @@ -1,4 +1,4 @@ -error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> +error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>` --> $DIR/ref_option_ref.rs:10:23 | LL | static REF_THRESHOLD: &Option<&i32> = &Some(&THRESHOLD); @@ -6,61 +6,61 @@ LL | static REF_THRESHOLD: &Option<&i32> = &Some(&THRESHOLD); | = note: `-D clippy::ref-option-ref` implied by `-D warnings` -error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> +error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>` --> $DIR/ref_option_ref.rs:12:18 | LL | const REF_CONST: &Option<&i32> = &Some(&CONST_THRESHOLD); | ^^^^^^^^^^^^^ help: try: `Option<&i32>` -error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> +error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>` --> $DIR/ref_option_ref.rs:14:25 | LL | type RefOptRefU32<'a> = &'a Option<&'a u32>; | ^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a u32>` -error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> +error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>` --> $DIR/ref_option_ref.rs:15:25 | LL | type RefOptRef<'a, T> = &'a Option<&'a T>; | ^^^^^^^^^^^^^^^^^ help: try: `Option<&'a T>` -error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> +error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>` --> $DIR/ref_option_ref.rs:17:14 | LL | fn foo(data: &Option<&u32>) {} | ^^^^^^^^^^^^^ help: try: `Option<&u32>` -error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> +error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>` --> $DIR/ref_option_ref.rs:19:23 | LL | fn bar(data: &u32) -> &Option<&u32> { | ^^^^^^^^^^^^^ help: try: `Option<&u32>` -error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> +error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>` --> $DIR/ref_option_ref.rs:24:11 | LL | data: &'a Option<&'a u32>, | ^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a u32>` -error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> +error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>` --> $DIR/ref_option_ref.rs:27:32 | LL | struct StructTupleRef<'a>(u32, &'a Option<&'a u32>); | ^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a u32>` -error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> +error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>` --> $DIR/ref_option_ref.rs:31:14 | LL | Variant2(&'a Option<&'a u32>), | ^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'a u32>` -error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> +error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>` --> $DIR/ref_option_ref.rs:40:14 | LL | type A = &'static Option<&'static Self>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Option<&'static Self>` -error: since & implements Copy trait, &Option<&T> can be simplifyied into Option<&T> +error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>` --> $DIR/ref_option_ref.rs:46:12 | LL | let x: &Option<&u32> = &None; -- cgit 1.4.1-3-g733a5 From fa0a78b130d52d4504eefbd16689b140e05fce59 Mon Sep 17 00:00:00 2001 From: henil Date: Fri, 30 Oct 2020 17:11:44 +0530 Subject: removed lint for division/modulo for literal `0` --- clippy_lints/src/arithmetic.rs | 7 +------ tests/ui/integer_arithmetic.stderr | 14 +------------- 2 files changed, 2 insertions(+), 19 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/arithmetic.rs b/clippy_lints/src/arithmetic.rs index b1cdbab6de9..9861d8cfc4e 100644 --- a/clippy_lints/src/arithmetic.rs +++ b/clippy_lints/src/arithmetic.rs @@ -90,12 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for Arithmetic { if l_ty.peel_refs().is_integral() && r_ty.peel_refs().is_integral() { match op.node { hir::BinOpKind::Div | hir::BinOpKind::Rem => match &r.kind { - hir::ExprKind::Lit(lit) => { - if let rustc_ast::ast::LitKind::Int(0, _) = lit.node { - span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected"); - self.expr_span = Some(expr.span); - } - }, + hir::ExprKind::Lit(_lit) => (), hir::ExprKind::Unary(hir::UnOp::UnNeg, expr) => { if let hir::ExprKind::Lit(lit) = &expr.kind { if let rustc_ast::ast::LitKind::Int(1, _) = lit.node { diff --git a/tests/ui/integer_arithmetic.stderr b/tests/ui/integer_arithmetic.stderr index 20356afc358..add3b6b90fa 100644 --- a/tests/ui/integer_arithmetic.stderr +++ b/tests/ui/integer_arithmetic.stderr @@ -75,12 +75,6 @@ error: integer arithmetic detected LL | i *= 2; | ^^^^^^ -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:37:5 - | -LL | i /= 0; - | ^^^^^^ - error: integer arithmetic detected --> $DIR/integer_arithmetic.rs:38:11 | @@ -99,12 +93,6 @@ error: integer arithmetic detected LL | i /= var2; | ^^^^^^^^^ -error: integer arithmetic detected - --> $DIR/integer_arithmetic.rs:42:5 - | -LL | i %= 0; - | ^^^^^^ - error: integer arithmetic detected --> $DIR/integer_arithmetic.rs:43:11 | @@ -177,5 +165,5 @@ error: integer arithmetic detected LL | (&x + &y) | ^^^^^^^^^ -error: aborting due to 29 previous errors +error: aborting due to 27 previous errors -- cgit 1.4.1-3-g733a5 From 7c74d870b5978fb100f8947850d0da3f38dd5c1c Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 30 Oct 2020 13:01:34 -0500 Subject: Fix vec_box scope error Fixes #6236 --- clippy_lints/src/types.rs | 2 +- tests/ui/vec_box_sized.fixed | 14 ++++++++++++++ tests/ui/vec_box_sized.rs | 14 ++++++++++++++ tests/ui/vec_box_sized.stderr | 8 +++++++- 4 files changed, 36 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 45f3bc3ea85..1bb81bbc9a3 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -553,7 +553,7 @@ impl Types { hir_ty.span, "`Vec` is already on the heap, the boxing is unnecessary.", "try", - format!("Vec<{}>", ty_ty), + format!("Vec<{}>", snippet(cx, boxed_ty.span, "..")), Applicability::MachineApplicable, ); return; // don't recurse into the type diff --git a/tests/ui/vec_box_sized.fixed b/tests/ui/vec_box_sized.fixed index d0bee2460dd..4fa28b525c3 100644 --- a/tests/ui/vec_box_sized.fixed +++ b/tests/ui/vec_box_sized.fixed @@ -35,4 +35,18 @@ mod should_not_trigger { } } +mod inner_mod { + mod inner { + pub struct S; + } + + mod inner2 { + use super::inner::S; + + pub fn f() -> Vec { + vec![] + } + } +} + fn main() {} diff --git a/tests/ui/vec_box_sized.rs b/tests/ui/vec_box_sized.rs index 500a0ae263e..7dc735cd90b 100644 --- a/tests/ui/vec_box_sized.rs +++ b/tests/ui/vec_box_sized.rs @@ -35,4 +35,18 @@ mod should_not_trigger { } } +mod inner_mod { + mod inner { + pub struct S; + } + + mod inner2 { + use super::inner::S; + + pub fn f() -> Vec> { + vec![] + } + } +} + fn main() {} diff --git a/tests/ui/vec_box_sized.stderr b/tests/ui/vec_box_sized.stderr index 29bf7069e8a..57e2f1fdf9a 100644 --- a/tests/ui/vec_box_sized.stderr +++ b/tests/ui/vec_box_sized.stderr @@ -18,5 +18,11 @@ error: `Vec` is already on the heap, the boxing is unnecessary. LL | struct B(Vec>>); | ^^^^^^^^^^^^^^^ help: try: `Vec` -error: aborting due to 3 previous errors +error: `Vec` is already on the heap, the boxing is unnecessary. + --> $DIR/vec_box_sized.rs:46:23 + | +LL | pub fn f() -> Vec> { + | ^^^^^^^^^^^ help: try: `Vec` + +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From c0d1002d9328104808253b5cb680d0cd0ab70c1a Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 30 Oct 2020 16:06:27 -0500 Subject: Fix unnecessary_lazy_eval suggestion applicability Fixes #6240 --- clippy_lints/src/methods/unnecessary_lazy_eval.rs | 12 +++++++++++- tests/ui/unnecessary_lazy_eval_unfixable.rs | 18 ++++++++++++++++++ tests/ui/unnecessary_lazy_eval_unfixable.stderr | 22 ++++++++++++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 tests/ui/unnecessary_lazy_eval_unfixable.rs create mode 100644 tests/ui/unnecessary_lazy_eval_unfixable.stderr (limited to 'tests') diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs index 08b3eab9b7c..aec1b7e2e46 100644 --- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -32,6 +32,16 @@ pub(super) fn lint<'tcx>( } else { "unnecessary closure used to substitute value for `Result::Err`" }; + let applicability = if body + .params + .iter() + .all(|param| matches!(param.pat.kind, hir::PatKind::Wild)) + { + Applicability::MachineApplicable + } else { + // replacing the lambda may break type inference + Applicability::MaybeIncorrect + }; span_lint_and_sugg( cx, @@ -45,7 +55,7 @@ pub(super) fn lint<'tcx>( simplify_using, snippet(cx, body_expr.span, ".."), ), - Applicability::MachineApplicable, + applicability, ); } } diff --git a/tests/ui/unnecessary_lazy_eval_unfixable.rs b/tests/ui/unnecessary_lazy_eval_unfixable.rs new file mode 100644 index 00000000000..2e923bc97a2 --- /dev/null +++ b/tests/ui/unnecessary_lazy_eval_unfixable.rs @@ -0,0 +1,18 @@ +#![warn(clippy::unnecessary_lazy_evaluations)] + +struct Deep(Option); + +#[derive(Copy, Clone)] +struct SomeStruct { + some_field: usize, +} + +fn main() { + // fix will break type inference + let _ = Ok(1).unwrap_or_else(|()| 2); + mod e { + pub struct E; + } + let _ = Ok(1).unwrap_or_else(|e::E| 2); + let _ = Ok(1).unwrap_or_else(|SomeStruct { .. }| 2); +} diff --git a/tests/ui/unnecessary_lazy_eval_unfixable.stderr b/tests/ui/unnecessary_lazy_eval_unfixable.stderr new file mode 100644 index 00000000000..581d641cbf5 --- /dev/null +++ b/tests/ui/unnecessary_lazy_eval_unfixable.stderr @@ -0,0 +1,22 @@ +error: unnecessary closure used to substitute value for `Result::Err` + --> $DIR/unnecessary_lazy_eval_unfixable.rs:12:13 + | +LL | let _ = Ok(1).unwrap_or_else(|()| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `Ok(1).unwrap_or(2)` + | + = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings` + +error: unnecessary closure used to substitute value for `Result::Err` + --> $DIR/unnecessary_lazy_eval_unfixable.rs:16:13 + | +LL | let _ = Ok(1).unwrap_or_else(|e::E| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `Ok(1).unwrap_or(2)` + +error: unnecessary closure used to substitute value for `Result::Err` + --> $DIR/unnecessary_lazy_eval_unfixable.rs:17:13 + | +LL | let _ = Ok(1).unwrap_or_else(|SomeStruct { .. }| 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `Ok(1).unwrap_or(2)` + +error: aborting due to 3 previous errors + -- cgit 1.4.1-3-g733a5 From 2350ee75b2cc9c3d8cfbbb8d950012678913b92d Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Sun, 13 Sep 2020 13:00:45 +0200 Subject: single_char_push_str lint using insert_str() on single-char literals and suggest insert() changelog: single_char_push_str: lint using string.insert_str() with single char literals and suggests string.insert() with a char Fixes #6026 --- clippy_lints/src/methods/mod.rs | 25 +++++++++++++++++++-- clippy_lints/src/utils/paths.rs | 1 + tests/ui/single_char_insert_str.fixed | 18 +++++++++++++++ tests/ui/single_char_insert_str.rs | 18 +++++++++++++++ tests/ui/single_char_insert_str.stderr | 40 ++++++++++++++++++++++++++++++++++ 5 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 tests/ui/single_char_insert_str.fixed create mode 100644 tests/ui/single_char_insert_str.rs create mode 100644 tests/ui/single_char_insert_str.stderr (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 3c3093e869c..86e7c57afae 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1290,8 +1290,8 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Warns when using `push_str` with a single-character string literal, - /// and `push` with a `char` would work fine. + /// **What it does:** Warns when using `push_str` with a single-character string literal + /// where `push` with a `char` would work fine. /// /// **Why is this bad?** It's less clear that we are pushing a single character. /// @@ -1521,6 +1521,8 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { if match_def_path(cx, fn_def_id, &paths::PUSH_STR) { lint_single_char_push_string(cx, expr, args); + } else if match_def_path(cx, fn_def_id, &paths::INSERT_STR) { + lint_single_char_insert_string(cx, expr, args); } } @@ -3255,6 +3257,25 @@ fn lint_single_char_push_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args } } +/// lint for length-1 `str`s as argument for `insert_str` +fn lint_single_char_insert_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { + let mut applicability = Applicability::MachineApplicable; + if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[2], &mut applicability) { + let base_string_snippet = snippet_with_applicability(cx, args[0].span, "_", &mut applicability); + let pos_arg = snippet(cx, args[1].span, ".."); + let sugg = format!("{}.insert({}, {})", base_string_snippet, pos_arg, extension_string); + span_lint_and_sugg( + cx, + SINGLE_CHAR_PUSH_STR, + expr.span, + "calling `insert_str()` using a single-character string literal", + "consider using `insert` with a character literal", + sugg, + applicability, + ); + } +} + /// Checks for the `USELESS_ASREF` lint. fn lint_asref(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, as_ref_args: &[hir::Expr<'_>]) { // when we get here, we've already checked that the call name is "as_ref" or "as_mut" diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index cd72fdd61fd..34d5c8d2eb7 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -52,6 +52,7 @@ pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entr pub const HASHSET: [&str; 5] = ["std", "collections", "hash", "set", "HashSet"]; pub const INDEX: [&str; 3] = ["core", "ops", "Index"]; pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"]; +pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"]; pub const INTO: [&str; 3] = ["core", "convert", "Into"]; pub const INTO_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "IntoIterator"]; pub const IO_READ: [&str; 3] = ["std", "io", "Read"]; diff --git a/tests/ui/single_char_insert_str.fixed b/tests/ui/single_char_insert_str.fixed new file mode 100644 index 00000000000..c3416720ec3 --- /dev/null +++ b/tests/ui/single_char_insert_str.fixed @@ -0,0 +1,18 @@ +// run-rustfix +#![warn(clippy::single_char_push_str)] + +fn main() { + let mut string = String::new(); + string.insert(0, 'R'); + string.insert(1, '\''); + + string.insert(0, 'u'); + string.insert_str(2, "st"); + string.insert_str(0, ""); + string.insert(0, '\x52'); + string.insert(0, '\u{0052}'); + let x: usize = 2; + string.insert(x, 'a'); + const Y: usize = 1; + string.insert(Y, 'a'); +} diff --git a/tests/ui/single_char_insert_str.rs b/tests/ui/single_char_insert_str.rs new file mode 100644 index 00000000000..2295669e81d --- /dev/null +++ b/tests/ui/single_char_insert_str.rs @@ -0,0 +1,18 @@ +// run-rustfix +#![warn(clippy::single_char_push_str)] + +fn main() { + let mut string = String::new(); + string.insert_str(0, "R"); + string.insert_str(1, "'"); + + string.insert(0, 'u'); + string.insert_str(2, "st"); + string.insert_str(0, ""); + string.insert_str(0, "\x52"); + string.insert_str(0, "\u{0052}"); + let x: usize = 2; + string.insert_str(x, r##"a"##); + const Y: usize = 1; + string.insert_str(Y, r##"a"##); +} diff --git a/tests/ui/single_char_insert_str.stderr b/tests/ui/single_char_insert_str.stderr new file mode 100644 index 00000000000..65dc8a69d69 --- /dev/null +++ b/tests/ui/single_char_insert_str.stderr @@ -0,0 +1,40 @@ +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_insert_str.rs:6:5 + | +LL | string.insert_str(0, "R"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, 'R')` + | + = note: `-D clippy::single-char-push-str` implied by `-D warnings` + +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_insert_str.rs:7:5 + | +LL | string.insert_str(1, "'"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(1, '/'')` + +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_insert_str.rs:12:5 + | +LL | string.insert_str(0, "/x52"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, '/x52')` + +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_insert_str.rs:13:5 + | +LL | string.insert_str(0, "/u{0052}"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, '/u{0052}')` + +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_insert_str.rs:15:5 + | +LL | string.insert_str(x, r##"a"##); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(x, 'a')` + +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_insert_str.rs:17:5 + | +LL | string.insert_str(Y, r##"a"##); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(Y, 'a')` + +error: aborting due to 6 previous errors + -- cgit 1.4.1-3-g733a5 From c6412aeebc432ab49d46fe1f45a0fb5322c805d1 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Mon, 14 Sep 2020 15:31:04 +0200 Subject: handle macros returning Strings in single_char_push_str and single_char_insert_str --- clippy_lints/src/methods/mod.rs | 6 ++++-- tests/ui/single_char_insert_str.fixed | 8 ++++++++ tests/ui/single_char_insert_str.rs | 8 ++++++++ tests/ui/single_char_insert_str.stderr | 20 +++++++++++++------- tests/ui/single_char_push_str.fixed | 8 ++++++++ tests/ui/single_char_push_str.rs | 8 ++++++++ tests/ui/single_char_push_str.stderr | 10 +++++----- 7 files changed, 54 insertions(+), 14 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 86e7c57afae..68a152270e0 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3243,7 +3243,8 @@ fn lint_single_char_pattern(cx: &LateContext<'_>, _expr: &hir::Expr<'_>, arg: &h fn lint_single_char_push_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { let mut applicability = Applicability::MachineApplicable; if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[1], &mut applicability) { - let base_string_snippet = snippet_with_applicability(cx, args[0].span, "..", &mut applicability); + let base_string_snippet = + snippet_with_applicability(cx, args[0].span.source_callsite(), "..", &mut applicability); let sugg = format!("{}.push({})", base_string_snippet, extension_string); span_lint_and_sugg( cx, @@ -3261,7 +3262,8 @@ fn lint_single_char_push_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args fn lint_single_char_insert_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { let mut applicability = Applicability::MachineApplicable; if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[2], &mut applicability) { - let base_string_snippet = snippet_with_applicability(cx, args[0].span, "_", &mut applicability); + let base_string_snippet = + snippet_with_applicability(cx, args[0].span.source_callsite(), "_", &mut applicability); let pos_arg = snippet(cx, args[1].span, ".."); let sugg = format!("{}.insert({}, {})", base_string_snippet, pos_arg, extension_string); span_lint_and_sugg( diff --git a/tests/ui/single_char_insert_str.fixed b/tests/ui/single_char_insert_str.fixed index c3416720ec3..fd43f913330 100644 --- a/tests/ui/single_char_insert_str.fixed +++ b/tests/ui/single_char_insert_str.fixed @@ -1,6 +1,12 @@ // run-rustfix #![warn(clippy::single_char_push_str)] +macro_rules! get_string { + () => { + String::from("Hello world!") + }; +} + fn main() { let mut string = String::new(); string.insert(0, 'R'); @@ -15,4 +21,6 @@ fn main() { string.insert(x, 'a'); const Y: usize = 1; string.insert(Y, 'a'); + + get_string!().insert(1, '?'); } diff --git a/tests/ui/single_char_insert_str.rs b/tests/ui/single_char_insert_str.rs index 2295669e81d..4278f7ef9fd 100644 --- a/tests/ui/single_char_insert_str.rs +++ b/tests/ui/single_char_insert_str.rs @@ -1,6 +1,12 @@ // run-rustfix #![warn(clippy::single_char_push_str)] +macro_rules! get_string { + () => { + String::from("Hello world!") + }; +} + fn main() { let mut string = String::new(); string.insert_str(0, "R"); @@ -15,4 +21,6 @@ fn main() { string.insert_str(x, r##"a"##); const Y: usize = 1; string.insert_str(Y, r##"a"##); + + get_string!().insert_str(1, "?"); } diff --git a/tests/ui/single_char_insert_str.stderr b/tests/ui/single_char_insert_str.stderr index 65dc8a69d69..3d00c91a2ac 100644 --- a/tests/ui/single_char_insert_str.stderr +++ b/tests/ui/single_char_insert_str.stderr @@ -1,5 +1,5 @@ error: calling `insert_str()` using a single-character string literal - --> $DIR/single_char_insert_str.rs:6:5 + --> $DIR/single_char_insert_str.rs:12:5 | LL | string.insert_str(0, "R"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, 'R')` @@ -7,34 +7,40 @@ LL | string.insert_str(0, "R"); = note: `-D clippy::single-char-push-str` implied by `-D warnings` error: calling `insert_str()` using a single-character string literal - --> $DIR/single_char_insert_str.rs:7:5 + --> $DIR/single_char_insert_str.rs:13:5 | LL | string.insert_str(1, "'"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(1, '/'')` error: calling `insert_str()` using a single-character string literal - --> $DIR/single_char_insert_str.rs:12:5 + --> $DIR/single_char_insert_str.rs:18:5 | LL | string.insert_str(0, "/x52"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, '/x52')` error: calling `insert_str()` using a single-character string literal - --> $DIR/single_char_insert_str.rs:13:5 + --> $DIR/single_char_insert_str.rs:19:5 | LL | string.insert_str(0, "/u{0052}"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, '/u{0052}')` error: calling `insert_str()` using a single-character string literal - --> $DIR/single_char_insert_str.rs:15:5 + --> $DIR/single_char_insert_str.rs:21:5 | LL | string.insert_str(x, r##"a"##); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(x, 'a')` error: calling `insert_str()` using a single-character string literal - --> $DIR/single_char_insert_str.rs:17:5 + --> $DIR/single_char_insert_str.rs:23:5 | LL | string.insert_str(Y, r##"a"##); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(Y, 'a')` -error: aborting due to 6 previous errors +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_insert_str.rs:25:5 + | +LL | get_string!().insert_str(1, "?"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `get_string!().insert(1, '?')` + +error: aborting due to 7 previous errors diff --git a/tests/ui/single_char_push_str.fixed b/tests/ui/single_char_push_str.fixed index 0812c026a64..da742fe70e2 100644 --- a/tests/ui/single_char_push_str.fixed +++ b/tests/ui/single_char_push_str.fixed @@ -1,6 +1,12 @@ // run-rustfix #![warn(clippy::single_char_push_str)] +macro_rules! get_string { + () => { + String::from("Hello world!") + }; +} + fn main() { let mut string = String::new(); string.push('R'); @@ -12,4 +18,6 @@ fn main() { string.push('\x52'); string.push('\u{0052}'); string.push('a'); + + get_string!().push_str("ö"); } diff --git a/tests/ui/single_char_push_str.rs b/tests/ui/single_char_push_str.rs index ab293bbe4ee..a8203e6503e 100644 --- a/tests/ui/single_char_push_str.rs +++ b/tests/ui/single_char_push_str.rs @@ -1,6 +1,12 @@ // run-rustfix #![warn(clippy::single_char_push_str)] +macro_rules! get_string { + () => { + String::from("Hello world!") + }; +} + fn main() { let mut string = String::new(); string.push_str("R"); @@ -12,4 +18,6 @@ fn main() { string.push_str("\x52"); string.push_str("\u{0052}"); string.push_str(r##"a"##); + + get_string!().push_str("ö"); } diff --git a/tests/ui/single_char_push_str.stderr b/tests/ui/single_char_push_str.stderr index 0e9bdaa23e7..1f535589121 100644 --- a/tests/ui/single_char_push_str.stderr +++ b/tests/ui/single_char_push_str.stderr @@ -1,5 +1,5 @@ error: calling `push_str()` using a single-character string literal - --> $DIR/single_char_push_str.rs:6:5 + --> $DIR/single_char_push_str.rs:12:5 | LL | string.push_str("R"); | ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('R')` @@ -7,25 +7,25 @@ LL | string.push_str("R"); = note: `-D clippy::single-char-push-str` implied by `-D warnings` error: calling `push_str()` using a single-character string literal - --> $DIR/single_char_push_str.rs:7:5 + --> $DIR/single_char_push_str.rs:13:5 | LL | string.push_str("'"); | ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/'')` error: calling `push_str()` using a single-character string literal - --> $DIR/single_char_push_str.rs:12:5 + --> $DIR/single_char_push_str.rs:18:5 | LL | string.push_str("/x52"); | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/x52')` error: calling `push_str()` using a single-character string literal - --> $DIR/single_char_push_str.rs:13:5 + --> $DIR/single_char_push_str.rs:19:5 | LL | string.push_str("/u{0052}"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/u{0052}')` error: calling `push_str()` using a single-character string literal - --> $DIR/single_char_push_str.rs:14:5 + --> $DIR/single_char_push_str.rs:20:5 | LL | string.push_str(r##"a"##); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('a')` -- cgit 1.4.1-3-g733a5 From c1eb8ceede6538590a39721c65ee2f943ceffe40 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 24 Sep 2020 16:36:29 +0200 Subject: get_hint_if_single_char_arg: fix bug where multi-char letters are not detected properly --- clippy_lints/src/methods/mod.rs | 2 +- tests/ui/single_char_pattern.fixed | 6 +++--- tests/ui/single_char_pattern.stderr | 20 +++++++++++++++++++- tests/ui/single_char_push_str.fixed | 2 +- tests/ui/single_char_push_str.stderr | 8 +++++++- 5 files changed, 31 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 68a152270e0..5cc3c25e01d 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3204,7 +3204,7 @@ fn get_hint_if_single_char_arg( if let hir::ExprKind::Lit(lit) = &arg.kind; if let ast::LitKind::Str(r, style) = lit.node; let string = r.as_str(); - if string.len() == 1; + if string.chars().count() == 1; then { let snip = snippet_with_applicability(cx, arg.span, &string, applicability); let ch = if let ast::StrStyle::Raw(nhash) = style { diff --git a/tests/ui/single_char_pattern.fixed b/tests/ui/single_char_pattern.fixed index 3871c4f2268..817a06199ff 100644 --- a/tests/ui/single_char_pattern.fixed +++ b/tests/ui/single_char_pattern.fixed @@ -18,9 +18,9 @@ fn main() { // // We may not want to suggest changing these anyway // See: https://github.com/rust-lang/rust-clippy/issues/650#issuecomment-184328984 - x.split("ß"); - x.split("ℝ"); - x.split("💣"); + x.split('ß'); + x.split('ℝ'); + x.split('💣'); // Can't use this lint for unicode code points which don't fit in a char x.split("❤️"); x.contains('x'); diff --git a/tests/ui/single_char_pattern.stderr b/tests/ui/single_char_pattern.stderr index fe7211c53f8..ecaabd9155b 100644 --- a/tests/ui/single_char_pattern.stderr +++ b/tests/ui/single_char_pattern.stderr @@ -6,6 +6,24 @@ LL | x.split("x"); | = note: `-D clippy::single-char-pattern` implied by `-D warnings` +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:21:13 + | +LL | x.split("ß"); + | ^^^ help: try using a `char` instead: `'ß'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:22:13 + | +LL | x.split("ℝ"); + | ^^^ help: try using a `char` instead: `'ℝ'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:23:13 + | +LL | x.split("💣"); + | ^^^^ help: try using a `char` instead: `'💣'` + error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:26:16 | @@ -162,5 +180,5 @@ error: single-character string constant used as pattern LL | x.split(r###"#"###); | ^^^^^^^^^^ help: try using a `char` instead: `'#'` -error: aborting due to 27 previous errors +error: aborting due to 30 previous errors diff --git a/tests/ui/single_char_push_str.fixed b/tests/ui/single_char_push_str.fixed index da742fe70e2..3c550bee9a3 100644 --- a/tests/ui/single_char_push_str.fixed +++ b/tests/ui/single_char_push_str.fixed @@ -19,5 +19,5 @@ fn main() { string.push('\u{0052}'); string.push('a'); - get_string!().push_str("ö"); + get_string!().push('ö'); } diff --git a/tests/ui/single_char_push_str.stderr b/tests/ui/single_char_push_str.stderr index 1f535589121..d6e6e635cc5 100644 --- a/tests/ui/single_char_push_str.stderr +++ b/tests/ui/single_char_push_str.stderr @@ -30,5 +30,11 @@ error: calling `push_str()` using a single-character string literal LL | string.push_str(r##"a"##); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('a')` -error: aborting due to 5 previous errors +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_push_str.rs:22:5 + | +LL | get_string!().push_str("ö"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `get_string!().push('ö')` + +error: aborting due to 6 previous errors -- cgit 1.4.1-3-g733a5 From d958269fe5afb26ee5026be5bc0cea459593bccf Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Thu, 8 Oct 2020 00:03:26 +0200 Subject: Rename single_char_push_str to single_char_add_str --- CHANGELOG.md | 2 +- clippy_lints/src/lib.rs | 6 +-- clippy_lints/src/methods/mod.rs | 16 ++++--- src/lintlist/mod.rs | 12 ++--- tests/ui/single_char_add_str.fixed | 43 ++++++++++++++++++ tests/ui/single_char_add_str.rs | 43 ++++++++++++++++++ tests/ui/single_char_add_str.stderr | 82 ++++++++++++++++++++++++++++++++++ tests/ui/single_char_insert_str.fixed | 26 ----------- tests/ui/single_char_insert_str.rs | 26 ----------- tests/ui/single_char_insert_str.stderr | 46 ------------------- tests/ui/single_char_pattern.fixed | 6 --- tests/ui/single_char_pattern.rs | 6 --- tests/ui/single_char_pattern.stderr | 58 ++++++++++++------------ tests/ui/single_char_push_str.fixed | 23 ---------- tests/ui/single_char_push_str.rs | 23 ---------- tests/ui/single_char_push_str.stderr | 40 ----------------- 16 files changed, 216 insertions(+), 242 deletions(-) create mode 100644 tests/ui/single_char_add_str.fixed create mode 100644 tests/ui/single_char_add_str.rs create mode 100644 tests/ui/single_char_add_str.stderr delete mode 100644 tests/ui/single_char_insert_str.fixed delete mode 100644 tests/ui/single_char_insert_str.rs delete mode 100644 tests/ui/single_char_insert_str.stderr delete mode 100644 tests/ui/single_char_push_str.fixed delete mode 100644 tests/ui/single_char_push_str.rs delete mode 100644 tests/ui/single_char_push_str.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b63dbb7eff..27e861b0d3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1939,8 +1939,8 @@ Released 2018-09-13 [`should_assert_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_assert_eq [`should_implement_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait [`similar_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#similar_names +[`single_char_add_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_add_str [`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern -[`single_char_push_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_push_str [`single_component_path_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports [`single_element_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_element_loop [`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 97358e06c63..f2056c72c07 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -713,8 +713,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::RESULT_MAP_OR_INTO_OPTION, &methods::SEARCH_IS_SOME, &methods::SHOULD_IMPLEMENT_TRAIT, + &methods::SINGLE_CHAR_ADD_STR, &methods::SINGLE_CHAR_PATTERN, - &methods::SINGLE_CHAR_PUSH_STR, &methods::SKIP_WHILE_NEXT, &methods::STRING_EXTEND_CHARS, &methods::SUSPICIOUS_MAP, @@ -1438,8 +1438,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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::SINGLE_CHAR_PUSH_STR), LintId::of(&methods::SKIP_WHILE_NEXT), LintId::of(&methods::STRING_EXTEND_CHARS), LintId::of(&methods::SUSPICIOUS_MAP), @@ -1631,7 +1631,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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_PUSH_STR), + LintId::of(&methods::SINGLE_CHAR_ADD_STR), LintId::of(&methods::STRING_EXTEND_CHARS), LintId::of(&methods::UNNECESSARY_FOLD), LintId::of(&methods::UNNECESSARY_LAZY_EVALUATIONS), diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 5cc3c25e01d..89a2db2b76b 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1290,8 +1290,8 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Warns when using `push_str` with a single-character string literal - /// where `push` with a `char` would work fine. + /// **What it does:** Warns when using `push_str`/`insert_str` with a single-character string literal + /// where `push`/`insert` with a `char` would work fine. /// /// **Why is this bad?** It's less clear that we are pushing a single character. /// @@ -1300,16 +1300,18 @@ declare_clippy_lint! { /// **Example:** /// ```rust /// let mut string = String::new(); + /// string.insert_str(0, "R"); /// string.push_str("R"); /// ``` /// Could be written as /// ```rust /// let mut string = String::new(); + /// string.insert(0, 'R'); /// string.push('R'); /// ``` - pub SINGLE_CHAR_PUSH_STR, + pub SINGLE_CHAR_ADD_STR, style, - "`push_str()` used with a single-character string literal as parameter" + "`push_str()` or `insert_str()` used with a single-character string literal as parameter" } declare_clippy_lint! { @@ -1390,7 +1392,7 @@ declare_lint_pass!(Methods => [ INEFFICIENT_TO_STRING, NEW_RET_NO_SELF, SINGLE_CHAR_PATTERN, - SINGLE_CHAR_PUSH_STR, + SINGLE_CHAR_ADD_STR, SEARCH_IS_SOME, FILTER_NEXT, SKIP_WHILE_NEXT, @@ -3248,7 +3250,7 @@ fn lint_single_char_push_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args let sugg = format!("{}.push({})", base_string_snippet, extension_string); span_lint_and_sugg( cx, - SINGLE_CHAR_PUSH_STR, + SINGLE_CHAR_ADD_STR, expr.span, "calling `push_str()` using a single-character string literal", "consider using `push` with a character literal", @@ -3268,7 +3270,7 @@ fn lint_single_char_insert_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ar let sugg = format!("{}.insert({}, {})", base_string_snippet, pos_arg, extension_string); span_lint_and_sugg( cx, - SINGLE_CHAR_PUSH_STR, + SINGLE_CHAR_ADD_STR, expr.span, "calling `insert_str()` using a single-character string literal", "consider using `insert` with a character literal", diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 016bda77ef5..b22bb074238 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2147,16 +2147,16 @@ vec![ module: "non_expressive_names", }, Lint { - name: "single_char_pattern", - group: "perf", - desc: "using a single-character str where a char could be used, e.g., `_.split(\"x\")`", + name: "single_char_add_str", + group: "style", + desc: "`push_str()` or `insert_str()` used with a single-character string literal as parameter", deprecation: None, module: "methods", }, Lint { - name: "single_char_push_str", - group: "style", - desc: "`push_str()` used with a single-character string literal as parameter", + name: "single_char_pattern", + group: "perf", + desc: "using a single-character str where a char could be used, e.g., `_.split(\"x\")`", deprecation: None, module: "methods", }, diff --git a/tests/ui/single_char_add_str.fixed b/tests/ui/single_char_add_str.fixed new file mode 100644 index 00000000000..2feba4b8069 --- /dev/null +++ b/tests/ui/single_char_add_str.fixed @@ -0,0 +1,43 @@ +// run-rustfix +#![warn(clippy::single_char_add_str)] + +macro_rules! get_string { + () => { + String::from("Hello world!") + }; +} + +fn main() { + // `push_str` tests + + let mut string = String::new(); + string.push('R'); + string.push('\''); + + string.push('u'); + string.push_str("st"); + string.push_str(""); + string.push('\x52'); + string.push('\u{0052}'); + string.push('a'); + + get_string!().push('ö'); + + // `insert_str` tests + + let mut string = String::new(); + string.insert(0, 'R'); + string.insert(1, '\''); + + string.insert(0, 'u'); + string.insert_str(2, "st"); + string.insert_str(0, ""); + string.insert(0, '\x52'); + string.insert(0, '\u{0052}'); + let x: usize = 2; + string.insert(x, 'a'); + const Y: usize = 1; + string.insert(Y, 'a'); + + get_string!().insert(1, '?'); +} diff --git a/tests/ui/single_char_add_str.rs b/tests/ui/single_char_add_str.rs new file mode 100644 index 00000000000..681b3d10345 --- /dev/null +++ b/tests/ui/single_char_add_str.rs @@ -0,0 +1,43 @@ +// run-rustfix +#![warn(clippy::single_char_add_str)] + +macro_rules! get_string { + () => { + String::from("Hello world!") + }; +} + +fn main() { + // `push_str` tests + + let mut string = String::new(); + string.push_str("R"); + string.push_str("'"); + + string.push('u'); + string.push_str("st"); + string.push_str(""); + string.push_str("\x52"); + string.push_str("\u{0052}"); + string.push_str(r##"a"##); + + get_string!().push_str("ö"); + + // `insert_str` tests + + let mut string = String::new(); + string.insert_str(0, "R"); + string.insert_str(1, "'"); + + string.insert(0, 'u'); + string.insert_str(2, "st"); + string.insert_str(0, ""); + string.insert_str(0, "\x52"); + string.insert_str(0, "\u{0052}"); + let x: usize = 2; + string.insert_str(x, r##"a"##); + const Y: usize = 1; + string.insert_str(Y, r##"a"##); + + get_string!().insert_str(1, "?"); +} diff --git a/tests/ui/single_char_add_str.stderr b/tests/ui/single_char_add_str.stderr new file mode 100644 index 00000000000..2b17279a564 --- /dev/null +++ b/tests/ui/single_char_add_str.stderr @@ -0,0 +1,82 @@ +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:14:5 + | +LL | string.push_str("R"); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('R')` + | + = note: `-D clippy::single-char-add-str` implied by `-D warnings` + +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:15:5 + | +LL | string.push_str("'"); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/'')` + +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:20:5 + | +LL | string.push_str("/x52"); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/x52')` + +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:21:5 + | +LL | string.push_str("/u{0052}"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/u{0052}')` + +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:22:5 + | +LL | string.push_str(r##"a"##); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('a')` + +error: calling `push_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:24:5 + | +LL | get_string!().push_str("ö"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `get_string!().push('ö')` + +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:29:5 + | +LL | string.insert_str(0, "R"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, 'R')` + +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:30:5 + | +LL | string.insert_str(1, "'"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(1, '/'')` + +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:35:5 + | +LL | string.insert_str(0, "/x52"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, '/x52')` + +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:36:5 + | +LL | string.insert_str(0, "/u{0052}"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, '/u{0052}')` + +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:38:5 + | +LL | string.insert_str(x, r##"a"##); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(x, 'a')` + +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:40:5 + | +LL | string.insert_str(Y, r##"a"##); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(Y, 'a')` + +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:42:5 + | +LL | get_string!().insert_str(1, "?"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `get_string!().insert(1, '?')` + +error: aborting due to 13 previous errors + diff --git a/tests/ui/single_char_insert_str.fixed b/tests/ui/single_char_insert_str.fixed deleted file mode 100644 index fd43f913330..00000000000 --- a/tests/ui/single_char_insert_str.fixed +++ /dev/null @@ -1,26 +0,0 @@ -// run-rustfix -#![warn(clippy::single_char_push_str)] - -macro_rules! get_string { - () => { - String::from("Hello world!") - }; -} - -fn main() { - let mut string = String::new(); - string.insert(0, 'R'); - string.insert(1, '\''); - - string.insert(0, 'u'); - string.insert_str(2, "st"); - string.insert_str(0, ""); - string.insert(0, '\x52'); - string.insert(0, '\u{0052}'); - let x: usize = 2; - string.insert(x, 'a'); - const Y: usize = 1; - string.insert(Y, 'a'); - - get_string!().insert(1, '?'); -} diff --git a/tests/ui/single_char_insert_str.rs b/tests/ui/single_char_insert_str.rs deleted file mode 100644 index 4278f7ef9fd..00000000000 --- a/tests/ui/single_char_insert_str.rs +++ /dev/null @@ -1,26 +0,0 @@ -// run-rustfix -#![warn(clippy::single_char_push_str)] - -macro_rules! get_string { - () => { - String::from("Hello world!") - }; -} - -fn main() { - let mut string = String::new(); - string.insert_str(0, "R"); - string.insert_str(1, "'"); - - string.insert(0, 'u'); - string.insert_str(2, "st"); - string.insert_str(0, ""); - string.insert_str(0, "\x52"); - string.insert_str(0, "\u{0052}"); - let x: usize = 2; - string.insert_str(x, r##"a"##); - const Y: usize = 1; - string.insert_str(Y, r##"a"##); - - get_string!().insert_str(1, "?"); -} diff --git a/tests/ui/single_char_insert_str.stderr b/tests/ui/single_char_insert_str.stderr deleted file mode 100644 index 3d00c91a2ac..00000000000 --- a/tests/ui/single_char_insert_str.stderr +++ /dev/null @@ -1,46 +0,0 @@ -error: calling `insert_str()` using a single-character string literal - --> $DIR/single_char_insert_str.rs:12:5 - | -LL | string.insert_str(0, "R"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, 'R')` - | - = note: `-D clippy::single-char-push-str` implied by `-D warnings` - -error: calling `insert_str()` using a single-character string literal - --> $DIR/single_char_insert_str.rs:13:5 - | -LL | string.insert_str(1, "'"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(1, '/'')` - -error: calling `insert_str()` using a single-character string literal - --> $DIR/single_char_insert_str.rs:18:5 - | -LL | string.insert_str(0, "/x52"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, '/x52')` - -error: calling `insert_str()` using a single-character string literal - --> $DIR/single_char_insert_str.rs:19:5 - | -LL | string.insert_str(0, "/u{0052}"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, '/u{0052}')` - -error: calling `insert_str()` using a single-character string literal - --> $DIR/single_char_insert_str.rs:21:5 - | -LL | string.insert_str(x, r##"a"##); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(x, 'a')` - -error: calling `insert_str()` using a single-character string literal - --> $DIR/single_char_insert_str.rs:23:5 - | -LL | string.insert_str(Y, r##"a"##); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(Y, 'a')` - -error: calling `insert_str()` using a single-character string literal - --> $DIR/single_char_insert_str.rs:25:5 - | -LL | get_string!().insert_str(1, "?"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `get_string!().insert(1, '?')` - -error: aborting due to 7 previous errors - diff --git a/tests/ui/single_char_pattern.fixed b/tests/ui/single_char_pattern.fixed index 817a06199ff..d8b5f19e144 100644 --- a/tests/ui/single_char_pattern.fixed +++ b/tests/ui/single_char_pattern.fixed @@ -12,12 +12,6 @@ fn main() { let y = "x"; x.split(y); - // Not yet testing for multi-byte characters - // Changing `r.len() == 1` to `r.chars().count() == 1` in `lint_clippy::single_char_pattern` - // should have done this but produced an ICE - // - // We may not want to suggest changing these anyway - // See: https://github.com/rust-lang/rust-clippy/issues/650#issuecomment-184328984 x.split('ß'); x.split('ℝ'); x.split('💣'); diff --git a/tests/ui/single_char_pattern.rs b/tests/ui/single_char_pattern.rs index 32afe339cd8..a7bc73e3756 100644 --- a/tests/ui/single_char_pattern.rs +++ b/tests/ui/single_char_pattern.rs @@ -12,12 +12,6 @@ fn main() { let y = "x"; x.split(y); - // Not yet testing for multi-byte characters - // Changing `r.len() == 1` to `r.chars().count() == 1` in `lint_clippy::single_char_pattern` - // should have done this but produced an ICE - // - // We may not want to suggest changing these anyway - // See: https://github.com/rust-lang/rust-clippy/issues/650#issuecomment-184328984 x.split("ß"); x.split("ℝ"); x.split("💣"); diff --git a/tests/ui/single_char_pattern.stderr b/tests/ui/single_char_pattern.stderr index ecaabd9155b..ee4e7e50efd 100644 --- a/tests/ui/single_char_pattern.stderr +++ b/tests/ui/single_char_pattern.stderr @@ -7,175 +7,175 @@ LL | x.split("x"); = note: `-D clippy::single-char-pattern` implied by `-D warnings` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:21:13 + --> $DIR/single_char_pattern.rs:15:13 | LL | x.split("ß"); | ^^^ help: try using a `char` instead: `'ß'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:22:13 + --> $DIR/single_char_pattern.rs:16:13 | LL | x.split("ℝ"); | ^^^ help: try using a `char` instead: `'ℝ'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:23:13 + --> $DIR/single_char_pattern.rs:17:13 | LL | x.split("💣"); | ^^^^ help: try using a `char` instead: `'💣'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:26:16 + --> $DIR/single_char_pattern.rs:20:16 | LL | x.contains("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:27:19 + --> $DIR/single_char_pattern.rs:21:19 | LL | x.starts_with("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:28:17 + --> $DIR/single_char_pattern.rs:22:17 | LL | x.ends_with("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:29:12 + --> $DIR/single_char_pattern.rs:23:12 | LL | x.find("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:30:13 + --> $DIR/single_char_pattern.rs:24:13 | LL | x.rfind("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:31:14 + --> $DIR/single_char_pattern.rs:25:14 | LL | x.rsplit("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:32:24 + --> $DIR/single_char_pattern.rs:26:24 | LL | x.split_terminator("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:33:25 + --> $DIR/single_char_pattern.rs:27:25 | LL | x.rsplit_terminator("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:34:17 + --> $DIR/single_char_pattern.rs:28:17 | LL | x.splitn(0, "x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:35:18 + --> $DIR/single_char_pattern.rs:29:18 | LL | x.rsplitn(0, "x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:36:15 + --> $DIR/single_char_pattern.rs:30:15 | LL | x.matches("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:37:16 + --> $DIR/single_char_pattern.rs:31:16 | LL | x.rmatches("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:38:21 + --> $DIR/single_char_pattern.rs:32:21 | LL | x.match_indices("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:39:22 + --> $DIR/single_char_pattern.rs:33:22 | LL | x.rmatch_indices("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:40:26 + --> $DIR/single_char_pattern.rs:34:26 | LL | x.trim_start_matches("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:41:24 + --> $DIR/single_char_pattern.rs:35:24 | LL | x.trim_end_matches("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:43:13 + --> $DIR/single_char_pattern.rs:37:13 | LL | x.split("/n"); | ^^^^ help: try using a `char` instead: `'/n'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:44:13 + --> $DIR/single_char_pattern.rs:38:13 | LL | x.split("'"); | ^^^ help: try using a `char` instead: `'/''` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:45:13 + --> $DIR/single_char_pattern.rs:39:13 | LL | x.split("/'"); | ^^^^ help: try using a `char` instead: `'/''` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:50:31 + --> $DIR/single_char_pattern.rs:44:31 | LL | x.replace(";", ",").split(","); // issue #2978 | ^^^ help: try using a `char` instead: `','` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:51:19 + --> $DIR/single_char_pattern.rs:45:19 | LL | x.starts_with("/x03"); // issue #2996 | ^^^^^^ help: try using a `char` instead: `'/x03'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:58:13 + --> $DIR/single_char_pattern.rs:52:13 | LL | x.split(r"a"); | ^^^^ help: try using a `char` instead: `'a'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:59:13 + --> $DIR/single_char_pattern.rs:53:13 | LL | x.split(r#"a"#); | ^^^^^^ help: try using a `char` instead: `'a'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:60:13 + --> $DIR/single_char_pattern.rs:54:13 | LL | x.split(r###"a"###); | ^^^^^^^^^^ help: try using a `char` instead: `'a'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:61:13 + --> $DIR/single_char_pattern.rs:55:13 | LL | x.split(r###"'"###); | ^^^^^^^^^^ help: try using a `char` instead: `'/''` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:62:13 + --> $DIR/single_char_pattern.rs:56:13 | LL | x.split(r###"#"###); | ^^^^^^^^^^ help: try using a `char` instead: `'#'` diff --git a/tests/ui/single_char_push_str.fixed b/tests/ui/single_char_push_str.fixed deleted file mode 100644 index 3c550bee9a3..00000000000 --- a/tests/ui/single_char_push_str.fixed +++ /dev/null @@ -1,23 +0,0 @@ -// run-rustfix -#![warn(clippy::single_char_push_str)] - -macro_rules! get_string { - () => { - String::from("Hello world!") - }; -} - -fn main() { - let mut string = String::new(); - string.push('R'); - string.push('\''); - - string.push('u'); - string.push_str("st"); - string.push_str(""); - string.push('\x52'); - string.push('\u{0052}'); - string.push('a'); - - get_string!().push('ö'); -} diff --git a/tests/ui/single_char_push_str.rs b/tests/ui/single_char_push_str.rs deleted file mode 100644 index a8203e6503e..00000000000 --- a/tests/ui/single_char_push_str.rs +++ /dev/null @@ -1,23 +0,0 @@ -// run-rustfix -#![warn(clippy::single_char_push_str)] - -macro_rules! get_string { - () => { - String::from("Hello world!") - }; -} - -fn main() { - let mut string = String::new(); - string.push_str("R"); - string.push_str("'"); - - string.push('u'); - string.push_str("st"); - string.push_str(""); - string.push_str("\x52"); - string.push_str("\u{0052}"); - string.push_str(r##"a"##); - - get_string!().push_str("ö"); -} diff --git a/tests/ui/single_char_push_str.stderr b/tests/ui/single_char_push_str.stderr deleted file mode 100644 index d6e6e635cc5..00000000000 --- a/tests/ui/single_char_push_str.stderr +++ /dev/null @@ -1,40 +0,0 @@ -error: calling `push_str()` using a single-character string literal - --> $DIR/single_char_push_str.rs:12:5 - | -LL | string.push_str("R"); - | ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('R')` - | - = note: `-D clippy::single-char-push-str` implied by `-D warnings` - -error: calling `push_str()` using a single-character string literal - --> $DIR/single_char_push_str.rs:13:5 - | -LL | string.push_str("'"); - | ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/'')` - -error: calling `push_str()` using a single-character string literal - --> $DIR/single_char_push_str.rs:18:5 - | -LL | string.push_str("/x52"); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/x52')` - -error: calling `push_str()` using a single-character string literal - --> $DIR/single_char_push_str.rs:19:5 - | -LL | string.push_str("/u{0052}"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/u{0052}')` - -error: calling `push_str()` using a single-character string literal - --> $DIR/single_char_push_str.rs:20:5 - | -LL | string.push_str(r##"a"##); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('a')` - -error: calling `push_str()` using a single-character string literal - --> $DIR/single_char_push_str.rs:22:5 - | -LL | get_string!().push_str("ö"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `get_string!().push('ö')` - -error: aborting due to 6 previous errors - -- cgit 1.4.1-3-g733a5 From f8ac1f99efd996bea2b05c83a915a830d69e1690 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 30 Oct 2020 23:47:22 +0100 Subject: Address suggestions in PR review --- CHANGELOG.md | 2 +- clippy_lints/src/lib.rs | 1 + clippy_lints/src/methods/mod.rs | 2 +- tests/ui/single_char_add_str.fixed | 2 ++ tests/ui/single_char_add_str.rs | 2 ++ tests/ui/single_char_add_str.stderr | 14 +++++++++++++- 6 files changed, 20 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 27e861b0d3e..be47eeaa073 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,7 +22,7 @@ Current beta, release 2020-11-19 * [`map_err_ignore`] [#5998](https://github.com/rust-lang/rust-clippy/pull/5998) * [`rc_buffer`] [#6044](https://github.com/rust-lang/rust-clippy/pull/6044) * [`to_string_in_display`] [#5831](https://github.com/rust-lang/rust-clippy/pull/5831) -* [`single_char_push_str`] [#5881](https://github.com/rust-lang/rust-clippy/pull/5881) +* `single_char_push_str` [#5881](https://github.com/rust-lang/rust-clippy/pull/5881) ### Moves and Deprecations diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f2056c72c07..9d9097002d6 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1958,6 +1958,7 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) { ls.register_renamed("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles"); ls.register_renamed("clippy::identity_conversion", "clippy::useless_conversion"); ls.register_renamed("clippy::zero_width_space", "clippy::invisible_characters"); + ls.register_renamed("clippy::single_char_push_str", "clippy::single_char_add_str"); } // only exists to let the dogfood integration test works. diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 89a2db2b76b..558c90249cc 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3266,7 +3266,7 @@ fn lint_single_char_insert_string(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ar if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[2], &mut applicability) { let base_string_snippet = snippet_with_applicability(cx, args[0].span.source_callsite(), "_", &mut applicability); - let pos_arg = snippet(cx, args[1].span, ".."); + let pos_arg = snippet_with_applicability(cx, args[1].span, "..", &mut applicability); let sugg = format!("{}.insert({}, {})", base_string_snippet, pos_arg, extension_string); span_lint_and_sugg( cx, diff --git a/tests/ui/single_char_add_str.fixed b/tests/ui/single_char_add_str.fixed index 2feba4b8069..63a6d37a9cc 100644 --- a/tests/ui/single_char_add_str.fixed +++ b/tests/ui/single_char_add_str.fixed @@ -38,6 +38,8 @@ fn main() { string.insert(x, 'a'); const Y: usize = 1; string.insert(Y, 'a'); + string.insert(Y, '"'); + string.insert(Y, '\''); get_string!().insert(1, '?'); } diff --git a/tests/ui/single_char_add_str.rs b/tests/ui/single_char_add_str.rs index 681b3d10345..a799ea7d885 100644 --- a/tests/ui/single_char_add_str.rs +++ b/tests/ui/single_char_add_str.rs @@ -38,6 +38,8 @@ fn main() { string.insert_str(x, r##"a"##); const Y: usize = 1; string.insert_str(Y, r##"a"##); + string.insert_str(Y, r##"""##); + string.insert_str(Y, r##"'"##); get_string!().insert_str(1, "?"); } diff --git a/tests/ui/single_char_add_str.stderr b/tests/ui/single_char_add_str.stderr index 2b17279a564..55d91583ad0 100644 --- a/tests/ui/single_char_add_str.stderr +++ b/tests/ui/single_char_add_str.stderr @@ -72,11 +72,23 @@ error: calling `insert_str()` using a single-character string literal LL | string.insert_str(Y, r##"a"##); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(Y, 'a')` +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:41:5 + | +LL | string.insert_str(Y, r##"""##); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(Y, '"')` + error: calling `insert_str()` using a single-character string literal --> $DIR/single_char_add_str.rs:42:5 | +LL | string.insert_str(Y, r##"'"##); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(Y, '/'')` + +error: calling `insert_str()` using a single-character string literal + --> $DIR/single_char_add_str.rs:44:5 + | LL | get_string!().insert_str(1, "?"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `get_string!().insert(1, '?')` -error: aborting due to 13 previous errors +error: aborting due to 15 previous errors -- cgit 1.4.1-3-g733a5 From 7b203f3da6b590059c12bb01d0873b31be347928 Mon Sep 17 00:00:00 2001 From: Henri Lunnikivi Date: Thu, 31 Oct 2019 18:02:02 +0200 Subject: Implement field_reassign_with_default - Implement `field_reassign_with_default` as a `LateLintPass` - Avoid triggering `default_trait_access` on a span already linted by `field_reassigned_with_default` - Merge `default_trait_access` and `field_reassign_with_default` into `Default` - Co-authored-by: Eduardo Broto - Fixes #568 --- CHANGELOG.md | 1 + clippy_lints/src/default.rs | 304 ++++++++++++++++++++++++++++ clippy_lints/src/default_trait_access.rs | 62 ------ clippy_lints/src/lib.rs | 11 +- src/lintlist/mod.rs | 9 +- tests/ui/field_reassign_with_default.rs | 110 ++++++++++ tests/ui/field_reassign_with_default.stderr | 75 +++++++ 7 files changed, 505 insertions(+), 67 deletions(-) create mode 100644 clippy_lints/src/default.rs delete mode 100644 clippy_lints/src/default_trait_access.rs create mode 100644 tests/ui/field_reassign_with_default.rs create mode 100644 tests/ui/field_reassign_with_default.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b63dbb7eff..36b54416a19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1714,6 +1714,7 @@ Released 2018-09-13 [`extend_from_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_from_slice [`extra_unused_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_lifetimes [`fallible_impl_from`]: https://rust-lang.github.io/rust-clippy/master/index.html#fallible_impl_from +[`field_reassign_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#field_reassign_with_default [`filetype_is_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#filetype_is_file [`filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map [`filter_map_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_next diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs new file mode 100644 index 00000000000..612c5355338 --- /dev/null +++ b/clippy_lints/src/default.rs @@ -0,0 +1,304 @@ +use crate::utils::{any_parent_is_automatically_derived, contains_name, match_def_path, paths, qpath_res, snippet}; +use crate::utils::{span_lint_and_note, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Applicability; +use rustc_hir::def::Res; +use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, Adt, Ty}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for literal calls to `Default::default()`. + /// + /// **Why is this bad?** It's more clear to the reader to use the name of the type whose default is + /// being gotten than the generic `Default`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// // Bad + /// let s: String = Default::default(); + /// + /// // Good + /// let s = String::default(); + /// ``` + pub DEFAULT_TRAIT_ACCESS, + pedantic, + "checks for literal calls to `Default::default()`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for immediate reassignment of fields initialized + /// with Default::default(). + /// + /// **Why is this bad?**It's more idiomatic to use the [functional update syntax](https://doc.rust-lang.org/reference/expressions/struct-expr.html#functional-update-syntax). + /// + /// **Known problems:** Assignments to patterns that are of tuple type are not linted. + /// + /// **Example:** + /// Bad: + /// ``` + /// # #[derive(Default)] + /// # struct A { i: i32 } + /// let mut a: A = Default::default(); + /// a.i = 42; + /// ``` + /// Use instead: + /// ``` + /// # #[derive(Default)] + /// # struct A { i: i32 } + /// let a = A { + /// i: 42, + /// .. Default::default() + /// }; + /// ``` + pub FIELD_REASSIGN_WITH_DEFAULT, + style, + "binding initialized with Default should have its fields set in the initializer" +} + +#[derive(Default)] +pub struct Default { + // Spans linted by `field_reassign_with_default`. + reassigned_linted: FxHashSet, +} + +impl_lint_pass!(Default => [DEFAULT_TRAIT_ACCESS, FIELD_REASSIGN_WITH_DEFAULT]); + +impl LateLintPass<'_> for Default { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + // Avoid cases already linted by `field_reassign_with_default` + if !self.reassigned_linted.contains(&expr.span); + if let ExprKind::Call(ref path, ..) = expr.kind; + if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id); + if let ExprKind::Path(ref qpath) = path.kind; + if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id(); + if match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD); + // Detect and ignore ::default() because these calls do explicitly name the type. + if let QPath::Resolved(None, _path) = qpath; + then { + let expr_ty = cx.typeck_results().expr_ty(expr); + if let ty::Adt(def, ..) = expr_ty.kind() { + // TODO: Work out a way to put "whatever the imported way of referencing + // this type in this file" rather than a fully-qualified type. + let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did)); + span_lint_and_sugg( + cx, + DEFAULT_TRAIT_ACCESS, + expr.span, + &format!("calling `{}` is more clear than this expression", replacement), + "try", + replacement, + Applicability::Unspecified, // First resolve the TODO above + ); + } + } + } + } + + fn check_block<'tcx>(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) { + // find all binding statements like `let mut _ = T::default()` where `T::default()` is the + // `default` method of the `Default` trait, and store statement index in current block being + // checked and the name of the bound variable + let binding_statements_using_default = enumerate_bindings_using_default(cx, block); + + // start from the `let mut _ = _::default();` and look at all the following + // statements, see if they re-assign the fields of the binding + for (stmt_idx, binding_name, binding_type, span) in binding_statements_using_default { + // the last statement of a block cannot trigger the lint + if stmt_idx == block.stmts.len() - 1 { + break; + } + + // find all "later statement"'s where the fields of the binding set as + // Default::default() get reassigned, unless the reassignment refers to the original binding + let mut first_assign = None; + let mut assigned_fields = Vec::new(); + let mut cancel_lint = false; + for consecutive_statement in &block.stmts[stmt_idx + 1..] { + // interrupt if the statement is a let binding (`Local`) that shadows the original + // binding + if stmt_shadows_binding(consecutive_statement, binding_name) { + break; + } + // find out if and which field was set by this `consecutive_statement` + else if let Some((field_ident, assign_rhs)) = + field_reassigned_by_stmt(consecutive_statement, binding_name) + { + // interrupt and cancel lint if assign_rhs references the original binding + if contains_name(binding_name, assign_rhs) { + cancel_lint = true; + break; + } + + // if the field was previously assigned, replace the assignment, otherwise insert the assignment + if let Some(prev) = assigned_fields + .iter_mut() + .find(|(field_name, _)| field_name == &field_ident.name) + { + *prev = (field_ident.name, assign_rhs); + } else { + assigned_fields.push((field_ident.name, assign_rhs)); + } + + // also set first instance of error for help message + if first_assign.is_none() { + first_assign = Some(consecutive_statement); + } + } + // interrupt also if no field was assigned, since we only want to look at consecutive statements + else { + break; + } + } + + // if there are incorrectly assigned fields, do a span_lint_and_note to suggest + // construction using `Ty { fields, ..Default::default() }` + if !assigned_fields.is_empty() && !cancel_lint { + // take the original assignment as span + let stmt = &block.stmts[stmt_idx]; + + if let StmtKind::Local(preceding_local) = &stmt.kind { + // filter out fields like `= Default::default()`, because the FRU already covers them + let assigned_fields = assigned_fields + .into_iter() + .filter(|(_, rhs)| !is_expr_default(rhs, cx)) + .collect::)>>(); + + // if all fields of the struct are not assigned, add `.. Default::default()` to the suggestion. + let ext_with_default = !fields_of_type(binding_type) + .iter() + .all(|field| assigned_fields.iter().any(|(a, _)| a == &field.name)); + + let field_list = assigned_fields + .into_iter() + .map(|(field, rhs)| { + // extract and store the assigned value for help message + let value_snippet = snippet(cx, rhs.span, ".."); + format!("{}: {}", field, value_snippet) + }) + .collect::>() + .join(", "); + + let sugg = if ext_with_default { + if field_list.is_empty() { + format!("{}::default()", binding_type) + } else { + format!("{} {{ {}, ..Default::default() }}", binding_type, field_list) + } + } else { + format!("{} {{ {} }}", binding_type, field_list) + }; + + // span lint once per statement that binds default + span_lint_and_note( + cx, + FIELD_REASSIGN_WITH_DEFAULT, + first_assign.unwrap().span, + "field assignment outside of initializer for an instance created with Default::default()", + Some(preceding_local.span), + &format!( + "consider initializing the variable with `{}` and removing relevant reassignments", + sugg + ), + ); + self.reassigned_linted.insert(span); + } + } + } + } +} + +/// Checks if the given expression is the `default` method belonging to the `Default` trait. +fn is_expr_default<'tcx>(expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> bool { + if_chain! { + if let ExprKind::Call(ref fn_expr, _) = &expr.kind; + if let ExprKind::Path(qpath) = &fn_expr.kind; + if let Res::Def(_, def_id) = qpath_res(cx, qpath, fn_expr.hir_id); + then { + // right hand side of assignment is `Default::default` + match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD) + } else { + false + } + } +} + +/// Returns the block indices, identifiers and types of bindings set as `Default::default()`, except +/// for when the pattern type is a tuple. +fn enumerate_bindings_using_default<'tcx>( + cx: &LateContext<'tcx>, + block: &Block<'tcx>, +) -> Vec<(usize, Symbol, Ty<'tcx>, Span)> { + block + .stmts + .iter() + .enumerate() + .filter_map(|(idx, stmt)| { + if_chain! { + // only take `let ...` statements + if let StmtKind::Local(ref local) = stmt.kind; + // only take bindings to identifiers + if let PatKind::Binding(_, _, ident, _) = local.pat.kind; + // that are not tuples + let ty = cx.typeck_results().pat_ty(local.pat); + if !matches!(ty.kind(), ty::Tuple(_)); + // only when assigning `... = Default::default()` + if let Some(ref expr) = local.init; + if is_expr_default(expr, cx); + then { + Some((idx, ident.name, ty, expr.span)) + } else { + None + } + } + }) + .collect() +} + +fn stmt_shadows_binding(this: &Stmt<'_>, shadowed: Symbol) -> bool { + if let StmtKind::Local(local) = &this.kind { + if let PatKind::Binding(_, _, ident, _) = local.pat.kind { + return ident.name == shadowed; + } + } + false +} + +/// Returns the reassigned field and the assigning expression (right-hand side of assign). +fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Option<(Ident, &'tcx Expr<'tcx>)> { + if_chain! { + // only take assignments + if let StmtKind::Semi(ref later_expr) = this.kind; + if let ExprKind::Assign(ref assign_lhs, ref assign_rhs, _) = later_expr.kind; + // only take assignments to fields where the left-hand side field is a field of + // the same binding as the previous statement + if let ExprKind::Field(ref binding, field_ident) = assign_lhs.kind; + if let ExprKind::Path(ref qpath) = binding.kind; + if let QPath::Resolved(_, path) = qpath; + if let Some(second_binding_name) = path.segments.last(); + if second_binding_name.ident.name == binding_name; + then { + Some((field_ident, assign_rhs)) + } else { + None + } + } +} + +/// Returns the vec of fields for a struct and an empty vec for non-struct ADTs. +fn fields_of_type(ty: Ty<'_>) -> Vec { + if let Adt(adt, _) = ty.kind() { + if adt.is_struct() { + let variant = &adt.non_enum_variant(); + return variant.fields.iter().map(|f| f.ident).collect(); + } + } + vec![] +} diff --git a/clippy_lints/src/default_trait_access.rs b/clippy_lints/src/default_trait_access.rs deleted file mode 100644 index 3048436d9a7..00000000000 --- a/clippy_lints/src/default_trait_access.rs +++ /dev/null @@ -1,62 +0,0 @@ -use if_chain::if_chain; -use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, QPath}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; -use rustc_session::{declare_lint_pass, declare_tool_lint}; - -use crate::utils::{any_parent_is_automatically_derived, match_def_path, paths, span_lint_and_sugg}; - -declare_clippy_lint! { - /// **What it does:** Checks for literal calls to `Default::default()`. - /// - /// **Why is this bad?** It's more clear to the reader to use the name of the type whose default is - /// being gotten than the generic `Default`. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// // Bad - /// let s: String = Default::default(); - /// - /// // Good - /// let s = String::default(); - /// ``` - pub DEFAULT_TRAIT_ACCESS, - pedantic, - "checks for literal calls to `Default::default()`" -} - -declare_lint_pass!(DefaultTraitAccess => [DEFAULT_TRAIT_ACCESS]); - -impl<'tcx> LateLintPass<'tcx> for DefaultTraitAccess { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - if let ExprKind::Call(ref path, ..) = expr.kind; - if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id); - if let ExprKind::Path(ref qpath) = path.kind; - if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id(); - if match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD); - // Detect and ignore ::default() because these calls do explicitly name the type. - if let QPath::Resolved(None, _path) = qpath; - then { - let expr_ty = cx.typeck_results().expr_ty(expr); - if let ty::Adt(def, ..) = expr_ty.kind() { - // TODO: Work out a way to put "whatever the imported way of referencing - // this type in this file" rather than a fully-qualified type. - let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did)); - span_lint_and_sugg( - cx, - DEFAULT_TRAIT_ACCESS, - expr.span, - &format!("calling `{}` is more clear than this expression", replacement), - "try", - replacement, - Applicability::Unspecified, // First resolve the TODO above - ); - } - } - } - } -} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 5c3af014ee1..bb94b3f6cbe 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -176,7 +176,7 @@ mod copies; mod copy_iterator; mod create_dir; mod dbg_macro; -mod default_trait_access; +mod default; mod dereference; mod derive; mod disallowed_method; @@ -537,7 +537,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ©_iterator::COPY_ITERATOR, &create_dir::CREATE_DIR, &dbg_macro::DBG_MACRO, - &default_trait_access::DEFAULT_TRAIT_ACCESS, + &default::DEFAULT_TRAIT_ACCESS, + &default::FIELD_REASSIGN_WITH_DEFAULT, &dereference::EXPLICIT_DEREF_METHODS, &derive::DERIVE_HASH_XOR_EQ, &derive::DERIVE_ORD_XOR_PARTIAL_ORD, @@ -1049,7 +1050,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd); store.register_late_pass(|| box unwrap::Unwrap); store.register_late_pass(|| box duration_subsec::DurationSubsec); - store.register_late_pass(|| box default_trait_access::DefaultTraitAccess); store.register_late_pass(|| box indexing_slicing::IndexingSlicing); store.register_late_pass(|| box non_copy_const::NonCopyConst); store.register_late_pass(|| box ptr_offset_with_cast::PtrOffsetWithCast); @@ -1100,6 +1100,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let enum_variant_name_threshold = conf.enum_variant_name_threshold; store.register_early_pass(move || box enum_variants::EnumVariantNames::new(enum_variant_name_threshold)); store.register_early_pass(|| box tabs_in_doc_comments::TabsInDocComments); + store.register_late_pass(|| box default::Default::default()); store.register_late_pass(|| box unused_self::UnusedSelf); store.register_late_pass(|| box mutable_debug_assertion::DebugAssertWithMutCall); store.register_late_pass(|| box exit::Exit); @@ -1212,7 +1213,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&checked_conversions::CHECKED_CONVERSIONS), LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION), LintId::of(©_iterator::COPY_ITERATOR), - LintId::of(&default_trait_access::DEFAULT_TRAIT_ACCESS), + 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), @@ -1321,6 +1322,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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(&derive::DERIVE_HASH_XOR_EQ), LintId::of(&derive::DERIVE_ORD_XOR_PARTIAL_ORD), LintId::of(&doc::MISSING_SAFETY_DOC), @@ -1581,6 +1583,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), LintId::of(&collapsible_if::COLLAPSIBLE_IF), 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), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 016bda77ef5..a3645a65182 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -359,7 +359,7 @@ vec![ group: "pedantic", desc: "checks for literal calls to `Default::default()`", deprecation: None, - module: "default_trait_access", + module: "default", }, Lint { name: "deprecated_cfg_attr", @@ -627,6 +627,13 @@ vec![ deprecation: None, module: "fallible_impl_from", }, + Lint { + name: "field_reassign_with_default", + group: "style", + desc: "binding initialized with Default should have its fields set in the initializer", + deprecation: None, + module: "default", + }, Lint { name: "filetype_is_file", group: "restriction", diff --git a/tests/ui/field_reassign_with_default.rs b/tests/ui/field_reassign_with_default.rs new file mode 100644 index 00000000000..79a30c22f95 --- /dev/null +++ b/tests/ui/field_reassign_with_default.rs @@ -0,0 +1,110 @@ +#![warn(clippy::field_reassign_with_default)] + +#[derive(Default)] +struct A { + i: i32, + j: i64, +} + +struct B { + i: i32, + j: i64, +} + +/// Implements .next() that returns a different number each time. +struct SideEffect(i32); + +impl SideEffect { + fn new() -> SideEffect { + SideEffect(0) + } + fn next(&mut self) -> i32 { + self.0 += 1; + self.0 + } +} + +fn main() { + // wrong, produces first error in stderr + let mut a: A = Default::default(); + a.i = 42; + + // right + let mut a: A = Default::default(); + + // right + let a = A { + i: 42, + ..Default::default() + }; + + // right + let mut a: A = Default::default(); + if a.i == 0 { + a.j = 12; + } + + // right + let mut a: A = Default::default(); + let b = 5; + + // right + let mut b = 32; + let mut a: A = Default::default(); + b = 2; + + // right + let b: B = B { i: 42, j: 24 }; + + // right + let mut b: B = B { i: 42, j: 24 }; + b.i = 52; + + // right + let mut b = B { i: 15, j: 16 }; + let mut a: A = Default::default(); + b.i = 2; + + // wrong, produces second error in stderr + let mut a: A = Default::default(); + a.j = 43; + a.i = 42; + + // wrong, produces third error in stderr + let mut a: A = Default::default(); + a.i = 42; + a.j = 43; + a.j = 44; + + // wrong, produces fourth error in stderr + let mut a = A::default(); + a.i = 42; + + // wrong, but does not produce an error in stderr, because we can't produce a correct kind of + // suggestion with current implementation + let mut c: (i32, i32) = Default::default(); + c.0 = 42; + c.1 = 21; + + // wrong, produces the fifth error in stderr + let mut a: A = Default::default(); + a.i = Default::default(); + + // wrong, produces the sixth error in stderr + let mut a: A = Default::default(); + a.i = Default::default(); + a.j = 45; + + // right, because an assignment refers to another field + let mut x = A::default(); + x.i = 42; + x.j = 21 + x.i as i64; + + // right, we bail out if there's a reassignment to the same variable, since there is a risk of + // side-effects affecting the outcome + let mut x = A::default(); + let mut side_effect = SideEffect::new(); + x.i = side_effect.next(); + x.j = 2; + x.i = side_effect.next(); +} diff --git a/tests/ui/field_reassign_with_default.stderr b/tests/ui/field_reassign_with_default.stderr new file mode 100644 index 00000000000..c788ebae552 --- /dev/null +++ b/tests/ui/field_reassign_with_default.stderr @@ -0,0 +1,75 @@ +error: field assignment outside of initializer for an instance created with Default::default() + --> $DIR/field_reassign_with_default.rs:30:5 + | +LL | a.i = 42; + | ^^^^^^^^^ + | + = note: `-D clippy::field-reassign-with-default` implied by `-D warnings` +note: consider initializing the variable with `A { i: 42, ..Default::default() }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:29:5 + | +LL | let mut a: A = Default::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: field assignment outside of initializer for an instance created with Default::default() + --> $DIR/field_reassign_with_default.rs:70:5 + | +LL | a.j = 43; + | ^^^^^^^^^ + | +note: consider initializing the variable with `A { j: 43, i: 42 }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:69:5 + | +LL | let mut a: A = Default::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: field assignment outside of initializer for an instance created with Default::default() + --> $DIR/field_reassign_with_default.rs:75:5 + | +LL | a.i = 42; + | ^^^^^^^^^ + | +note: consider initializing the variable with `A { i: 42, j: 44 }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:74:5 + | +LL | let mut a: A = Default::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: field assignment outside of initializer for an instance created with Default::default() + --> $DIR/field_reassign_with_default.rs:81:5 + | +LL | a.i = 42; + | ^^^^^^^^^ + | +note: consider initializing the variable with `A { i: 42, ..Default::default() }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:80:5 + | +LL | let mut a = A::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: field assignment outside of initializer for an instance created with Default::default() + --> $DIR/field_reassign_with_default.rs:91:5 + | +LL | a.i = Default::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: consider initializing the variable with `A::default()` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:90:5 + | +LL | let mut a: A = Default::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: field assignment outside of initializer for an instance created with Default::default() + --> $DIR/field_reassign_with_default.rs:95:5 + | +LL | a.i = Default::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: consider initializing the variable with `A { j: 45, ..Default::default() }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:94:5 + | +LL | let mut a: A = Default::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors + -- cgit 1.4.1-3-g733a5 From 158bf9aa443c5ea93ff4b3def8c7220632e45442 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sat, 31 Oct 2020 19:56:51 +0100 Subject: Fix incorrect suggestion for macro expansion in `deref_addrof` lint --- clippy_lints/src/reference.rs | 8 +++++++- tests/ui/deref_addrof.fixed | 15 ++++++++++++++- tests/ui/deref_addrof.rs | 15 ++++++++++++++- tests/ui/deref_addrof.stderr | 13 ++++++++++++- 4 files changed, 47 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index 3fda00403c6..f1555ab3ec1 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -46,13 +46,19 @@ impl EarlyLintPass for DerefAddrOf { if !in_macro(addrof_target.span); then { let mut applicability = Applicability::MachineApplicable; + let sugg = if e.span.from_expansion() { + let snip = snippet_with_applicability(cx, e.span, "_", &mut applicability); + snip.trim_start_matches(|c| c == '&' || c == '*').to_string() + } else { + snippet_with_applicability(cx, addrof_target.span, "_", &mut applicability).to_string() + }; span_lint_and_sugg( cx, DEREF_ADDROF, e.span, "immediately dereferencing a reference", "try this", - format!("{}", snippet_with_applicability(cx, addrof_target.span, "_", &mut applicability)), + sugg, applicability, ); } diff --git a/tests/ui/deref_addrof.fixed b/tests/ui/deref_addrof.fixed index 9e5b51d6d5e..8cdec4201fe 100644 --- a/tests/ui/deref_addrof.fixed +++ b/tests/ui/deref_addrof.fixed @@ -1,4 +1,5 @@ // run-rustfix +#![warn(clippy::deref_addrof)] fn get_number() -> usize { 10 @@ -10,7 +11,6 @@ fn get_reference(n: &usize) -> &usize { #[allow(clippy::many_single_char_names, clippy::double_parens)] #[allow(unused_variables, unused_parens)] -#[warn(clippy::deref_addrof)] fn main() { let a = 10; let aref = &a; @@ -37,3 +37,16 @@ fn main() { let b = *aref; } + +macro_rules! m { + ($visitor: expr) => { + $visitor + }; +} + +pub struct S; +impl S { + pub fn f(&self) -> &Self { + m!(self) + } +} diff --git a/tests/ui/deref_addrof.rs b/tests/ui/deref_addrof.rs index 5641a73cbc1..6433e63ca59 100644 --- a/tests/ui/deref_addrof.rs +++ b/tests/ui/deref_addrof.rs @@ -1,4 +1,5 @@ // run-rustfix +#![warn(clippy::deref_addrof)] fn get_number() -> usize { 10 @@ -10,7 +11,6 @@ fn get_reference(n: &usize) -> &usize { #[allow(clippy::many_single_char_names, clippy::double_parens)] #[allow(unused_variables, unused_parens)] -#[warn(clippy::deref_addrof)] fn main() { let a = 10; let aref = &a; @@ -37,3 +37,16 @@ fn main() { let b = **&aref; } + +macro_rules! m { + ($visitor: expr) => { + *&$visitor + }; +} + +pub struct S; +impl S { + pub fn f(&self) -> &Self { + m!(self) + } +} diff --git a/tests/ui/deref_addrof.stderr b/tests/ui/deref_addrof.stderr index bc51719e8a7..1fe49ad49cd 100644 --- a/tests/ui/deref_addrof.stderr +++ b/tests/ui/deref_addrof.stderr @@ -48,5 +48,16 @@ error: immediately dereferencing a reference LL | let b = **&aref; | ^^^^^^ help: try this: `aref` -error: aborting due to 8 previous errors +error: immediately dereferencing a reference + --> $DIR/deref_addrof.rs:43:9 + | +LL | *&$visitor + | ^^^^^^^^^^ help: try this: `$visitor` +... +LL | m!(self) + | -------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 9 previous errors -- cgit 1.4.1-3-g733a5 From 343bdb33647627a6a001be039a107e7ef3362707 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Mon, 2 Nov 2020 17:56:54 +0100 Subject: Give better suggestion by working on span on `deref_addrof` lint --- clippy_lints/src/reference.rs | 36 +++++++++++++++++++++++++++++++----- tests/ui/deref_addrof.fixed | 10 ++++++++++ tests/ui/deref_addrof.rs | 12 +++++++++++- tests/ui/deref_addrof.stderr | 17 ++++++++++++++--- 4 files changed, 66 insertions(+), 9 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index f1555ab3ec1..8646d616735 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -1,9 +1,11 @@ -use crate::utils::{in_macro, snippet_with_applicability, span_lint_and_sugg}; +use crate::utils::{in_macro, snippet_opt, snippet_with_applicability, span_lint_and_sugg}; use if_chain::if_chain; -use rustc_ast::ast::{Expr, ExprKind, UnOp}; +use rustc_ast::ast::{Expr, ExprKind, UnOp, Mutability}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::BytePos; +// use rustc_span::source_map::{BytePos, Span}; declare_clippy_lint! { /// **What it does:** Checks for usage of `*&` and `*&mut` in expressions. @@ -42,13 +44,37 @@ impl EarlyLintPass for DerefAddrOf { fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) { if_chain! { if let ExprKind::Unary(UnOp::Deref, ref deref_target) = e.kind; - if let ExprKind::AddrOf(_, _, ref addrof_target) = without_parens(deref_target).kind; + if let ExprKind::AddrOf(_, ref mutability, ref addrof_target) = without_parens(deref_target).kind; if !in_macro(addrof_target.span); then { let mut applicability = Applicability::MachineApplicable; let sugg = if e.span.from_expansion() { - let snip = snippet_with_applicability(cx, e.span, "_", &mut applicability); - snip.trim_start_matches(|c| c == '&' || c == '*').to_string() + if let Ok(macro_source) = cx.sess.source_map().span_to_snippet(e.span) { + // Remove leading whitespace from the given span + // e.g: ` $visitor` turns into `$visitor` + let trim_leading_whitespaces = |span| { + if let Some(start_no_whitespace) = snippet_opt(cx, span).and_then(|snip| { + snip.find(|c: char| !c.is_whitespace()).map(|pos| { + span.lo() + BytePos(pos as u32) + }) + }) { + e.span.with_lo(start_no_whitespace) + } else { + span + } + }; + + let rpos = if *mutability == Mutability::Mut { + macro_source.rfind("mut").expect("already checked this is a mutable reference") + "mut".len() + } else { + macro_source.rfind("&").expect("already checked this is a reference") + "&".len() + }; + let span_after_ref = e.span.with_lo(BytePos(e.span.lo().0 + rpos as u32)); + let span = trim_leading_whitespaces(span_after_ref); + snippet_with_applicability(cx, span, "_", &mut applicability).to_string() + } else { + snippet_with_applicability(cx, e.span, "_", &mut applicability).to_string() + } } else { snippet_with_applicability(cx, addrof_target.span, "_", &mut applicability).to_string() }; diff --git a/tests/ui/deref_addrof.fixed b/tests/ui/deref_addrof.fixed index 8cdec4201fe..689e9d55223 100644 --- a/tests/ui/deref_addrof.fixed +++ b/tests/ui/deref_addrof.fixed @@ -44,9 +44,19 @@ macro_rules! m { }; } +#[rustfmt::skip] +macro_rules! m_mut { + ($visitor: expr) => { + $visitor + }; +} + pub struct S; impl S { pub fn f(&self) -> &Self { m!(self) } + pub fn f_mut(&self) -> &Self { + m_mut!(self) + } } diff --git a/tests/ui/deref_addrof.rs b/tests/ui/deref_addrof.rs index 6433e63ca59..57effce5d12 100644 --- a/tests/ui/deref_addrof.rs +++ b/tests/ui/deref_addrof.rs @@ -40,7 +40,14 @@ fn main() { macro_rules! m { ($visitor: expr) => { - *&$visitor + *& $visitor + }; +} + +#[rustfmt::skip] +macro_rules! m_mut { + ($visitor: expr) => { + *& mut $visitor }; } @@ -49,4 +56,7 @@ impl S { pub fn f(&self) -> &Self { m!(self) } + pub fn f_mut(&self) -> &Self { + m_mut!(self) + } } diff --git a/tests/ui/deref_addrof.stderr b/tests/ui/deref_addrof.stderr index 1fe49ad49cd..52c1f7d1da8 100644 --- a/tests/ui/deref_addrof.stderr +++ b/tests/ui/deref_addrof.stderr @@ -51,13 +51,24 @@ LL | let b = **&aref; error: immediately dereferencing a reference --> $DIR/deref_addrof.rs:43:9 | -LL | *&$visitor - | ^^^^^^^^^^ help: try this: `$visitor` +LL | *& $visitor + | ^^^^^^^^^^^ help: try this: `$visitor` ... LL | m!(self) | -------- in this macro invocation | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 9 previous errors +error: immediately dereferencing a reference + --> $DIR/deref_addrof.rs:50:9 + | +LL | *& mut $visitor + | ^^^^^^^^^^^^^^^ help: try this: `$visitor` +... +LL | m_mut!(self) + | ------------ in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 10 previous errors -- cgit 1.4.1-3-g733a5 From ce98468158318a47f6bab3707d348e116451ad39 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Mon, 2 Nov 2020 17:59:47 +0100 Subject: Fix incorrect suggestion when from expansion in `try_err` lint --- clippy_lints/src/try_err.rs | 10 ++++++++-- tests/ui/try_err.fixed | 16 ++++++++++++++++ tests/ui/try_err.rs | 16 ++++++++++++++++ tests/ui/try_err.stderr | 21 ++++++++++++++++----- 4 files changed, 56 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/try_err.rs b/clippy_lints/src/try_err.rs index 3e747ec4ad9..e6d8e7521a8 100644 --- a/clippy_lints/src/try_err.rs +++ b/clippy_lints/src/try_err.rs @@ -1,5 +1,5 @@ use crate::utils::{ - is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet, snippet_with_macro_callsite, + in_macro, is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet, snippet_with_macro_callsite, span_lint_and_sugg, }; use if_chain::if_chain; @@ -92,9 +92,13 @@ impl<'tcx> LateLintPass<'tcx> for TryErr { let expr_err_ty = cx.typeck_results().expr_ty(err_arg); - let origin_snippet = if err_arg.span.from_expansion() { + // println!("\n\n{:?}", in_macro(expr.span)); + // println!("{:#?}", snippet(cx, err_arg.span, "_")); + let origin_snippet = if err_arg.span.from_expansion() && !in_macro(expr.span) { + // println!("from expansion"); snippet_with_macro_callsite(cx, err_arg.span, "_") } else { + // println!("just a snippet"); snippet(cx, err_arg.span, "_") }; let suggestion = if err_ty == expr_err_ty { @@ -102,6 +106,8 @@ impl<'tcx> LateLintPass<'tcx> for TryErr { } else { format!("return {}{}.into(){}", prefix, origin_snippet, suffix) }; + // println!("origin_snippet: {:#?}", origin_snippet); + // println!("suggestion: {:#?}", suggestion); span_lint_and_sugg( cx, diff --git a/tests/ui/try_err.fixed b/tests/ui/try_err.fixed index 9e77dcd8731..053dd45f23e 100644 --- a/tests/ui/try_err.fixed +++ b/tests/ui/try_err.fixed @@ -78,12 +78,28 @@ fn nested_error() -> Result { Ok(1) } +// Bad suggestion when in macro (see #6242) +macro_rules! try_validation { + ($e: expr) => {{ + match $e { + Ok(_) => 0, + Err(_) => return Err(1), + } + }}; +} + +fn calling_macro() -> Result { + try_validation!(Ok::<_, i32>(5)); + Ok(5) +} + fn main() { basic_test().unwrap(); into_test().unwrap(); negative_test().unwrap(); closure_matches_test().unwrap(); closure_into_test().unwrap(); + calling_macro().unwrap(); // We don't want to lint in external macros try_err!(); diff --git a/tests/ui/try_err.rs b/tests/ui/try_err.rs index 41bcb0a189e..215ca6a07e6 100644 --- a/tests/ui/try_err.rs +++ b/tests/ui/try_err.rs @@ -78,12 +78,28 @@ fn nested_error() -> Result { Ok(1) } +// Bad suggestion when in macro (see #6242) +macro_rules! try_validation { + ($e: expr) => {{ + match $e { + Ok(_) => 0, + Err(_) => Err(1)?, + } + }}; +} + +fn calling_macro() -> Result { + try_validation!(Ok::<_, i32>(5)); + Ok(5) +} + fn main() { basic_test().unwrap(); into_test().unwrap(); negative_test().unwrap(); closure_matches_test().unwrap(); closure_into_test().unwrap(); + calling_macro().unwrap(); // We don't want to lint in external macros try_err!(); diff --git a/tests/ui/try_err.stderr b/tests/ui/try_err.stderr index 3f1cbc17e72..443c8d08472 100644 --- a/tests/ui/try_err.stderr +++ b/tests/ui/try_err.stderr @@ -29,28 +29,39 @@ LL | Err(err)?; | ^^^^^^^^^ help: try this: `return Err(err.into())` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:106:9 + --> $DIR/try_err.rs:86:23 + | +LL | Err(_) => Err(1)?, + | ^^^^^^^ help: try this: `return Err(1)` +... +LL | try_validation!(Ok::<_, i32>(5)); + | --------------------------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: returning an `Err(_)` with the `?` operator + --> $DIR/try_err.rs:122:9 | LL | Err(foo!())?; | ^^^^^^^^^^^^ help: try this: `return Err(foo!())` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:113:9 + --> $DIR/try_err.rs:129:9 | LL | Err(io::ErrorKind::WriteZero)? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Err(io::ErrorKind::WriteZero.into()))` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:115:9 + --> $DIR/try_err.rs:131:9 | LL | Err(io::Error::new(io::ErrorKind::InvalidInput, "error"))? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput, "error")))` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:123:9 + --> $DIR/try_err.rs:139:9 | LL | Err(io::ErrorKind::NotFound)? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Some(Err(io::ErrorKind::NotFound.into())))` -error: aborting due to 8 previous errors +error: aborting due to 9 previous errors -- cgit 1.4.1-3-g733a5 From b2332a7357b5da7220592aea4c711609eed54ef7 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 2 Nov 2020 11:47:17 -0600 Subject: Change lint to use const sym --- clippy_lints/src/utils/internal_lints.rs | 4 ++-- tests/ui/match_type_on_diag_item.stderr | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 6ca72d895c8..8b59a9541a7 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -225,7 +225,7 @@ declare_clippy_lint! { /// /// Good: /// ```rust,ignore - /// utils::is_type_diagnostic_item(cx, ty, sym!(vec_type)) + /// utils::is_type_diagnostic_item(cx, ty, sym::vec_type) /// ``` pub MATCH_TYPE_ON_DIAGNOSTIC_ITEM, internal, @@ -724,7 +724,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem { expr.span, "usage of `utils::match_type()` on a type diagnostic item", "try", - format!("utils::is_type_diagnostic_item({}, {}, sym!({}))", cx_snippet, ty_snippet, item_name), + format!("utils::is_type_diagnostic_item({}, {}, sym::{})", cx_snippet, ty_snippet, item_name), Applicability::MaybeIncorrect, ); } diff --git a/tests/ui/match_type_on_diag_item.stderr b/tests/ui/match_type_on_diag_item.stderr index 5e5fe9e3a3e..82465dbaf6e 100644 --- a/tests/ui/match_type_on_diag_item.stderr +++ b/tests/ui/match_type_on_diag_item.stderr @@ -2,7 +2,7 @@ error: usage of `utils::match_type()` on a type diagnostic item --> $DIR/match_type_on_diag_item.rs:41:17 | LL | let _ = match_type(cx, ty, &paths::VEC); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(vec_type))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::vec_type)` | note: the lint level is defined here --> $DIR/match_type_on_diag_item.rs:1:9 @@ -15,19 +15,19 @@ error: usage of `utils::match_type()` on a type diagnostic item --> $DIR/match_type_on_diag_item.rs:42:17 | LL | let _ = match_type(cx, ty, &OPTION); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(option_type))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::option_type)` error: usage of `utils::match_type()` on a type diagnostic item --> $DIR/match_type_on_diag_item.rs:43:17 | LL | let _ = match_type(cx, ty, &["core", "result", "Result"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(result_type))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::result_type)` error: usage of `utils::match_type()` on a type diagnostic item --> $DIR/match_type_on_diag_item.rs:46:17 | LL | let _ = utils::match_type(cx, ty, rc_path); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym!(Rc))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::Rc)` error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From 1b117f46296b5c15848d02a4433912a604cc9873 Mon Sep 17 00:00:00 2001 From: Piti the little Light Date: Thu, 10 Sep 2020 20:18:23 +0200 Subject: Add tests for `from_iter_instead_of_collect` --- tests/ui/from_iter_instead_of_collect.rs | 15 +++++++++++++++ tests/ui/from_iter_instead_of_collect.stderr | 19 +++++++++++++++++++ tests/ui/from_iter_instead_of_collect.stdout | 0 3 files changed, 34 insertions(+) create mode 100644 tests/ui/from_iter_instead_of_collect.rs create mode 100644 tests/ui/from_iter_instead_of_collect.stderr create mode 100644 tests/ui/from_iter_instead_of_collect.stdout (limited to 'tests') diff --git a/tests/ui/from_iter_instead_of_collect.rs b/tests/ui/from_iter_instead_of_collect.rs new file mode 100644 index 00000000000..7a1bc64f4bd --- /dev/null +++ b/tests/ui/from_iter_instead_of_collect.rs @@ -0,0 +1,15 @@ +#![warn(clippy::from_iter_instead_of_collect)] + +use std::collections::HashMap; +use std::iter::FromIterator; + +fn main() { + { + let iter_expr = std::iter::repeat(5).take(5); + + Vec::from_iter(iter_expr); + HashMap::::from_iter(vec![5, 5, 5, 5].iter().enumerate()); + //let v: Vec = iter_expr.collect(); + let a: Vec = Vec::new(); + } +} diff --git a/tests/ui/from_iter_instead_of_collect.stderr b/tests/ui/from_iter_instead_of_collect.stderr new file mode 100644 index 00000000000..4fadfb658fe --- /dev/null +++ b/tests/ui/from_iter_instead_of_collect.stderr @@ -0,0 +1,19 @@ +error: use `.collect()` instead of `::from_iter()` + --> $DIR/from_iter_instead_of_collect.rs:10:5 + | +LL | Vec::from_iter(iter_expr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::from-iter-instead-of-collect` implied by `-D warnings` + = help: consider using `iter_expr.collect()` + +error: use `.collect()` instead of `::from_iter()` + --> $DIR/from_iter_instead_of_collect.rs:11:5 + | +LL | HashMap::::from_iter(vec![5,5,5,5].iter().enumerate()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using `vec![5,5,5,5].iter().enumerate().collect()` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/from_iter_instead_of_collect.stdout b/tests/ui/from_iter_instead_of_collect.stdout new file mode 100644 index 00000000000..e69de29bb2d -- cgit 1.4.1-3-g733a5 From a85670652a37238db7328b5eaab7cb7794fa40c8 Mon Sep 17 00:00:00 2001 From: Piti the little Light Date: Thu, 1 Oct 2020 12:36:17 +0200 Subject: Update: stderr message format --- tests/ui/from_iter_instead_of_collect.stderr | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/tests/ui/from_iter_instead_of_collect.stderr b/tests/ui/from_iter_instead_of_collect.stderr index 4fadfb658fe..cfb92dfbbfd 100644 --- a/tests/ui/from_iter_instead_of_collect.stderr +++ b/tests/ui/from_iter_instead_of_collect.stderr @@ -1,19 +1,19 @@ error: use `.collect()` instead of `::from_iter()` - --> $DIR/from_iter_instead_of_collect.rs:10:5 + --> $DIR/from_iter_instead_of_collect.rs:10:9 | -LL | Vec::from_iter(iter_expr); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | Vec::from_iter(iter_expr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::from-iter-instead-of-collect` implied by `-D warnings` = help: consider using `iter_expr.collect()` error: use `.collect()` instead of `::from_iter()` - --> $DIR/from_iter_instead_of_collect.rs:11:5 + --> $DIR/from_iter_instead_of_collect.rs:11:9 | -LL | HashMap::::from_iter(vec![5,5,5,5].iter().enumerate()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | HashMap::::from_iter(vec![5, 5, 5, 5].iter().enumerate()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: consider using `vec![5,5,5,5].iter().enumerate().collect()` + = help: consider using `vec![5, 5, 5, 5].iter().enumerate().collect()` error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From 8906040445a6700be2aaf40ec09937b84dea5d6a Mon Sep 17 00:00:00 2001 From: Piti the little Light Date: Thu, 1 Oct 2020 18:04:05 +0200 Subject: Improvements from PR feedback --- clippy_lints/src/methods/mod.rs | 14 ++++++++------ clippy_lints/src/utils/paths.rs | 2 +- tests/ui/from_iter_instead_of_collect.rs | 10 +++------- tests/ui/from_iter_instead_of_collect.stderr | 15 ++++++--------- tests/ui/from_iter_instead_of_collect.stdout | 0 5 files changed, 18 insertions(+), 23 deletions(-) delete mode 100644 tests/ui/from_iter_instead_of_collect.stdout (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index c0c39dae865..70be8909c43 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1370,10 +1370,11 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for `from_iter()` function calls that implements `FromIterator` + /// **What it does:** Checks for `from_iter()` function calls on types that implement the `FromIterator` /// trait. /// - /// **Why is this bad?** Makes code less readable especially in method chaining. + /// **Why is this bad?** It is recommended style to use collect. See + /// [FromIterator documentation](https://doc.rust-lang.org/std/iter/trait.FromIterator.html) /// /// **Known problems:** None. /// @@ -3873,18 +3874,19 @@ fn lint_filetype_is_file(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir fn lint_from_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { let ty = cx.typeck_results().expr_ty(expr); - let id = get_trait_def_id(cx, &paths::FROM_ITERATOR_TRAIT).unwrap(); + let id = get_trait_def_id(cx, &paths::FROM_ITERATOR).unwrap(); if implements_trait(cx, ty, id, &[]) { // `expr` implements `FromIterator` trait let iter_expr = snippet(cx, args[0].span, ".."); - span_lint_and_help( + span_lint_and_sugg( cx, FROM_ITER_INSTEAD_OF_COLLECT, expr.span, "use `.collect()` instead of `::from_iter()`", - None, - &format!("consider using `{}.collect()`", iter_expr), + "consider using", + format!("`{}.collect()`", iter_expr), + Applicability::MaybeIncorrect ); } } diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 3aade8ca8a2..8afbd8930b6 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -44,7 +44,7 @@ pub const FN: [&str; 3] = ["core", "ops", "Fn"]; pub const FN_MUT: [&str; 3] = ["core", "ops", "FnMut"]; pub const FN_ONCE: [&str; 3] = ["core", "ops", "FnOnce"]; pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"]; -pub const FROM_ITERATOR_TRAIT: [&str; 3] = ["std", "iter", "FromIterator"]; +pub const FROM_ITERATOR: [&str; 3] = ["std", "iter", "FromIterator"]; pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"]; pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; pub const HASH: [&str; 3] = ["core", "hash", "Hash"]; diff --git a/tests/ui/from_iter_instead_of_collect.rs b/tests/ui/from_iter_instead_of_collect.rs index 7a1bc64f4bd..9071be33c64 100644 --- a/tests/ui/from_iter_instead_of_collect.rs +++ b/tests/ui/from_iter_instead_of_collect.rs @@ -4,12 +4,8 @@ use std::collections::HashMap; use std::iter::FromIterator; fn main() { - { - let iter_expr = std::iter::repeat(5).take(5); + let iter_expr = std::iter::repeat(5).take(5); - Vec::from_iter(iter_expr); - HashMap::::from_iter(vec![5, 5, 5, 5].iter().enumerate()); - //let v: Vec = iter_expr.collect(); - let a: Vec = Vec::new(); - } + Vec::from_iter(iter_expr); + HashMap::::from_iter(vec![5, 5, 5, 5].iter().enumerate()); } diff --git a/tests/ui/from_iter_instead_of_collect.stderr b/tests/ui/from_iter_instead_of_collect.stderr index cfb92dfbbfd..1bc787aa795 100644 --- a/tests/ui/from_iter_instead_of_collect.stderr +++ b/tests/ui/from_iter_instead_of_collect.stderr @@ -1,19 +1,16 @@ error: use `.collect()` instead of `::from_iter()` - --> $DIR/from_iter_instead_of_collect.rs:10:9 + --> $DIR/from_iter_instead_of_collect.rs:9:5 | -LL | Vec::from_iter(iter_expr); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | Vec::from_iter(iter_expr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: ``iter_expr.collect()`` | = note: `-D clippy::from-iter-instead-of-collect` implied by `-D warnings` - = help: consider using `iter_expr.collect()` error: use `.collect()` instead of `::from_iter()` - --> $DIR/from_iter_instead_of_collect.rs:11:9 + --> $DIR/from_iter_instead_of_collect.rs:10:5 | -LL | HashMap::::from_iter(vec![5, 5, 5, 5].iter().enumerate()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using `vec![5, 5, 5, 5].iter().enumerate().collect()` +LL | HashMap::::from_iter(vec![5, 5, 5, 5].iter().enumerate()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: ``vec![5, 5, 5, 5].iter().enumerate().collect()`` error: aborting due to 2 previous errors diff --git a/tests/ui/from_iter_instead_of_collect.stdout b/tests/ui/from_iter_instead_of_collect.stdout deleted file mode 100644 index e69de29bb2d..00000000000 -- cgit 1.4.1-3-g733a5 From 0ab96ba2c0196558dc49624f59e6fd9c717a02f6 Mon Sep 17 00:00:00 2001 From: Piti the little Light Date: Thu, 1 Oct 2020 18:17:50 +0200 Subject: Allow lint --- tests/ui/get_unwrap.fixed | 2 +- tests/ui/get_unwrap.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/ui/get_unwrap.fixed b/tests/ui/get_unwrap.fixed index 97e6b20f471..924c02a4054 100644 --- a/tests/ui/get_unwrap.fixed +++ b/tests/ui/get_unwrap.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![allow(unused_mut)] +#![allow(unused_mut, clippy::from_iter_instead_of_collect)] #![deny(clippy::get_unwrap)] use std::collections::BTreeMap; diff --git a/tests/ui/get_unwrap.rs b/tests/ui/get_unwrap.rs index 1c9a71c0969..c0c37bb7206 100644 --- a/tests/ui/get_unwrap.rs +++ b/tests/ui/get_unwrap.rs @@ -1,5 +1,5 @@ // run-rustfix -#![allow(unused_mut)] +#![allow(unused_mut, clippy::from_iter_instead_of_collect)] #![deny(clippy::get_unwrap)] use std::collections::BTreeMap; -- cgit 1.4.1-3-g733a5 From e320dd30428aeb85577b36afa594429a41bc07f6 Mon Sep 17 00:00:00 2001 From: Piti the little Light Date: Thu, 1 Oct 2020 18:34:36 +0200 Subject: Improve: error message --- clippy_lints/src/utils/paths.rs | 2 +- tests/ui/from_iter_instead_of_collect.rs | 2 +- tests/ui/from_iter_instead_of_collect.stderr | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 8afbd8930b6..95fe8733421 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -44,7 +44,7 @@ pub const FN: [&str; 3] = ["core", "ops", "Fn"]; pub const FN_MUT: [&str; 3] = ["core", "ops", "FnMut"]; pub const FN_ONCE: [&str; 3] = ["core", "ops", "FnOnce"]; pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"]; -pub const FROM_ITERATOR: [&str; 3] = ["std", "iter", "FromIterator"]; +pub const FROM_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "FromIterator"]; pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"]; pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; pub const HASH: [&str; 3] = ["core", "hash", "Hash"]; diff --git a/tests/ui/from_iter_instead_of_collect.rs b/tests/ui/from_iter_instead_of_collect.rs index 9071be33c64..25b87a0a903 100644 --- a/tests/ui/from_iter_instead_of_collect.rs +++ b/tests/ui/from_iter_instead_of_collect.rs @@ -5,7 +5,7 @@ use std::iter::FromIterator; fn main() { let iter_expr = std::iter::repeat(5).take(5); - Vec::from_iter(iter_expr); + HashMap::::from_iter(vec![5, 5, 5, 5].iter().enumerate()); } diff --git a/tests/ui/from_iter_instead_of_collect.stderr b/tests/ui/from_iter_instead_of_collect.stderr index 1bc787aa795..3263005d7ec 100644 --- a/tests/ui/from_iter_instead_of_collect.stderr +++ b/tests/ui/from_iter_instead_of_collect.stderr @@ -1,5 +1,5 @@ error: use `.collect()` instead of `::from_iter()` - --> $DIR/from_iter_instead_of_collect.rs:9:5 + --> $DIR/from_iter_instead_of_collect.rs:8:5 | LL | Vec::from_iter(iter_expr); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: ``iter_expr.collect()`` -- cgit 1.4.1-3-g733a5 From abdb7aeb55461f0355829be49a09ed86af066ae8 Mon Sep 17 00:00:00 2001 From: Piti the little Light Date: Thu, 1 Oct 2020 18:44:12 +0200 Subject: Remove backticks --- clippy_lints/src/methods/mod.rs | 2 +- tests/ui/from_iter_instead_of_collect.stderr | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 013ed67b8e5..2119cb28fc3 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3885,7 +3885,7 @@ fn lint_from_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr< expr.span, "use `.collect()` instead of `::from_iter()`", "consider using", - format!("`{}.collect()`", iter_expr), + format!("{}.collect()", iter_expr), Applicability::MaybeIncorrect, ); } diff --git a/tests/ui/from_iter_instead_of_collect.stderr b/tests/ui/from_iter_instead_of_collect.stderr index 3263005d7ec..7f248beadbe 100644 --- a/tests/ui/from_iter_instead_of_collect.stderr +++ b/tests/ui/from_iter_instead_of_collect.stderr @@ -2,7 +2,7 @@ error: use `.collect()` instead of `::from_iter()` --> $DIR/from_iter_instead_of_collect.rs:8:5 | LL | Vec::from_iter(iter_expr); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: ``iter_expr.collect()`` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `iter_expr.collect()` | = note: `-D clippy::from-iter-instead-of-collect` implied by `-D warnings` @@ -10,7 +10,7 @@ error: use `.collect()` instead of `::from_iter()` --> $DIR/from_iter_instead_of_collect.rs:10:5 | LL | HashMap::::from_iter(vec![5, 5, 5, 5].iter().enumerate()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: ``vec![5, 5, 5, 5].iter().enumerate().collect()`` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `vec![5, 5, 5, 5].iter().enumerate().collect()` error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From f359fb872b405fca196f40eadd341d1d06f1fb8b Mon Sep 17 00:00:00 2001 From: Piti the little Light Date: Thu, 1 Oct 2020 19:04:19 +0200 Subject: Improve error message --- clippy_lints/src/methods/mod.rs | 2 +- tests/ui/from_iter_instead_of_collect.stderr | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 2119cb28fc3..fde43f0055d 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3883,8 +3883,8 @@ fn lint_from_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr< cx, FROM_ITER_INSTEAD_OF_COLLECT, expr.span, + "usage of `FromIterator::from_iter`", "use `.collect()` instead of `::from_iter()`", - "consider using", format!("{}.collect()", iter_expr), Applicability::MaybeIncorrect, ); diff --git a/tests/ui/from_iter_instead_of_collect.stderr b/tests/ui/from_iter_instead_of_collect.stderr index 7f248beadbe..46bdc2f4e19 100644 --- a/tests/ui/from_iter_instead_of_collect.stderr +++ b/tests/ui/from_iter_instead_of_collect.stderr @@ -1,16 +1,16 @@ -error: use `.collect()` instead of `::from_iter()` +error: usage of `FromIterator::from_iter` --> $DIR/from_iter_instead_of_collect.rs:8:5 | LL | Vec::from_iter(iter_expr); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `iter_expr.collect()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter_expr.collect()` | = note: `-D clippy::from-iter-instead-of-collect` implied by `-D warnings` -error: use `.collect()` instead of `::from_iter()` +error: usage of `FromIterator::from_iter` --> $DIR/from_iter_instead_of_collect.rs:10:5 | LL | HashMap::::from_iter(vec![5, 5, 5, 5].iter().enumerate()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `vec![5, 5, 5, 5].iter().enumerate().collect()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `vec![5, 5, 5, 5].iter().enumerate().collect()` error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From 52d1ea3c9ad8ea97350ba7a0ca0a8e172cfcae78 Mon Sep 17 00:00:00 2001 From: Piti the little Light Date: Mon, 12 Oct 2020 17:27:50 +0200 Subject: Fix: Don't show lint for types that doesn't implement Iterator --- clippy_lints/src/methods/mod.rs | 7 +++++-- tests/ui/from_iter_instead_of_collect.rs | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index fde43f0055d..521b151f5e1 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3874,9 +3874,12 @@ fn lint_filetype_is_file(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir fn lint_from_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { let ty = cx.typeck_results().expr_ty(expr); - let id = get_trait_def_id(cx, &paths::FROM_ITERATOR).unwrap(); + let arg_ty = cx.typeck_results().expr_ty(&args[0]); - if implements_trait(cx, ty, id, &[]) { + let from_iter_id = get_trait_def_id(cx, &paths::FROM_ITERATOR).unwrap(); + let iter_id = get_trait_def_id(cx, &paths::ITERATOR).unwrap(); + + if implements_trait(cx, ty, from_iter_id, &[]) && implements_trait(cx, arg_ty, iter_id, &[]) { // `expr` implements `FromIterator` trait let iter_expr = snippet(cx, args[0].span, ".."); span_lint_and_sugg( diff --git a/tests/ui/from_iter_instead_of_collect.rs b/tests/ui/from_iter_instead_of_collect.rs index 25b87a0a903..045eb3133d3 100644 --- a/tests/ui/from_iter_instead_of_collect.rs +++ b/tests/ui/from_iter_instead_of_collect.rs @@ -8,4 +8,6 @@ fn main() { Vec::from_iter(iter_expr); HashMap::::from_iter(vec![5, 5, 5, 5].iter().enumerate()); + + Vec::from_iter(vec![42u32]); } -- cgit 1.4.1-3-g733a5 From bc27d1492d60ecfb0673629e28bc4bbe0b7fd886 Mon Sep 17 00:00:00 2001 From: Patrick José Pereira Date: Thu, 8 Oct 2020 00:19:56 -0300 Subject: Add string_from_utf8_as_bytes linter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Patrick José Pereira --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 3 ++ clippy_lints/src/strings.rs | 68 +++++++++++++++++++++++++++++-- clippy_lints/src/utils/paths.rs | 1 + src/lintlist/mod.rs | 7 ++++ tests/ui/string_from_utf8_as_bytes.fixed | 6 +++ tests/ui/string_from_utf8_as_bytes.rs | 6 +++ tests/ui/string_from_utf8_as_bytes.stderr | 10 +++++ 8 files changed, 99 insertions(+), 3 deletions(-) create mode 100644 tests/ui/string_from_utf8_as_bytes.fixed create mode 100644 tests/ui/string_from_utf8_as_bytes.rs create mode 100644 tests/ui/string_from_utf8_as_bytes.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b63dbb7eff..aeccf6cf83b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1952,6 +1952,7 @@ Released 2018-09-13 [`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add [`string_add_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add_assign [`string_extend_chars`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_extend_chars +[`string_from_utf8_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_from_utf8_as_bytes [`string_lit_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_lit_as_bytes [`string_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_to_string [`struct_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#struct_excessive_bools diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 5c3af014ee1..d29ba33064c 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -826,6 +826,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &stable_sort_primitive::STABLE_SORT_PRIMITIVE, &strings::STRING_ADD, &strings::STRING_ADD_ASSIGN, + &strings::STRING_FROM_UTF8_AS_BYTES, &strings::STRING_LIT_AS_BYTES, &suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL, &suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL, @@ -1515,6 +1516,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), 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(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), LintId::of(&swap::ALMOST_SWAPPED), @@ -1738,6 +1740,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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(&swap::MANUAL_SWAP), LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT), LintId::of(&transmute::CROSSPOINTER_TRANSMUTE), diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 3783bd78de2..a55df1e5502 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -1,5 +1,5 @@ use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -8,7 +8,10 @@ use rustc_span::source_map::Spanned; use if_chain::if_chain; use crate::utils::SpanlessEq; -use crate::utils::{get_parent_expr, is_allowed, is_type_diagnostic_item, span_lint, span_lint_and_sugg}; +use crate::utils::{ + get_parent_expr, is_allowed, is_type_diagnostic_item, match_function_call, method_calls, paths, span_lint, + span_lint_and_sugg, +}; declare_clippy_lint! { /// **What it does:** Checks for string appends of the form `x = x + y` (without @@ -173,16 +176,75 @@ fn is_add(cx: &LateContext<'_>, src: &Expr<'_>, target: &Expr<'_>) -> bool { } } +declare_clippy_lint! { + /// **What it does:** Check if the string is transformed to byte array and casted back to string. + /// + /// **Why is this bad?** It's unnecessary, the string can be used directly. + /// + /// **Known problems:** None + /// + /// **Example:** + /// ```rust + /// let _ = std::str::from_utf8(&"Hello World!".as_bytes()[6..11]).unwrap(); + /// ``` + /// could be written as + /// ```rust + /// let _ = &"Hello World!"[6..11]; + /// ``` + pub STRING_FROM_UTF8_AS_BYTES, + complexity, + "casting string slices to byte slices and back" +} + // Max length a b"foo" string can take const MAX_LENGTH_BYTE_STRING_LIT: usize = 32; -declare_lint_pass!(StringLitAsBytes => [STRING_LIT_AS_BYTES]); +declare_lint_pass!(StringLitAsBytes => [STRING_LIT_AS_BYTES, STRING_FROM_UTF8_AS_BYTES]); impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { use crate::utils::{snippet, snippet_with_applicability}; use rustc_ast::LitKind; + if_chain! { + // Find std::str::converts::from_utf8 + if let Some(args) = match_function_call(cx, e, &paths::STR_FROM_UTF8); + + // Find string::as_bytes + if let ExprKind::AddrOf(BorrowKind::Ref, _, ref args) = args[0].kind; + if let ExprKind::Index(ref left, ref right) = args.kind; + let (method_names, expressions, _) = method_calls(left, 1); + if method_names.len() == 1; + if expressions.len() == 1; + if expressions[0].len() == 1; + if method_names[0] == sym!(as_bytes); + + // Check for slicer + if let ExprKind::Struct(ref path, _, _) = right.kind; + if let QPath::LangItem(LangItem::Range, _) = path; + + then { + let mut applicability = Applicability::MachineApplicable; + let string_expression = &expressions[0][0]; + + let snippet_app = snippet_with_applicability( + cx, + string_expression.span, "..", + &mut applicability, + ); + + span_lint_and_sugg( + cx, + STRING_FROM_UTF8_AS_BYTES, + e.span, + "calling a slice of `as_bytes()` with `from_utf8` should be not necessary", + "try", + format!("Some(&{}[{}])", snippet_app, snippet(cx, right.span, "..")), + applicability + ) + } + } + if_chain! { if let ExprKind::MethodCall(path, _, args, _) = &e.kind; if path.ident.name == sym!(as_bytes); diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index cd72fdd61fd..0d0ecf9ae9f 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -120,6 +120,7 @@ pub const STRING: [&str; 3] = ["alloc", "string", "String"]; pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"]; pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"]; pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "", "ends_with"]; +pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"]; pub const STR_LEN: [&str; 4] = ["core", "str", "", "len"]; pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "", "starts_with"]; pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]; diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 016bda77ef5..4d824cf94be 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2230,6 +2230,13 @@ vec![ deprecation: None, module: "methods", }, + Lint { + name: "string_from_utf8_as_bytes", + group: "complexity", + desc: "casting string slices to byte slices and back", + deprecation: None, + module: "strings", + }, Lint { name: "string_lit_as_bytes", group: "nursery", diff --git a/tests/ui/string_from_utf8_as_bytes.fixed b/tests/ui/string_from_utf8_as_bytes.fixed new file mode 100644 index 00000000000..6e665cdd563 --- /dev/null +++ b/tests/ui/string_from_utf8_as_bytes.fixed @@ -0,0 +1,6 @@ +// run-rustfix +#![warn(clippy::string_from_utf8_as_bytes)] + +fn main() { + let _ = Some(&"Hello World!"[6..11]); +} diff --git a/tests/ui/string_from_utf8_as_bytes.rs b/tests/ui/string_from_utf8_as_bytes.rs new file mode 100644 index 00000000000..670d206d367 --- /dev/null +++ b/tests/ui/string_from_utf8_as_bytes.rs @@ -0,0 +1,6 @@ +// run-rustfix +#![warn(clippy::string_from_utf8_as_bytes)] + +fn main() { + let _ = std::str::from_utf8(&"Hello World!".as_bytes()[6..11]); +} diff --git a/tests/ui/string_from_utf8_as_bytes.stderr b/tests/ui/string_from_utf8_as_bytes.stderr new file mode 100644 index 00000000000..bf5e5d33e8f --- /dev/null +++ b/tests/ui/string_from_utf8_as_bytes.stderr @@ -0,0 +1,10 @@ +error: calling a slice of `as_bytes()` with `from_utf8` should be not necessary + --> $DIR/string_from_utf8_as_bytes.rs:5:13 + | +LL | let _ = std::str::from_utf8(&"Hello World!".as_bytes()[6..11]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(&"Hello World!"[6..11])` + | + = note: `-D clippy::string-from-utf8-as-bytes` implied by `-D warnings` + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From f83762b79cf23bfa91d77d6f04ec2b87b2159b07 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Mon, 2 Nov 2020 18:03:16 +0100 Subject: Skip rustfmt as it is wanted for this test --- clippy_lints/src/reference.rs | 23 ++++++++++------------- clippy_lints/src/try_err.rs | 6 ------ tests/ui/deref_addrof.fixed | 1 + tests/ui/deref_addrof.rs | 1 + tests/ui/deref_addrof.stderr | 4 ++-- 5 files changed, 14 insertions(+), 21 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index 8646d616735..35a1310d68b 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -1,11 +1,10 @@ use crate::utils::{in_macro, snippet_opt, snippet_with_applicability, span_lint_and_sugg}; use if_chain::if_chain; -use rustc_ast::ast::{Expr, ExprKind, UnOp, Mutability}; +use rustc_ast::ast::{Expr, ExprKind, Mutability, UnOp}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::BytePos; -// use rustc_span::source_map::{BytePos, Span}; declare_clippy_lint! { /// **What it does:** Checks for usage of `*&` and `*&mut` in expressions. @@ -53,31 +52,29 @@ impl EarlyLintPass for DerefAddrOf { // Remove leading whitespace from the given span // e.g: ` $visitor` turns into `$visitor` let trim_leading_whitespaces = |span| { - if let Some(start_no_whitespace) = snippet_opt(cx, span).and_then(|snip| { + snippet_opt(cx, span).and_then(|snip| { + #[allow(clippy::cast_possible_truncation)] snip.find(|c: char| !c.is_whitespace()).map(|pos| { span.lo() + BytePos(pos as u32) }) - }) { - e.span.with_lo(start_no_whitespace) - } else { - span - } + }).map_or(span, |start_no_whitespace| e.span.with_lo(start_no_whitespace)) }; let rpos = if *mutability == Mutability::Mut { macro_source.rfind("mut").expect("already checked this is a mutable reference") + "mut".len() } else { - macro_source.rfind("&").expect("already checked this is a reference") + "&".len() + macro_source.rfind('&').expect("already checked this is a reference") + "&".len() }; + #[allow(clippy::cast_possible_truncation)] let span_after_ref = e.span.with_lo(BytePos(e.span.lo().0 + rpos as u32)); let span = trim_leading_whitespaces(span_after_ref); - snippet_with_applicability(cx, span, "_", &mut applicability).to_string() + snippet_with_applicability(cx, span, "_", &mut applicability) } else { - snippet_with_applicability(cx, e.span, "_", &mut applicability).to_string() + snippet_with_applicability(cx, e.span, "_", &mut applicability) } } else { - snippet_with_applicability(cx, addrof_target.span, "_", &mut applicability).to_string() - }; + snippet_with_applicability(cx, addrof_target.span, "_", &mut applicability) + }.to_string(); span_lint_and_sugg( cx, DEREF_ADDROF, diff --git a/clippy_lints/src/try_err.rs b/clippy_lints/src/try_err.rs index e6d8e7521a8..9c1185d30f2 100644 --- a/clippy_lints/src/try_err.rs +++ b/clippy_lints/src/try_err.rs @@ -92,13 +92,9 @@ impl<'tcx> LateLintPass<'tcx> for TryErr { let expr_err_ty = cx.typeck_results().expr_ty(err_arg); - // println!("\n\n{:?}", in_macro(expr.span)); - // println!("{:#?}", snippet(cx, err_arg.span, "_")); let origin_snippet = if err_arg.span.from_expansion() && !in_macro(expr.span) { - // println!("from expansion"); snippet_with_macro_callsite(cx, err_arg.span, "_") } else { - // println!("just a snippet"); snippet(cx, err_arg.span, "_") }; let suggestion = if err_ty == expr_err_ty { @@ -106,8 +102,6 @@ impl<'tcx> LateLintPass<'tcx> for TryErr { } else { format!("return {}{}.into(){}", prefix, origin_snippet, suffix) }; - // println!("origin_snippet: {:#?}", origin_snippet); - // println!("suggestion: {:#?}", suggestion); span_lint_and_sugg( cx, diff --git a/tests/ui/deref_addrof.fixed b/tests/ui/deref_addrof.fixed index 689e9d55223..0795900558b 100644 --- a/tests/ui/deref_addrof.fixed +++ b/tests/ui/deref_addrof.fixed @@ -38,6 +38,7 @@ fn main() { let b = *aref; } +#[rustfmt::skip] macro_rules! m { ($visitor: expr) => { $visitor diff --git a/tests/ui/deref_addrof.rs b/tests/ui/deref_addrof.rs index 57effce5d12..60c4318601b 100644 --- a/tests/ui/deref_addrof.rs +++ b/tests/ui/deref_addrof.rs @@ -38,6 +38,7 @@ fn main() { let b = **&aref; } +#[rustfmt::skip] macro_rules! m { ($visitor: expr) => { *& $visitor diff --git a/tests/ui/deref_addrof.stderr b/tests/ui/deref_addrof.stderr index 52c1f7d1da8..e85b30fa56e 100644 --- a/tests/ui/deref_addrof.stderr +++ b/tests/ui/deref_addrof.stderr @@ -49,7 +49,7 @@ LL | let b = **&aref; | ^^^^^^ help: try this: `aref` error: immediately dereferencing a reference - --> $DIR/deref_addrof.rs:43:9 + --> $DIR/deref_addrof.rs:44:9 | LL | *& $visitor | ^^^^^^^^^^^ help: try this: `$visitor` @@ -60,7 +60,7 @@ LL | m!(self) = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: immediately dereferencing a reference - --> $DIR/deref_addrof.rs:50:9 + --> $DIR/deref_addrof.rs:51:9 | LL | *& mut $visitor | ^^^^^^^^^^^^^^^ help: try this: `$visitor` -- cgit 1.4.1-3-g733a5 From 83e75f92079909aa07306633f26f42eccfb608e1 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Thu, 5 Nov 2020 18:00:34 +0100 Subject: Fix incorrect suggestion for `try_err` lint when `Err` arg is itself a macro --- clippy_lints/src/try_err.rs | 9 ++++++--- tests/ui/try_err.fixed | 18 ++++++++++++++++++ tests/ui/try_err.rs | 18 ++++++++++++++++++ tests/ui/try_err.stderr | 21 ++++++++++++++++----- 4 files changed, 58 insertions(+), 8 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/try_err.rs b/clippy_lints/src/try_err.rs index 9c1185d30f2..e1dec63a98a 100644 --- a/clippy_lints/src/try_err.rs +++ b/clippy_lints/src/try_err.rs @@ -1,6 +1,6 @@ use crate::utils::{ - in_macro, is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet, snippet_with_macro_callsite, - span_lint_and_sugg, + differing_macro_contexts, in_macro, is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet, + snippet_with_macro_callsite, span_lint_and_sugg, }; use if_chain::if_chain; use rustc_errors::Applicability; @@ -91,8 +91,11 @@ impl<'tcx> LateLintPass<'tcx> for TryErr { }; let expr_err_ty = cx.typeck_results().expr_ty(err_arg); + let differing_contexts = differing_macro_contexts(expr.span, err_arg.span); - let origin_snippet = if err_arg.span.from_expansion() && !in_macro(expr.span) { + let origin_snippet = if in_macro(expr.span) && in_macro(err_arg.span) && differing_contexts { + snippet(cx, err_arg.span.ctxt().outer_expn_data().call_site, "_") + } else if err_arg.span.from_expansion() && !in_macro(expr.span) { snippet_with_macro_callsite(cx, err_arg.span, "_") } else { snippet(cx, err_arg.span, "_") diff --git a/tests/ui/try_err.fixed b/tests/ui/try_err.fixed index 053dd45f23e..aa43e69f79e 100644 --- a/tests/ui/try_err.fixed +++ b/tests/ui/try_err.fixed @@ -88,8 +88,26 @@ macro_rules! try_validation { }}; } +macro_rules! ret_one { + () => { + 1 + }; +} + +macro_rules! try_validation_in_macro { + ($e: expr) => {{ + match $e { + Ok(_) => 0, + Err(_) => return Err(ret_one!()), + } + }}; +} + fn calling_macro() -> Result { + // macro try_validation!(Ok::<_, i32>(5)); + // `Err` arg is another macro + try_validation_in_macro!(Ok::<_, i32>(5)); Ok(5) } diff --git a/tests/ui/try_err.rs b/tests/ui/try_err.rs index 215ca6a07e6..df3a9dc5367 100644 --- a/tests/ui/try_err.rs +++ b/tests/ui/try_err.rs @@ -88,8 +88,26 @@ macro_rules! try_validation { }}; } +macro_rules! ret_one { + () => { + 1 + }; +} + +macro_rules! try_validation_in_macro { + ($e: expr) => {{ + match $e { + Ok(_) => 0, + Err(_) => Err(ret_one!())?, + } + }}; +} + fn calling_macro() -> Result { + // macro try_validation!(Ok::<_, i32>(5)); + // `Err` arg is another macro + try_validation_in_macro!(Ok::<_, i32>(5)); Ok(5) } diff --git a/tests/ui/try_err.stderr b/tests/ui/try_err.stderr index 443c8d08472..3905ed2476b 100644 --- a/tests/ui/try_err.stderr +++ b/tests/ui/try_err.stderr @@ -40,28 +40,39 @@ LL | try_validation!(Ok::<_, i32>(5)); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:122:9 + --> $DIR/try_err.rs:101:23 + | +LL | Err(_) => Err(ret_one!())?, + | ^^^^^^^^^^^^^^^^ help: try this: `return Err(ret_one!())` +... +LL | try_validation_in_macro!(Ok::<_, i32>(5)); + | ------------------------------------------ in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: returning an `Err(_)` with the `?` operator + --> $DIR/try_err.rs:140:9 | LL | Err(foo!())?; | ^^^^^^^^^^^^ help: try this: `return Err(foo!())` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:129:9 + --> $DIR/try_err.rs:147:9 | LL | Err(io::ErrorKind::WriteZero)? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Err(io::ErrorKind::WriteZero.into()))` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:131:9 + --> $DIR/try_err.rs:149:9 | LL | Err(io::Error::new(io::ErrorKind::InvalidInput, "error"))? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput, "error")))` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:139:9 + --> $DIR/try_err.rs:157:9 | LL | Err(io::ErrorKind::NotFound)? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Some(Err(io::ErrorKind::NotFound.into())))` -error: aborting due to 9 previous errors +error: aborting due to 10 previous errors -- cgit 1.4.1-3-g733a5 From 1624b00bde42a674c50a03e63868e8b4d08b6b49 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Thu, 5 Nov 2020 13:56:57 +0900 Subject: Fix suggestion to add unneeded space in `manual_async` --- clippy_lints/src/manual_async_fn.rs | 17 ++++++++- tests/ui/manual_async_fn.fixed | 14 ++++++- tests/ui/manual_async_fn.rs | 20 ++++++++++ tests/ui/manual_async_fn.stderr | 74 +++++++++++++++++++++++++++++++++---- 4 files changed, 115 insertions(+), 10 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index 864d1ea87f5..e9d65abb443 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -69,7 +69,20 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { |diag| { if_chain! { if let Some(header_snip) = snippet_opt(cx, header_span); - if let Some(ret_pos) = header_snip.rfind("->"); + if let Some(ret_pos) = header_snip.rfind("->").map(|rpos| { + let mut rpos = rpos; + let chars: Vec = header_snip.chars().collect(); + while rpos > 1 { + if let Some(c) = chars.get(rpos - 1) { + if c.is_whitespace() { + rpos -= 1; + continue; + } + } + break; + } + rpos + }); if let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output); then { let help = format!("make the function `async` and {}", ret_sugg); @@ -194,7 +207,7 @@ fn suggested_ret(cx: &LateContext<'_>, output: &Ty<'_>) -> Option<(&'static str, }, _ => { let sugg = "return the output of the future directly"; - snippet_opt(cx, output.span).map(|snip| (sugg, format!("-> {}", snip))) + snippet_opt(cx, output.span).map(|snip| (sugg, format!(" -> {}", snip))) }, } } diff --git a/tests/ui/manual_async_fn.fixed b/tests/ui/manual_async_fn.fixed index 4f551690c43..5184f6fdb88 100644 --- a/tests/ui/manual_async_fn.fixed +++ b/tests/ui/manual_async_fn.fixed @@ -7,7 +7,19 @@ use std::future::Future; async fn fut() -> i32 { 42 } -async fn empty_fut() {} +#[rustfmt::skip] +async fn fut2() -> i32 { 42 } + +#[rustfmt::skip] +async fn fut3() -> i32 { 42 } + +async fn empty_fut() {} + +#[rustfmt::skip] +async fn empty_fut2() {} + +#[rustfmt::skip] +async fn empty_fut3() {} async fn core_fut() -> i32 { 42 } diff --git a/tests/ui/manual_async_fn.rs b/tests/ui/manual_async_fn.rs index 6ed60309947..68c0e591f0b 100644 --- a/tests/ui/manual_async_fn.rs +++ b/tests/ui/manual_async_fn.rs @@ -9,10 +9,30 @@ fn fut() -> impl Future { async { 42 } } +#[rustfmt::skip] +fn fut2() ->impl Future { + async { 42 } +} + +#[rustfmt::skip] +fn fut3()-> impl Future { + async { 42 } +} + fn empty_fut() -> impl Future { async {} } +#[rustfmt::skip] +fn empty_fut2() ->impl Future { + async {} +} + +#[rustfmt::skip] +fn empty_fut3()-> impl Future { + async {} +} + fn core_fut() -> impl core::future::Future { async move { 42 } } diff --git a/tests/ui/manual_async_fn.stderr b/tests/ui/manual_async_fn.stderr index ccd82867427..fdd43db3255 100644 --- a/tests/ui/manual_async_fn.stderr +++ b/tests/ui/manual_async_fn.stderr @@ -15,14 +15,44 @@ LL | fn fut() -> impl Future { 42 } | ^^^^^^ error: this function can be simplified using the `async fn` syntax - --> $DIR/manual_async_fn.rs:12:1 + --> $DIR/manual_async_fn.rs:13:1 + | +LL | fn fut2() ->impl Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: make the function `async` and return the output of the future directly + | +LL | async fn fut2() -> i32 { + | ^^^^^^^^^^^^^^^^^^^^^^ +help: move the body of the async block to the enclosing function + | +LL | fn fut2() ->impl Future { 42 } + | ^^^^^^ + +error: this function can be simplified using the `async fn` syntax + --> $DIR/manual_async_fn.rs:18:1 + | +LL | fn fut3()-> impl Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: make the function `async` and return the output of the future directly + | +LL | async fn fut3() -> i32 { + | ^^^^^^^^^^^^^^^^^^^^^^ +help: move the body of the async block to the enclosing function + | +LL | fn fut3()-> impl Future { 42 } + | ^^^^^^ + +error: this function can be simplified using the `async fn` syntax + --> $DIR/manual_async_fn.rs:22:1 | LL | fn empty_fut() -> impl Future { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: make the function `async` and remove the return type | -LL | async fn empty_fut() { +LL | async fn empty_fut() { | ^^^^^^^^^^^^^^^^^^^^ help: move the body of the async block to the enclosing function | @@ -30,7 +60,37 @@ LL | fn empty_fut() -> impl Future {} | ^^ error: this function can be simplified using the `async fn` syntax - --> $DIR/manual_async_fn.rs:16:1 + --> $DIR/manual_async_fn.rs:27:1 + | +LL | fn empty_fut2() ->impl Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: make the function `async` and remove the return type + | +LL | async fn empty_fut2() { + | ^^^^^^^^^^^^^^^^^^^^^ +help: move the body of the async block to the enclosing function + | +LL | fn empty_fut2() ->impl Future {} + | ^^ + +error: this function can be simplified using the `async fn` syntax + --> $DIR/manual_async_fn.rs:32:1 + | +LL | fn empty_fut3()-> impl Future { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: make the function `async` and remove the return type + | +LL | async fn empty_fut3() { + | ^^^^^^^^^^^^^^^^^^^^^ +help: move the body of the async block to the enclosing function + | +LL | fn empty_fut3()-> impl Future {} + | ^^ + +error: this function can be simplified using the `async fn` syntax + --> $DIR/manual_async_fn.rs:36:1 | LL | fn core_fut() -> impl core::future::Future { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -45,7 +105,7 @@ LL | fn core_fut() -> impl core::future::Future { 42 } | ^^^^^^ error: this function can be simplified using the `async fn` syntax - --> $DIR/manual_async_fn.rs:38:5 + --> $DIR/manual_async_fn.rs:58:5 | LL | fn inh_fut() -> impl Future { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -65,7 +125,7 @@ LL | let c = 21; ... error: this function can be simplified using the `async fn` syntax - --> $DIR/manual_async_fn.rs:73:1 + --> $DIR/manual_async_fn.rs:93:1 | LL | fn elided(_: &i32) -> impl Future + '_ { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -80,7 +140,7 @@ LL | fn elided(_: &i32) -> impl Future + '_ { 42 } | ^^^^^^ error: this function can be simplified using the `async fn` syntax - --> $DIR/manual_async_fn.rs:82:1 + --> $DIR/manual_async_fn.rs:102:1 | LL | fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future + 'a + 'b { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -94,5 +154,5 @@ help: move the body of the async block to the enclosing function LL | fn explicit<'a, 'b>(_: &'a i32, _: &'b i32) -> impl Future + 'a + 'b { 42 } | ^^^^^^ -error: aborting due to 6 previous errors +error: aborting due to 10 previous errors -- cgit 1.4.1-3-g733a5 From 5f57608604bd35bd11c1f33cbd7202250e072b54 Mon Sep 17 00:00:00 2001 From: Aleksei Latyshev Date: Fri, 6 Nov 2020 14:38:46 +0300 Subject: do not trigger map_clone in the case of &mut --- clippy_lints/src/map_clone.rs | 8 +++++--- tests/ui/map_clone.fixed | 8 ++++++++ tests/ui/map_clone.rs | 8 ++++++++ 3 files changed, 21 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs index 034cd99a9be..9a00608ce39 100644 --- a/clippy_lints/src/map_clone.rs +++ b/clippy_lints/src/map_clone.rs @@ -80,9 +80,11 @@ impl<'tcx> LateLintPass<'tcx> for MapClone { && match_trait_method(cx, closure_expr, &paths::CLONE_TRAIT) { let obj_ty = cx.typeck_results().expr_ty(&obj[0]); - if let ty::Ref(_, ty, _) = obj_ty.kind() { - let copy = is_copy(cx, ty); - lint(cx, e.span, args[0].span, copy); + if let ty::Ref(_, ty, mutability) = obj_ty.kind() { + if matches!(mutability, Mutability::Not) { + let copy = is_copy(cx, ty); + lint(cx, e.span, args[0].span, copy); + } } else { lint_needless_cloning(cx, e.span, args[0].span); } diff --git a/tests/ui/map_clone.fixed b/tests/ui/map_clone.fixed index 81c7f659efb..6e3a8e67e81 100644 --- a/tests/ui/map_clone.fixed +++ b/tests/ui/map_clone.fixed @@ -44,4 +44,12 @@ fn main() { let v = vec![&mut d]; let _: Vec = v.into_iter().map(|&mut x| x).collect(); } + + // Issue #6299 + { + let mut aa = 5; + let mut bb = 3; + let items = vec![&mut aa, &mut bb]; + let _: Vec<_> = items.into_iter().map(|x| x.clone()).collect(); + } } diff --git a/tests/ui/map_clone.rs b/tests/ui/map_clone.rs index 8ed164f0ed5..6fd395710d4 100644 --- a/tests/ui/map_clone.rs +++ b/tests/ui/map_clone.rs @@ -44,4 +44,12 @@ fn main() { let v = vec![&mut d]; let _: Vec = v.into_iter().map(|&mut x| x).collect(); } + + // Issue #6299 + { + let mut aa = 5; + let mut bb = 3; + let items = vec![&mut aa, &mut bb]; + let _: Vec<_> = items.into_iter().map(|x| x.clone()).collect(); + } } -- cgit 1.4.1-3-g733a5 From 8242b2f0a4a53f066fa579d7497ae4574e291e2e Mon Sep 17 00:00:00 2001 From: ThibsG Date: Fri, 30 Oct 2020 18:43:27 +0100 Subject: Remove needless allow --- tests/ui/option_option.rs | 2 -- tests/ui/option_option.stderr | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'tests') diff --git a/tests/ui/option_option.rs b/tests/ui/option_option.rs index a2617a13eca..24344833641 100644 --- a/tests/ui/option_option.rs +++ b/tests/ui/option_option.rs @@ -72,8 +72,6 @@ mod issue_4298 { #[serde(skip_serializing_if = "Option::is_none")] #[serde(default)] #[serde(borrow)] - // FIXME: should not lint here - #[allow(clippy::option_option)] foo: Option>>, } diff --git a/tests/ui/option_option.stderr b/tests/ui/option_option.stderr index 0cd4c96eb4f..8ae1d23a8e3 100644 --- a/tests/ui/option_option.stderr +++ b/tests/ui/option_option.stderr @@ -59,7 +59,7 @@ LL | Struct { x: Option> }, | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:77:14 + --> $DIR/option_option.rs:75:14 | LL | foo: Option>>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From c6a91df838b17a2366346db664072d7914ab241a Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Thu, 22 Oct 2020 23:39:25 -0700 Subject: Enable empty_loop lint for no_std crates We skip the lint if the `loop {}` is in the `#[panic_handler]` as the main recommendation we give is to panic, which obviously isn't possible in a panic handler. Signed-off-by: Joe Richey --- clippy_lints/src/loops.rs | 28 +++++++++++++--------------- clippy_lints/src/utils/mod.rs | 7 +++++++ tests/ui/empty_loop_no_std.rs | 7 ++++++- tests/ui/empty_loop_no_std.stderr | 19 +++++++++++++++++++ 4 files changed, 45 insertions(+), 16 deletions(-) create mode 100644 tests/ui/empty_loop_no_std.stderr (limited to 'tests') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 32c2562ee95..2a53210efe3 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -4,10 +4,10 @@ use crate::utils::sugg::Sugg; use crate::utils::usage::{is_unused, mutated_variables}; use crate::utils::{ contains_name, get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, - indent_of, is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, - match_trait_method, match_type, match_var, multispan_sugg, qpath_res, single_segment_path, snippet, - snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, - span_lint_and_then, sugg, SpanlessEq, + indent_of, is_in_panic_handler, is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, + last_path_segment, match_trait_method, match_type, match_var, multispan_sugg, qpath_res, single_segment_path, + snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, + span_lint_and_sugg, span_lint_and_then, sugg, SpanlessEq, }; use if_chain::if_chain; use rustc_ast::ast; @@ -543,17 +543,15 @@ impl<'tcx> LateLintPass<'tcx> for Loops { // (also matches an explicit "match" instead of "if let") // (even if the "match" or "if let" is used for declaration) if let ExprKind::Loop(ref block, _, LoopSource::Loop) = expr.kind { - // also check for empty `loop {}` statements - // TODO(issue #6161): Enable for no_std crates (outside of #[panic_handler]) - if block.stmts.is_empty() && block.expr.is_none() && !is_no_std_crate(cx.tcx.hir().krate()) { - span_lint_and_help( - cx, - EMPTY_LOOP, - expr.span, - "empty `loop {}` wastes CPU cycles", - None, - "You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body.", - ); + // also check for empty `loop {}` statements, skipping those in #[panic_handler] + if block.stmts.is_empty() && block.expr.is_none() && !is_in_panic_handler(cx, expr) { + let msg = "empty `loop {}` wastes CPU cycles"; + let help = if is_no_std_crate(cx.tcx.hir().krate()) { + "You should either use `panic!()` or add a call pausing or sleeping the thread to the loop body." + } else { + "You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body." + }; + span_lint_and_help(cx, EMPTY_LOOP, expr.span, msg, None, help); } // extract the expression from the first statement (if any) in a block diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 8e4149df032..cba3a050249 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -468,6 +468,13 @@ pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool { .map_or(false, |(entry_fn_def_id, _)| def_id == entry_fn_def_id.to_def_id()) } +/// Returns `true` if the expression is in the program's `#[panic_handler]`. +pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { + let parent = cx.tcx.hir().get_parent_item(e.hir_id); + let def_id = cx.tcx.hir().local_def_id(parent).to_def_id(); + Some(def_id) == cx.tcx.lang_items().panic_impl() +} + /// Gets the name of the item the expression is in, if available. pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id); diff --git a/tests/ui/empty_loop_no_std.rs b/tests/ui/empty_loop_no_std.rs index 879d1d5d916..4553d3ec505 100644 --- a/tests/ui/empty_loop_no_std.rs +++ b/tests/ui/empty_loop_no_std.rs @@ -10,13 +10,18 @@ use core::panic::PanicInfo; #[start] fn main(argc: isize, argv: *const *const u8) -> isize { + // This should trigger the lint loop {} } #[panic_handler] fn panic(_info: &PanicInfo) -> ! { + // This should NOT trigger the lint loop {} } #[lang = "eh_personality"] -extern "C" fn eh_personality() {} +extern "C" fn eh_personality() { + // This should also trigger the lint + loop {} +} diff --git a/tests/ui/empty_loop_no_std.stderr b/tests/ui/empty_loop_no_std.stderr new file mode 100644 index 00000000000..1cb454c3bbe --- /dev/null +++ b/tests/ui/empty_loop_no_std.stderr @@ -0,0 +1,19 @@ +error: empty `loop {}` wastes CPU cycles + --> $DIR/empty_loop_no_std.rs:14:5 + | +LL | loop {} + | ^^^^^^^ + | + = note: `-D clippy::empty-loop` implied by `-D warnings` + = help: You should either use `panic!()` or add a call pausing or sleeping the thread to the loop body. + +error: empty `loop {}` wastes CPU cycles + --> $DIR/empty_loop_no_std.rs:26:5 + | +LL | loop {} + | ^^^^^^^ + | + = help: You should either use `panic!()` or add a call pausing or sleeping the thread to the loop body. + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From 00dee9d9160e9030d387f2cef1ec2b6422478229 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sun, 8 Nov 2020 12:40:41 +0100 Subject: Update reference files --- tests/ui/crashes/ice-360.stderr | 2 +- tests/ui/empty_loop.stderr | 6 +++--- tests/ui/empty_loop_no_std.stderr | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/tests/ui/crashes/ice-360.stderr b/tests/ui/crashes/ice-360.stderr index bb03ce40355..0eb7bb12b35 100644 --- a/tests/ui/crashes/ice-360.stderr +++ b/tests/ui/crashes/ice-360.stderr @@ -19,7 +19,7 @@ LL | loop {} | ^^^^^^^ | = note: `-D clippy::empty-loop` implied by `-D warnings` - = help: You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body. + = help: you should either use `panic!()` or add `std::thread::sleep(..);` to the loop body error: aborting due to 2 previous errors diff --git a/tests/ui/empty_loop.stderr b/tests/ui/empty_loop.stderr index fd3979f259a..555f3d3d884 100644 --- a/tests/ui/empty_loop.stderr +++ b/tests/ui/empty_loop.stderr @@ -5,7 +5,7 @@ LL | loop {} | ^^^^^^^ | = note: `-D clippy::empty-loop` implied by `-D warnings` - = help: You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body. + = help: you should either use `panic!()` or add `std::thread::sleep(..);` to the loop body error: empty `loop {}` wastes CPU cycles --> $DIR/empty_loop.rs:11:9 @@ -13,7 +13,7 @@ error: empty `loop {}` wastes CPU cycles LL | loop {} | ^^^^^^^ | - = help: You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body. + = help: you should either use `panic!()` or add `std::thread::sleep(..);` to the loop body error: empty `loop {}` wastes CPU cycles --> $DIR/empty_loop.rs:15:9 @@ -21,7 +21,7 @@ error: empty `loop {}` wastes CPU cycles LL | 'inner: loop {} | ^^^^^^^^^^^^^^^ | - = help: You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body. + = help: you should either use `panic!()` or add `std::thread::sleep(..);` to the loop body error: aborting due to 3 previous errors diff --git a/tests/ui/empty_loop_no_std.stderr b/tests/ui/empty_loop_no_std.stderr index 1cb454c3bbe..520248fcb68 100644 --- a/tests/ui/empty_loop_no_std.stderr +++ b/tests/ui/empty_loop_no_std.stderr @@ -5,7 +5,7 @@ LL | loop {} | ^^^^^^^ | = note: `-D clippy::empty-loop` implied by `-D warnings` - = help: You should either use `panic!()` or add a call pausing or sleeping the thread to the loop body. + = help: you should either use `panic!()` or add a call pausing or sleeping the thread to the loop body error: empty `loop {}` wastes CPU cycles --> $DIR/empty_loop_no_std.rs:26:5 @@ -13,7 +13,7 @@ error: empty `loop {}` wastes CPU cycles LL | loop {} | ^^^^^^^ | - = help: You should either use `panic!()` or add a call pausing or sleeping the thread to the loop body. + = help: you should either use `panic!()` or add a call pausing or sleeping the thread to the loop body error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From f1f780c9422f038ae78e72a99b1ca2a0d7b392bc Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius III" Date: Sat, 7 Nov 2020 18:26:14 -0500 Subject: Add let_underscore_drop --- CHANGELOG.md | 1 + clippy_lints/src/let_underscore.rs | 56 +++++++++++++++++++++++++++++++++++-- clippy_lints/src/lib.rs | 3 ++ src/lintlist/mod.rs | 7 +++++ tests/ui/let_underscore_drop.rs | 19 +++++++++++++ tests/ui/let_underscore_drop.stderr | 27 ++++++++++++++++++ 6 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 tests/ui/let_underscore_drop.rs create mode 100644 tests/ui/let_underscore_drop.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index e0770a45c53..816d25bcd93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1787,6 +1787,7 @@ Released 2018-09-13 [`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty [`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero [`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return +[`let_underscore_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_drop [`let_underscore_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_lock [`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use [`let_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs index ae2f6131b5b..9c7fd634547 100644 --- a/clippy_lints/src/let_underscore.rs +++ b/clippy_lints/src/let_underscore.rs @@ -5,7 +5,7 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use crate::utils::{is_must_use_func_call, is_must_use_ty, match_type, paths, span_lint_and_help}; +use crate::utils::{implements_trait, is_must_use_func_call, is_must_use_ty, match_type, paths, span_lint_and_help}; declare_clippy_lint! { /// **What it does:** Checks for `let _ = ` @@ -58,7 +58,40 @@ declare_clippy_lint! { "non-binding let on a synchronization lock" } -declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK]); +declare_clippy_lint! { + /// **What it does:** Checks for `let _ = ` + /// where expr has a type that implements `Drop` + /// + /// **Why is this bad?** This statement immediately drops the initializer + /// expression instead of extending its lifetime to the end of the scope, which + /// is often not intended. To extend the expression's lifetime to the end of the + /// scope, use an underscore-prefixed name instead (i.e. _var). If you want to + /// explicitly drop the expression, `std::mem::drop` conveys your intention + /// better and is less error-prone. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// Bad: + /// ```rust,ignore + /// struct Droppable; + /// impl Drop for Droppable { + /// fn drop(&mut self) {} + /// } + /// let _ = Droppable; + /// ``` + /// + /// Good: + /// ```rust,ignore + /// let _droppable = Droppable; + /// ``` + pub LET_UNDERSCORE_DROP, + correctness, + "non-binding let on a type that implements `Drop`" +} + +declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_DROP]); const SYNC_GUARD_PATHS: [&[&str]; 3] = [ &paths::MUTEX_GUARD, @@ -84,6 +117,15 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, }); + let implements_drop = cx.tcx.lang_items().drop_trait().map_or(false, |drop_trait| + init_ty.walk().any(|inner| match inner.unpack() { + GenericArgKind::Type(inner_ty) => { + implements_trait(cx, inner_ty, drop_trait, &[]) + }, + + GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, + }) + ); if contains_sync_guard { span_lint_and_help( cx, @@ -94,6 +136,16 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { "consider using an underscore-prefixed named \ binding or dropping explicitly with `std::mem::drop`" ) + } else if implements_drop { + span_lint_and_help( + cx, + LET_UNDERSCORE_DROP, + local.span, + "non-binding let on a type that implements `Drop`", + None, + "consider using an underscore-prefixed named \ + binding or dropping explicitly with `std::mem::drop`" + ) } else if is_must_use_ty(cx, cx.typeck_results().expr_ty(init)) { span_lint_and_help( cx, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 1f4bed92e69..b44e28b4596 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -622,6 +622,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &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, @@ -1383,6 +1384,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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_DROP), LintId::of(&let_underscore::LET_UNDERSCORE_LOCK), LintId::of(&lifetimes::EXTRA_UNUSED_LIFETIMES), LintId::of(&lifetimes::NEEDLESS_LIFETIMES), @@ -1809,6 +1811,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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_DROP), LintId::of(&let_underscore::LET_UNDERSCORE_LOCK), LintId::of(&literal_representation::MISTYPED_LITERAL_SUFFIXES), LintId::of(&loops::FOR_LOOPS_OVER_FALLIBLES), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index bc0a0ad2b17..8b5e6cc916f 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1117,6 +1117,13 @@ vec![ deprecation: None, module: "returns", }, + Lint { + name: "let_underscore_drop", + group: "correctness", + desc: "non-binding let on a type that implements `Drop`", + deprecation: None, + module: "let_underscore", + }, Lint { name: "let_underscore_lock", group: "correctness", diff --git a/tests/ui/let_underscore_drop.rs b/tests/ui/let_underscore_drop.rs new file mode 100644 index 00000000000..98593edb9c5 --- /dev/null +++ b/tests/ui/let_underscore_drop.rs @@ -0,0 +1,19 @@ +#![warn(clippy::let_underscore_drop)] + +struct Droppable; + +impl Drop for Droppable { + fn drop(&mut self) {} +} + +fn main() { + let unit = (); + let boxed = Box::new(()); + let droppable = Droppable; + let optional = Some(Droppable); + + let _ = (); + let _ = Box::new(()); + let _ = Droppable; + let _ = Some(Droppable); +} diff --git a/tests/ui/let_underscore_drop.stderr b/tests/ui/let_underscore_drop.stderr new file mode 100644 index 00000000000..6dc8904c4fe --- /dev/null +++ b/tests/ui/let_underscore_drop.stderr @@ -0,0 +1,27 @@ +error: non-binding let on a type that implements `Drop` + --> $DIR/let_underscore_drop.rs:16:5 + | +LL | let _ = Box::new(()); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::let-underscore-drop` implied by `-D warnings` + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: non-binding let on a type that implements `Drop` + --> $DIR/let_underscore_drop.rs:17:5 + | +LL | let _ = Droppable; + | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: non-binding let on a type that implements `Drop` + --> $DIR/let_underscore_drop.rs:18:5 + | +LL | let _ = Some(Droppable); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: aborting due to 3 previous errors + -- cgit 1.4.1-3-g733a5 From 9c6a0b9c3490b321756c56d07c57f5537ea2b7fb Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius III" Date: Sun, 8 Nov 2020 07:07:49 -0500 Subject: Update references --- tests/ui/borrow_box.stderr | 11 +++- .../ui/borrow_interior_mutable_const/others.stderr | 27 ++++++++- tests/ui/box_vec.stderr | 15 ++++- tests/ui/crashes/ice-4968.stderr | 11 ++++ tests/ui/crashes/ice-5223.stderr | 11 ++++ tests/ui/escape_analysis.stderr | 23 +++++++- tests/ui/eta.stderr | 19 +++++- tests/ui/filter_methods.stderr | 47 ++++++++++++++- tests/ui/get_unwrap.stderr | 19 +++++- tests/ui/into_iter_on_ref.stderr | 11 +++- tests/ui/iter_cloned_collect.stderr | 23 +++++++- tests/ui/map_clone.stderr | 59 ++++++++++++++++++- tests/ui/map_collect_result_unit.stderr | 19 +++++- tests/ui/map_flatten.stderr | 43 +++++++++++++- tests/ui/map_identity.stderr | 35 ++++++++++- tests/ui/match_single_binding.stderr | 16 +++++- tests/ui/needless_pass_by_value.stderr | 11 +++- tests/ui/redundant_clone.stderr | 11 +++- tests/ui/reversed_empty_ranges_fixable.stderr | 11 +++- tests/ui/transmute.stderr | 43 +++++++++++++- tests/ui/transmute_collection.stderr | 67 +++++++++++++++++++++- tests/ui/unnecessary_clone.stderr | 11 +++- tests/ui/useless_conversion.stderr | 11 +++- 23 files changed, 533 insertions(+), 21 deletions(-) create mode 100644 tests/ui/crashes/ice-4968.stderr create mode 100644 tests/ui/crashes/ice-5223.stderr (limited to 'tests') diff --git a/tests/ui/borrow_box.stderr b/tests/ui/borrow_box.stderr index 3eac32815be..a40789cd426 100644 --- a/tests/ui/borrow_box.stderr +++ b/tests/ui/borrow_box.stderr @@ -22,6 +22,15 @@ error: you seem to be trying to use `&Box`. Consider using just `&T` LL | fn test4(a: &Box); | ^^^^^^^^^^ help: try: `&bool` +error: non-binding let on a type that implements `Drop` + --> $DIR/borrow_box.rs:63:5 + | +LL | let _ = foo; + | ^^^^^^^^^^^^ + | + = note: `#[deny(clippy::let_underscore_drop)]` on by default + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: you seem to be trying to use `&Box`. Consider using just `&T` --> $DIR/borrow_box.rs:95:25 | @@ -64,5 +73,5 @@ error: you seem to be trying to use `&Box`. Consider using just `&T` LL | pub fn test20(_display: &Box<(dyn Display + Send)>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(dyn Display + Send)` -error: aborting due to 10 previous errors +error: aborting due to 11 previous errors diff --git a/tests/ui/borrow_interior_mutable_const/others.stderr b/tests/ui/borrow_interior_mutable_const/others.stderr index 9a908cf30e9..976c412c7a8 100644 --- a/tests/ui/borrow_interior_mutable_const/others.stderr +++ b/tests/ui/borrow_interior_mutable_const/others.stderr @@ -47,6 +47,15 @@ LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability | = help: assign this const to a local or static variable, and use the variable here +error: non-binding let on a type that implements `Drop` + --> $DIR/others.rs:72:5 + | +LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::let_underscore_drop)]` on by default + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: a `const` item with interior mutability should not be borrowed --> $DIR/others.rs:72:14 | @@ -95,6 +104,22 @@ LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability | = help: assign this const to a local or static variable, and use the variable here +error: non-binding let on a type that implements `Drop` + --> $DIR/others.rs:83:5 + | +LL | let _ = ATOMIC_TUPLE.1.into_iter(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: non-binding let on a type that implements `Drop` + --> $DIR/others.rs:85:5 + | +LL | let _ = &{ ATOMIC_TUPLE }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: a `const` item with interior mutability should not be borrowed --> $DIR/others.rs:87:5 | @@ -111,5 +136,5 @@ LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability | = help: assign this const to a local or static variable, and use the variable here -error: aborting due to 14 previous errors +error: aborting due to 17 previous errors diff --git a/tests/ui/box_vec.stderr b/tests/ui/box_vec.stderr index fca12eddd57..a4983df1d30 100644 --- a/tests/ui/box_vec.stderr +++ b/tests/ui/box_vec.stderr @@ -1,3 +1,16 @@ +error: non-binding let on a type that implements `Drop` + --> $DIR/box_vec.rs:7:9 + | +LL | let _: Box<$x> = Box::new($init); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | boxit!(Vec::new(), Vec); + | ---------------------------- in this macro invocation + | + = note: `-D clippy::let-underscore-drop` implied by `-D warnings` + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + error: you seem to be trying to use `Box>`. Consider using just `Vec` --> $DIR/box_vec.rs:14:18 | @@ -7,5 +20,5 @@ LL | pub fn test(foo: Box>) { = note: `-D clippy::box-vec` implied by `-D warnings` = help: `Vec` is already on the heap, `Box>` makes an extra allocation. -error: aborting due to previous error +error: aborting due to 2 previous errors diff --git a/tests/ui/crashes/ice-4968.stderr b/tests/ui/crashes/ice-4968.stderr new file mode 100644 index 00000000000..9ce39027451 --- /dev/null +++ b/tests/ui/crashes/ice-4968.stderr @@ -0,0 +1,11 @@ +error: non-binding let on a type that implements `Drop` + --> $DIR/ice-4968.rs:16:9 + | +LL | let _: Vec> = mem::transmute(slice); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::let_underscore_drop)]` on by default + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: aborting due to previous error + diff --git a/tests/ui/crashes/ice-5223.stderr b/tests/ui/crashes/ice-5223.stderr new file mode 100644 index 00000000000..3ae2dd5f770 --- /dev/null +++ b/tests/ui/crashes/ice-5223.stderr @@ -0,0 +1,11 @@ +error: non-binding let on a type that implements `Drop` + --> $DIR/ice-5223.rs:14:9 + | +LL | let _ = self.arr.iter().cloned().collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::let_underscore_drop)]` on by default + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: aborting due to previous error + diff --git a/tests/ui/escape_analysis.stderr b/tests/ui/escape_analysis.stderr index c86a769a3da..6e1c1c07b6e 100644 --- a/tests/ui/escape_analysis.stderr +++ b/tests/ui/escape_analysis.stderr @@ -12,5 +12,26 @@ error: local variable doesn't need to be boxed here LL | pub fn new(_needs_name: Box>) -> () {} | ^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: non-binding let on a type that implements `Drop` + --> $DIR/escape_analysis.rs:166:9 + | +LL | / let _ = move || { +LL | | consume(x); +LL | | }; + | |__________^ + | + = note: `#[deny(clippy::let_underscore_drop)]` on by default + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: non-binding let on a type that implements `Drop` + --> $DIR/escape_analysis.rs:172:9 + | +LL | / let _ = || { +LL | | borrow(&x); +LL | | }; + | |__________^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: aborting due to 4 previous errors diff --git a/tests/ui/eta.stderr b/tests/ui/eta.stderr index c4713ca8083..bc79caee887 100644 --- a/tests/ui/eta.stderr +++ b/tests/ui/eta.stderr @@ -64,6 +64,15 @@ error: redundant closure found LL | let e: std::vec::Vec = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove closure as shown: `char::to_ascii_uppercase` +error: non-binding let on a type that implements `Drop` + --> $DIR/eta.rs:107:5 + | +LL | let _: Vec<_> = arr.iter().map(|x| x.map_err(|e| some.take().unwrap()(e))).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::let_underscore_drop)]` on by default + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: redundant closure found --> $DIR/eta.rs:172:27 | @@ -76,5 +85,13 @@ error: redundant closure found LL | let a = Some(1u8).map(|a| closure(a)); | ^^^^^^^^^^^^^^ help: remove closure as shown: `closure` -error: aborting due to 12 previous errors +error: non-binding let on a type that implements `Drop` + --> $DIR/eta.rs:203:5 + | +LL | let _ = [Bar].iter().map(|s| s.to_string()).collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: aborting due to 14 previous errors diff --git a/tests/ui/filter_methods.stderr b/tests/ui/filter_methods.stderr index 91718dd1175..08b781d7363 100644 --- a/tests/ui/filter_methods.stderr +++ b/tests/ui/filter_methods.stderr @@ -1,3 +1,12 @@ +error: non-binding let on a type that implements `Drop` + --> $DIR/filter_methods.rs:5:5 + | +LL | let _: Vec<_> = vec![5; 6].into_iter().filter(|&x| x == 0).map(|x| x * 2).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::let-underscore-drop` implied by `-D warnings` + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: called `filter(..).map(..)` on an `Iterator` --> $DIR/filter_methods.rs:5:21 | @@ -7,6 +16,18 @@ LL | let _: Vec<_> = vec![5; 6].into_iter().filter(|&x| x == 0).map(|x| x * = note: `-D clippy::filter-map` implied by `-D warnings` = help: this is more succinctly expressed by calling `.filter_map(..)` instead +error: non-binding let on a type that implements `Drop` + --> $DIR/filter_methods.rs:7:5 + | +LL | / let _: Vec<_> = vec![5_i8; 6] +LL | | .into_iter() +LL | | .filter(|&x| x == 0) +LL | | .flat_map(|x| x.checked_mul(2)) +LL | | .collect(); + | |___________________^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: called `filter(..).flat_map(..)` on an `Iterator` --> $DIR/filter_methods.rs:7:21 | @@ -19,6 +40,18 @@ LL | | .flat_map(|x| x.checked_mul(2)) | = help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()` +error: non-binding let on a type that implements `Drop` + --> $DIR/filter_methods.rs:13:5 + | +LL | / let _: Vec<_> = vec![5_i8; 6] +LL | | .into_iter() +LL | | .filter_map(|x| x.checked_mul(2)) +LL | | .flat_map(|x| x.checked_mul(2)) +LL | | .collect(); + | |___________________^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: called `filter_map(..).flat_map(..)` on an `Iterator` --> $DIR/filter_methods.rs:13:21 | @@ -31,6 +64,18 @@ LL | | .flat_map(|x| x.checked_mul(2)) | = help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()` +error: non-binding let on a type that implements `Drop` + --> $DIR/filter_methods.rs:19:5 + | +LL | / let _: Vec<_> = vec![5_i8; 6] +LL | | .into_iter() +LL | | .filter_map(|x| x.checked_mul(2)) +LL | | .map(|x| x.checked_mul(2)) +LL | | .collect(); + | |___________________^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: called `filter_map(..).map(..)` on an `Iterator` --> $DIR/filter_methods.rs:19:21 | @@ -43,5 +88,5 @@ LL | | .map(|x| x.checked_mul(2)) | = help: this is more succinctly expressed by only calling `.filter_map(..)` instead -error: aborting due to 4 previous errors +error: aborting due to 8 previous errors diff --git a/tests/ui/get_unwrap.stderr b/tests/ui/get_unwrap.stderr index 76a098df82a..6aa5452bac1 100644 --- a/tests/ui/get_unwrap.stderr +++ b/tests/ui/get_unwrap.stderr @@ -70,17 +70,34 @@ error: called `.get_mut().unwrap()` on a VecDeque. Using `[]` is more clear and LL | *some_vecdeque.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vecdeque[0]` +error: non-binding let on a type that implements `Drop` + --> $DIR/get_unwrap.rs:59:9 + | +LL | let _ = some_vec.get(0..1).unwrap().to_vec(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::let_underscore_drop)]` on by default + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise --> $DIR/get_unwrap.rs:59:17 | LL | let _ = some_vec.get(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` +error: non-binding let on a type that implements `Drop` + --> $DIR/get_unwrap.rs:60:9 + | +LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise --> $DIR/get_unwrap.rs:60:17 | LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` -error: aborting due to 13 previous errors +error: aborting due to 15 previous errors diff --git a/tests/ui/into_iter_on_ref.stderr b/tests/ui/into_iter_on_ref.stderr index 28003b365bb..efe9d20920b 100644 --- a/tests/ui/into_iter_on_ref.stderr +++ b/tests/ui/into_iter_on_ref.stderr @@ -1,3 +1,12 @@ +error: non-binding let on a type that implements `Drop` + --> $DIR/into_iter_on_ref.rs:13:5 + | +LL | let _ = vec![1, 2, 3].into_iter(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::let_underscore_drop)]` on by default + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Vec` --> $DIR/into_iter_on_ref.rs:14:30 | @@ -162,5 +171,5 @@ error: this `.into_iter()` call is equivalent to `.iter()` and will not consume LL | let _ = (&[1, 2, 3]).into_iter().next(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: aborting due to 27 previous errors +error: aborting due to 28 previous errors diff --git a/tests/ui/iter_cloned_collect.stderr b/tests/ui/iter_cloned_collect.stderr index b90a1e6c919..f5cd43b3da5 100644 --- a/tests/ui/iter_cloned_collect.stderr +++ b/tests/ui/iter_cloned_collect.stderr @@ -6,12 +6,33 @@ LL | let v2: Vec = v.iter().cloned().collect(); | = note: `-D clippy::iter-cloned-collect` implied by `-D warnings` +error: non-binding let on a type that implements `Drop` + --> $DIR/iter_cloned_collect.rs:15:5 + | +LL | let _: Vec = vec![1, 2, 3].iter().cloned().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::let_underscore_drop)]` on by default + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable --> $DIR/iter_cloned_collect.rs:15:38 | LL | let _: Vec = vec![1, 2, 3].iter().cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()` +error: non-binding let on a type that implements `Drop` + --> $DIR/iter_cloned_collect.rs:19:9 + | +LL | / let _: Vec = std::ffi::CStr::from_ptr(std::ptr::null()) +LL | | .to_bytes() +LL | | .iter() +LL | | .cloned() +LL | | .collect(); + | |_______________________^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable --> $DIR/iter_cloned_collect.rs:20:24 | @@ -22,5 +43,5 @@ LL | | .cloned() LL | | .collect(); | |______________________^ help: try: `.to_vec()` -error: aborting due to 3 previous errors +error: aborting due to 5 previous errors diff --git a/tests/ui/map_clone.stderr b/tests/ui/map_clone.stderr index 4f43cff5024..122a678f118 100644 --- a/tests/ui/map_clone.stderr +++ b/tests/ui/map_clone.stderr @@ -1,3 +1,12 @@ +error: non-binding let on a type that implements `Drop` + --> $DIR/map_clone.rs:10:5 + | +LL | let _: Vec = vec![5_i8; 6].iter().map(|x| *x).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::let-underscore-drop` implied by `-D warnings` + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: you are using an explicit closure for copying elements --> $DIR/map_clone.rs:10:22 | @@ -6,12 +15,28 @@ LL | let _: Vec = vec![5_i8; 6].iter().map(|x| *x).collect(); | = note: `-D clippy::map-clone` implied by `-D warnings` +error: non-binding let on a type that implements `Drop` + --> $DIR/map_clone.rs:11:5 + | +LL | let _: Vec = vec![String::new()].iter().map(|x| x.clone()).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: you are using an explicit closure for cloning elements --> $DIR/map_clone.rs:11:26 | LL | let _: Vec = vec![String::new()].iter().map(|x| x.clone()).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `vec![String::new()].iter().cloned()` +error: non-binding let on a type that implements `Drop` + --> $DIR/map_clone.rs:12:5 + | +LL | let _: Vec = vec![42, 43].iter().map(|&x| x).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: you are using an explicit closure for copying elements --> $DIR/map_clone.rs:12:23 | @@ -36,5 +61,37 @@ error: you are needlessly cloning iterator elements LL | let _ = std::env::args().map(|v| v.clone()); | ^^^^^^^^^^^^^^^^^^^ help: remove the `map` call -error: aborting due to 6 previous errors +error: non-binding let on a type that implements `Drop` + --> $DIR/map_clone.rs:35:9 + | +LL | let _: Vec = v.into_iter().map(|x| *x).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: non-binding let on a type that implements `Drop` + --> $DIR/map_clone.rs:42:9 + | +LL | let _: Vec = v.into_iter().map(|x| *x).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: non-binding let on a type that implements `Drop` + --> $DIR/map_clone.rs:45:9 + | +LL | let _: Vec = v.into_iter().map(|&mut x| x).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: non-binding let on a type that implements `Drop` + --> $DIR/map_clone.rs:53:9 + | +LL | let _: Vec<_> = items.into_iter().map(|x| x.clone()).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: aborting due to 13 previous errors diff --git a/tests/ui/map_collect_result_unit.stderr b/tests/ui/map_collect_result_unit.stderr index 8b06e13baa6..26e876b1808 100644 --- a/tests/ui/map_collect_result_unit.stderr +++ b/tests/ui/map_collect_result_unit.stderr @@ -12,5 +12,22 @@ error: `.map().collect()` can be replaced with `.try_for_each()` LL | let _: Result<(), _> = (0..3).map(|t| Err(t + 1)).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(0..3).try_for_each(|t| Err(t + 1))` -error: aborting due to 2 previous errors +error: non-binding let on a type that implements `Drop` + --> $DIR/map_collect_result_unit.rs:14:5 + | +LL | let _ = (0..3).map(|t| Err(t + 1)).collect::, _>>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::let_underscore_drop)]` on by default + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: non-binding let on a type that implements `Drop` + --> $DIR/map_collect_result_unit.rs:15:5 + | +LL | let _ = (0..3).map(|t| Err(t + 1)).collect::>>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: aborting due to 4 previous errors diff --git a/tests/ui/map_flatten.stderr b/tests/ui/map_flatten.stderr index b6479cd69ea..6159b5256ee 100644 --- a/tests/ui/map_flatten.stderr +++ b/tests/ui/map_flatten.stderr @@ -1,3 +1,12 @@ +error: non-binding let on a type that implements `Drop` + --> $DIR/map_flatten.rs:14:5 + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::let-underscore-drop` implied by `-D warnings` + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: called `map(..).flatten()` on an `Iterator` --> $DIR/map_flatten.rs:14:46 | @@ -6,24 +15,56 @@ LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().coll | = note: `-D clippy::map-flatten` implied by `-D warnings` +error: non-binding let on a type that implements `Drop` + --> $DIR/map_flatten.rs:15:5 + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: called `map(..).flatten()` on an `Iterator` --> $DIR/map_flatten.rs:15:46 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_ref)` +error: non-binding let on a type that implements `Drop` + --> $DIR/map_flatten.rs:16:5 + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: called `map(..).flatten()` on an `Iterator` --> $DIR/map_flatten.rs:16:46 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_closure)` +error: non-binding let on a type that implements `Drop` + --> $DIR/map_flatten.rs:17:5 + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: called `map(..).flatten()` on an `Iterator` --> $DIR/map_flatten.rs:17:46 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(|x| x.checked_add(1))` +error: non-binding let on a type that implements `Drop` + --> $DIR/map_flatten.rs:20:5 + | +LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: called `map(..).flatten()` on an `Iterator` --> $DIR/map_flatten.rs:20:46 | @@ -36,5 +77,5 @@ error: called `map(..).flatten()` on an `Option` LL | let _: Option<_> = (Some(Some(1))).map(|x| x).flatten(); | ^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `.and_then(|x| x)` -error: aborting due to 6 previous errors +error: aborting due to 11 previous errors diff --git a/tests/ui/map_identity.stderr b/tests/ui/map_identity.stderr index e4a0320cbda..6bfeb186bad 100644 --- a/tests/ui/map_identity.stderr +++ b/tests/ui/map_identity.stderr @@ -1,3 +1,12 @@ +error: non-binding let on a type that implements `Drop` + --> $DIR/map_identity.rs:8:5 + | +LL | let _: Vec<_> = x.iter().map(not_identity).map(|x| return x).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::let_underscore_drop)]` on by default + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: unnecessary map of the identity function --> $DIR/map_identity.rs:8:47 | @@ -6,6 +15,14 @@ LL | let _: Vec<_> = x.iter().map(not_identity).map(|x| return x).collect(); | = note: `-D clippy::map-identity` implied by `-D warnings` +error: non-binding let on a type that implements `Drop` + --> $DIR/map_identity.rs:9:5 + | +LL | let _: Vec<_> = x.iter().map(std::convert::identity).map(|y| y).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: unnecessary map of the identity function --> $DIR/map_identity.rs:9:57 | @@ -33,5 +50,21 @@ LL | | return x; LL | | }); | |______^ help: remove the call to `map` -error: aborting due to 5 previous errors +error: non-binding let on a type that implements `Drop` + --> $DIR/map_identity.rs:15:5 + | +LL | let _: Vec<_> = x.iter().map(|x| 2 * x).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: non-binding let on a type that implements `Drop` + --> $DIR/map_identity.rs:16:5 + | +LL | let _: Vec<_> = x.iter().map(not_identity).map(|x| return x - 4).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: aborting due to 9 previous errors diff --git a/tests/ui/match_single_binding.stderr b/tests/ui/match_single_binding.stderr index 795c8c3e24d..8b07599817b 100644 --- a/tests/ui/match_single_binding.stderr +++ b/tests/ui/match_single_binding.stderr @@ -150,6 +150,20 @@ LL | let Point { x, y } = coords(); LL | let product = x * y; | +error: non-binding let on a type that implements `Drop` + --> $DIR/match_single_binding.rs:96:5 + | +LL | / let _ = v +LL | | .iter() +LL | | .map(|i| match i.unwrap() { +LL | | unwrapped => unwrapped, +LL | | }) +LL | | .collect::>(); + | |______________________________^ + | + = note: `#[deny(clippy::let_underscore_drop)]` on by default + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: this match could be written as a `let` statement --> $DIR/match_single_binding.rs:98:18 | @@ -167,5 +181,5 @@ LL | unwrapped LL | }) | -error: aborting due to 11 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/needless_pass_by_value.stderr b/tests/ui/needless_pass_by_value.stderr index 9aa783bf904..cf04ee4b257 100644 --- a/tests/ui/needless_pass_by_value.stderr +++ b/tests/ui/needless_pass_by_value.stderr @@ -90,6 +90,15 @@ help: change `v.clone()` to LL | let _ = v.to_owned(); | ^^^^^^^^^^^^ +error: non-binding let on a type that implements `Drop` + --> $DIR/needless_pass_by_value.rs:85:5 + | +LL | let _ = v.clone(); + | ^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::let_underscore_drop)]` on by default + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: this argument is passed by value, but not consumed in the function body --> $DIR/needless_pass_by_value.rs:94:12 | @@ -174,5 +183,5 @@ error: this argument is passed by value, but not consumed in the function body LL | fn more_fun(_item: impl Club<'static, i32>) {} | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider taking a reference instead: `&impl Club<'static, i32>` -error: aborting due to 22 previous errors +error: aborting due to 23 previous errors diff --git a/tests/ui/redundant_clone.stderr b/tests/ui/redundant_clone.stderr index 89b39254299..270c3fac990 100644 --- a/tests/ui/redundant_clone.stderr +++ b/tests/ui/redundant_clone.stderr @@ -167,5 +167,14 @@ note: cloned value is neither consumed nor mutated LL | let y = x.clone().join("matthias"); | ^^^^^^^^^ -error: aborting due to 14 previous errors +error: non-binding let on a type that implements `Drop` + --> $DIR/redundant_clone.rs:180:5 + | +LL | let _ = a.clone(); // OK + | ^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::let_underscore_drop)]` on by default + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + +error: aborting due to 15 previous errors diff --git a/tests/ui/reversed_empty_ranges_fixable.stderr b/tests/ui/reversed_empty_ranges_fixable.stderr index de83c4f3d63..707a5d5032e 100644 --- a/tests/ui/reversed_empty_ranges_fixable.stderr +++ b/tests/ui/reversed_empty_ranges_fixable.stderr @@ -10,6 +10,15 @@ help: consider using the following if you are attempting to iterate over this ra LL | (21..=42).rev().for_each(|x| println!("{}", x)); | ^^^^^^^^^^^^^^^ +error: non-binding let on a type that implements `Drop` + --> $DIR/reversed_empty_ranges_fixable.rs:10:5 + | +LL | let _ = (ANSWER..21).filter(|x| x % 2 == 0).take(10).collect::>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::let_underscore_drop)]` on by default + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: this range is empty so it will yield no values --> $DIR/reversed_empty_ranges_fixable.rs:10:13 | @@ -43,5 +52,5 @@ help: consider using the following if you are attempting to iterate over this ra LL | for _ in (21u32..42u32).rev() {} | ^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors diff --git a/tests/ui/transmute.stderr b/tests/ui/transmute.stderr index ad9953d12bc..d6767dc9f15 100644 --- a/tests/ui/transmute.stderr +++ b/tests/ui/transmute.stderr @@ -24,30 +24,71 @@ error: transmute from a reference to a pointer LL | let _: *const U = core::intrinsics::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *const U` +error: non-binding let on a type that implements `Drop` + --> $DIR/transmute.rs:34:9 + | +LL | let _: Vec = core::intrinsics::transmute(my_vec()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::let_underscore_drop)]` on by default + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: transmute from a type (`std::vec::Vec`) to itself --> $DIR/transmute.rs:34:27 | LL | let _: Vec = core::intrinsics::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: non-binding let on a type that implements `Drop` + --> $DIR/transmute.rs:36:9 + | +LL | let _: Vec = core::mem::transmute(my_vec()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: transmute from a type (`std::vec::Vec`) to itself --> $DIR/transmute.rs:36:27 | LL | let _: Vec = core::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: non-binding let on a type that implements `Drop` + --> $DIR/transmute.rs:38:9 + | +LL | let _: Vec = std::intrinsics::transmute(my_vec()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: transmute from a type (`std::vec::Vec`) to itself --> $DIR/transmute.rs:38:27 | LL | let _: Vec = std::intrinsics::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: non-binding let on a type that implements `Drop` + --> $DIR/transmute.rs:40:9 + | +LL | let _: Vec = std::mem::transmute(my_vec()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: transmute from a type (`std::vec::Vec`) to itself --> $DIR/transmute.rs:40:27 | LL | let _: Vec = std::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: non-binding let on a type that implements `Drop` + --> $DIR/transmute.rs:42:9 + | +LL | let _: Vec = my_transmute(my_vec()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: transmute from a type (`std::vec::Vec`) to itself --> $DIR/transmute.rs:42:27 | @@ -154,5 +195,5 @@ error: transmute from a `&mut [u8]` to a `&mut str` 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 29 previous errors diff --git a/tests/ui/transmute_collection.stderr b/tests/ui/transmute_collection.stderr index ebc05c402ab..e89f6d8539f 100644 --- a/tests/ui/transmute_collection.stderr +++ b/tests/ui/transmute_collection.stderr @@ -1,3 +1,12 @@ +error: non-binding let on a type that implements `Drop` + --> $DIR/transmute_collection.rs:9:9 + | +LL | let _ = transmute::<_, Vec>(vec![0u8]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::let_underscore_drop)]` on by default + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: transmute from `std::vec::Vec` to `std::vec::Vec` with mismatched layout is unsound --> $DIR/transmute_collection.rs:9:17 | @@ -6,18 +15,42 @@ LL | let _ = transmute::<_, Vec>(vec![0u8]); | = note: `-D clippy::unsound-collection-transmute` implied by `-D warnings` +error: non-binding let on a type that implements `Drop` + --> $DIR/transmute_collection.rs:11:9 + | +LL | let _ = transmute::<_, Vec<[u8; 4]>>(vec![1234u32]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: transmute from `std::vec::Vec` to `std::vec::Vec<[u8; 4]>` with mismatched layout is unsound --> $DIR/transmute_collection.rs:11:17 | LL | let _ = transmute::<_, Vec<[u8; 4]>>(vec![1234u32]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: non-binding let on a type that implements `Drop` + --> $DIR/transmute_collection.rs:14:9 + | +LL | let _ = transmute::<_, VecDeque>(VecDeque::::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: transmute from `std::collections::VecDeque` to `std::collections::VecDeque` with mismatched layout is unsound --> $DIR/transmute_collection.rs:14:17 | LL | let _ = transmute::<_, VecDeque>(VecDeque::::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: non-binding let on a type that implements `Drop` + --> $DIR/transmute_collection.rs:16:9 + | +LL | let _ = transmute::<_, VecDeque>(VecDeque::<[u8; 4]>::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: transmute from `std::collections::VecDeque<[u8; 4]>` to `std::collections::VecDeque` with mismatched layout is unsound --> $DIR/transmute_collection.rs:16:17 | @@ -60,24 +93,56 @@ error: transmute from `std::collections::HashSet<[u8; 4]>` to `std::collections: LL | let _ = transmute::<_, HashSet>(HashSet::<[u8; 4]>::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: non-binding let on a type that implements `Drop` + --> $DIR/transmute_collection.rs:34:9 + | +LL | let _ = transmute::<_, BTreeMap>(BTreeMap::::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: transmute from `std::collections::BTreeMap` to `std::collections::BTreeMap` with mismatched layout is unsound --> $DIR/transmute_collection.rs:34:17 | LL | let _ = transmute::<_, BTreeMap>(BTreeMap::::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: non-binding let on a type that implements `Drop` + --> $DIR/transmute_collection.rs:35:9 + | +LL | let _ = transmute::<_, BTreeMap>(BTreeMap::::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: transmute from `std::collections::BTreeMap` to `std::collections::BTreeMap` with mismatched layout is unsound --> $DIR/transmute_collection.rs:35:17 | LL | let _ = transmute::<_, BTreeMap>(BTreeMap::::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: non-binding let on a type that implements `Drop` + --> $DIR/transmute_collection.rs:37:9 + | +LL | let _ = transmute::<_, BTreeMap>(BTreeMap::::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: transmute from `std::collections::BTreeMap` to `std::collections::BTreeMap` with mismatched layout is unsound --> $DIR/transmute_collection.rs:37:17 | LL | let _ = transmute::<_, BTreeMap>(BTreeMap::::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: non-binding let on a type that implements `Drop` + --> $DIR/transmute_collection.rs:38:9 + | +LL | let _ = transmute::<_, BTreeMap>(BTreeMap::<[u8; 4], u32>::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: transmute from `std::collections::BTreeMap<[u8; 4], u32>` to `std::collections::BTreeMap` with mismatched layout is unsound --> $DIR/transmute_collection.rs:38:17 | @@ -108,5 +173,5 @@ error: transmute from `std::collections::HashMap<[u8; 4], u32>` to `std::collect LL | let _ = transmute::<_, HashMap>(HashMap::<[u8; 4], u32>::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 18 previous errors +error: aborting due to 26 previous errors diff --git a/tests/ui/unnecessary_clone.stderr b/tests/ui/unnecessary_clone.stderr index 5ffa6c4fd06..1f89cb0cdef 100644 --- a/tests/ui/unnecessary_clone.stderr +++ b/tests/ui/unnecessary_clone.stderr @@ -24,6 +24,15 @@ error: using `.clone()` on a ref-counted pointer LL | arc_weak.clone(); | ^^^^^^^^^^^^^^^^ help: try this: `Weak::::clone(&arc_weak)` +error: non-binding let on a type that implements `Drop` + --> $DIR/unnecessary_clone.rs:36:5 + | +LL | let _: Arc = x.clone(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::let_underscore_drop)]` on by default + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: using `.clone()` on a ref-counted pointer --> $DIR/unnecessary_clone.rs:36:33 | @@ -102,5 +111,5 @@ error: using `.clone()` on a ref-counted pointer LL | Some(try_opt!(Some(rc)).clone()) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Rc::::clone(&try_opt!(Some(rc)))` -error: aborting due to 12 previous errors +error: aborting due to 13 previous errors diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index 11c6efb25cc..ea3e96111cb 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -52,6 +52,15 @@ error: useless conversion to the same type: `std::str::Lines` LL | let _ = "".lines().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()` +error: non-binding let on a type that implements `Drop` + --> $DIR/useless_conversion.rs:65:5 + | +LL | let _ = vec![1, 2, 3].into_iter().into_iter(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::let_underscore_drop)]` on by default + = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` + error: useless conversion to the same type: `std::vec::IntoIter` --> $DIR/useless_conversion.rs:65:13 | @@ -70,5 +79,5 @@ error: useless conversion to the same type: `i32` LL | let _ = i32::from(a + b) * 3; | ^^^^^^^^^^^^^^^^ help: consider removing `i32::from()`: `(a + b)` -error: aborting due to 11 previous errors +error: aborting due to 12 previous errors -- cgit 1.4.1-3-g733a5 From 9cab08465b5d5b4bb4f5ff406b2e18ca550f7d93 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 29 Oct 2020 15:30:20 -0500 Subject: Fix or_fun_call for index operator --- clippy_lints/src/methods/mod.rs | 45 ++++++++++++++------------------- clippy_lints/src/utils/eager_or_lazy.rs | 7 ++++- tests/ui/or_fun_call.fixed | 9 +++++++ tests/ui/or_fun_call.rs | 9 +++++++ tests/ui/or_fun_call.stderr | 18 ++++++++++--- 5 files changed, 58 insertions(+), 30 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 5309253531b..b6fb3d06934 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1797,11 +1797,11 @@ fn lint_or_fun_call<'tcx>( cx: &LateContext<'tcx>, name: &str, method_span: Span, - fun_span: Span, self_expr: &hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, - or_has_args: bool, span: Span, + // None if lambda is required + fun_span: Option, ) { // (path, fn_has_argument, methods, suffix) static KNOW_TYPES: [(&[&str], bool, &[&str], &str); 4] = [ @@ -1840,10 +1840,18 @@ fn lint_or_fun_call<'tcx>( if poss.contains(&name); then { - let sugg: Cow<'_, _> = match (fn_has_arguments, !or_has_args) { - (true, _) => format!("|_| {}", snippet_with_macro_callsite(cx, arg.span, "..")).into(), - (false, false) => format!("|| {}", snippet_with_macro_callsite(cx, arg.span, "..")).into(), - (false, true) => snippet_with_macro_callsite(cx, fun_span, ".."), + let sugg: Cow<'_, str> = { + let (snippet_span, use_lambda) = match (fn_has_arguments, fun_span) { + (false, Some(fun_span)) => (fun_span, false), + _ => (arg.span, true), + }; + let snippet = snippet_with_macro_callsite(cx, snippet_span, ".."); + if use_lambda { + let l_arg = if fn_has_arguments { "_" } else { "" }; + format!("|{}| {}", l_arg, snippet).into() + } else { + snippet + } }; let span_replace_word = method_span.with_hi(span.hi()); span_lint_and_sugg( @@ -1864,28 +1872,13 @@ fn lint_or_fun_call<'tcx>( hir::ExprKind::Call(ref fun, ref or_args) => { let or_has_args = !or_args.is_empty(); if !check_unwrap_or_default(cx, name, fun, &args[0], &args[1], or_has_args, expr.span) { - check_general_case( - cx, - name, - method_span, - fun.span, - &args[0], - &args[1], - or_has_args, - expr.span, - ); + let fun_span = if or_has_args { None } else { Some(fun.span) }; + check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, fun_span); } }, - hir::ExprKind::MethodCall(_, span, ref or_args, _) => check_general_case( - cx, - name, - method_span, - span, - &args[0], - &args[1], - !or_args.is_empty(), - expr.span, - ), + hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => { + check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, None); + }, _ => {}, } } diff --git a/clippy_lints/src/utils/eager_or_lazy.rs b/clippy_lints/src/utils/eager_or_lazy.rs index 4ceea13df37..8fe5ddee1ca 100644 --- a/clippy_lints/src/utils/eager_or_lazy.rs +++ b/clippy_lints/src/utils/eager_or_lazy.rs @@ -9,7 +9,7 @@ //! - or-fun-call //! - option-if-let-else -use crate::utils::is_ctor_or_promotable_const_function; +use crate::utils::{is_ctor_or_promotable_const_function, is_type_diagnostic_item, match_type, paths}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit; @@ -96,6 +96,11 @@ fn identify_some_potentially_expensive_patterns<'tcx>(cx: &LateContext<'tcx>, ex let call_found = match &expr.kind { // ignore enum and struct constructors ExprKind::Call(..) => !is_ctor_or_promotable_const_function(self.cx, expr), + ExprKind::Index(obj, _) => { + let ty = self.cx.typeck_results().expr_ty(obj); + is_type_diagnostic_item(self.cx, ty, sym!(hashmap_type)) + || match_type(self.cx, ty, &paths::BTREEMAP) + }, ExprKind::MethodCall(..) => true, _ => false, }; diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 2045ffdb5f0..20e5016bc17 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -70,6 +70,15 @@ fn or_fun_call() { let opt = Some(1); let hello = "Hello"; let _ = opt.ok_or(format!("{} world.", hello)); + + // index + let map = HashMap::::new(); + let _ = Some(1).unwrap_or_else(|| map[&1]); + let map = BTreeMap::::new(); + let _ = Some(1).unwrap_or_else(|| map[&1]); + // don't lint index vec + let vec = vec![1]; + let _ = Some(1).unwrap_or(vec[1]); } struct Foo(u8); diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 522f31b72d0..e7192deeebb 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -70,6 +70,15 @@ fn or_fun_call() { let opt = Some(1); let hello = "Hello"; let _ = opt.ok_or(format!("{} world.", hello)); + + // index + let map = HashMap::::new(); + let _ = Some(1).unwrap_or(map[&1]); + let map = BTreeMap::::new(); + let _ = Some(1).unwrap_or(map[&1]); + // don't lint index vec + let vec = vec![1]; + let _ = Some(1).unwrap_or(vec[1]); } struct Foo(u8); diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index bc5978b538f..d0c4df0e008 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -78,17 +78,29 @@ error: use of `unwrap_or` followed by a function call LL | let _ = stringy.unwrap_or("".to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "".to_owned())` +error: use of `unwrap_or` followed by a function call + --> $DIR/or_fun_call.rs:76:21 + | +LL | let _ = Some(1).unwrap_or(map[&1]); + | ^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| map[&1])` + +error: use of `unwrap_or` followed by a function call + --> $DIR/or_fun_call.rs:78:21 + | +LL | let _ = Some(1).unwrap_or(map[&1]); + | ^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| map[&1])` + error: use of `or` followed by a function call - --> $DIR/or_fun_call.rs:93:35 + --> $DIR/or_fun_call.rs:102:35 | LL | let _ = Some("a".to_string()).or(Some("b".to_string())); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some("b".to_string()))` error: use of `or` followed by a function call - --> $DIR/or_fun_call.rs:97:10 + --> $DIR/or_fun_call.rs:106:10 | LL | .or(Some(Bar(b, Duration::from_secs(2)))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some(Bar(b, Duration::from_secs(2))))` -error: aborting due to 15 previous errors +error: aborting due to 17 previous errors -- cgit 1.4.1-3-g733a5 From 06e81bb49370a0154c3fa7de0bbc8b31c7bbe37b Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius III" Date: Sun, 8 Nov 2020 18:32:12 -0500 Subject: Update references --- tests/ui/borrow_box.stderr | 11 +--- .../ui/borrow_interior_mutable_const/others.stderr | 27 +------- tests/ui/box_vec.stderr | 15 +---- tests/ui/crashes/ice-4968.stderr | 11 ---- tests/ui/crashes/ice-5223.stderr | 11 ---- tests/ui/escape_analysis.stderr | 23 +------ tests/ui/eta.stderr | 19 +----- tests/ui/filter_methods.stderr | 8 +-- tests/ui/get_unwrap.stderr | 19 +----- tests/ui/into_iter_on_ref.stderr | 11 +--- tests/ui/iter_cloned_collect.stderr | 23 +------ tests/ui/let_underscore_drop.stderr | 6 +- tests/ui/map_clone.fixed | 1 + tests/ui/map_clone.rs | 1 + tests/ui/map_clone.stderr | 71 +++------------------- tests/ui/map_collect_result_unit.stderr | 19 +----- tests/ui/map_flatten.fixed | 1 + tests/ui/map_flatten.rs | 1 + tests/ui/map_flatten.stderr | 55 +++-------------- tests/ui/map_identity.stderr | 35 +---------- tests/ui/match_single_binding.stderr | 16 +---- tests/ui/needless_pass_by_value.stderr | 11 +--- tests/ui/redundant_clone.stderr | 11 +--- tests/ui/reversed_empty_ranges_fixable.stderr | 11 +--- tests/ui/transmute.stderr | 43 +------------ tests/ui/transmute_collection.stderr | 67 +------------------- tests/ui/unnecessary_clone.stderr | 11 +--- tests/ui/useless_conversion.stderr | 11 +--- 28 files changed, 43 insertions(+), 506 deletions(-) delete mode 100644 tests/ui/crashes/ice-4968.stderr delete mode 100644 tests/ui/crashes/ice-5223.stderr (limited to 'tests') diff --git a/tests/ui/borrow_box.stderr b/tests/ui/borrow_box.stderr index a40789cd426..3eac32815be 100644 --- a/tests/ui/borrow_box.stderr +++ b/tests/ui/borrow_box.stderr @@ -22,15 +22,6 @@ error: you seem to be trying to use `&Box`. Consider using just `&T` LL | fn test4(a: &Box); | ^^^^^^^^^^ help: try: `&bool` -error: non-binding let on a type that implements `Drop` - --> $DIR/borrow_box.rs:63:5 - | -LL | let _ = foo; - | ^^^^^^^^^^^^ - | - = note: `#[deny(clippy::let_underscore_drop)]` on by default - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: you seem to be trying to use `&Box`. Consider using just `&T` --> $DIR/borrow_box.rs:95:25 | @@ -73,5 +64,5 @@ error: you seem to be trying to use `&Box`. Consider using just `&T` LL | pub fn test20(_display: &Box<(dyn Display + Send)>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&(dyn Display + Send)` -error: aborting due to 11 previous errors +error: aborting due to 10 previous errors diff --git a/tests/ui/borrow_interior_mutable_const/others.stderr b/tests/ui/borrow_interior_mutable_const/others.stderr index 976c412c7a8..9a908cf30e9 100644 --- a/tests/ui/borrow_interior_mutable_const/others.stderr +++ b/tests/ui/borrow_interior_mutable_const/others.stderr @@ -47,15 +47,6 @@ LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability | = help: assign this const to a local or static variable, and use the variable here -error: non-binding let on a type that implements `Drop` - --> $DIR/others.rs:72:5 - | -LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability - | ^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `#[deny(clippy::let_underscore_drop)]` on by default - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: a `const` item with interior mutability should not be borrowed --> $DIR/others.rs:72:14 | @@ -104,22 +95,6 @@ LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability | = help: assign this const to a local or static variable, and use the variable here -error: non-binding let on a type that implements `Drop` - --> $DIR/others.rs:83:5 - | -LL | let _ = ATOMIC_TUPLE.1.into_iter(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: non-binding let on a type that implements `Drop` - --> $DIR/others.rs:85:5 - | -LL | let _ = &{ ATOMIC_TUPLE }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: a `const` item with interior mutability should not be borrowed --> $DIR/others.rs:87:5 | @@ -136,5 +111,5 @@ LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability | = help: assign this const to a local or static variable, and use the variable here -error: aborting due to 17 previous errors +error: aborting due to 14 previous errors diff --git a/tests/ui/box_vec.stderr b/tests/ui/box_vec.stderr index a4983df1d30..fca12eddd57 100644 --- a/tests/ui/box_vec.stderr +++ b/tests/ui/box_vec.stderr @@ -1,16 +1,3 @@ -error: non-binding let on a type that implements `Drop` - --> $DIR/box_vec.rs:7:9 - | -LL | let _: Box<$x> = Box::new($init); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -... -LL | boxit!(Vec::new(), Vec); - | ---------------------------- in this macro invocation - | - = note: `-D clippy::let-underscore-drop` implied by `-D warnings` - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - error: you seem to be trying to use `Box>`. Consider using just `Vec` --> $DIR/box_vec.rs:14:18 | @@ -20,5 +7,5 @@ LL | pub fn test(foo: Box>) { = note: `-D clippy::box-vec` implied by `-D warnings` = help: `Vec` is already on the heap, `Box>` makes an extra allocation. -error: aborting due to 2 previous errors +error: aborting due to previous error diff --git a/tests/ui/crashes/ice-4968.stderr b/tests/ui/crashes/ice-4968.stderr deleted file mode 100644 index 9ce39027451..00000000000 --- a/tests/ui/crashes/ice-4968.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error: non-binding let on a type that implements `Drop` - --> $DIR/ice-4968.rs:16:9 - | -LL | let _: Vec> = mem::transmute(slice); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `#[deny(clippy::let_underscore_drop)]` on by default - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: aborting due to previous error - diff --git a/tests/ui/crashes/ice-5223.stderr b/tests/ui/crashes/ice-5223.stderr deleted file mode 100644 index 3ae2dd5f770..00000000000 --- a/tests/ui/crashes/ice-5223.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error: non-binding let on a type that implements `Drop` - --> $DIR/ice-5223.rs:14:9 - | -LL | let _ = self.arr.iter().cloned().collect::>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `#[deny(clippy::let_underscore_drop)]` on by default - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: aborting due to previous error - diff --git a/tests/ui/escape_analysis.stderr b/tests/ui/escape_analysis.stderr index 6e1c1c07b6e..c86a769a3da 100644 --- a/tests/ui/escape_analysis.stderr +++ b/tests/ui/escape_analysis.stderr @@ -12,26 +12,5 @@ error: local variable doesn't need to be boxed here LL | pub fn new(_needs_name: Box>) -> () {} | ^^^^^^^^^^^ -error: non-binding let on a type that implements `Drop` - --> $DIR/escape_analysis.rs:166:9 - | -LL | / let _ = move || { -LL | | consume(x); -LL | | }; - | |__________^ - | - = note: `#[deny(clippy::let_underscore_drop)]` on by default - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: non-binding let on a type that implements `Drop` - --> $DIR/escape_analysis.rs:172:9 - | -LL | / let _ = || { -LL | | borrow(&x); -LL | | }; - | |__________^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors diff --git a/tests/ui/eta.stderr b/tests/ui/eta.stderr index bc79caee887..c4713ca8083 100644 --- a/tests/ui/eta.stderr +++ b/tests/ui/eta.stderr @@ -64,15 +64,6 @@ error: redundant closure found LL | let e: std::vec::Vec = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove closure as shown: `char::to_ascii_uppercase` -error: non-binding let on a type that implements `Drop` - --> $DIR/eta.rs:107:5 - | -LL | let _: Vec<_> = arr.iter().map(|x| x.map_err(|e| some.take().unwrap()(e))).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `#[deny(clippy::let_underscore_drop)]` on by default - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: redundant closure found --> $DIR/eta.rs:172:27 | @@ -85,13 +76,5 @@ error: redundant closure found LL | let a = Some(1u8).map(|a| closure(a)); | ^^^^^^^^^^^^^^ help: remove closure as shown: `closure` -error: non-binding let on a type that implements `Drop` - --> $DIR/eta.rs:203:5 - | -LL | let _ = [Bar].iter().map(|s| s.to_string()).collect::>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: aborting due to 14 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/filter_methods.stderr b/tests/ui/filter_methods.stderr index 08b781d7363..b5ac90282dc 100644 --- a/tests/ui/filter_methods.stderr +++ b/tests/ui/filter_methods.stderr @@ -1,4 +1,4 @@ -error: non-binding let on a type that implements `Drop` +error: non-binding `let` on a type that implements `Drop` --> $DIR/filter_methods.rs:5:5 | LL | let _: Vec<_> = vec![5; 6].into_iter().filter(|&x| x == 0).map(|x| x * 2).collect(); @@ -16,7 +16,7 @@ LL | let _: Vec<_> = vec![5; 6].into_iter().filter(|&x| x == 0).map(|x| x * = note: `-D clippy::filter-map` implied by `-D warnings` = help: this is more succinctly expressed by calling `.filter_map(..)` instead -error: non-binding let on a type that implements `Drop` +error: non-binding `let` on a type that implements `Drop` --> $DIR/filter_methods.rs:7:5 | LL | / let _: Vec<_> = vec![5_i8; 6] @@ -40,7 +40,7 @@ LL | | .flat_map(|x| x.checked_mul(2)) | = help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()` -error: non-binding let on a type that implements `Drop` +error: non-binding `let` on a type that implements `Drop` --> $DIR/filter_methods.rs:13:5 | LL | / let _: Vec<_> = vec![5_i8; 6] @@ -64,7 +64,7 @@ LL | | .flat_map(|x| x.checked_mul(2)) | = help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()` -error: non-binding let on a type that implements `Drop` +error: non-binding `let` on a type that implements `Drop` --> $DIR/filter_methods.rs:19:5 | LL | / let _: Vec<_> = vec![5_i8; 6] diff --git a/tests/ui/get_unwrap.stderr b/tests/ui/get_unwrap.stderr index 6aa5452bac1..76a098df82a 100644 --- a/tests/ui/get_unwrap.stderr +++ b/tests/ui/get_unwrap.stderr @@ -70,34 +70,17 @@ error: called `.get_mut().unwrap()` on a VecDeque. Using `[]` is more clear and LL | *some_vecdeque.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vecdeque[0]` -error: non-binding let on a type that implements `Drop` - --> $DIR/get_unwrap.rs:59:9 - | -LL | let _ = some_vec.get(0..1).unwrap().to_vec(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `#[deny(clippy::let_underscore_drop)]` on by default - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise --> $DIR/get_unwrap.rs:59:17 | LL | let _ = some_vec.get(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` -error: non-binding let on a type that implements `Drop` - --> $DIR/get_unwrap.rs:60:9 - | -LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise --> $DIR/get_unwrap.rs:60:17 | LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` -error: aborting due to 15 previous errors +error: aborting due to 13 previous errors diff --git a/tests/ui/into_iter_on_ref.stderr b/tests/ui/into_iter_on_ref.stderr index efe9d20920b..28003b365bb 100644 --- a/tests/ui/into_iter_on_ref.stderr +++ b/tests/ui/into_iter_on_ref.stderr @@ -1,12 +1,3 @@ -error: non-binding let on a type that implements `Drop` - --> $DIR/into_iter_on_ref.rs:13:5 - | -LL | let _ = vec![1, 2, 3].into_iter(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `#[deny(clippy::let_underscore_drop)]` on by default - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: this `.into_iter()` call is equivalent to `.iter()` and will not consume the `Vec` --> $DIR/into_iter_on_ref.rs:14:30 | @@ -171,5 +162,5 @@ error: this `.into_iter()` call is equivalent to `.iter()` and will not consume LL | let _ = (&[1, 2, 3]).into_iter().next(); //~ WARN equivalent to .iter() | ^^^^^^^^^ help: call directly: `iter` -error: aborting due to 28 previous errors +error: aborting due to 27 previous errors diff --git a/tests/ui/iter_cloned_collect.stderr b/tests/ui/iter_cloned_collect.stderr index f5cd43b3da5..b90a1e6c919 100644 --- a/tests/ui/iter_cloned_collect.stderr +++ b/tests/ui/iter_cloned_collect.stderr @@ -6,33 +6,12 @@ LL | let v2: Vec = v.iter().cloned().collect(); | = note: `-D clippy::iter-cloned-collect` implied by `-D warnings` -error: non-binding let on a type that implements `Drop` - --> $DIR/iter_cloned_collect.rs:15:5 - | -LL | let _: Vec = vec![1, 2, 3].iter().cloned().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `#[deny(clippy::let_underscore_drop)]` on by default - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable --> $DIR/iter_cloned_collect.rs:15:38 | LL | let _: Vec = vec![1, 2, 3].iter().cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()` -error: non-binding let on a type that implements `Drop` - --> $DIR/iter_cloned_collect.rs:19:9 - | -LL | / let _: Vec = std::ffi::CStr::from_ptr(std::ptr::null()) -LL | | .to_bytes() -LL | | .iter() -LL | | .cloned() -LL | | .collect(); - | |_______________________^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable --> $DIR/iter_cloned_collect.rs:20:24 | @@ -43,5 +22,5 @@ LL | | .cloned() LL | | .collect(); | |______________________^ help: try: `.to_vec()` -error: aborting due to 5 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/let_underscore_drop.stderr b/tests/ui/let_underscore_drop.stderr index 6dc8904c4fe..66069e0c5e1 100644 --- a/tests/ui/let_underscore_drop.stderr +++ b/tests/ui/let_underscore_drop.stderr @@ -1,4 +1,4 @@ -error: non-binding let on a type that implements `Drop` +error: non-binding `let` on a type that implements `Drop` --> $DIR/let_underscore_drop.rs:16:5 | LL | let _ = Box::new(()); @@ -7,7 +7,7 @@ LL | let _ = Box::new(()); = note: `-D clippy::let-underscore-drop` implied by `-D warnings` = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` -error: non-binding let on a type that implements `Drop` +error: non-binding `let` on a type that implements `Drop` --> $DIR/let_underscore_drop.rs:17:5 | LL | let _ = Droppable; @@ -15,7 +15,7 @@ LL | let _ = Droppable; | = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` -error: non-binding let on a type that implements `Drop` +error: non-binding `let` on a type that implements `Drop` --> $DIR/let_underscore_drop.rs:18:5 | LL | let _ = Some(Droppable); diff --git a/tests/ui/map_clone.fixed b/tests/ui/map_clone.fixed index 6e3a8e67e81..ce92b3c0c30 100644 --- a/tests/ui/map_clone.fixed +++ b/tests/ui/map_clone.fixed @@ -2,6 +2,7 @@ #![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)] diff --git a/tests/ui/map_clone.rs b/tests/ui/map_clone.rs index 6fd395710d4..324c776c3c9 100644 --- a/tests/ui/map_clone.rs +++ b/tests/ui/map_clone.rs @@ -2,6 +2,7 @@ #![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)] diff --git a/tests/ui/map_clone.stderr b/tests/ui/map_clone.stderr index 122a678f118..d84a5bf8d4d 100644 --- a/tests/ui/map_clone.stderr +++ b/tests/ui/map_clone.stderr @@ -1,97 +1,40 @@ -error: non-binding let on a type that implements `Drop` - --> $DIR/map_clone.rs:10:5 - | -LL | let _: Vec = vec![5_i8; 6].iter().map(|x| *x).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::let-underscore-drop` implied by `-D warnings` - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: you are using an explicit closure for copying elements - --> $DIR/map_clone.rs:10:22 + --> $DIR/map_clone.rs:11:22 | LL | let _: Vec = vec![5_i8; 6].iter().map(|x| *x).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `vec![5_i8; 6].iter().copied()` | = note: `-D clippy::map-clone` implied by `-D warnings` -error: non-binding let on a type that implements `Drop` - --> $DIR/map_clone.rs:11:5 - | -LL | let _: Vec = vec![String::new()].iter().map(|x| x.clone()).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: you are using an explicit closure for cloning elements - --> $DIR/map_clone.rs:11:26 + --> $DIR/map_clone.rs:12:26 | LL | let _: Vec = vec![String::new()].iter().map(|x| x.clone()).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `cloned` method: `vec![String::new()].iter().cloned()` -error: non-binding let on a type that implements `Drop` - --> $DIR/map_clone.rs:12:5 - | -LL | let _: Vec = vec![42, 43].iter().map(|&x| x).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: you are using an explicit closure for copying elements - --> $DIR/map_clone.rs:12:23 + --> $DIR/map_clone.rs:13:23 | LL | let _: Vec = vec![42, 43].iter().map(|&x| x).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `vec![42, 43].iter().copied()` error: you are using an explicit closure for copying elements - --> $DIR/map_clone.rs:14:26 + --> $DIR/map_clone.rs:15:26 | LL | let _: Option = Some(&16).map(|b| *b); | ^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `Some(&16).copied()` error: you are using an explicit closure for copying elements - --> $DIR/map_clone.rs:15:25 + --> $DIR/map_clone.rs:16:25 | LL | let _: Option = Some(&1).map(|x| x.clone()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling the dedicated `copied` method: `Some(&1).copied()` error: you are needlessly cloning iterator elements - --> $DIR/map_clone.rs:26:29 + --> $DIR/map_clone.rs:27:29 | LL | let _ = std::env::args().map(|v| v.clone()); | ^^^^^^^^^^^^^^^^^^^ help: remove the `map` call -error: non-binding let on a type that implements `Drop` - --> $DIR/map_clone.rs:35:9 - | -LL | let _: Vec = v.into_iter().map(|x| *x).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: non-binding let on a type that implements `Drop` - --> $DIR/map_clone.rs:42:9 - | -LL | let _: Vec = v.into_iter().map(|x| *x).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: non-binding let on a type that implements `Drop` - --> $DIR/map_clone.rs:45:9 - | -LL | let _: Vec = v.into_iter().map(|&mut x| x).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: non-binding let on a type that implements `Drop` - --> $DIR/map_clone.rs:53:9 - | -LL | let _: Vec<_> = items.into_iter().map(|x| x.clone()).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: aborting due to 13 previous errors +error: aborting due to 6 previous errors diff --git a/tests/ui/map_collect_result_unit.stderr b/tests/ui/map_collect_result_unit.stderr index 26e876b1808..8b06e13baa6 100644 --- a/tests/ui/map_collect_result_unit.stderr +++ b/tests/ui/map_collect_result_unit.stderr @@ -12,22 +12,5 @@ error: `.map().collect()` can be replaced with `.try_for_each()` LL | let _: Result<(), _> = (0..3).map(|t| Err(t + 1)).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(0..3).try_for_each(|t| Err(t + 1))` -error: non-binding let on a type that implements `Drop` - --> $DIR/map_collect_result_unit.rs:14:5 - | -LL | let _ = (0..3).map(|t| Err(t + 1)).collect::, _>>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `#[deny(clippy::let_underscore_drop)]` on by default - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: non-binding let on a type that implements `Drop` - --> $DIR/map_collect_result_unit.rs:15:5 - | -LL | let _ = (0..3).map(|t| Err(t + 1)).collect::>>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors diff --git a/tests/ui/map_flatten.fixed b/tests/ui/map_flatten.fixed index a5fdf7df613..a7ab5a12cb7 100644 --- a/tests/ui/map_flatten.fixed +++ b/tests/ui/map_flatten.fixed @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::all, clippy::pedantic)] +#![allow(clippy::let_underscore_drop)] #![allow(clippy::missing_docs_in_private_items)] #![allow(clippy::map_identity)] diff --git a/tests/ui/map_flatten.rs b/tests/ui/map_flatten.rs index abbc4e16e56..e364a05f376 100644 --- a/tests/ui/map_flatten.rs +++ b/tests/ui/map_flatten.rs @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::all, clippy::pedantic)] +#![allow(clippy::let_underscore_drop)] #![allow(clippy::missing_docs_in_private_items)] #![allow(clippy::map_identity)] diff --git a/tests/ui/map_flatten.stderr b/tests/ui/map_flatten.stderr index 6159b5256ee..d4e27f9aa07 100644 --- a/tests/ui/map_flatten.stderr +++ b/tests/ui/map_flatten.stderr @@ -1,81 +1,40 @@ -error: non-binding let on a type that implements `Drop` - --> $DIR/map_flatten.rs:14:5 - | -LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::let-underscore-drop` implied by `-D warnings` - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: called `map(..).flatten()` on an `Iterator` - --> $DIR/map_flatten.rs:14:46 + --> $DIR/map_flatten.rs:15:46 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id)` | = note: `-D clippy::map-flatten` implied by `-D warnings` -error: non-binding let on a type that implements `Drop` - --> $DIR/map_flatten.rs:15:5 - | -LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: called `map(..).flatten()` on an `Iterator` - --> $DIR/map_flatten.rs:15:46 + --> $DIR/map_flatten.rs:16:46 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_ref)` -error: non-binding let on a type that implements `Drop` - --> $DIR/map_flatten.rs:16:5 - | -LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: called `map(..).flatten()` on an `Iterator` - --> $DIR/map_flatten.rs:16:46 + --> $DIR/map_flatten.rs:17:46 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_closure)` -error: non-binding let on a type that implements `Drop` - --> $DIR/map_flatten.rs:17:5 - | -LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: called `map(..).flatten()` on an `Iterator` - --> $DIR/map_flatten.rs:17:46 + --> $DIR/map_flatten.rs:18:46 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(|x| x.checked_add(1))` -error: non-binding let on a type that implements `Drop` - --> $DIR/map_flatten.rs:20:5 - | -LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: called `map(..).flatten()` on an `Iterator` - --> $DIR/map_flatten.rs:20:46 + --> $DIR/map_flatten.rs:21:46 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `.flat_map(|x| 0..x)` error: called `map(..).flatten()` on an `Option` - --> $DIR/map_flatten.rs:23:39 + --> $DIR/map_flatten.rs:24:39 | LL | let _: Option<_> = (Some(Some(1))).map(|x| x).flatten(); | ^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `.and_then(|x| x)` -error: aborting due to 11 previous errors +error: aborting due to 6 previous errors diff --git a/tests/ui/map_identity.stderr b/tests/ui/map_identity.stderr index 6bfeb186bad..e4a0320cbda 100644 --- a/tests/ui/map_identity.stderr +++ b/tests/ui/map_identity.stderr @@ -1,12 +1,3 @@ -error: non-binding let on a type that implements `Drop` - --> $DIR/map_identity.rs:8:5 - | -LL | let _: Vec<_> = x.iter().map(not_identity).map(|x| return x).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `#[deny(clippy::let_underscore_drop)]` on by default - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: unnecessary map of the identity function --> $DIR/map_identity.rs:8:47 | @@ -15,14 +6,6 @@ LL | let _: Vec<_> = x.iter().map(not_identity).map(|x| return x).collect(); | = note: `-D clippy::map-identity` implied by `-D warnings` -error: non-binding let on a type that implements `Drop` - --> $DIR/map_identity.rs:9:5 - | -LL | let _: Vec<_> = x.iter().map(std::convert::identity).map(|y| y).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: unnecessary map of the identity function --> $DIR/map_identity.rs:9:57 | @@ -50,21 +33,5 @@ LL | | return x; LL | | }); | |______^ help: remove the call to `map` -error: non-binding let on a type that implements `Drop` - --> $DIR/map_identity.rs:15:5 - | -LL | let _: Vec<_> = x.iter().map(|x| 2 * x).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: non-binding let on a type that implements `Drop` - --> $DIR/map_identity.rs:16:5 - | -LL | let _: Vec<_> = x.iter().map(not_identity).map(|x| return x - 4).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: aborting due to 9 previous errors +error: aborting due to 5 previous errors diff --git a/tests/ui/match_single_binding.stderr b/tests/ui/match_single_binding.stderr index 8b07599817b..795c8c3e24d 100644 --- a/tests/ui/match_single_binding.stderr +++ b/tests/ui/match_single_binding.stderr @@ -150,20 +150,6 @@ LL | let Point { x, y } = coords(); LL | let product = x * y; | -error: non-binding let on a type that implements `Drop` - --> $DIR/match_single_binding.rs:96:5 - | -LL | / let _ = v -LL | | .iter() -LL | | .map(|i| match i.unwrap() { -LL | | unwrapped => unwrapped, -LL | | }) -LL | | .collect::>(); - | |______________________________^ - | - = note: `#[deny(clippy::let_underscore_drop)]` on by default - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: this match could be written as a `let` statement --> $DIR/match_single_binding.rs:98:18 | @@ -181,5 +167,5 @@ LL | unwrapped LL | }) | -error: aborting due to 12 previous errors +error: aborting due to 11 previous errors diff --git a/tests/ui/needless_pass_by_value.stderr b/tests/ui/needless_pass_by_value.stderr index cf04ee4b257..9aa783bf904 100644 --- a/tests/ui/needless_pass_by_value.stderr +++ b/tests/ui/needless_pass_by_value.stderr @@ -90,15 +90,6 @@ help: change `v.clone()` to LL | let _ = v.to_owned(); | ^^^^^^^^^^^^ -error: non-binding let on a type that implements `Drop` - --> $DIR/needless_pass_by_value.rs:85:5 - | -LL | let _ = v.clone(); - | ^^^^^^^^^^^^^^^^^^ - | - = note: `#[deny(clippy::let_underscore_drop)]` on by default - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: this argument is passed by value, but not consumed in the function body --> $DIR/needless_pass_by_value.rs:94:12 | @@ -183,5 +174,5 @@ error: this argument is passed by value, but not consumed in the function body LL | fn more_fun(_item: impl Club<'static, i32>) {} | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider taking a reference instead: `&impl Club<'static, i32>` -error: aborting due to 23 previous errors +error: aborting due to 22 previous errors diff --git a/tests/ui/redundant_clone.stderr b/tests/ui/redundant_clone.stderr index 270c3fac990..89b39254299 100644 --- a/tests/ui/redundant_clone.stderr +++ b/tests/ui/redundant_clone.stderr @@ -167,14 +167,5 @@ note: cloned value is neither consumed nor mutated LL | let y = x.clone().join("matthias"); | ^^^^^^^^^ -error: non-binding let on a type that implements `Drop` - --> $DIR/redundant_clone.rs:180:5 - | -LL | let _ = a.clone(); // OK - | ^^^^^^^^^^^^^^^^^^ - | - = note: `#[deny(clippy::let_underscore_drop)]` on by default - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - -error: aborting due to 15 previous errors +error: aborting due to 14 previous errors diff --git a/tests/ui/reversed_empty_ranges_fixable.stderr b/tests/ui/reversed_empty_ranges_fixable.stderr index 707a5d5032e..de83c4f3d63 100644 --- a/tests/ui/reversed_empty_ranges_fixable.stderr +++ b/tests/ui/reversed_empty_ranges_fixable.stderr @@ -10,15 +10,6 @@ help: consider using the following if you are attempting to iterate over this ra LL | (21..=42).rev().for_each(|x| println!("{}", x)); | ^^^^^^^^^^^^^^^ -error: non-binding let on a type that implements `Drop` - --> $DIR/reversed_empty_ranges_fixable.rs:10:5 - | -LL | let _ = (ANSWER..21).filter(|x| x % 2 == 0).take(10).collect::>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `#[deny(clippy::let_underscore_drop)]` on by default - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: this range is empty so it will yield no values --> $DIR/reversed_empty_ranges_fixable.rs:10:13 | @@ -52,5 +43,5 @@ help: consider using the following if you are attempting to iterate over this ra LL | for _ in (21u32..42u32).rev() {} | ^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/transmute.stderr b/tests/ui/transmute.stderr index d6767dc9f15..ad9953d12bc 100644 --- a/tests/ui/transmute.stderr +++ b/tests/ui/transmute.stderr @@ -24,71 +24,30 @@ error: transmute from a reference to a pointer LL | let _: *const U = core::intrinsics::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *const U` -error: non-binding let on a type that implements `Drop` - --> $DIR/transmute.rs:34:9 - | -LL | let _: Vec = core::intrinsics::transmute(my_vec()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `#[deny(clippy::let_underscore_drop)]` on by default - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: transmute from a type (`std::vec::Vec`) to itself --> $DIR/transmute.rs:34:27 | LL | let _: Vec = core::intrinsics::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: non-binding let on a type that implements `Drop` - --> $DIR/transmute.rs:36:9 - | -LL | let _: Vec = core::mem::transmute(my_vec()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: transmute from a type (`std::vec::Vec`) to itself --> $DIR/transmute.rs:36:27 | LL | let _: Vec = core::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: non-binding let on a type that implements `Drop` - --> $DIR/transmute.rs:38:9 - | -LL | let _: Vec = std::intrinsics::transmute(my_vec()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: transmute from a type (`std::vec::Vec`) to itself --> $DIR/transmute.rs:38:27 | LL | let _: Vec = std::intrinsics::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: non-binding let on a type that implements `Drop` - --> $DIR/transmute.rs:40:9 - | -LL | let _: Vec = std::mem::transmute(my_vec()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: transmute from a type (`std::vec::Vec`) to itself --> $DIR/transmute.rs:40:27 | LL | let _: Vec = std::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: non-binding let on a type that implements `Drop` - --> $DIR/transmute.rs:42:9 - | -LL | let _: Vec = my_transmute(my_vec()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: transmute from a type (`std::vec::Vec`) to itself --> $DIR/transmute.rs:42:27 | @@ -195,5 +154,5 @@ error: transmute from a `&mut [u8]` to a `&mut str` LL | let _: &mut str = unsafe { std::mem::transmute(mb) }; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()` -error: aborting due to 29 previous errors +error: aborting due to 24 previous errors diff --git a/tests/ui/transmute_collection.stderr b/tests/ui/transmute_collection.stderr index e89f6d8539f..ebc05c402ab 100644 --- a/tests/ui/transmute_collection.stderr +++ b/tests/ui/transmute_collection.stderr @@ -1,12 +1,3 @@ -error: non-binding let on a type that implements `Drop` - --> $DIR/transmute_collection.rs:9:9 - | -LL | let _ = transmute::<_, Vec>(vec![0u8]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `#[deny(clippy::let_underscore_drop)]` on by default - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: transmute from `std::vec::Vec` to `std::vec::Vec` with mismatched layout is unsound --> $DIR/transmute_collection.rs:9:17 | @@ -15,42 +6,18 @@ LL | let _ = transmute::<_, Vec>(vec![0u8]); | = note: `-D clippy::unsound-collection-transmute` implied by `-D warnings` -error: non-binding let on a type that implements `Drop` - --> $DIR/transmute_collection.rs:11:9 - | -LL | let _ = transmute::<_, Vec<[u8; 4]>>(vec![1234u32]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: transmute from `std::vec::Vec` to `std::vec::Vec<[u8; 4]>` with mismatched layout is unsound --> $DIR/transmute_collection.rs:11:17 | LL | let _ = transmute::<_, Vec<[u8; 4]>>(vec![1234u32]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: non-binding let on a type that implements `Drop` - --> $DIR/transmute_collection.rs:14:9 - | -LL | let _ = transmute::<_, VecDeque>(VecDeque::::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: transmute from `std::collections::VecDeque` to `std::collections::VecDeque` with mismatched layout is unsound --> $DIR/transmute_collection.rs:14:17 | LL | let _ = transmute::<_, VecDeque>(VecDeque::::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: non-binding let on a type that implements `Drop` - --> $DIR/transmute_collection.rs:16:9 - | -LL | let _ = transmute::<_, VecDeque>(VecDeque::<[u8; 4]>::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: transmute from `std::collections::VecDeque<[u8; 4]>` to `std::collections::VecDeque` with mismatched layout is unsound --> $DIR/transmute_collection.rs:16:17 | @@ -93,56 +60,24 @@ error: transmute from `std::collections::HashSet<[u8; 4]>` to `std::collections: LL | let _ = transmute::<_, HashSet>(HashSet::<[u8; 4]>::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: non-binding let on a type that implements `Drop` - --> $DIR/transmute_collection.rs:34:9 - | -LL | let _ = transmute::<_, BTreeMap>(BTreeMap::::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: transmute from `std::collections::BTreeMap` to `std::collections::BTreeMap` with mismatched layout is unsound --> $DIR/transmute_collection.rs:34:17 | LL | let _ = transmute::<_, BTreeMap>(BTreeMap::::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: non-binding let on a type that implements `Drop` - --> $DIR/transmute_collection.rs:35:9 - | -LL | let _ = transmute::<_, BTreeMap>(BTreeMap::::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: transmute from `std::collections::BTreeMap` to `std::collections::BTreeMap` with mismatched layout is unsound --> $DIR/transmute_collection.rs:35:17 | LL | let _ = transmute::<_, BTreeMap>(BTreeMap::::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: non-binding let on a type that implements `Drop` - --> $DIR/transmute_collection.rs:37:9 - | -LL | let _ = transmute::<_, BTreeMap>(BTreeMap::::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: transmute from `std::collections::BTreeMap` to `std::collections::BTreeMap` with mismatched layout is unsound --> $DIR/transmute_collection.rs:37:17 | LL | let _ = transmute::<_, BTreeMap>(BTreeMap::::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: non-binding let on a type that implements `Drop` - --> $DIR/transmute_collection.rs:38:9 - | -LL | let _ = transmute::<_, BTreeMap>(BTreeMap::<[u8; 4], u32>::new()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: transmute from `std::collections::BTreeMap<[u8; 4], u32>` to `std::collections::BTreeMap` with mismatched layout is unsound --> $DIR/transmute_collection.rs:38:17 | @@ -173,5 +108,5 @@ error: transmute from `std::collections::HashMap<[u8; 4], u32>` to `std::collect LL | let _ = transmute::<_, HashMap>(HashMap::<[u8; 4], u32>::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 26 previous errors +error: aborting due to 18 previous errors diff --git a/tests/ui/unnecessary_clone.stderr b/tests/ui/unnecessary_clone.stderr index 1f89cb0cdef..5ffa6c4fd06 100644 --- a/tests/ui/unnecessary_clone.stderr +++ b/tests/ui/unnecessary_clone.stderr @@ -24,15 +24,6 @@ error: using `.clone()` on a ref-counted pointer LL | arc_weak.clone(); | ^^^^^^^^^^^^^^^^ help: try this: `Weak::::clone(&arc_weak)` -error: non-binding let on a type that implements `Drop` - --> $DIR/unnecessary_clone.rs:36:5 - | -LL | let _: Arc = x.clone(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `#[deny(clippy::let_underscore_drop)]` on by default - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: using `.clone()` on a ref-counted pointer --> $DIR/unnecessary_clone.rs:36:33 | @@ -111,5 +102,5 @@ error: using `.clone()` on a ref-counted pointer LL | Some(try_opt!(Some(rc)).clone()) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Rc::::clone(&try_opt!(Some(rc)))` -error: aborting due to 13 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index ea3e96111cb..11c6efb25cc 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -52,15 +52,6 @@ error: useless conversion to the same type: `std::str::Lines` LL | let _ = "".lines().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()` -error: non-binding let on a type that implements `Drop` - --> $DIR/useless_conversion.rs:65:5 - | -LL | let _ = vec![1, 2, 3].into_iter().into_iter(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `#[deny(clippy::let_underscore_drop)]` on by default - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: useless conversion to the same type: `std::vec::IntoIter` --> $DIR/useless_conversion.rs:65:13 | @@ -79,5 +70,5 @@ error: useless conversion to the same type: `i32` LL | let _ = i32::from(a + b) * 3; | ^^^^^^^^^^^^^^^^ help: consider removing `i32::from()`: `(a + b)` -error: aborting due to 12 previous errors +error: aborting due to 11 previous errors -- cgit 1.4.1-3-g733a5 From 4852cca61bb989adf77b1d202eae6b40067fa9ab Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius III" Date: Mon, 9 Nov 2020 07:49:14 -0500 Subject: Allow `let_underscore_drop` in `filter_methods` test --- tests/ui/filter_methods.rs | 1 + tests/ui/filter_methods.stderr | 55 ++++-------------------------------------- 2 files changed, 6 insertions(+), 50 deletions(-) (limited to 'tests') diff --git a/tests/ui/filter_methods.rs b/tests/ui/filter_methods.rs index ef434245fd7..51450241619 100644 --- a/tests/ui/filter_methods.rs +++ b/tests/ui/filter_methods.rs @@ -1,4 +1,5 @@ #![warn(clippy::all, clippy::pedantic)] +#![allow(clippy::clippy::let_underscore_drop)] #![allow(clippy::missing_docs_in_private_items)] fn main() { diff --git a/tests/ui/filter_methods.stderr b/tests/ui/filter_methods.stderr index b5ac90282dc..11922681379 100644 --- a/tests/ui/filter_methods.stderr +++ b/tests/ui/filter_methods.stderr @@ -1,14 +1,5 @@ -error: non-binding `let` on a type that implements `Drop` - --> $DIR/filter_methods.rs:5:5 - | -LL | let _: Vec<_> = vec![5; 6].into_iter().filter(|&x| x == 0).map(|x| x * 2).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::let-underscore-drop` implied by `-D warnings` - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: called `filter(..).map(..)` on an `Iterator` - --> $DIR/filter_methods.rs:5:21 + --> $DIR/filter_methods.rs:6:21 | LL | let _: Vec<_> = vec![5; 6].into_iter().filter(|&x| x == 0).map(|x| x * 2).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,20 +7,8 @@ LL | let _: Vec<_> = vec![5; 6].into_iter().filter(|&x| x == 0).map(|x| x * = note: `-D clippy::filter-map` implied by `-D warnings` = help: this is more succinctly expressed by calling `.filter_map(..)` instead -error: non-binding `let` on a type that implements `Drop` - --> $DIR/filter_methods.rs:7:5 - | -LL | / let _: Vec<_> = vec![5_i8; 6] -LL | | .into_iter() -LL | | .filter(|&x| x == 0) -LL | | .flat_map(|x| x.checked_mul(2)) -LL | | .collect(); - | |___________________^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: called `filter(..).flat_map(..)` on an `Iterator` - --> $DIR/filter_methods.rs:7:21 + --> $DIR/filter_methods.rs:8:21 | LL | let _: Vec<_> = vec![5_i8; 6] | _____________________^ @@ -40,20 +19,8 @@ LL | | .flat_map(|x| x.checked_mul(2)) | = help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()` -error: non-binding `let` on a type that implements `Drop` - --> $DIR/filter_methods.rs:13:5 - | -LL | / let _: Vec<_> = vec![5_i8; 6] -LL | | .into_iter() -LL | | .filter_map(|x| x.checked_mul(2)) -LL | | .flat_map(|x| x.checked_mul(2)) -LL | | .collect(); - | |___________________^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: called `filter_map(..).flat_map(..)` on an `Iterator` - --> $DIR/filter_methods.rs:13:21 + --> $DIR/filter_methods.rs:14:21 | LL | let _: Vec<_> = vec![5_i8; 6] | _____________________^ @@ -64,20 +31,8 @@ LL | | .flat_map(|x| x.checked_mul(2)) | = help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()` -error: non-binding `let` on a type that implements `Drop` - --> $DIR/filter_methods.rs:19:5 - | -LL | / let _: Vec<_> = vec![5_i8; 6] -LL | | .into_iter() -LL | | .filter_map(|x| x.checked_mul(2)) -LL | | .map(|x| x.checked_mul(2)) -LL | | .collect(); - | |___________________^ - | - = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` - error: called `filter_map(..).map(..)` on an `Iterator` - --> $DIR/filter_methods.rs:19:21 + --> $DIR/filter_methods.rs:20:21 | LL | let _: Vec<_> = vec![5_i8; 6] | _____________________^ @@ -88,5 +43,5 @@ LL | | .map(|x| x.checked_mul(2)) | = help: this is more succinctly expressed by only calling `.filter_map(..)` instead -error: aborting due to 8 previous errors +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From faa3e233169523f6bb1537d9b6b2aabe66efd01b Mon Sep 17 00:00:00 2001 From: chansuke Date: Tue, 3 Nov 2020 09:00:30 +0900 Subject: Add exteranal macros for as_conversions --- tests/ui/as_conversions.rs | 14 +++++++++++++- tests/ui/as_conversions.stderr | 6 +++--- tests/ui/auxiliary/macro_rules.rs | 14 ++++++++++++++ 3 files changed, 30 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/tests/ui/as_conversions.rs b/tests/ui/as_conversions.rs index e01ba0c64df..cd745feec6d 100644 --- a/tests/ui/as_conversions.rs +++ b/tests/ui/as_conversions.rs @@ -1,7 +1,19 @@ -#[warn(clippy::as_conversions)] +// aux-build:macro_rules.rs + +#![warn(clippy::as_conversions)] + +#[macro_use] +extern crate macro_rules; + +fn with_external_macro() { + as_conv_with_arg!(0u32 as u64); + as_conv!(); +} fn main() { let i = 0u32 as u64; let j = &i as *const u64 as *mut u64; + + with_external_macro(); } diff --git a/tests/ui/as_conversions.stderr b/tests/ui/as_conversions.stderr index 312d3a7460e..f5f75d3aee0 100644 --- a/tests/ui/as_conversions.stderr +++ b/tests/ui/as_conversions.stderr @@ -1,5 +1,5 @@ error: using a potentially dangerous silent `as` conversion - --> $DIR/as_conversions.rs:4:13 + --> $DIR/as_conversions.rs:14:13 | LL | let i = 0u32 as u64; | ^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let i = 0u32 as u64; = help: consider using a safe wrapper for this conversion error: using a potentially dangerous silent `as` conversion - --> $DIR/as_conversions.rs:6:13 + --> $DIR/as_conversions.rs:16:13 | LL | let j = &i as *const u64 as *mut u64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | let j = &i as *const u64 as *mut u64; = help: consider using a safe wrapper for this conversion error: using a potentially dangerous silent `as` conversion - --> $DIR/as_conversions.rs:6:13 + --> $DIR/as_conversions.rs:16:13 | LL | let j = &i as *const u64 as *mut u64; | ^^^^^^^^^^^^^^^^ diff --git a/tests/ui/auxiliary/macro_rules.rs b/tests/ui/auxiliary/macro_rules.rs index 93303865e17..f985a15eda2 100644 --- a/tests/ui/auxiliary/macro_rules.rs +++ b/tests/ui/auxiliary/macro_rules.rs @@ -70,3 +70,17 @@ macro_rules! ref_arg_function { fn fun_example(ref _x: usize) {} }; } + +#[macro_export] +macro_rules! as_conv_with_arg { + (0u32 as u64) => { + () + }; +} + +#[macro_export] +macro_rules! as_conv { + () => { + 0u32 as u64 + }; +} -- cgit 1.4.1-3-g733a5 From 769094410a22849c426972dd421a1937463a48e7 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 30 Oct 2020 09:18:16 -0500 Subject: Fix map_clone with deref and clone --- clippy_lints/src/map_clone.rs | 14 +++++++++----- tests/ui/map_clone.fixed | 7 +++++++ tests/ui/map_clone.rs | 7 +++++++ 3 files changed, 23 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs index 9a00608ce39..3f34dd5112e 100644 --- a/clippy_lints/src/map_clone.rs +++ b/clippy_lints/src/map_clone.rs @@ -8,6 +8,7 @@ use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::Mutability; use rustc_middle::ty; +use rustc_middle::ty::adjustment::Adjust; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::Ident; use rustc_span::{sym, Span}; @@ -75,11 +76,14 @@ impl<'tcx> LateLintPass<'tcx> for MapClone { } } }, - hir::ExprKind::MethodCall(ref method, _, ref obj, _) => { - if ident_eq(name, &obj[0]) && method.ident.as_str() == "clone" - && match_trait_method(cx, closure_expr, &paths::CLONE_TRAIT) { - - let obj_ty = cx.typeck_results().expr_ty(&obj[0]); + hir::ExprKind::MethodCall(ref method, _, [obj], _) => if_chain! { + if ident_eq(name, obj) && method.ident.name == sym::clone; + if match_trait_method(cx, closure_expr, &paths::CLONE_TRAIT); + // no autoderefs + if !cx.typeck_results().expr_adjustments(obj).iter() + .any(|a| matches!(a.kind, Adjust::Deref(Some(..)))); + then { + let obj_ty = cx.typeck_results().expr_ty(obj); if let ty::Ref(_, ty, mutability) = obj_ty.kind() { if matches!(mutability, Mutability::Not) { let copy = is_copy(cx, ty); diff --git a/tests/ui/map_clone.fixed b/tests/ui/map_clone.fixed index 6e3a8e67e81..504ae281fe7 100644 --- a/tests/ui/map_clone.fixed +++ b/tests/ui/map_clone.fixed @@ -52,4 +52,11 @@ fn main() { let items = vec![&mut aa, &mut bb]; let _: Vec<_> = items.into_iter().map(|x| x.clone()).collect(); } + + // Issue #6239 deref coercion and clone deref + { + use std::cell::RefCell; + + let _ = Some(RefCell::new(String::new()).borrow()).map(|s| s.clone()); + } } diff --git a/tests/ui/map_clone.rs b/tests/ui/map_clone.rs index 6fd395710d4..9348e6bae7a 100644 --- a/tests/ui/map_clone.rs +++ b/tests/ui/map_clone.rs @@ -52,4 +52,11 @@ fn main() { let items = vec![&mut aa, &mut bb]; let _: Vec<_> = items.into_iter().map(|x| x.clone()).collect(); } + + // Issue #6239 deref coercion and clone deref + { + use std::cell::RefCell; + + let _ = Some(RefCell::new(String::new()).borrow()).map(|s| s.clone()); + } } -- cgit 1.4.1-3-g733a5 From a1cf2d334d685fa11fdc96fc98f35292254e5651 Mon Sep 17 00:00:00 2001 From: Ryan Sullivant Date: Mon, 5 Oct 2020 21:23:36 -0700 Subject: Added a lint as suggested in 6010 which recommends using `contains()` instead of `find()` follows by `is_some()` on strings Update clippy_lints/src/find_is_some_on_strs.rs Co-authored-by: Takayuki Nakata Update clippy_lints/src/methods/mod.rs Co-authored-by: Philipp Krones --- clippy_lints/src/methods/mod.rs | 38 +++++++++++++++++++++++++++++++++++--- src/lintlist/mod.rs | 2 +- tests/ui/methods.rs | 23 +++++++++++++++++++++-- tests/ui/methods.stderr | 2 -- 4 files changed, 57 insertions(+), 8 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index b6fb3d06934..bd04a95e4a1 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -515,11 +515,11 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for an iterator search (such as `find()`, + /// **What it does:** Checks for an iterator or string search (such as `find()`, /// `position()`, or `rposition()`) followed by a call to `is_some()`. /// /// **Why is this bad?** Readability, this can be written more concisely as - /// `_.any(_)`. + /// `_.any(_)` or `_.contains(_)`. /// /// **Known problems:** None. /// @@ -535,7 +535,7 @@ declare_clippy_lint! { /// ``` pub SEARCH_IS_SOME, complexity, - "using an iterator search followed by `is_some()`, which is more succinctly expressed as a call to `any()`" + "using an iterator or string search followed by `is_some()`, which is more succinctly expressed as a call to `any()` or `contains()`" } declare_clippy_lint! { @@ -3041,6 +3041,7 @@ fn lint_flat_map_identity<'tcx>( } /// lint searching an Iterator followed by `is_some()` +/// or calling `find()` on a string followed by `is_some()` fn lint_search_is_some<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, @@ -3094,6 +3095,37 @@ fn lint_search_is_some<'tcx>( span_lint(cx, SEARCH_IS_SOME, expr.span, &msg); } } + // lint if `find()` is called by `String` or `&str` + else if search_method == "find" { + let is_string_or_str_slice = |e| { + let self_ty = cx.typeck_results().expr_ty(e).peel_refs(); + if is_type_diagnostic_item(cx, self_ty, sym!(string_type)) { + true + } else { + *self_ty.kind() == ty::Str + } + }; + if_chain! { + if is_string_or_str_slice(&search_args[0]); + if is_string_or_str_slice(&search_args[1]); + then { + let msg = "called `is_some()` after calling `find()` \ + on a string. This is more succinctly expressed by calling \ + `contains()`.".to_string(); + let mut applicability = Applicability::MachineApplicable; + let find_arg = snippet_with_applicability(cx, search_args[1].span, "..", &mut applicability); + span_lint_and_sugg( + cx, + SEARCH_IS_SOME, + method_span.with_hi(expr.span.hi()), + &msg, + "try this", + format!("contains({})", find_arg), + applicability, + ); + } + } + } } /// Used for `lint_binary_expr_with_method_call`. diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 4f1b56ed9be..69acd3d9b8b 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2121,7 +2121,7 @@ vec![ Lint { name: "search_is_some", group: "complexity", - desc: "using an iterator search followed by `is_some()`, which is more succinctly expressed as a call to `any()`", + desc: "using an iterator or string search followed by `is_some()`, which is more succinctly expressed as a call to `any()` or `contains()`", deprecation: None, module: "methods", }, diff --git a/tests/ui/methods.rs b/tests/ui/methods.rs index d93e5b114ec..92ec00a11d2 100644 --- a/tests/ui/methods.rs +++ b/tests/ui/methods.rs @@ -168,8 +168,27 @@ fn search_is_some() { x < 0 } ).is_some(); - - // Check that we don't lint if the caller is not an `Iterator`. + + let s1 = String::from("hello world"); + let s2 = String::from("world"); + // Check caller `find()` is a &`static str case + let _ = "hello world".find("world").is_some(); + let _ = "hello world".find(&s2).is_some(); + let _ = "hello world".find(&s2[2..]).is_some(); + // Check caller of `find()` is a String case + let _ = s1.find("world").is_some(); + let _ = s1.find(&s2).is_some(); + let _ = s1.find(&s2[2..]).is_some(); + // Check caller of `find()` is a slice of String case + let _ = s1[2..].find("world").is_some(); + let _ = s1[2..].find(&s2).is_some(); + let _ = s1[2..].find(&s2[2..]).is_some(); + + // Check that we don't lint if `find()` is called with + // Pattern that is not a string + let _ = s1.find(|c: char| c == 'o' || c == 'l').is_some(); + + // Check that we don't lint if the caller is not an `Iterator` or string let foo = IteratorFalsePositives { foo: 0 }; let _ = foo.find().is_some(); let _ = foo.position().is_some(); diff --git a/tests/ui/methods.stderr b/tests/ui/methods.stderr index 8a281c2dbd2..b2b551bd5f8 100644 --- a/tests/ui/methods.stderr +++ b/tests/ui/methods.stderr @@ -88,5 +88,3 @@ LL | | } LL | | ).is_some(); | |______________________________^ -error: aborting due to 11 previous errors - -- cgit 1.4.1-3-g733a5 From 431fcbcc00eb4634178406c1afdf955e5b3be07a Mon Sep 17 00:00:00 2001 From: Ryan Sullivant Date: Sat, 17 Oct 2020 14:07:22 -0700 Subject: Moved the tests for lint `search_is_some` to new files `search_is_some.rs` and `search_is_some_fixable.rs` --- tests/ui/methods.rs | 63 -------------------------------------- tests/ui/search_is_some.rs | 39 +++++++++++++++++++++++ tests/ui/search_is_some_fixable.rs | 35 +++++++++++++++++++++ 3 files changed, 74 insertions(+), 63 deletions(-) create mode 100644 tests/ui/search_is_some.rs create mode 100644 tests/ui/search_is_some_fixable.rs (limited to 'tests') diff --git a/tests/ui/methods.rs b/tests/ui/methods.rs index 92ec00a11d2..513d930e056 100644 --- a/tests/ui/methods.rs +++ b/tests/ui/methods.rs @@ -133,69 +133,6 @@ fn filter_next() { let _ = foo.filter().next(); } -/// Checks implementation of `SEARCH_IS_SOME` lint. -#[rustfmt::skip] -fn search_is_some() { - let v = vec![3, 2, 1, 0, -1, -2, -3]; - let y = &&42; - - // Check `find().is_some()`, single-line case. - let _ = v.iter().find(|&x| *x < 0).is_some(); - let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less - let _ = (0..1).find(|x| *x == 0).is_some(); - let _ = v.iter().find(|x| **x == 0).is_some(); - - // Check `find().is_some()`, multi-line case. - let _ = v.iter().find(|&x| { - *x < 0 - } - ).is_some(); - - // Check `position().is_some()`, single-line case. - let _ = v.iter().position(|&x| x < 0).is_some(); - - // Check `position().is_some()`, multi-line case. - let _ = v.iter().position(|&x| { - x < 0 - } - ).is_some(); - - // Check `rposition().is_some()`, single-line case. - let _ = v.iter().rposition(|&x| x < 0).is_some(); - - // Check `rposition().is_some()`, multi-line case. - let _ = v.iter().rposition(|&x| { - x < 0 - } - ).is_some(); - - let s1 = String::from("hello world"); - let s2 = String::from("world"); - // Check caller `find()` is a &`static str case - let _ = "hello world".find("world").is_some(); - let _ = "hello world".find(&s2).is_some(); - let _ = "hello world".find(&s2[2..]).is_some(); - // Check caller of `find()` is a String case - let _ = s1.find("world").is_some(); - let _ = s1.find(&s2).is_some(); - let _ = s1.find(&s2[2..]).is_some(); - // Check caller of `find()` is a slice of String case - let _ = s1[2..].find("world").is_some(); - let _ = s1[2..].find(&s2).is_some(); - let _ = s1[2..].find(&s2[2..]).is_some(); - - // Check that we don't lint if `find()` is called with - // Pattern that is not a string - let _ = s1.find(|c: char| c == 'o' || c == 'l').is_some(); - - // Check that we don't lint if the caller is not an `Iterator` or string - let foo = IteratorFalsePositives { foo: 0 }; - let _ = foo.find().is_some(); - let _ = foo.position().is_some(); - let _ = foo.rposition().is_some(); -} - fn main() { filter_next(); - search_is_some(); } diff --git a/tests/ui/search_is_some.rs b/tests/ui/search_is_some.rs new file mode 100644 index 00000000000..1ce372ab1d3 --- /dev/null +++ b/tests/ui/search_is_some.rs @@ -0,0 +1,39 @@ +#[macro_use] +extern crate option_helpers; +use option_helpers::IteratorFalsePositives; + +#[warn(clippy::search_is_some)] +#[rustfmt::skip] +fn main() { + let v = vec![3, 2, 1, 0, -1, -2, -3]; + let y = &&42; + + + // Check `find().is_some()`, multi-line case. + let _ = v.iter().find(|&x| { + *x < 0 + } + ).is_some(); + + // Check `position().is_some()`, multi-line case. + let _ = v.iter().position(|&x| { + x < 0 + } + ).is_some(); + + // Check `rposition().is_some()`, multi-line case. + let _ = v.iter().rposition(|&x| { + x < 0 + } + ).is_some(); + + // Check that we don't lint if the caller is not an `Iterator` or string + let foo = IteratorFalsePositives { foo: 0 }; + let _ = foo.find().is_some(); + let _ = foo.position().is_some(); + let _ = foo.rposition().is_some(); + // check that we don't lint if `find()` is called with + // `Pattern` that is not a string + let _ = "hello world".find(|c: char| c == 'o' || c == 'l').is_some(); +} + diff --git a/tests/ui/search_is_some_fixable.rs b/tests/ui/search_is_some_fixable.rs new file mode 100644 index 00000000000..5bffb7e849f --- /dev/null +++ b/tests/ui/search_is_some_fixable.rs @@ -0,0 +1,35 @@ +// run-rustfix + +#![warn(clippy::search_is_some)] + +fn main() { + let v = vec![3, 2, 1, 0, -1, -2, -3]; + let y = &&42; + + // Check `find().is_some()`, single-line case. + let _ = v.iter().find(|&x| *x < 0).is_some(); + let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less + let _ = (0..1).find(|x| *x == 0).is_some(); + let _ = v.iter().find(|x| **x == 0).is_some(); + + // Check `position().is_some()`, single-line case. + let _ = v.iter().position(|&x| x < 0).is_some(); + + // Check `rposition().is_some()`, single-line case. + let _ = v.iter().rposition(|&x| x < 0).is_some(); + + let s1 = String::from("hello world"); + let s2 = String::from("world"); + // caller of `find()` is a `&`static str` + let _ = "hello world".find("world").is_some(); + let _ = "hello world".find(&s2).is_some(); + let _ = "hello world".find(&s2[2..]).is_some(); + // caller of `find()` is a `String` + let _ = s1.find("world").is_some(); + let _ = s1.find(&s2).is_some(); + let _ = s1.find(&s2[2..]).is_some(); + // caller of `find()` is slice of `String` + let _ = s1[2..].find("world").is_some(); + let _ = s1[2..].find(&s2).is_some(); + let _ = s1[2..].find(&s2[2..]).is_some(); +} -- cgit 1.4.1-3-g733a5 From 55dc822062eb760afff0d242dea193aabc2c9771 Mon Sep 17 00:00:00 2001 From: Ryan Sullivant Date: Sat, 17 Oct 2020 14:28:00 -0700 Subject: Ran `tests/ui/update-all-references.sh" and `cargo dev fmt` --- tests/ui/methods.stderr | 68 ------------------------ tests/ui/search_is_some.rs | 3 +- tests/ui/search_is_some.stderr | 42 +++++++++++++++ tests/ui/search_is_some_fixable.fixed | 35 +++++++++++++ tests/ui/search_is_some_fixable.rs | 6 +-- tests/ui/search_is_some_fixable.stderr | 94 ++++++++++++++++++++++++++++++++++ 6 files changed, 175 insertions(+), 73 deletions(-) create mode 100644 tests/ui/search_is_some.stderr create mode 100644 tests/ui/search_is_some_fixable.fixed create mode 100644 tests/ui/search_is_some_fixable.stderr (limited to 'tests') diff --git a/tests/ui/methods.stderr b/tests/ui/methods.stderr index b2b551bd5f8..bf4675966df 100644 --- a/tests/ui/methods.stderr +++ b/tests/ui/methods.stderr @@ -20,71 +20,3 @@ LL | | ).next(); | = note: `-D clippy::filter-next` implied by `-D warnings` -error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:143:22 - | -LL | let _ = v.iter().find(|&x| *x < 0).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x < 0)` - | - = note: `-D clippy::search-is-some` implied by `-D warnings` - -error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:144:20 - | -LL | let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| **y == x)` - -error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:145:20 - | -LL | let _ = (0..1).find(|x| *x == 0).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| x == 0)` - -error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:146:22 - | -LL | let _ = v.iter().find(|x| **x == 0).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x == 0)` - -error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:149:13 - | -LL | let _ = v.iter().find(|&x| { - | _____________^ -LL | | *x < 0 -LL | | } -LL | | ).is_some(); - | |______________________________^ - -error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:155:22 - | -LL | let _ = v.iter().position(|&x| x < 0).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` - -error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:158:13 - | -LL | let _ = v.iter().position(|&x| { - | _____________^ -LL | | x < 0 -LL | | } -LL | | ).is_some(); - | |______________________________^ - -error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:164:22 - | -LL | let _ = v.iter().rposition(|&x| x < 0).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` - -error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. - --> $DIR/methods.rs:167:13 - | -LL | let _ = v.iter().rposition(|&x| { - | _____________^ -LL | | x < 0 -LL | | } -LL | | ).is_some(); - | |______________________________^ - diff --git a/tests/ui/search_is_some.rs b/tests/ui/search_is_some.rs index 1ce372ab1d3..1399138a0d2 100644 --- a/tests/ui/search_is_some.rs +++ b/tests/ui/search_is_some.rs @@ -1,4 +1,4 @@ -#[macro_use] +// aux-build:option_helpers.rs extern crate option_helpers; use option_helpers::IteratorFalsePositives; @@ -36,4 +36,3 @@ fn main() { // `Pattern` that is not a string let _ = "hello world".find(|c: char| c == 'o' || c == 'l').is_some(); } - diff --git a/tests/ui/search_is_some.stderr b/tests/ui/search_is_some.stderr new file mode 100644 index 00000000000..a124ab1dfd4 --- /dev/null +++ b/tests/ui/search_is_some.stderr @@ -0,0 +1,42 @@ +error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. + --> $DIR/search_is_some.rs:13:13 + | +LL | let _ = v.iter().find(|&x| { + | _____________^ +LL | | *x < 0 +LL | | } +LL | | ).is_some(); + | |______________________________^ + | + = note: `-D clippy::search-is-some` implied by `-D warnings` + +error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. + --> $DIR/search_is_some.rs:19:13 + | +LL | let _ = v.iter().position(|&x| { + | _____________^ +LL | | x < 0 +LL | | } +LL | | ).is_some(); + | |______________________________^ + +error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. + --> $DIR/search_is_some.rs:25:13 + | +LL | let _ = v.iter().rposition(|&x| { + | _____________^ +LL | | x < 0 +LL | | } +LL | | ).is_some(); + | |______________________________^ + +error: use of a blacklisted/placeholder name `foo` + --> $DIR/search_is_some.rs:31:9 + | +LL | let foo = IteratorFalsePositives { foo: 0 }; + | ^^^ + | + = note: `-D clippy::blacklisted-name` implied by `-D warnings` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/search_is_some_fixable.fixed b/tests/ui/search_is_some_fixable.fixed new file mode 100644 index 00000000000..dc3f290e562 --- /dev/null +++ b/tests/ui/search_is_some_fixable.fixed @@ -0,0 +1,35 @@ +// run-rustfix + +#![warn(clippy::search_is_some)] + +fn main() { + let v = vec![3, 2, 1, 0, -1, -2, -3]; + let y = &&42; + + // Check `find().is_some()`, single-line case. + let _ = v.iter().any(|x| *x < 0); + let _ = (0..1).any(|x| **y == x); // one dereference less + let _ = (0..1).any(|x| x == 0); + let _ = v.iter().any(|x| *x == 0); + + // Check `position().is_some()`, single-line case. + let _ = v.iter().any(|&x| x < 0); + + // Check `rposition().is_some()`, single-line case. + let _ = v.iter().any(|&x| x < 0); + + let s1 = String::from("hello world"); + let s2 = String::from("world"); + // caller of `find()` is a `&`static str` + let _ = "hello world".contains("world"); + let _ = "hello world".contains(&s2); + let _ = "hello world".contains(&s2[2..]); + // caller of `find()` is a `String` + let _ = s1.contains("world"); + let _ = s1.contains(&s2); + let _ = s1.contains(&s2[2..]); + // caller of `find()` is slice of `String` + let _ = s1[2..].contains("world"); + let _ = s1[2..].contains(&s2); + let _ = s1[2..].contains(&s2[2..]); +} diff --git a/tests/ui/search_is_some_fixable.rs b/tests/ui/search_is_some_fixable.rs index 5bffb7e849f..146cf5adf1b 100644 --- a/tests/ui/search_is_some_fixable.rs +++ b/tests/ui/search_is_some_fixable.rs @@ -5,16 +5,16 @@ fn main() { let v = vec![3, 2, 1, 0, -1, -2, -3]; let y = &&42; - + // Check `find().is_some()`, single-line case. let _ = v.iter().find(|&x| *x < 0).is_some(); let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less let _ = (0..1).find(|x| *x == 0).is_some(); let _ = v.iter().find(|x| **x == 0).is_some(); - + // Check `position().is_some()`, single-line case. let _ = v.iter().position(|&x| x < 0).is_some(); - + // Check `rposition().is_some()`, single-line case. let _ = v.iter().rposition(|&x| x < 0).is_some(); diff --git a/tests/ui/search_is_some_fixable.stderr b/tests/ui/search_is_some_fixable.stderr new file mode 100644 index 00000000000..7a2c063fee8 --- /dev/null +++ b/tests/ui/search_is_some_fixable.stderr @@ -0,0 +1,94 @@ +error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. + --> $DIR/search_is_some_fixable.rs:10:22 + | +LL | let _ = v.iter().find(|&x| *x < 0).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x < 0)` + | + = note: `-D clippy::search-is-some` implied by `-D warnings` + +error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. + --> $DIR/search_is_some_fixable.rs:11:20 + | +LL | let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| **y == x)` + +error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. + --> $DIR/search_is_some_fixable.rs:12:20 + | +LL | let _ = (0..1).find(|x| *x == 0).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| x == 0)` + +error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. + --> $DIR/search_is_some_fixable.rs:13:22 + | +LL | let _ = v.iter().find(|x| **x == 0).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x == 0)` + +error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. + --> $DIR/search_is_some_fixable.rs:16:22 + | +LL | let _ = v.iter().position(|&x| x < 0).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` + +error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. + --> $DIR/search_is_some_fixable.rs:19:22 + | +LL | let _ = v.iter().rposition(|&x| x < 0).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` + +error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. + --> $DIR/search_is_some_fixable.rs:24:27 + | +LL | let _ = "hello world".find("world").is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `contains("world")` + +error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. + --> $DIR/search_is_some_fixable.rs:25:27 + | +LL | let _ = "hello world".find(&s2).is_some(); + | ^^^^^^^^^^^^^^^^^^^ help: try this: `contains(&s2)` + +error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. + --> $DIR/search_is_some_fixable.rs:26:27 + | +LL | let _ = "hello world".find(&s2[2..]).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `contains(&s2[2..])` + +error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. + --> $DIR/search_is_some_fixable.rs:28:16 + | +LL | let _ = s1.find("world").is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `contains("world")` + +error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. + --> $DIR/search_is_some_fixable.rs:29:16 + | +LL | let _ = s1.find(&s2).is_some(); + | ^^^^^^^^^^^^^^^^^^^ help: try this: `contains(&s2)` + +error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. + --> $DIR/search_is_some_fixable.rs:30:16 + | +LL | let _ = s1.find(&s2[2..]).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `contains(&s2[2..])` + +error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. + --> $DIR/search_is_some_fixable.rs:32:21 + | +LL | let _ = s1[2..].find("world").is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `contains("world")` + +error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. + --> $DIR/search_is_some_fixable.rs:33:21 + | +LL | let _ = s1[2..].find(&s2).is_some(); + | ^^^^^^^^^^^^^^^^^^^ help: try this: `contains(&s2)` + +error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. + --> $DIR/search_is_some_fixable.rs:34:21 + | +LL | let _ = s1[2..].find(&s2[2..]).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `contains(&s2[2..])` + +error: aborting due to 15 previous errors + -- cgit 1.4.1-3-g733a5 From ee1b959054aaf69968d440915766e834568de8fd Mon Sep 17 00:00:00 2001 From: Ryan Sullivant Date: Sat, 31 Oct 2020 12:40:56 -0700 Subject: Added period back to lint `search_is_some` and ran `update-all-references.sh` --- clippy_lints/src/methods/mod.rs | 2 +- tests/ui/methods.stderr | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index fc66bf0422f..19e63fbddf7 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3111,7 +3111,7 @@ fn lint_search_is_some<'tcx>( then { let msg = "called `is_some()` after calling `find()` \ on a string. This is more succinctly expressed by calling \ - `contains()`"; + `contains()`."; let mut applicability = Applicability::MachineApplicable; let find_arg = snippet_with_applicability(cx, search_args[1].span, "..", &mut applicability); span_lint_and_sugg( diff --git a/tests/ui/methods.stderr b/tests/ui/methods.stderr index bf4675966df..33aba630a53 100644 --- a/tests/ui/methods.stderr +++ b/tests/ui/methods.stderr @@ -20,3 +20,5 @@ LL | | ).next(); | = note: `-D clippy::filter-next` implied by `-D warnings` +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From fd303132a27f0fa9bbbfb3282200f8190353574b Mon Sep 17 00:00:00 2001 From: Ryan Sullivant Date: Sat, 7 Nov 2020 00:21:22 -0700 Subject: Cleaned up message and suggestion for `lint_search_is_some` --- clippy_lints/src/methods/mod.rs | 14 ++++---- tests/ui/search_is_some.stderr | 11 +++++-- tests/ui/search_is_some_fixable.stderr | 60 +++++++++++++++++----------------- 3 files changed, 44 insertions(+), 41 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 19e63fbddf7..66f5aa0c6a0 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3053,10 +3053,10 @@ fn lint_search_is_some<'tcx>( // lint if caller of search is an Iterator if match_trait_method(cx, &is_some_args[0], &paths::ITERATOR) { let msg = format!( - "called `is_some()` after searching an `Iterator` with {}. This is more succinctly \ - expressed by calling `any()`.", + "called `is_some()` after searching an `Iterator` with {}", search_method ); + let hint = "this is more succinctly expressed by calling `any()`"; let search_snippet = snippet(cx, search_args[1].span, ".."); if search_snippet.lines().count() <= 1 { // suggest `any(|x| ..)` instead of `any(|&x| ..)` for `find(|&x| ..).is_some()` @@ -3084,7 +3084,7 @@ fn lint_search_is_some<'tcx>( SEARCH_IS_SOME, method_span.with_hi(expr.span.hi()), &msg, - "try this", + "use `any()` instead", format!( "any({})", any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) @@ -3092,7 +3092,7 @@ fn lint_search_is_some<'tcx>( Applicability::MachineApplicable, ); } else { - span_lint(cx, SEARCH_IS_SOME, expr.span, &msg); + span_lint_and_help(cx, SEARCH_IS_SOME, expr.span, &msg, None, hint); } } // lint if `find()` is called by `String` or `&str` @@ -3109,9 +3109,7 @@ fn lint_search_is_some<'tcx>( if is_string_or_str_slice(&search_args[0]); if is_string_or_str_slice(&search_args[1]); then { - let msg = "called `is_some()` after calling `find()` \ - on a string. This is more succinctly expressed by calling \ - `contains()`."; + let msg = "called `is_some()` after calling `find()` on a string"; let mut applicability = Applicability::MachineApplicable; let find_arg = snippet_with_applicability(cx, search_args[1].span, "..", &mut applicability); span_lint_and_sugg( @@ -3119,7 +3117,7 @@ fn lint_search_is_some<'tcx>( SEARCH_IS_SOME, method_span.with_hi(expr.span.hi()), msg, - "try this", + "use `contains()` instead", format!("contains({})", find_arg), applicability, ); diff --git a/tests/ui/search_is_some.stderr b/tests/ui/search_is_some.stderr index a124ab1dfd4..43827a6a98d 100644 --- a/tests/ui/search_is_some.stderr +++ b/tests/ui/search_is_some.stderr @@ -1,4 +1,4 @@ -error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. +error: called `is_some()` after searching an `Iterator` with find --> $DIR/search_is_some.rs:13:13 | LL | let _ = v.iter().find(|&x| { @@ -9,8 +9,9 @@ LL | | ).is_some(); | |______________________________^ | = note: `-D clippy::search-is-some` implied by `-D warnings` + = help: this is more succinctly expressed by calling `any()` -error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. +error: called `is_some()` after searching an `Iterator` with position --> $DIR/search_is_some.rs:19:13 | LL | let _ = v.iter().position(|&x| { @@ -19,8 +20,10 @@ LL | | x < 0 LL | | } LL | | ).is_some(); | |______________________________^ + | + = help: this is more succinctly expressed by calling `any()` -error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. +error: called `is_some()` after searching an `Iterator` with rposition --> $DIR/search_is_some.rs:25:13 | LL | let _ = v.iter().rposition(|&x| { @@ -29,6 +32,8 @@ LL | | x < 0 LL | | } LL | | ).is_some(); | |______________________________^ + | + = help: this is more succinctly expressed by calling `any()` error: use of a blacklisted/placeholder name `foo` --> $DIR/search_is_some.rs:31:9 diff --git a/tests/ui/search_is_some_fixable.stderr b/tests/ui/search_is_some_fixable.stderr index 7a2c063fee8..f4c5d7a3389 100644 --- a/tests/ui/search_is_some_fixable.stderr +++ b/tests/ui/search_is_some_fixable.stderr @@ -1,94 +1,94 @@ -error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. +error: called `is_some()` after searching an `Iterator` with find --> $DIR/search_is_some_fixable.rs:10:22 | LL | let _ = v.iter().find(|&x| *x < 0).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x < 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| *x < 0)` | = note: `-D clippy::search-is-some` implied by `-D warnings` -error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. +error: called `is_some()` after searching an `Iterator` with find --> $DIR/search_is_some_fixable.rs:11:20 | LL | let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| **y == x)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| **y == x)` -error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. +error: called `is_some()` after searching an `Iterator` with find --> $DIR/search_is_some_fixable.rs:12:20 | LL | let _ = (0..1).find(|x| *x == 0).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| x == 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| x == 0)` -error: called `is_some()` after searching an `Iterator` with find. This is more succinctly expressed by calling `any()`. +error: called `is_some()` after searching an `Iterator` with find --> $DIR/search_is_some_fixable.rs:13:22 | LL | let _ = v.iter().find(|x| **x == 0).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|x| *x == 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| *x == 0)` -error: called `is_some()` after searching an `Iterator` with position. This is more succinctly expressed by calling `any()`. +error: called `is_some()` after searching an `Iterator` with position --> $DIR/search_is_some_fixable.rs:16:22 | LL | let _ = v.iter().position(|&x| x < 0).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|&x| x < 0)` -error: called `is_some()` after searching an `Iterator` with rposition. This is more succinctly expressed by calling `any()`. +error: called `is_some()` after searching an `Iterator` with rposition --> $DIR/search_is_some_fixable.rs:19:22 | LL | let _ = v.iter().rposition(|&x| x < 0).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `any(|&x| x < 0)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|&x| x < 0)` -error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. +error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable.rs:24:27 | LL | let _ = "hello world".find("world").is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `contains("world")` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")` -error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. +error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable.rs:25:27 | LL | let _ = "hello world".find(&s2).is_some(); - | ^^^^^^^^^^^^^^^^^^^ help: try this: `contains(&s2)` + | ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)` -error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. +error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable.rs:26:27 | LL | let _ = "hello world".find(&s2[2..]).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `contains(&s2[2..])` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])` -error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. +error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable.rs:28:16 | LL | let _ = s1.find("world").is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `contains("world")` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")` -error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. +error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable.rs:29:16 | LL | let _ = s1.find(&s2).is_some(); - | ^^^^^^^^^^^^^^^^^^^ help: try this: `contains(&s2)` + | ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)` -error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. +error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable.rs:30:16 | LL | let _ = s1.find(&s2[2..]).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `contains(&s2[2..])` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])` -error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. +error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable.rs:32:21 | LL | let _ = s1[2..].find("world").is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `contains("world")` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")` -error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. +error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable.rs:33:21 | LL | let _ = s1[2..].find(&s2).is_some(); - | ^^^^^^^^^^^^^^^^^^^ help: try this: `contains(&s2)` + | ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)` -error: called `is_some()` after calling `find()` on a string. This is more succinctly expressed by calling `contains()`. +error: called `is_some()` after calling `find()` on a string --> $DIR/search_is_some_fixable.rs:34:21 | LL | let _ = s1[2..].find(&s2[2..]).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `contains(&s2[2..])` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])` error: aborting due to 15 previous errors -- cgit 1.4.1-3-g733a5 From 5c1c50ee174f20574ddbf67b43ab0e9bb5b2c60d Mon Sep 17 00:00:00 2001 From: Ryan Sullivant Date: Tue, 10 Nov 2020 23:48:01 -0700 Subject: Change variable named `foo` and rerun `update-all-references` --- tests/ui/search_is_some.rs | 8 ++++---- tests/ui/search_is_some.stderr | 16 ++++------------ tests/ui/search_is_some_fixable.stderr | 12 ++++++------ 3 files changed, 14 insertions(+), 22 deletions(-) (limited to 'tests') diff --git a/tests/ui/search_is_some.rs b/tests/ui/search_is_some.rs index 1399138a0d2..f0dc3b3d06b 100644 --- a/tests/ui/search_is_some.rs +++ b/tests/ui/search_is_some.rs @@ -28,10 +28,10 @@ fn main() { ).is_some(); // Check that we don't lint if the caller is not an `Iterator` or string - let foo = IteratorFalsePositives { foo: 0 }; - let _ = foo.find().is_some(); - let _ = foo.position().is_some(); - let _ = foo.rposition().is_some(); + let falsepos = IteratorFalsePositives { foo: 0 }; + let _ = falsepos.find().is_some(); + let _ = falsepos.position().is_some(); + let _ = falsepos.rposition().is_some(); // check that we don't lint if `find()` is called with // `Pattern` that is not a string let _ = "hello world".find(|c: char| c == 'o' || c == 'l').is_some(); diff --git a/tests/ui/search_is_some.stderr b/tests/ui/search_is_some.stderr index 43827a6a98d..c601f568c60 100644 --- a/tests/ui/search_is_some.stderr +++ b/tests/ui/search_is_some.stderr @@ -1,4 +1,4 @@ -error: called `is_some()` after searching an `Iterator` with find +error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some.rs:13:13 | LL | let _ = v.iter().find(|&x| { @@ -11,7 +11,7 @@ LL | | ).is_some(); = note: `-D clippy::search-is-some` implied by `-D warnings` = help: this is more succinctly expressed by calling `any()` -error: called `is_some()` after searching an `Iterator` with position +error: called `is_some()` after searching an `Iterator` with `position` --> $DIR/search_is_some.rs:19:13 | LL | let _ = v.iter().position(|&x| { @@ -23,7 +23,7 @@ LL | | ).is_some(); | = help: this is more succinctly expressed by calling `any()` -error: called `is_some()` after searching an `Iterator` with rposition +error: called `is_some()` after searching an `Iterator` with `rposition` --> $DIR/search_is_some.rs:25:13 | LL | let _ = v.iter().rposition(|&x| { @@ -35,13 +35,5 @@ LL | | ).is_some(); | = help: this is more succinctly expressed by calling `any()` -error: use of a blacklisted/placeholder name `foo` - --> $DIR/search_is_some.rs:31:9 - | -LL | let foo = IteratorFalsePositives { foo: 0 }; - | ^^^ - | - = note: `-D clippy::blacklisted-name` implied by `-D warnings` - -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/search_is_some_fixable.stderr b/tests/ui/search_is_some_fixable.stderr index f4c5d7a3389..23c1d9a901b 100644 --- a/tests/ui/search_is_some_fixable.stderr +++ b/tests/ui/search_is_some_fixable.stderr @@ -1,4 +1,4 @@ -error: called `is_some()` after searching an `Iterator` with find +error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable.rs:10:22 | LL | let _ = v.iter().find(|&x| *x < 0).is_some(); @@ -6,31 +6,31 @@ LL | let _ = v.iter().find(|&x| *x < 0).is_some(); | = note: `-D clippy::search-is-some` implied by `-D warnings` -error: called `is_some()` after searching an `Iterator` with find +error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable.rs:11:20 | LL | let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| **y == x)` -error: called `is_some()` after searching an `Iterator` with find +error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable.rs:12:20 | LL | let _ = (0..1).find(|x| *x == 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| x == 0)` -error: called `is_some()` after searching an `Iterator` with find +error: called `is_some()` after searching an `Iterator` with `find` --> $DIR/search_is_some_fixable.rs:13:22 | LL | let _ = v.iter().find(|x| **x == 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| *x == 0)` -error: called `is_some()` after searching an `Iterator` with position +error: called `is_some()` after searching an `Iterator` with `position` --> $DIR/search_is_some_fixable.rs:16:22 | LL | let _ = v.iter().position(|&x| x < 0).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|&x| x < 0)` -error: called `is_some()` after searching an `Iterator` with rposition +error: called `is_some()` after searching an `Iterator` with `rposition` --> $DIR/search_is_some_fixable.rs:19:22 | LL | let _ = v.iter().rposition(|&x| x < 0).is_some(); -- cgit 1.4.1-3-g733a5 From 5f64867e1d5344da8ff7526308d4b63676242c0d Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Wed, 11 Nov 2020 22:36:53 +0900 Subject: Fix suggestion in `manual_range_contains` when using float --- clippy_lints/src/ranges.rs | 6 ++++-- tests/ui/range_contains.fixed | 5 +++++ tests/ui/range_contains.rs | 5 +++++ tests/ui/range_contains.stderr | 14 +++++++++++++- 4 files changed, 27 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 79e9a56af9a..4b514bbd42c 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -222,13 +222,14 @@ fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<' let name = snippet_with_applicability(cx, name_span, "_", &mut applicability); let lo = snippet_with_applicability(cx, l_span, "_", &mut applicability); let hi = snippet_with_applicability(cx, u_span, "_", &mut applicability); + let space = if lo.ends_with('.') { " " } else { "" }; span_lint_and_sugg( cx, MANUAL_RANGE_CONTAINS, span, &format!("manual `{}::contains` implementation", range_type), "use", - format!("({}{}{}).contains(&{})", lo, range_op, hi, name), + format!("({}{}{}{}).contains(&{})", lo, space, range_op, hi, name), applicability, ); } else if !combine_and && ord == Some(lord) { @@ -251,13 +252,14 @@ fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<' let name = snippet_with_applicability(cx, name_span, "_", &mut applicability); let lo = snippet_with_applicability(cx, l_span, "_", &mut applicability); let hi = snippet_with_applicability(cx, u_span, "_", &mut applicability); + let space = if lo.ends_with('.') { " " } else { "" }; span_lint_and_sugg( cx, MANUAL_RANGE_CONTAINS, span, &format!("manual `!{}::contains` implementation", range_type), "use", - format!("!({}{}{}).contains(&{})", lo, range_op, hi, name), + format!("!({}{}{}{}).contains(&{})", lo, space, range_op, hi, name), applicability, ); } diff --git a/tests/ui/range_contains.fixed b/tests/ui/range_contains.fixed index 632a6592a28..048874a7f82 100644 --- a/tests/ui/range_contains.fixed +++ b/tests/ui/range_contains.fixed @@ -38,4 +38,9 @@ fn main() { x >= 8 || x >= 12; x < 12 || 12 < x; x >= 8 || x <= 12; + + // Fix #6315 + let y = 3.; + (0. ..1.).contains(&y); + !(0. ..=1.).contains(&y); } diff --git a/tests/ui/range_contains.rs b/tests/ui/range_contains.rs index 6af0d034ef6..60ad259f404 100644 --- a/tests/ui/range_contains.rs +++ b/tests/ui/range_contains.rs @@ -38,4 +38,9 @@ fn main() { x >= 8 || x >= 12; x < 12 || 12 < x; x >= 8 || x <= 12; + + // Fix #6315 + let y = 3.; + y >= 0. && y < 1.; + y < 0. || y > 1.; } diff --git a/tests/ui/range_contains.stderr b/tests/ui/range_contains.stderr index 69b009eafc3..bc79f1bca84 100644 --- a/tests/ui/range_contains.stderr +++ b/tests/ui/range_contains.stderr @@ -72,5 +72,17 @@ error: manual `!RangeInclusive::contains` implementation LL | 999 < x || 1 > x; | ^^^^^^^^^^^^^^^^ help: use: `!(1..=999).contains(&x)` -error: aborting due to 12 previous errors +error: manual `Range::contains` implementation + --> $DIR/range_contains.rs:44:5 + | +LL | y >= 0. && y < 1.; + | ^^^^^^^^^^^^^^^^^ help: use: `(0. ..1.).contains(&y)` + +error: manual `!RangeInclusive::contains` implementation + --> $DIR/range_contains.rs:45:5 + | +LL | y < 0. || y > 1.; + | ^^^^^^^^^^^^^^^^ help: use: `!(0. ..=1.).contains(&y)` + +error: aborting due to 14 previous errors -- cgit 1.4.1-3-g733a5 From 8f89108533690afe964799d8f514956ec8b72377 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Mon, 9 Nov 2020 22:14:11 +0900 Subject: Fix FP in indirect `needless_collect` when used multiple times --- clippy_lints/src/loops.rs | 34 +++++++++++++++++++++++++++++++++- tests/ui/needless_collect_indirect.rs | 20 ++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 0d31e9cfc3d..143cbea5537 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2950,7 +2950,7 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo for ref stmt in block.stmts { if_chain! { if let StmtKind::Local( - Local { pat: Pat { kind: PatKind::Binding(_, _, ident, .. ), .. }, + Local { pat: Pat { hir_id: pat_id, kind: PatKind::Binding(_, _, ident, .. ), .. }, init: Some(ref init_expr), .. } ) = stmt.kind; if let ExprKind::MethodCall(ref method_name, _, &[ref iter_source], ..) = init_expr.kind; @@ -2964,6 +2964,16 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo if let Some(iter_calls) = detect_iter_and_into_iters(block, *ident); if iter_calls.len() == 1; then { + let mut used_count_visitor = UsedCountVisitor { + cx, + id: *pat_id, + count: 0, + }; + walk_block(&mut used_count_visitor, block); + if used_count_visitor.count > 1 { + return; + } + // Suggest replacing iter_call with iter_replacement, and removing stmt let iter_call = &iter_calls[0]; span_lint_and_then( @@ -3087,6 +3097,28 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor { } } +struct UsedCountVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + id: HirId, + count: usize, +} + +impl<'a, 'tcx> Visitor<'tcx> for UsedCountVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if same_var(self.cx, expr, self.id) { + self.count += 1; + } else { + walk_expr(self, expr); + } + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) + } +} + /// Detect the occurrences of calls to `iter` or `into_iter` for the /// given identifier fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, identifier: Ident) -> Option> { diff --git a/tests/ui/needless_collect_indirect.rs b/tests/ui/needless_collect_indirect.rs index 4f6e5357727..0918a6868ab 100644 --- a/tests/ui/needless_collect_indirect.rs +++ b/tests/ui/needless_collect_indirect.rs @@ -22,4 +22,24 @@ fn main() { let sample = vec![a.clone(), "b".to_string(), "c".to_string()]; let non_copy_contains = sample.into_iter().collect::>(); non_copy_contains.contains(&a); + + // Fix #5991 + let vec_a = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + let vec_b = vec_a.iter().collect::>(); + if vec_b.len() > 3 {} + let other_vec = vec![1, 3, 12, 4, 16, 2]; + let we_got_the_same_numbers = other_vec.iter().filter(|item| vec_b.contains(item)).collect::>(); + + // Fix #6297 + let sample = [1; 5]; + let multiple_indirect = sample.iter().collect::>(); + let sample2 = vec![2, 3]; + if multiple_indirect.is_empty() { + // do something + } else { + let found = sample2 + .iter() + .filter(|i| multiple_indirect.iter().any(|s| **s % **i == 0)) + .collect::>(); + } } -- cgit 1.4.1-3-g733a5 From 0e803417f997ba35c0045704dd347e64c2a1786c Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Mon, 16 Nov 2020 12:14:10 +0900 Subject: Add `rustfmt::skip` as a work around because comments are checked and removed by rustfmt for some reason --- tests/ui/cast_ref_to_mut.rs | 4 ++++ tests/ui/cast_ref_to_mut.stderr | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/tests/ui/cast_ref_to_mut.rs b/tests/ui/cast_ref_to_mut.rs index 089e5cfabe4..0ede958d170 100644 --- a/tests/ui/cast_ref_to_mut.rs +++ b/tests/ui/cast_ref_to_mut.rs @@ -2,6 +2,10 @@ #![allow(clippy::no_effect)] extern "C" { + #[rustfmt::skip] + // TODO: This `rustfmt::skip` is a work around of #6336 because + // the following comments are checked by rustfmt for some reason. + // // N.B., mutability can be easily incorrect in FFI calls -- as // in C, the default is mutable pointers. fn ffi(c: *mut u8); diff --git a/tests/ui/cast_ref_to_mut.stderr b/tests/ui/cast_ref_to_mut.stderr index aacd99437d9..d36aa0e00ee 100644 --- a/tests/ui/cast_ref_to_mut.stderr +++ b/tests/ui/cast_ref_to_mut.stderr @@ -1,5 +1,5 @@ error: casting `&T` to `&mut T` may cause undefined behavior, consider instead using an `UnsafeCell` - --> $DIR/cast_ref_to_mut.rs:18:9 + --> $DIR/cast_ref_to_mut.rs:22:9 | LL | (*(a as *const _ as *mut String)).push_str(" world"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,13 +7,13 @@ LL | (*(a as *const _ as *mut String)).push_str(" world"); = note: `-D clippy::cast-ref-to-mut` implied by `-D warnings` error: casting `&T` to `&mut T` may cause undefined behavior, consider instead using an `UnsafeCell` - --> $DIR/cast_ref_to_mut.rs:19:9 + --> $DIR/cast_ref_to_mut.rs:23:9 | LL | *(a as *const _ as *mut _) = String::from("Replaced"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `&T` to `&mut T` may cause undefined behavior, consider instead using an `UnsafeCell` - --> $DIR/cast_ref_to_mut.rs:20:9 + --> $DIR/cast_ref_to_mut.rs:24:9 | LL | *(a as *const _ as *mut String) += " world"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From 5b8f2b6c93ea819cd6d6e02ad7e2b8b9da23fd67 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Mon, 16 Nov 2020 18:32:01 +0100 Subject: Remove `expect()` calls to avoid ICEs in `deref_addrof` lint --- clippy_lints/src/reference.rs | 48 +++++++++++++++++++++++++------------------ tests/ui/crashes/ice-6332.rs | 11 ++++++++++ 2 files changed, 39 insertions(+), 20 deletions(-) create mode 100644 tests/ui/crashes/ice-6332.rs (limited to 'tests') diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index 35a1310d68b..efe3237990d 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -60,30 +60,38 @@ impl EarlyLintPass for DerefAddrOf { }).map_or(span, |start_no_whitespace| e.span.with_lo(start_no_whitespace)) }; - let rpos = if *mutability == Mutability::Mut { - macro_source.rfind("mut").expect("already checked this is a mutable reference") + "mut".len() - } else { - macro_source.rfind('&').expect("already checked this is a reference") + "&".len() + let mut generate_snippet = |pattern: &str| { + #[allow(clippy::cast_possible_truncation)] + macro_source.rfind(pattern).map(|pattern_pos| { + let rpos = pattern_pos + pattern.len(); + let span_after_ref = e.span.with_lo(BytePos(e.span.lo().0 + rpos as u32)); + let span = trim_leading_whitespaces(span_after_ref); + snippet_with_applicability(cx, span, "_", &mut applicability) + }) }; - #[allow(clippy::cast_possible_truncation)] - let span_after_ref = e.span.with_lo(BytePos(e.span.lo().0 + rpos as u32)); - let span = trim_leading_whitespaces(span_after_ref); - snippet_with_applicability(cx, span, "_", &mut applicability) + + if *mutability == Mutability::Mut { + generate_snippet("mut") + } else { + generate_snippet("&") + } } else { - snippet_with_applicability(cx, e.span, "_", &mut applicability) + Some(snippet_with_applicability(cx, e.span, "_", &mut applicability)) } } else { - snippet_with_applicability(cx, addrof_target.span, "_", &mut applicability) - }.to_string(); - span_lint_and_sugg( - cx, - DEREF_ADDROF, - e.span, - "immediately dereferencing a reference", - "try this", - sugg, - applicability, - ); + Some(snippet_with_applicability(cx, addrof_target.span, "_", &mut applicability)) + }; + if let Some(sugg) = sugg { + span_lint_and_sugg( + cx, + DEREF_ADDROF, + e.span, + "immediately dereferencing a reference", + "try this", + sugg.to_string(), + applicability, + ); + } } } } diff --git a/tests/ui/crashes/ice-6332.rs b/tests/ui/crashes/ice-6332.rs new file mode 100644 index 00000000000..9dc92aa500b --- /dev/null +++ b/tests/ui/crashes/ice-6332.rs @@ -0,0 +1,11 @@ +fn cmark_check() { + let mut link_err = false; + macro_rules! cmark_error { + ($bad:expr) => { + *$bad = true; + }; + } + cmark_error!(&mut link_err); +} + +pub fn main() {} -- cgit 1.4.1-3-g733a5 From a7ac441760ae034ff7401439b38da821f4e2df3a Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sun, 20 Sep 2020 02:03:14 +0900 Subject: Add new lint to detect unnecessarily wrapped value --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 + clippy_lints/src/unnecessary_wrap.rs | 177 +++++++++++++++++++++++++++++++++++ src/lintlist/mod.rs | 7 ++ tests/ui/unnecessary_wrap.fixed | 47 ++++++++++ tests/ui/unnecessary_wrap.rs | 47 ++++++++++ tests/ui/unnecessary_wrap.stderr | 34 +++++++ 7 files changed, 318 insertions(+) create mode 100644 clippy_lints/src/unnecessary_wrap.rs create mode 100644 tests/ui/unnecessary_wrap.fixed create mode 100644 tests/ui/unnecessary_wrap.rs create mode 100644 tests/ui/unnecessary_wrap.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 816d25bcd93..02b862d3196 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2008,6 +2008,7 @@ Released 2018-09-13 [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation [`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by [`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap +[`unnecessary_wrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wrap [`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern [`unneeded_wildcard_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_wildcard_pattern [`unnested_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 20b38cbb6d0..2d1f75391bb 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -323,6 +323,7 @@ mod unicode; mod unit_return_expecting_ord; mod unnamed_address; mod unnecessary_sort_by; +mod unnecessary_wrap; mod unnested_or_patterns; mod unsafe_removed_from_name; mod unused_io_amount; @@ -892,6 +893,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &unnamed_address::FN_ADDRESS_COMPARISONS, &unnamed_address::VTABLE_ADDRESS_COMPARISONS, &unnecessary_sort_by::UNNECESSARY_SORT_BY, + &unnecessary_wrap::UNNECESSARY_WRAP, &unnested_or_patterns::UNNESTED_OR_PATTERNS, &unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, &unused_io_amount::UNUSED_IO_AMOUNT, @@ -1064,6 +1066,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box redundant_clone::RedundantClone); store.register_late_pass(|| box slow_vector_initialization::SlowVectorInit); store.register_late_pass(|| box unnecessary_sort_by::UnnecessarySortBy); + store.register_late_pass(|| box unnecessary_wrap::UnnecessaryWrap); store.register_late_pass(|| box types::RefToMut); store.register_late_pass(|| box assertions_on_constants::AssertionsOnConstants); store.register_late_pass(|| box missing_const_for_fn::MissingConstForFn); @@ -1571,6 +1574,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), + LintId::of(&unnecessary_wrap::UNNECESSARY_WRAP), LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), LintId::of(&unused_unit::UNUSED_UNIT), @@ -1775,6 +1779,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::UNNECESSARY_CAST), LintId::of(&types::VEC_BOX), LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), + LintId::of(&unnecessary_wrap::UNNECESSARY_WRAP), 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/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs new file mode 100644 index 00000000000..26a57517258 --- /dev/null +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -0,0 +1,177 @@ +use crate::utils::{ + is_type_diagnostic_item, match_qpath, multispan_sugg_with_applicability, paths, return_ty, snippet, + span_lint_and_then, +}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::intravisit::{FnKind, NestedVisitorMap, Visitor}; +use rustc_hir::*; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for private functions that only return `Ok` or `Some`. + /// + /// **Why is this bad?** It is not meaningful to wrap values when no `None` or `Err` is returned. + /// + /// **Known problems:** Since this lint changes function type signature, you may need to + /// adjust some codes at callee side. + /// + /// **Example:** + /// + /// ```rust + /// pub fn get_cool_number(a: bool, b: bool) -> Option { + /// if a && b { + /// return Some(50); + /// } + /// if a { + /// Some(0) + /// } else { + /// Some(10) + /// } + /// } + /// ``` + /// Use instead: + /// ```rust + /// pub fn get_cool_number(a: bool, b: bool) -> i32 { + /// if a && b { + /// return 50; + /// } + /// if a { + /// 0 + /// } else { + /// 10 + /// } + /// } + /// ``` + pub UNNECESSARY_WRAP, + complexity, + "functions that only return `Ok` or `Some`" +} + +declare_lint_pass!(UnnecessaryWrap => [UNNECESSARY_WRAP]); + +impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + fn_kind: FnKind<'tcx>, + fn_decl: &FnDecl<'tcx>, + body: &Body<'tcx>, + span: Span, + hir_id: HirId, + ) { + if_chain! { + if let FnKind::ItemFn(.., visibility, _) = fn_kind; + if visibility.node.is_pub(); + then { + return; + } + } + + if let ExprKind::Block(ref block, ..) = body.value.kind { + let path = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(option_type)) { + &paths::OPTION_SOME + } else if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)) { + &paths::RESULT_OK + } else { + return; + }; + + let mut visitor = UnnecessaryWrapVisitor { result: Vec::new() }; + visitor.visit_block(block); + let result = visitor.result; + + if result.iter().any(|expr| { + if_chain! { + if let ExprKind::Call(ref func, ref args) = expr.kind; + if let ExprKind::Path(ref qpath) = func.kind; + if match_qpath(qpath, path); + if args.len() == 1; + then { + false + } else { + true + } + } + }) { + return; + } + + let suggs = result.iter().filter_map(|expr| { + let snippet = if let ExprKind::Call(_, ref args) = expr.kind { + Some(snippet(cx, args[0].span, "..").to_string()) + } else { + None + }; + snippet.map(|snip| (expr.span, snip)) + }); + + span_lint_and_then( + cx, + UNNECESSARY_WRAP, + span, + "this function returns unnecessarily wrapping data", + move |diag| { + multispan_sugg_with_applicability( + diag, + "factor this out to", + Applicability::MachineApplicable, + suggs, + ); + }, + ); + } + } +} + +struct UnnecessaryWrapVisitor<'tcx> { + result: Vec<&'tcx Expr<'tcx>>, +} + +impl<'tcx> Visitor<'tcx> for UnnecessaryWrapVisitor<'tcx> { + type Map = Map<'tcx>; + + fn visit_block(&mut self, block: &'tcx Block<'tcx>) { + for stmt in block.stmts { + self.visit_stmt(stmt); + } + if let Some(expr) = block.expr { + self.visit_expr(expr) + } + } + + fn visit_stmt(&mut self, stmt: &'tcx Stmt<'tcx>) { + match stmt.kind { + StmtKind::Semi(ref expr) => { + if let ExprKind::Ret(Some(value)) = expr.kind { + self.result.push(value); + } + }, + StmtKind::Expr(ref expr) => self.visit_expr(expr), + _ => (), + } + } + + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { + match expr.kind { + ExprKind::Ret(Some(value)) => self.result.push(value), + ExprKind::Call(..) | ExprKind::Path(..) => self.result.push(expr), + ExprKind::Block(ref block, _) | ExprKind::Loop(ref block, ..) => { + self.visit_block(block); + }, + ExprKind::Match(_, arms, _) => { + for arm in arms { + self.visit_expr(arm.body); + } + }, + _ => intravisit::walk_expr(self, expr), + } + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 69acd3d9b8b..4a0cdc5d82f 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2608,6 +2608,13 @@ vec![ deprecation: None, module: "unwrap", }, + Lint { + name: "unnecessary_wrap", + group: "complexity", + desc: "functions that only return `Ok` or `Some`", + deprecation: None, + module: "unnecessary_wrap", + }, Lint { name: "unneeded_field_pattern", group: "restriction", diff --git a/tests/ui/unnecessary_wrap.fixed b/tests/ui/unnecessary_wrap.fixed new file mode 100644 index 00000000000..1657a3173db --- /dev/null +++ b/tests/ui/unnecessary_wrap.fixed @@ -0,0 +1,47 @@ +// run-rustfix +#![warn(clippy::unnecessary_wrap)] +#![allow(clippy::no_effect)] +#![allow(clippy::needless_return)] +#![allow(clippy::if_same_then_else)] + +// should be linted +fn func1(a: bool, b: bool) -> Option { + if a && b { + return Some(42); + } + if a { + Some(-1); + Some(2) + } else { + return Some(1337); + } +} + +// public fns should not be linted +pub fn func2(a: bool) -> Option { + if a { + Some(1) + } else { + Some(1) + } +} + +// should not be linted +fn func3(a: bool) -> Option { + if a { + Some(1) + } else { + None + } +} + +// should be linted +fn func4() -> Option { + 1 +} + +fn main() { + // method calls are not linted + func1(true, true); + func2(true); +} diff --git a/tests/ui/unnecessary_wrap.rs b/tests/ui/unnecessary_wrap.rs new file mode 100644 index 00000000000..edf41dad790 --- /dev/null +++ b/tests/ui/unnecessary_wrap.rs @@ -0,0 +1,47 @@ +// run-rustfix +#![warn(clippy::unnecessary_wrap)] +#![allow(clippy::no_effect)] +#![allow(clippy::needless_return)] +#![allow(clippy::if_same_then_else)] + +// should be linted +fn func1(a: bool, b: bool) -> Option { + if a && b { + return Some(42); + } + if a { + Some(-1); + Some(2) + } else { + return Some(1337); + } +} + +// public fns should not be linted +pub fn func2(a: bool) -> Option { + if a { + Some(1) + } else { + Some(1) + } +} + +// should not be linted +fn func3(a: bool) -> Option { + if a { + Some(1) + } else { + None + } +} + +// should be linted +fn func4() -> Option { + Some(1) +} + +fn main() { + // method calls are not linted + func1(true, true); + func2(true); +} diff --git a/tests/ui/unnecessary_wrap.stderr b/tests/ui/unnecessary_wrap.stderr new file mode 100644 index 00000000000..8473bd81839 --- /dev/null +++ b/tests/ui/unnecessary_wrap.stderr @@ -0,0 +1,34 @@ +error: this function unnecessarily wrapping data + --> $DIR/unnecessary_wrap.rs:8:1 + | +LL | / fn func1(a: bool, b: bool) -> Option { +LL | | if a && b { +LL | | return Some(42); +LL | | } +... | +LL | | } +LL | | } + | |_^ + | + = note: `-D clippy::unnecessary-wrap` implied by `-D warnings` +help: factor this out to + | +LL | return 42; +LL | } +LL | if a { +LL | Some(-1); +LL | 2 +LL | } else { + ... + +error: this function unnecessarily wrapping data + --> $DIR/unnecessary_wrap.rs:39:1 + | +LL | / fn func4() -> Option { +LL | | Some(1) + | | ------- help: factor this out to: `1` +LL | | } + | |_^ + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From 750c118b347af938383c5bff53040480e0974071 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sun, 20 Sep 2020 18:22:01 +0900 Subject: Add suggestion on type signatures --- clippy_lints/src/unnecessary_wrap.rs | 31 ++++++++++++++++++++++--------- tests/ui/unnecessary_wrap.fixed | 18 +++++++++++++++++- tests/ui/unnecessary_wrap.rs | 16 ++++++++++++++++ tests/ui/unnecessary_wrap.stderr | 35 +++++++++++++++++++++++++++-------- 4 files changed, 82 insertions(+), 18 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index 26a57517258..7b586c1df0c 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -7,7 +7,7 @@ use rustc_errors::Applicability; use rustc_hir::intravisit::{FnKind, NestedVisitorMap, Visitor}; use rustc_hir::*; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::hir::map::Map; +use rustc_middle::{hir::map::Map, ty::subst::GenericArgKind}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; @@ -100,14 +100,27 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { return; } - let suggs = result.iter().filter_map(|expr| { - let snippet = if let ExprKind::Call(_, ref args) = expr.kind { - Some(snippet(cx, args[0].span, "..").to_string()) - } else { - None - }; - snippet.map(|snip| (expr.span, snip)) - }); + let suggs = result + .iter() + .filter_map(|expr| { + let snippet = if let ExprKind::Call(_, ref args) = expr.kind { + Some(snippet(cx, args[0].span, "..").to_string()) + } else { + None + }; + snippet.map(|snip| (expr.span, snip)) + }) + .chain({ + let inner_ty = return_ty(cx, hir_id) + .walk() + .skip(1) // skip `std::option::Option` or `std::result::Result` + .take(1) // first outermost inner type is needed + .filter_map(|inner| match inner.unpack() { + GenericArgKind::Type(inner_ty) => Some(inner_ty.to_string()), + _ => None, + }); + inner_ty.map(|inner_ty| (fn_decl.output.span(), inner_ty)) + }); span_lint_and_then( cx, diff --git a/tests/ui/unnecessary_wrap.fixed b/tests/ui/unnecessary_wrap.fixed index 1657a3173db..749bb95c417 100644 --- a/tests/ui/unnecessary_wrap.fixed +++ b/tests/ui/unnecessary_wrap.fixed @@ -1,8 +1,10 @@ // run-rustfix + #![warn(clippy::unnecessary_wrap)] #![allow(clippy::no_effect)] #![allow(clippy::needless_return)] #![allow(clippy::if_same_then_else)] +#![allow(dead_code)] // should be linted fn func1(a: bool, b: bool) -> Option { @@ -37,7 +39,21 @@ fn func3(a: bool) -> Option { // should be linted fn func4() -> Option { - 1 + Some(1) +} + +// should be linted +fn func5() -> Result { + Ok(1) +} + +// should not be linted +fn func6(a: bool) -> Result { + if a { + Ok(1) + } else { + Err(()) + } } fn main() { diff --git a/tests/ui/unnecessary_wrap.rs b/tests/ui/unnecessary_wrap.rs index edf41dad790..749bb95c417 100644 --- a/tests/ui/unnecessary_wrap.rs +++ b/tests/ui/unnecessary_wrap.rs @@ -1,8 +1,10 @@ // run-rustfix + #![warn(clippy::unnecessary_wrap)] #![allow(clippy::no_effect)] #![allow(clippy::needless_return)] #![allow(clippy::if_same_then_else)] +#![allow(dead_code)] // should be linted fn func1(a: bool, b: bool) -> Option { @@ -40,6 +42,20 @@ fn func4() -> Option { Some(1) } +// should be linted +fn func5() -> Result { + Ok(1) +} + +// should not be linted +fn func6(a: bool) -> Result { + if a { + Ok(1) + } else { + Err(()) + } +} + fn main() { // method calls are not linted func1(true, true); diff --git a/tests/ui/unnecessary_wrap.stderr b/tests/ui/unnecessary_wrap.stderr index 8473bd81839..511d085c82f 100644 --- a/tests/ui/unnecessary_wrap.stderr +++ b/tests/ui/unnecessary_wrap.stderr @@ -1,5 +1,5 @@ -error: this function unnecessarily wrapping data - --> $DIR/unnecessary_wrap.rs:8:1 +error: this function returns unnecessarily wrapping data + --> $DIR/unnecessary_wrap.rs:10:1 | LL | / fn func1(a: bool, b: bool) -> Option { LL | | if a && b { @@ -13,22 +13,41 @@ LL | | } = note: `-D clippy::unnecessary-wrap` implied by `-D warnings` help: factor this out to | +LL | fn func1(a: bool, b: bool) -> i32 { +LL | if a && b { LL | return 42; LL | } LL | if a { LL | Some(-1); -LL | 2 -LL | } else { ... -error: this function unnecessarily wrapping data - --> $DIR/unnecessary_wrap.rs:39:1 +error: this function returns unnecessarily wrapping data + --> $DIR/unnecessary_wrap.rs:41:1 | LL | / fn func4() -> Option { LL | | Some(1) - | | ------- help: factor this out to: `1` LL | | } | |_^ + | +help: factor this out to + | +LL | fn func4() -> i32 { +LL | 1 + | + +error: this function returns unnecessarily wrapping data + --> $DIR/unnecessary_wrap.rs:46:1 + | +LL | / fn func5() -> Result { +LL | | Ok(1) +LL | | } + | |_^ + | +help: factor this out to + | +LL | fn func5() -> i32 { +LL | 1 + | -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors -- cgit 1.4.1-3-g733a5 From 0e9d227c043c1b990912508662e2e5158383ea54 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Mon, 21 Sep 2020 00:11:28 +0900 Subject: Add test cases --- tests/ui/unnecessary_wrap.fixed | 14 ++++++++++++-- tests/ui/unnecessary_wrap.rs | 14 ++++++++++++-- tests/ui/unnecessary_wrap.stderr | 6 +++--- 3 files changed, 27 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/tests/ui/unnecessary_wrap.fixed b/tests/ui/unnecessary_wrap.fixed index 749bb95c417..e7e3120f82f 100644 --- a/tests/ui/unnecessary_wrap.fixed +++ b/tests/ui/unnecessary_wrap.fixed @@ -42,13 +42,18 @@ fn func4() -> Option { Some(1) } +// should not be linted +fn func5() -> Option { + None +} + // should be linted -fn func5() -> Result { +fn func6() -> Result { Ok(1) } // should not be linted -fn func6(a: bool) -> Result { +fn func7(a: bool) -> Result { if a { Ok(1) } else { @@ -56,6 +61,11 @@ fn func6(a: bool) -> Result { } } +// should not be linted +fn func8(a: bool) -> Result { + Err(()) +} + fn main() { // method calls are not linted func1(true, true); diff --git a/tests/ui/unnecessary_wrap.rs b/tests/ui/unnecessary_wrap.rs index 749bb95c417..e7e3120f82f 100644 --- a/tests/ui/unnecessary_wrap.rs +++ b/tests/ui/unnecessary_wrap.rs @@ -42,13 +42,18 @@ fn func4() -> Option { Some(1) } +// should not be linted +fn func5() -> Option { + None +} + // should be linted -fn func5() -> Result { +fn func6() -> Result { Ok(1) } // should not be linted -fn func6(a: bool) -> Result { +fn func7(a: bool) -> Result { if a { Ok(1) } else { @@ -56,6 +61,11 @@ fn func6(a: bool) -> Result { } } +// should not be linted +fn func8(a: bool) -> Result { + Err(()) +} + fn main() { // method calls are not linted func1(true, true); diff --git a/tests/ui/unnecessary_wrap.stderr b/tests/ui/unnecessary_wrap.stderr index 511d085c82f..76859fac589 100644 --- a/tests/ui/unnecessary_wrap.stderr +++ b/tests/ui/unnecessary_wrap.stderr @@ -36,16 +36,16 @@ LL | 1 | error: this function returns unnecessarily wrapping data - --> $DIR/unnecessary_wrap.rs:46:1 + --> $DIR/unnecessary_wrap.rs:51:1 | -LL | / fn func5() -> Result { +LL | / fn func6() -> Result { LL | | Ok(1) LL | | } | |_^ | help: factor this out to | -LL | fn func5() -> i32 { +LL | fn func6() -> i32 { LL | 1 | -- cgit 1.4.1-3-g733a5 From 6a62390c86cb9f72465f1c314b64c94c273956b7 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Wed, 23 Sep 2020 02:57:47 +0900 Subject: Optout rustfix test --- tests/ui/unnecessary_wrap.fixed | 73 ---------------------------------------- tests/ui/unnecessary_wrap.rs | 2 -- tests/ui/unnecessary_wrap.stderr | 6 ++-- 3 files changed, 3 insertions(+), 78 deletions(-) delete mode 100644 tests/ui/unnecessary_wrap.fixed (limited to 'tests') diff --git a/tests/ui/unnecessary_wrap.fixed b/tests/ui/unnecessary_wrap.fixed deleted file mode 100644 index e7e3120f82f..00000000000 --- a/tests/ui/unnecessary_wrap.fixed +++ /dev/null @@ -1,73 +0,0 @@ -// run-rustfix - -#![warn(clippy::unnecessary_wrap)] -#![allow(clippy::no_effect)] -#![allow(clippy::needless_return)] -#![allow(clippy::if_same_then_else)] -#![allow(dead_code)] - -// should be linted -fn func1(a: bool, b: bool) -> Option { - if a && b { - return Some(42); - } - if a { - Some(-1); - Some(2) - } else { - return Some(1337); - } -} - -// public fns should not be linted -pub fn func2(a: bool) -> Option { - if a { - Some(1) - } else { - Some(1) - } -} - -// should not be linted -fn func3(a: bool) -> Option { - if a { - Some(1) - } else { - None - } -} - -// should be linted -fn func4() -> Option { - Some(1) -} - -// should not be linted -fn func5() -> Option { - None -} - -// should be linted -fn func6() -> Result { - Ok(1) -} - -// should not be linted -fn func7(a: bool) -> Result { - if a { - Ok(1) - } else { - Err(()) - } -} - -// should not be linted -fn func8(a: bool) -> Result { - Err(()) -} - -fn main() { - // method calls are not linted - func1(true, true); - func2(true); -} diff --git a/tests/ui/unnecessary_wrap.rs b/tests/ui/unnecessary_wrap.rs index e7e3120f82f..f78a7604a5a 100644 --- a/tests/ui/unnecessary_wrap.rs +++ b/tests/ui/unnecessary_wrap.rs @@ -1,5 +1,3 @@ -// run-rustfix - #![warn(clippy::unnecessary_wrap)] #![allow(clippy::no_effect)] #![allow(clippy::needless_return)] diff --git a/tests/ui/unnecessary_wrap.stderr b/tests/ui/unnecessary_wrap.stderr index 76859fac589..cd05104f490 100644 --- a/tests/ui/unnecessary_wrap.stderr +++ b/tests/ui/unnecessary_wrap.stderr @@ -1,5 +1,5 @@ error: this function returns unnecessarily wrapping data - --> $DIR/unnecessary_wrap.rs:10:1 + --> $DIR/unnecessary_wrap.rs:8:1 | LL | / fn func1(a: bool, b: bool) -> Option { LL | | if a && b { @@ -22,7 +22,7 @@ LL | Some(-1); ... error: this function returns unnecessarily wrapping data - --> $DIR/unnecessary_wrap.rs:41:1 + --> $DIR/unnecessary_wrap.rs:39:1 | LL | / fn func4() -> Option { LL | | Some(1) @@ -36,7 +36,7 @@ LL | 1 | error: this function returns unnecessarily wrapping data - --> $DIR/unnecessary_wrap.rs:51:1 + --> $DIR/unnecessary_wrap.rs:49:1 | LL | / fn func6() -> Result { LL | | Ok(1) -- cgit 1.4.1-3-g733a5 From cdb72df6f9f9d6946b0614b01e70bc9c46edfe89 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sat, 26 Sep 2020 15:39:39 +0900 Subject: Split lint suggestion into two --- clippy_lints/src/unnecessary_wrap.rs | 41 +++++++++++++++++++----------------- tests/ui/unnecessary_wrap.stderr | 18 ++++++++++++---- 2 files changed, 36 insertions(+), 23 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index 1c8a08172a3..3ddf921a04b 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -68,10 +68,10 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { } } - let path = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(option_type)) { - &paths::OPTION_SOME + let (return_type, path) = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(option_type)) { + ("Option", &paths::OPTION_SOME) } else if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)) { - &paths::RESULT_OK + ("Result", &paths::RESULT_OK) } else { return; }; @@ -98,23 +98,26 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { UNNECESSARY_WRAP, span, "this function returns unnecessarily wrapping data", - move |diag| { + |diag| { + let inner_ty = return_ty(cx, hir_id) + .walk() + .skip(1) // skip `std::option::Option` or `std::result::Result` + .take(1) // take the first outermost inner type + .filter_map(|inner| match inner.unpack() { + GenericArgKind::Type(inner_ty) => Some(inner_ty.to_string()), + _ => None, + }); + inner_ty.for_each(|inner_ty| { + diag.span_suggestion( + fn_decl.output.span(), + format!("remove `{}` from the return type...", return_type).as_str(), + inner_ty, + Applicability::MachineApplicable, + ); + }); diag.multipart_suggestion( - "factor this out to", - suggs - .into_iter() - .chain({ - let inner_ty = return_ty(cx, hir_id) - .walk() - .skip(1) // skip `std::option::Option` or `std::result::Result` - .take(1) // take the first outermost inner type - .filter_map(|inner| match inner.unpack() { - GenericArgKind::Type(inner_ty) => Some(inner_ty.to_string()), - _ => None, - }); - inner_ty.map(|inner_ty| (fn_decl.output.span(), inner_ty)) - }) - .collect(), + "...and change the returning expressions", + suggs, Applicability::MachineApplicable, ); }, diff --git a/tests/ui/unnecessary_wrap.stderr b/tests/ui/unnecessary_wrap.stderr index cd05104f490..7833ee4b213 100644 --- a/tests/ui/unnecessary_wrap.stderr +++ b/tests/ui/unnecessary_wrap.stderr @@ -11,14 +11,18 @@ LL | | } | |_^ | = note: `-D clippy::unnecessary-wrap` implied by `-D warnings` -help: factor this out to +help: remove `Option` from the return type... | LL | fn func1(a: bool, b: bool) -> i32 { -LL | if a && b { + | ^^^ +help: ...and change the returning expressions + | LL | return 42; LL | } LL | if a { LL | Some(-1); +LL | 2 +LL | } else { ... error: this function returns unnecessarily wrapping data @@ -29,9 +33,12 @@ LL | | Some(1) LL | | } | |_^ | -help: factor this out to +help: remove `Option` from the return type... | LL | fn func4() -> i32 { + | ^^^ +help: ...and change the returning expressions + | LL | 1 | @@ -43,9 +50,12 @@ LL | | Ok(1) LL | | } | |_^ | -help: factor this out to +help: remove `Result` from the return type... | LL | fn func6() -> i32 { + | ^^^ +help: ...and change the returning expressions + | LL | 1 | -- cgit 1.4.1-3-g733a5 From 6b55f3fec98fa8fd4ca15edbf1cc1ab86d22f08f Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sat, 26 Sep 2020 16:27:10 +0900 Subject: Add test case --- tests/ui/unnecessary_wrap.rs | 28 ++++++++++++++++++++-------- tests/ui/unnecessary_wrap.stderr | 40 +++++++++++++++++++++++++++++++++------- 2 files changed, 53 insertions(+), 15 deletions(-) (limited to 'tests') diff --git a/tests/ui/unnecessary_wrap.rs b/tests/ui/unnecessary_wrap.rs index f78a7604a5a..6037f5807d3 100644 --- a/tests/ui/unnecessary_wrap.rs +++ b/tests/ui/unnecessary_wrap.rs @@ -17,8 +17,20 @@ fn func1(a: bool, b: bool) -> Option { } } +// should be linted +fn func2(a: bool, b: bool) -> Option { + if a && b { + return Some(10); + } + if a { + Some(20) + } else { + Some(30) + } +} + // public fns should not be linted -pub fn func2(a: bool) -> Option { +pub fn func3(a: bool) -> Option { if a { Some(1) } else { @@ -27,7 +39,7 @@ pub fn func2(a: bool) -> Option { } // should not be linted -fn func3(a: bool) -> Option { +fn func4(a: bool) -> Option { if a { Some(1) } else { @@ -36,22 +48,22 @@ fn func3(a: bool) -> Option { } // should be linted -fn func4() -> Option { +fn func5() -> Option { Some(1) } // should not be linted -fn func5() -> Option { +fn func6() -> Option { None } // should be linted -fn func6() -> Result { +fn func7() -> Result { Ok(1) } // should not be linted -fn func7(a: bool) -> Result { +fn func8(a: bool) -> Result { if a { Ok(1) } else { @@ -60,12 +72,12 @@ fn func7(a: bool) -> Result { } // should not be linted -fn func8(a: bool) -> Result { +fn func9(a: bool) -> Result { Err(()) } fn main() { // method calls are not linted func1(true, true); - func2(true); + func2(true, true); } diff --git a/tests/ui/unnecessary_wrap.stderr b/tests/ui/unnecessary_wrap.stderr index 7833ee4b213..a3481330e99 100644 --- a/tests/ui/unnecessary_wrap.stderr +++ b/tests/ui/unnecessary_wrap.stderr @@ -26,16 +26,42 @@ LL | } else { ... error: this function returns unnecessarily wrapping data - --> $DIR/unnecessary_wrap.rs:39:1 + --> $DIR/unnecessary_wrap.rs:21:1 | -LL | / fn func4() -> Option { +LL | / fn func2(a: bool, b: bool) -> Option { +LL | | if a && b { +LL | | return Some(10); +LL | | } +... | +LL | | } +LL | | } + | |_^ + | +help: remove `Option` from the return type... + | +LL | fn func2(a: bool, b: bool) -> i32 { + | ^^^ +help: ...and change the returning expressions + | +LL | return 10; +LL | } +LL | if a { +LL | 20 +LL | } else { +LL | 30 + | + +error: this function returns unnecessarily wrapping data + --> $DIR/unnecessary_wrap.rs:51:1 + | +LL | / fn func5() -> Option { LL | | Some(1) LL | | } | |_^ | help: remove `Option` from the return type... | -LL | fn func4() -> i32 { +LL | fn func5() -> i32 { | ^^^ help: ...and change the returning expressions | @@ -43,21 +69,21 @@ LL | 1 | error: this function returns unnecessarily wrapping data - --> $DIR/unnecessary_wrap.rs:49:1 + --> $DIR/unnecessary_wrap.rs:61:1 | -LL | / fn func6() -> Result { +LL | / fn func7() -> Result { LL | | Ok(1) LL | | } | |_^ | help: remove `Result` from the return type... | -LL | fn func6() -> i32 { +LL | fn func7() -> i32 { | ^^^ help: ...and change the returning expressions | LL | 1 | -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From 1bdac8712868e1418dcb13e817254620a8c01158 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Mon, 12 Oct 2020 19:27:47 +0900 Subject: Improve lint message --- clippy_lints/src/unnecessary_wrap.rs | 2 +- tests/ui/unnecessary_wrap.stderr | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index 28762b8d265..db51ee681ef 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -100,7 +100,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { cx, UNNECESSARY_WRAP, span, - format!("this function's return value is unnecessarily wrapped by `{}`, return_type)", + format!("this function's return value is unnecessarily wrapped by `{}`", return_type).as_str(), |diag| { let inner_ty = return_ty(cx, hir_id) .walk() diff --git a/tests/ui/unnecessary_wrap.stderr b/tests/ui/unnecessary_wrap.stderr index a3481330e99..958bc998022 100644 --- a/tests/ui/unnecessary_wrap.stderr +++ b/tests/ui/unnecessary_wrap.stderr @@ -1,4 +1,4 @@ -error: this function returns unnecessarily wrapping data +error: this function's return value is unnecessarily wrapped by `Option` --> $DIR/unnecessary_wrap.rs:8:1 | LL | / fn func1(a: bool, b: bool) -> Option { @@ -25,7 +25,7 @@ LL | 2 LL | } else { ... -error: this function returns unnecessarily wrapping data +error: this function's return value is unnecessarily wrapped by `Option` --> $DIR/unnecessary_wrap.rs:21:1 | LL | / fn func2(a: bool, b: bool) -> Option { @@ -51,7 +51,7 @@ LL | } else { LL | 30 | -error: this function returns unnecessarily wrapping data +error: this function's return value is unnecessarily wrapped by `Option` --> $DIR/unnecessary_wrap.rs:51:1 | LL | / fn func5() -> Option { @@ -68,7 +68,7 @@ help: ...and change the returning expressions LL | 1 | -error: this function returns unnecessarily wrapping data +error: this function's return value is unnecessarily wrapped by `Result` --> $DIR/unnecessary_wrap.rs:61:1 | LL | / fn func7() -> Result { -- cgit 1.4.1-3-g733a5 From 12474c62ff89ab122c2e6881b00680913fdf1d35 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sun, 18 Oct 2020 16:53:18 +0900 Subject: Add support for methods --- clippy_lints/src/unnecessary_wrap.rs | 6 +++++- tests/ui/unnecessary_wrap.rs | 14 ++++++++++++++ tests/ui/unnecessary_wrap.stderr | 19 ++++++++++++++++++- 3 files changed, 37 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index a20d1f82e9e..365e9c9984e 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -64,7 +64,11 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { hir_id: HirId, ) { match fn_kind { - FnKind::ItemFn(.., visibility, _) if visibility.node.is_pub() => return, + FnKind::ItemFn(.., visibility, _) | FnKind::Method(.., Some(visibility), _) => { + if visibility.node.is_pub() { + return; + } + }, FnKind::Closure(..) => return, _ => (), } diff --git a/tests/ui/unnecessary_wrap.rs b/tests/ui/unnecessary_wrap.rs index 6037f5807d3..0ced20b7a56 100644 --- a/tests/ui/unnecessary_wrap.rs +++ b/tests/ui/unnecessary_wrap.rs @@ -76,6 +76,20 @@ fn func9(a: bool) -> Result { Err(()) } +struct A; + +impl A { + // should not be linted + pub fn func10() -> Option { + Some(1) + } + + // should be linted + fn func11() -> Option { + Some(1) + } +} + fn main() { // method calls are not linted func1(true, true); diff --git a/tests/ui/unnecessary_wrap.stderr b/tests/ui/unnecessary_wrap.stderr index 958bc998022..dbc5748c29e 100644 --- a/tests/ui/unnecessary_wrap.stderr +++ b/tests/ui/unnecessary_wrap.stderr @@ -85,5 +85,22 @@ help: ...and change the returning expressions LL | 1 | -error: aborting due to 4 previous errors +error: this function's return value is unnecessarily wrapped by `Option` + --> $DIR/unnecessary_wrap.rs:88:5 + | +LL | / fn func11() -> Option { +LL | | Some(1) +LL | | } + | |_____^ + | +help: remove `Option` from the return type... + | +LL | fn func11() -> i32 { + | ^^^ +help: ...and change the returning expressions + | +LL | 1 + | + +error: aborting due to 5 previous errors -- cgit 1.4.1-3-g733a5 From c5447eb3c167025fcc1b842fabd7bb43a2eb1e9e Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sun, 18 Oct 2020 17:17:45 +0900 Subject: Make lint skip macros --- clippy_lints/src/unnecessary_wrap.rs | 3 ++- tests/ui/unnecessary_wrap.rs | 9 +++++++-- tests/ui/unnecessary_wrap.stderr | 6 +++--- 3 files changed, 12 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index 365e9c9984e..7ec523293c8 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -1,5 +1,5 @@ use crate::utils::{ - is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_then, + in_macro, is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_then, visitors::find_all_ret_expressions, }; use if_chain::if_chain; @@ -84,6 +84,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { let mut suggs = Vec::new(); let can_sugg = find_all_ret_expressions(cx, &body.value, |ret_expr| { if_chain! { + if !in_macro(ret_expr.span); if let ExprKind::Call(ref func, ref args) = ret_expr.kind; if let ExprKind::Path(ref qpath) = func.kind; if match_qpath(qpath, path); diff --git a/tests/ui/unnecessary_wrap.rs b/tests/ui/unnecessary_wrap.rs index 0ced20b7a56..618c452065b 100644 --- a/tests/ui/unnecessary_wrap.rs +++ b/tests/ui/unnecessary_wrap.rs @@ -76,16 +76,21 @@ fn func9(a: bool) -> Result { Err(()) } +// should not be linted +fn func10() -> Option<()> { + unimplemented!() +} + struct A; impl A { // should not be linted - pub fn func10() -> Option { + pub fn func11() -> Option { Some(1) } // should be linted - fn func11() -> Option { + fn func12() -> Option { Some(1) } } diff --git a/tests/ui/unnecessary_wrap.stderr b/tests/ui/unnecessary_wrap.stderr index dbc5748c29e..5f21b74bc76 100644 --- a/tests/ui/unnecessary_wrap.stderr +++ b/tests/ui/unnecessary_wrap.stderr @@ -86,16 +86,16 @@ LL | 1 | error: this function's return value is unnecessarily wrapped by `Option` - --> $DIR/unnecessary_wrap.rs:88:5 + --> $DIR/unnecessary_wrap.rs:93:5 | -LL | / fn func11() -> Option { +LL | / fn func12() -> Option { LL | | Some(1) LL | | } | |_____^ | help: remove `Option` from the return type... | -LL | fn func11() -> i32 { +LL | fn func12() -> i32 { | ^^^ help: ...and change the returning expressions | -- cgit 1.4.1-3-g733a5 From 30632fb8e6e4d6b998f2af7da93931ddb9f5de03 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sun, 18 Oct 2020 17:55:25 +0900 Subject: Allow this lint on lint tests --- tests/ui/derive_ord_xor_partial_ord.rs | 1 + tests/ui/derive_ord_xor_partial_ord.stderr | 16 ++++++------ tests/ui/doc_errors.rs | 1 + tests/ui/drop_ref.rs | 1 + tests/ui/forget_ref.rs | 1 + tests/ui/forget_ref.stderr | 36 +++++++++++++-------------- tests/ui/let_underscore_must_use.rs | 1 + tests/ui/let_underscore_must_use.stderr | 24 +++++++++--------- tests/ui/map_flatten.fixed | 1 + tests/ui/map_flatten.rs | 1 + tests/ui/needless_lifetimes.rs | 2 +- tests/ui/option_map_unit_fn_fixable.fixed | 1 + tests/ui/option_map_unit_fn_fixable.rs | 1 + tests/ui/option_map_unit_fn_fixable.stderr | 36 +++++++++++++-------------- tests/ui/option_option.rs | 1 + tests/ui/option_option.stderr | 20 +++++++-------- tests/ui/or_fun_call.fixed | 1 + tests/ui/or_fun_call.rs | 1 + tests/ui/or_fun_call.stderr | 20 +++++++-------- tests/ui/panic_in_result_fn.rs | 1 + tests/ui/panic_in_result_fn.stderr | 24 +++++++++--------- tests/ui/question_mark.fixed | 1 + tests/ui/question_mark.rs | 1 + tests/ui/question_mark.stderr | 22 ++++++++-------- tests/ui/redundant_pattern_matching.fixed | 1 + tests/ui/redundant_pattern_matching.rs | 1 + tests/ui/redundant_pattern_matching.stderr | 6 ++--- tests/ui/try_err.fixed | 1 + tests/ui/try_err.rs | 1 + tests/ui/try_err.stderr | 8 +++--- tests/ui/unit_arg.rs | 1 + tests/ui/unit_arg.stderr | 20 +++++++-------- tests/ui/unnecessary_clone.rs | 2 +- tests/ui/useless_conversion.fixed | 1 + tests/ui/useless_conversion.rs | 1 + tests/ui/useless_conversion.stderr | 22 ++++++++-------- tests/ui/wildcard_imports.fixed | 1 + tests/ui/wildcard_imports.rs | 1 + tests/ui/wildcard_imports.stderr | 40 +++++++++++++++--------------- 39 files changed, 173 insertions(+), 149 deletions(-) (limited to 'tests') diff --git a/tests/ui/derive_ord_xor_partial_ord.rs b/tests/ui/derive_ord_xor_partial_ord.rs index b82dc518a3b..78ec1727fc1 100644 --- a/tests/ui/derive_ord_xor_partial_ord.rs +++ b/tests/ui/derive_ord_xor_partial_ord.rs @@ -1,4 +1,5 @@ #![warn(clippy::derive_ord_xor_partial_ord)] +#![allow(clippy::unnecessary_wrap)] use std::cmp::Ordering; diff --git a/tests/ui/derive_ord_xor_partial_ord.stderr b/tests/ui/derive_ord_xor_partial_ord.stderr index 66bc4d42ce8..97b46a4aa89 100644 --- a/tests/ui/derive_ord_xor_partial_ord.stderr +++ b/tests/ui/derive_ord_xor_partial_ord.stderr @@ -1,12 +1,12 @@ error: you are deriving `Ord` but have implemented `PartialOrd` explicitly - --> $DIR/derive_ord_xor_partial_ord.rs:20:10 + --> $DIR/derive_ord_xor_partial_ord.rs:21:10 | LL | #[derive(Ord, PartialEq, Eq)] | ^^^ | = note: `-D clippy::derive-ord-xor-partial-ord` implied by `-D warnings` note: `PartialOrd` implemented here - --> $DIR/derive_ord_xor_partial_ord.rs:23:1 + --> $DIR/derive_ord_xor_partial_ord.rs:24:1 | LL | / impl PartialOrd for DeriveOrd { LL | | fn partial_cmp(&self, other: &Self) -> Option { @@ -17,13 +17,13 @@ LL | | } = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error: you are deriving `Ord` but have implemented `PartialOrd` explicitly - --> $DIR/derive_ord_xor_partial_ord.rs:29:10 + --> $DIR/derive_ord_xor_partial_ord.rs:30:10 | LL | #[derive(Ord, PartialEq, Eq)] | ^^^ | note: `PartialOrd` implemented here - --> $DIR/derive_ord_xor_partial_ord.rs:32:1 + --> $DIR/derive_ord_xor_partial_ord.rs:33:1 | LL | / impl PartialOrd for DeriveOrdWithExplicitTypeVariable { LL | | fn partial_cmp(&self, other: &Self) -> Option { @@ -34,7 +34,7 @@ LL | | } = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error: you are implementing `Ord` explicitly but have derived `PartialOrd` - --> $DIR/derive_ord_xor_partial_ord.rs:41:1 + --> $DIR/derive_ord_xor_partial_ord.rs:42:1 | LL | / impl std::cmp::Ord for DerivePartialOrd { LL | | fn cmp(&self, other: &Self) -> Ordering { @@ -44,14 +44,14 @@ LL | | } | |_^ | note: `PartialOrd` implemented here - --> $DIR/derive_ord_xor_partial_ord.rs:38:10 + --> $DIR/derive_ord_xor_partial_ord.rs:39:10 | LL | #[derive(PartialOrd, PartialEq, Eq)] | ^^^^^^^^^^ = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) error: you are implementing `Ord` explicitly but have derived `PartialOrd` - --> $DIR/derive_ord_xor_partial_ord.rs:61:5 + --> $DIR/derive_ord_xor_partial_ord.rs:62:5 | LL | / impl Ord for DerivePartialOrdInUseOrd { LL | | fn cmp(&self, other: &Self) -> Ordering { @@ -61,7 +61,7 @@ LL | | } | |_____^ | note: `PartialOrd` implemented here - --> $DIR/derive_ord_xor_partial_ord.rs:58:14 + --> $DIR/derive_ord_xor_partial_ord.rs:59:14 | LL | #[derive(PartialOrd, PartialEq, Eq)] | ^^^^^^^^^^ diff --git a/tests/ui/doc_errors.rs b/tests/ui/doc_errors.rs index f47b81a450e..77df7f176f0 100644 --- a/tests/ui/doc_errors.rs +++ b/tests/ui/doc_errors.rs @@ -1,6 +1,7 @@ // edition:2018 #![warn(clippy::missing_errors_doc)] #![allow(clippy::result_unit_err)] +#![allow(clippy::unnecessary_wrap)] use std::io; diff --git a/tests/ui/drop_ref.rs b/tests/ui/drop_ref.rs index 6b5bcdaa78e..ba12e763821 100644 --- a/tests/ui/drop_ref.rs +++ b/tests/ui/drop_ref.rs @@ -1,6 +1,7 @@ #![warn(clippy::drop_ref)] #![allow(clippy::toplevel_ref_arg)] #![allow(clippy::map_err_ignore)] +#![allow(clippy::unnecessary_wrap)] use std::mem::drop; diff --git a/tests/ui/forget_ref.rs b/tests/ui/forget_ref.rs index 447fdbe1fac..a9c2a92ce6b 100644 --- a/tests/ui/forget_ref.rs +++ b/tests/ui/forget_ref.rs @@ -1,5 +1,6 @@ #![warn(clippy::forget_ref)] #![allow(clippy::toplevel_ref_arg)] +#![allow(clippy::unnecessary_wrap)] use std::mem::forget; diff --git a/tests/ui/forget_ref.stderr b/tests/ui/forget_ref.stderr index f90bcc2762c..b2c7f2023bf 100644 --- a/tests/ui/forget_ref.stderr +++ b/tests/ui/forget_ref.stderr @@ -1,108 +1,108 @@ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. - --> $DIR/forget_ref.rs:9:5 + --> $DIR/forget_ref.rs:10:5 | LL | forget(&SomeStruct); | ^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::forget-ref` implied by `-D warnings` note: argument has type `&SomeStruct` - --> $DIR/forget_ref.rs:9:12 + --> $DIR/forget_ref.rs:10:12 | LL | forget(&SomeStruct); | ^^^^^^^^^^^ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. - --> $DIR/forget_ref.rs:12:5 + --> $DIR/forget_ref.rs:13:5 | LL | forget(&owned); | ^^^^^^^^^^^^^^ | note: argument has type `&SomeStruct` - --> $DIR/forget_ref.rs:12:12 + --> $DIR/forget_ref.rs:13:12 | LL | forget(&owned); | ^^^^^^ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. - --> $DIR/forget_ref.rs:13:5 + --> $DIR/forget_ref.rs:14:5 | LL | forget(&&owned); | ^^^^^^^^^^^^^^^ | note: argument has type `&&SomeStruct` - --> $DIR/forget_ref.rs:13:12 + --> $DIR/forget_ref.rs:14:12 | LL | forget(&&owned); | ^^^^^^^ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. - --> $DIR/forget_ref.rs:14:5 + --> $DIR/forget_ref.rs:15:5 | LL | forget(&mut owned); | ^^^^^^^^^^^^^^^^^^ | note: argument has type `&mut SomeStruct` - --> $DIR/forget_ref.rs:14:12 + --> $DIR/forget_ref.rs:15:12 | LL | forget(&mut owned); | ^^^^^^^^^^ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. - --> $DIR/forget_ref.rs:18:5 + --> $DIR/forget_ref.rs:19:5 | LL | forget(&*reference1); | ^^^^^^^^^^^^^^^^^^^^ | note: argument has type `&SomeStruct` - --> $DIR/forget_ref.rs:18:12 + --> $DIR/forget_ref.rs:19:12 | LL | forget(&*reference1); | ^^^^^^^^^^^^ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. - --> $DIR/forget_ref.rs:21:5 + --> $DIR/forget_ref.rs:22:5 | LL | forget(reference2); | ^^^^^^^^^^^^^^^^^^ | note: argument has type `&mut SomeStruct` - --> $DIR/forget_ref.rs:21:12 + --> $DIR/forget_ref.rs:22:12 | LL | forget(reference2); | ^^^^^^^^^^ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. - --> $DIR/forget_ref.rs:24:5 + --> $DIR/forget_ref.rs:25:5 | LL | forget(reference3); | ^^^^^^^^^^^^^^^^^^ | note: argument has type `&SomeStruct` - --> $DIR/forget_ref.rs:24:12 + --> $DIR/forget_ref.rs:25:12 | LL | forget(reference3); | ^^^^^^^^^^ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. - --> $DIR/forget_ref.rs:29:5 + --> $DIR/forget_ref.rs:30:5 | LL | forget(&val); | ^^^^^^^^^^^^ | note: argument has type `&T` - --> $DIR/forget_ref.rs:29:12 + --> $DIR/forget_ref.rs:30:12 | LL | forget(&val); | ^^^^ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. - --> $DIR/forget_ref.rs:37:5 + --> $DIR/forget_ref.rs:38:5 | LL | std::mem::forget(&SomeStruct); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: argument has type `&SomeStruct` - --> $DIR/forget_ref.rs:37:22 + --> $DIR/forget_ref.rs:38:22 | LL | std::mem::forget(&SomeStruct); | ^^^^^^^^^^^ diff --git a/tests/ui/let_underscore_must_use.rs b/tests/ui/let_underscore_must_use.rs index 27dda606067..e3800cda1b1 100644 --- a/tests/ui/let_underscore_must_use.rs +++ b/tests/ui/let_underscore_must_use.rs @@ -1,4 +1,5 @@ #![warn(clippy::let_underscore_must_use)] +#![allow(clippy::unnecessary_wrap)] // Debug implementations can fire this lint, // so we shouldn't lint external macros diff --git a/tests/ui/let_underscore_must_use.stderr b/tests/ui/let_underscore_must_use.stderr index 447f2419e3b..5b751ea56de 100644 --- a/tests/ui/let_underscore_must_use.stderr +++ b/tests/ui/let_underscore_must_use.stderr @@ -1,5 +1,5 @@ error: non-binding let on a result of a `#[must_use]` function - --> $DIR/let_underscore_must_use.rs:66:5 + --> $DIR/let_underscore_must_use.rs:67:5 | LL | let _ = f(); | ^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let _ = f(); = help: consider explicitly using function result error: non-binding let on an expression with `#[must_use]` type - --> $DIR/let_underscore_must_use.rs:67:5 + --> $DIR/let_underscore_must_use.rs:68:5 | LL | let _ = g(); | ^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | let _ = g(); = help: consider explicitly using expression value error: non-binding let on a result of a `#[must_use]` function - --> $DIR/let_underscore_must_use.rs:69:5 + --> $DIR/let_underscore_must_use.rs:70:5 | LL | let _ = l(0_u32); | ^^^^^^^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL | let _ = l(0_u32); = help: consider explicitly using function result error: non-binding let on a result of a `#[must_use]` function - --> $DIR/let_underscore_must_use.rs:73:5 + --> $DIR/let_underscore_must_use.rs:74:5 | LL | let _ = s.f(); | ^^^^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | let _ = s.f(); = help: consider explicitly using function result error: non-binding let on an expression with `#[must_use]` type - --> $DIR/let_underscore_must_use.rs:74:5 + --> $DIR/let_underscore_must_use.rs:75:5 | LL | let _ = s.g(); | ^^^^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL | let _ = s.g(); = help: consider explicitly using expression value error: non-binding let on a result of a `#[must_use]` function - --> $DIR/let_underscore_must_use.rs:77:5 + --> $DIR/let_underscore_must_use.rs:78:5 | LL | let _ = S::h(); | ^^^^^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL | let _ = S::h(); = help: consider explicitly using function result error: non-binding let on an expression with `#[must_use]` type - --> $DIR/let_underscore_must_use.rs:78:5 + --> $DIR/let_underscore_must_use.rs:79:5 | LL | let _ = S::p(); | ^^^^^^^^^^^^^^^ @@ -56,7 +56,7 @@ LL | let _ = S::p(); = help: consider explicitly using expression value error: non-binding let on a result of a `#[must_use]` function - --> $DIR/let_underscore_must_use.rs:80:5 + --> $DIR/let_underscore_must_use.rs:81:5 | LL | let _ = S::a(); | ^^^^^^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | let _ = S::a(); = help: consider explicitly using function result error: non-binding let on an expression with `#[must_use]` type - --> $DIR/let_underscore_must_use.rs:82:5 + --> $DIR/let_underscore_must_use.rs:83:5 | LL | let _ = if true { Ok(()) } else { Err(()) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | let _ = if true { Ok(()) } else { Err(()) }; = help: consider explicitly using expression value error: non-binding let on a result of a `#[must_use]` function - --> $DIR/let_underscore_must_use.rs:86:5 + --> $DIR/let_underscore_must_use.rs:87:5 | LL | let _ = a.is_ok(); | ^^^^^^^^^^^^^^^^^^ @@ -80,7 +80,7 @@ LL | let _ = a.is_ok(); = help: consider explicitly using function result error: non-binding let on an expression with `#[must_use]` type - --> $DIR/let_underscore_must_use.rs:88:5 + --> $DIR/let_underscore_must_use.rs:89:5 | LL | let _ = a.map(|_| ()); | ^^^^^^^^^^^^^^^^^^^^^^ @@ -88,7 +88,7 @@ LL | let _ = a.map(|_| ()); = help: consider explicitly using expression value error: non-binding let on an expression with `#[must_use]` type - --> $DIR/let_underscore_must_use.rs:90:5 + --> $DIR/let_underscore_must_use.rs:91:5 | LL | let _ = a; | ^^^^^^^^^^ diff --git a/tests/ui/map_flatten.fixed b/tests/ui/map_flatten.fixed index a7ab5a12cb7..b4a51837b2f 100644 --- a/tests/ui/map_flatten.fixed +++ b/tests/ui/map_flatten.fixed @@ -4,6 +4,7 @@ #![allow(clippy::let_underscore_drop)] #![allow(clippy::missing_docs_in_private_items)] #![allow(clippy::map_identity)] +#![allow(clippy::unnecessary_wrap)] fn main() { // mapping to Option on Iterator diff --git a/tests/ui/map_flatten.rs b/tests/ui/map_flatten.rs index e364a05f376..e83cc46bda2 100644 --- a/tests/ui/map_flatten.rs +++ b/tests/ui/map_flatten.rs @@ -4,6 +4,7 @@ #![allow(clippy::let_underscore_drop)] #![allow(clippy::missing_docs_in_private_items)] #![allow(clippy::map_identity)] +#![allow(clippy::unnecessary_wrap)] fn main() { // mapping to Option on Iterator diff --git a/tests/ui/needless_lifetimes.rs b/tests/ui/needless_lifetimes.rs index 6001ef37eb7..e5973bbef8d 100644 --- a/tests/ui/needless_lifetimes.rs +++ b/tests/ui/needless_lifetimes.rs @@ -1,5 +1,5 @@ #![warn(clippy::needless_lifetimes)] -#![allow(dead_code, clippy::needless_pass_by_value)] +#![allow(dead_code, clippy::needless_pass_by_value, clippy::unnecessary_wrap)] fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} diff --git a/tests/ui/option_map_unit_fn_fixable.fixed b/tests/ui/option_map_unit_fn_fixable.fixed index 96d1c54946c..de2e9155906 100644 --- a/tests/ui/option_map_unit_fn_fixable.fixed +++ b/tests/ui/option_map_unit_fn_fixable.fixed @@ -2,6 +2,7 @@ #![warn(clippy::option_map_unit_fn)] #![allow(unused)] +#![allow(clippy::unnecessary_wrap)] fn do_nothing(_: T) {} diff --git a/tests/ui/option_map_unit_fn_fixable.rs b/tests/ui/option_map_unit_fn_fixable.rs index 931ffc18665..f0887c8a4bc 100644 --- a/tests/ui/option_map_unit_fn_fixable.rs +++ b/tests/ui/option_map_unit_fn_fixable.rs @@ -2,6 +2,7 @@ #![warn(clippy::option_map_unit_fn)] #![allow(unused)] +#![allow(clippy::unnecessary_wrap)] fn do_nothing(_: T) {} diff --git a/tests/ui/option_map_unit_fn_fixable.stderr b/tests/ui/option_map_unit_fn_fixable.stderr index d7d45ef9b0b..8abdbcafb6e 100644 --- a/tests/ui/option_map_unit_fn_fixable.stderr +++ b/tests/ui/option_map_unit_fn_fixable.stderr @@ -1,5 +1,5 @@ error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:38:5 + --> $DIR/option_map_unit_fn_fixable.rs:39:5 | LL | x.field.map(do_nothing); | ^^^^^^^^^^^^^^^^^^^^^^^- @@ -9,7 +9,7 @@ LL | x.field.map(do_nothing); = note: `-D clippy::option-map-unit-fn` implied by `-D warnings` error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:40:5 + --> $DIR/option_map_unit_fn_fixable.rs:41:5 | LL | x.field.map(do_nothing); | ^^^^^^^^^^^^^^^^^^^^^^^- @@ -17,7 +17,7 @@ LL | x.field.map(do_nothing); | help: try this: `if let Some(x_field) = x.field { do_nothing(x_field) }` error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:42:5 + --> $DIR/option_map_unit_fn_fixable.rs:43:5 | LL | x.field.map(diverge); | ^^^^^^^^^^^^^^^^^^^^- @@ -25,7 +25,7 @@ LL | x.field.map(diverge); | help: try this: `if let Some(x_field) = x.field { diverge(x_field) }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:48:5 + --> $DIR/option_map_unit_fn_fixable.rs:49:5 | LL | x.field.map(|value| x.do_option_nothing(value + captured)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -33,7 +33,7 @@ LL | x.field.map(|value| x.do_option_nothing(value + captured)); | help: try this: `if let Some(value) = x.field { x.do_option_nothing(value + captured) }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:50:5 + --> $DIR/option_map_unit_fn_fixable.rs:51:5 | LL | x.field.map(|value| { x.do_option_plus_one(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -41,7 +41,7 @@ LL | x.field.map(|value| { x.do_option_plus_one(value + captured); }); | help: try this: `if let Some(value) = x.field { x.do_option_plus_one(value + captured); }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:53:5 + --> $DIR/option_map_unit_fn_fixable.rs:54:5 | LL | x.field.map(|value| do_nothing(value + captured)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -49,7 +49,7 @@ LL | x.field.map(|value| do_nothing(value + captured)); | help: try this: `if let Some(value) = x.field { do_nothing(value + captured) }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:55:5 + --> $DIR/option_map_unit_fn_fixable.rs:56:5 | LL | x.field.map(|value| { do_nothing(value + captured) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -57,7 +57,7 @@ LL | x.field.map(|value| { do_nothing(value + captured) }); | help: try this: `if let Some(value) = x.field { do_nothing(value + captured) }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:57:5 + --> $DIR/option_map_unit_fn_fixable.rs:58:5 | LL | x.field.map(|value| { do_nothing(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -65,7 +65,7 @@ LL | x.field.map(|value| { do_nothing(value + captured); }); | help: try this: `if let Some(value) = x.field { do_nothing(value + captured); }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:59:5 + --> $DIR/option_map_unit_fn_fixable.rs:60:5 | LL | x.field.map(|value| { { do_nothing(value + captured); } }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -73,7 +73,7 @@ LL | x.field.map(|value| { { do_nothing(value + captured); } }); | help: try this: `if let Some(value) = x.field { do_nothing(value + captured); }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:62:5 + --> $DIR/option_map_unit_fn_fixable.rs:63:5 | LL | x.field.map(|value| diverge(value + captured)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -81,7 +81,7 @@ LL | x.field.map(|value| diverge(value + captured)); | help: try this: `if let Some(value) = x.field { diverge(value + captured) }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:64:5 + --> $DIR/option_map_unit_fn_fixable.rs:65:5 | LL | x.field.map(|value| { diverge(value + captured) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -89,7 +89,7 @@ LL | x.field.map(|value| { diverge(value + captured) }); | help: try this: `if let Some(value) = x.field { diverge(value + captured) }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:66:5 + --> $DIR/option_map_unit_fn_fixable.rs:67:5 | LL | x.field.map(|value| { diverge(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -97,7 +97,7 @@ LL | x.field.map(|value| { diverge(value + captured); }); | help: try this: `if let Some(value) = x.field { diverge(value + captured); }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:68:5 + --> $DIR/option_map_unit_fn_fixable.rs:69:5 | LL | x.field.map(|value| { { diverge(value + captured); } }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -105,7 +105,7 @@ LL | x.field.map(|value| { { diverge(value + captured); } }); | help: try this: `if let Some(value) = x.field { diverge(value + captured); }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:73:5 + --> $DIR/option_map_unit_fn_fixable.rs:74:5 | LL | x.field.map(|value| { let y = plus_one(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -113,7 +113,7 @@ LL | x.field.map(|value| { let y = plus_one(value + captured); }); | help: try this: `if let Some(value) = x.field { let y = plus_one(value + captured); }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:75:5 + --> $DIR/option_map_unit_fn_fixable.rs:76:5 | LL | x.field.map(|value| { plus_one(value + captured); }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -121,7 +121,7 @@ LL | x.field.map(|value| { plus_one(value + captured); }); | help: try this: `if let Some(value) = x.field { plus_one(value + captured); }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:77:5 + --> $DIR/option_map_unit_fn_fixable.rs:78:5 | LL | x.field.map(|value| { { plus_one(value + captured); } }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -129,7 +129,7 @@ LL | x.field.map(|value| { { plus_one(value + captured); } }); | help: try this: `if let Some(value) = x.field { plus_one(value + captured); }` error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:80:5 + --> $DIR/option_map_unit_fn_fixable.rs:81:5 | LL | x.field.map(|ref value| { do_nothing(value + captured) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -137,7 +137,7 @@ LL | x.field.map(|ref value| { do_nothing(value + captured) }); | help: try this: `if let Some(ref value) = x.field { do_nothing(value + captured) }` error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()` - --> $DIR/option_map_unit_fn_fixable.rs:82:5 + --> $DIR/option_map_unit_fn_fixable.rs:83:5 | LL | option().map(do_nothing);} | ^^^^^^^^^^^^^^^^^^^^^^^^- diff --git a/tests/ui/option_option.rs b/tests/ui/option_option.rs index 24344833641..557d29dff67 100644 --- a/tests/ui/option_option.rs +++ b/tests/ui/option_option.rs @@ -1,4 +1,5 @@ #![deny(clippy::option_option)] +#![allow(clippy::unnecessary_wrap)] fn input(_: Option>) {} diff --git a/tests/ui/option_option.stderr b/tests/ui/option_option.stderr index 8ae1d23a8e3..8ace8338fcf 100644 --- a/tests/ui/option_option.stderr +++ b/tests/ui/option_option.stderr @@ -1,5 +1,5 @@ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:3:13 + --> $DIR/option_option.rs:4:13 | LL | fn input(_: Option>) {} | ^^^^^^^^^^^^^^^^^^ @@ -11,55 +11,55 @@ LL | #![deny(clippy::option_option)] | ^^^^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:5:16 + --> $DIR/option_option.rs:6:16 | LL | fn output() -> Option> { | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:9:27 + --> $DIR/option_option.rs:10:27 | LL | fn output_nested() -> Vec>> { | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:14:30 + --> $DIR/option_option.rs:15:30 | LL | fn output_nested_nested() -> Option>> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:19:8 + --> $DIR/option_option.rs:20:8 | LL | x: Option>, | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:23:23 + --> $DIR/option_option.rs:24:23 | LL | fn struct_fn() -> Option> { | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:29:22 + --> $DIR/option_option.rs:30:22 | LL | fn trait_fn() -> Option>; | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:33:11 + --> $DIR/option_option.rs:34:11 | LL | Tuple(Option>), | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:34:17 + --> $DIR/option_option.rs:35:17 | LL | Struct { x: Option> }, | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:75:14 + --> $DIR/option_option.rs:78:14 | LL | foo: Option>>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 20e5016bc17..e1735bc88f5 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -2,6 +2,7 @@ #![warn(clippy::or_fun_call)] #![allow(dead_code)] +#![allow(clippy::unnecessary_wrap)] use std::collections::BTreeMap; use std::collections::HashMap; diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index e7192deeebb..a6abd2e8b34 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -2,6 +2,7 @@ #![warn(clippy::or_fun_call)] #![allow(dead_code)] +#![allow(clippy::unnecessary_wrap)] use std::collections::BTreeMap; use std::collections::HashMap; diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index d0c4df0e008..8a7b5574c84 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -1,5 +1,5 @@ error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:32:19 + --> $DIR/or_fun_call.rs:33:19 | LL | with_const_fn.unwrap_or(Duration::from_secs(5)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| Duration::from_secs(5))` @@ -7,55 +7,55 @@ LL | with_const_fn.unwrap_or(Duration::from_secs(5)); = note: `-D clippy::or-fun-call` implied by `-D warnings` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:35:22 + --> $DIR/or_fun_call.rs:36:22 | LL | with_constructor.unwrap_or(make()); | ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(make)` error: use of `unwrap_or` followed by a call to `new` - --> $DIR/or_fun_call.rs:38:5 + --> $DIR/or_fun_call.rs:39:5 | LL | with_new.unwrap_or(Vec::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `with_new.unwrap_or_default()` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:41:21 + --> $DIR/or_fun_call.rs:42:21 | LL | with_const_args.unwrap_or(Vec::with_capacity(12)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| Vec::with_capacity(12))` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:44:14 + --> $DIR/or_fun_call.rs:45:14 | LL | with_err.unwrap_or(make()); | ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| make())` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:47:19 + --> $DIR/or_fun_call.rs:48:19 | LL | with_err_args.unwrap_or(Vec::with_capacity(12)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| Vec::with_capacity(12))` error: use of `unwrap_or` followed by a call to `default` - --> $DIR/or_fun_call.rs:50:5 + --> $DIR/or_fun_call.rs:51:5 | LL | with_default_trait.unwrap_or(Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `with_default_trait.unwrap_or_default()` error: use of `unwrap_or` followed by a call to `default` - --> $DIR/or_fun_call.rs:53:5 + --> $DIR/or_fun_call.rs:54:5 | LL | with_default_type.unwrap_or(u64::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `with_default_type.unwrap_or_default()` error: use of `unwrap_or` followed by a call to `new` - --> $DIR/or_fun_call.rs:56:5 + --> $DIR/or_fun_call.rs:57:5 | LL | with_vec.unwrap_or(vec![]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `with_vec.unwrap_or_default()` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:59:21 + --> $DIR/or_fun_call.rs:60:21 | LL | without_default.unwrap_or(Foo::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(Foo::new)` diff --git a/tests/ui/panic_in_result_fn.rs b/tests/ui/panic_in_result_fn.rs index 287726f7a2d..be4e85d05a7 100644 --- a/tests/ui/panic_in_result_fn.rs +++ b/tests/ui/panic_in_result_fn.rs @@ -1,4 +1,5 @@ #![warn(clippy::panic_in_result_fn)] +#![allow(clippy::unnecessary_wrap)] struct A; diff --git a/tests/ui/panic_in_result_fn.stderr b/tests/ui/panic_in_result_fn.stderr index c6936fd8692..ca73ac5a411 100644 --- a/tests/ui/panic_in_result_fn.stderr +++ b/tests/ui/panic_in_result_fn.stderr @@ -1,5 +1,5 @@ error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` - --> $DIR/panic_in_result_fn.rs:6:5 + --> $DIR/panic_in_result_fn.rs:7:5 | LL | / fn result_with_panic() -> Result // should emit lint LL | | { @@ -10,14 +10,14 @@ LL | | } = note: `-D clippy::panic-in-result-fn` implied by `-D warnings` = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking - --> $DIR/panic_in_result_fn.rs:8:9 + --> $DIR/panic_in_result_fn.rs:9:9 | LL | panic!("error"); | ^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` - --> $DIR/panic_in_result_fn.rs:11:5 + --> $DIR/panic_in_result_fn.rs:12:5 | LL | / fn result_with_unimplemented() -> Result // should emit lint LL | | { @@ -27,14 +27,14 @@ LL | | } | = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking - --> $DIR/panic_in_result_fn.rs:13:9 + --> $DIR/panic_in_result_fn.rs:14:9 | LL | unimplemented!(); | ^^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` - --> $DIR/panic_in_result_fn.rs:16:5 + --> $DIR/panic_in_result_fn.rs:17:5 | LL | / fn result_with_unreachable() -> Result // should emit lint LL | | { @@ -44,14 +44,14 @@ LL | | } | = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking - --> $DIR/panic_in_result_fn.rs:18:9 + --> $DIR/panic_in_result_fn.rs:19:9 | LL | unreachable!(); | ^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` - --> $DIR/panic_in_result_fn.rs:21:5 + --> $DIR/panic_in_result_fn.rs:22:5 | LL | / fn result_with_todo() -> Result // should emit lint LL | | { @@ -61,14 +61,14 @@ LL | | } | = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking - --> $DIR/panic_in_result_fn.rs:23:9 + --> $DIR/panic_in_result_fn.rs:24:9 | LL | todo!("Finish this"); | ^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` - --> $DIR/panic_in_result_fn.rs:52:1 + --> $DIR/panic_in_result_fn.rs:53:1 | LL | / fn function_result_with_panic() -> Result // should emit lint LL | | { @@ -78,14 +78,14 @@ LL | | } | = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking - --> $DIR/panic_in_result_fn.rs:54:5 + --> $DIR/panic_in_result_fn.rs:55:5 | LL | panic!("error"); | ^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` - --> $DIR/panic_in_result_fn.rs:67:1 + --> $DIR/panic_in_result_fn.rs:68:1 | LL | / fn main() -> Result<(), String> { LL | | todo!("finish main method"); @@ -95,7 +95,7 @@ LL | | } | = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking - --> $DIR/panic_in_result_fn.rs:68:5 + --> $DIR/panic_in_result_fn.rs:69:5 | LL | todo!("finish main method"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/question_mark.fixed b/tests/ui/question_mark.fixed index 11dff94a288..df3c98a0aa5 100644 --- a/tests/ui/question_mark.fixed +++ b/tests/ui/question_mark.fixed @@ -1,5 +1,6 @@ // run-rustfix #![allow(unreachable_code)] +#![allow(clippy::unnecessary_wrap)] fn some_func(a: Option) -> Option { a?; diff --git a/tests/ui/question_mark.rs b/tests/ui/question_mark.rs index 1d0ee82b4f7..62b3e96b65a 100644 --- a/tests/ui/question_mark.rs +++ b/tests/ui/question_mark.rs @@ -1,5 +1,6 @@ // run-rustfix #![allow(unreachable_code)] +#![allow(clippy::unnecessary_wrap)] fn some_func(a: Option) -> Option { if a.is_none() { diff --git a/tests/ui/question_mark.stderr b/tests/ui/question_mark.stderr index 502615fb175..6f330cfa385 100644 --- a/tests/ui/question_mark.stderr +++ b/tests/ui/question_mark.stderr @@ -1,5 +1,5 @@ error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:5:5 + --> $DIR/question_mark.rs:6:5 | LL | / if a.is_none() { LL | | return None; @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::question-mark` implied by `-D warnings` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:50:9 + --> $DIR/question_mark.rs:51:9 | LL | / if (self.opt).is_none() { LL | | return None; @@ -17,7 +17,7 @@ LL | | } | |_________^ help: replace it with: `(self.opt)?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:54:9 + --> $DIR/question_mark.rs:55:9 | LL | / if self.opt.is_none() { LL | | return None @@ -25,7 +25,7 @@ LL | | } | |_________^ help: replace it with: `self.opt?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:58:17 + --> $DIR/question_mark.rs:59:17 | LL | let _ = if self.opt.is_none() { | _________________^ @@ -36,7 +36,7 @@ LL | | }; | |_________^ help: replace it with: `Some(self.opt?)` error: this if-let-else may be rewritten with the `?` operator - --> $DIR/question_mark.rs:64:17 + --> $DIR/question_mark.rs:65:17 | LL | let _ = if let Some(x) = self.opt { | _________________^ @@ -47,7 +47,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt?` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:81:9 + --> $DIR/question_mark.rs:82:9 | LL | / if self.opt.is_none() { LL | | return None; @@ -55,7 +55,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:89:9 + --> $DIR/question_mark.rs:90:9 | LL | / if self.opt.is_none() { LL | | return None; @@ -63,7 +63,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:97:9 + --> $DIR/question_mark.rs:98:9 | LL | / if self.opt.is_none() { LL | | return None; @@ -71,7 +71,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this if-let-else may be rewritten with the `?` operator - --> $DIR/question_mark.rs:104:26 + --> $DIR/question_mark.rs:105:26 | LL | let v: &Vec<_> = if let Some(ref v) = self.opt { | __________________________^ @@ -82,7 +82,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt.as_ref()?` error: this if-let-else may be rewritten with the `?` operator - --> $DIR/question_mark.rs:114:17 + --> $DIR/question_mark.rs:115:17 | LL | let v = if let Some(v) = self.opt { | _________________^ @@ -93,7 +93,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt?` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:129:5 + --> $DIR/question_mark.rs:130:5 | LL | / if f().is_none() { LL | | return None; diff --git a/tests/ui/redundant_pattern_matching.fixed b/tests/ui/redundant_pattern_matching.fixed index fe8f62503b7..2e7ba8bc04d 100644 --- a/tests/ui/redundant_pattern_matching.fixed +++ b/tests/ui/redundant_pattern_matching.fixed @@ -7,6 +7,7 @@ unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro, + clippy::unnecessary_wrap, deprecated )] diff --git a/tests/ui/redundant_pattern_matching.rs b/tests/ui/redundant_pattern_matching.rs index 09426a6e590..ea0e18f5970 100644 --- a/tests/ui/redundant_pattern_matching.rs +++ b/tests/ui/redundant_pattern_matching.rs @@ -7,6 +7,7 @@ unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro, + clippy::unnecessary_wrap, deprecated )] diff --git a/tests/ui/redundant_pattern_matching.stderr b/tests/ui/redundant_pattern_matching.stderr index 3473ceea00e..5341e81836a 100644 --- a/tests/ui/redundant_pattern_matching.stderr +++ b/tests/ui/redundant_pattern_matching.stderr @@ -1,5 +1,5 @@ error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:15:12 + --> $DIR/redundant_pattern_matching.rs:16:12 | LL | if let Ok(_) = &result {} | -------^^^^^---------- help: try this: `if result.is_ok()` @@ -7,13 +7,13 @@ LL | if let Ok(_) = &result {} = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:17:12 + --> $DIR/redundant_pattern_matching.rs:18:12 | LL | if let Ok(_) = Ok::(42) {} | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:19:12 + --> $DIR/redundant_pattern_matching.rs:20:12 | LL | if let Err(_) = Err::(42) {} | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` diff --git a/tests/ui/try_err.fixed b/tests/ui/try_err.fixed index aa43e69f79e..19458ddf68e 100644 --- a/tests/ui/try_err.fixed +++ b/tests/ui/try_err.fixed @@ -2,6 +2,7 @@ // aux-build:macro_rules.rs #![deny(clippy::try_err)] +#![allow(clippy::unnecessary_wrap)] #[macro_use] extern crate macro_rules; diff --git a/tests/ui/try_err.rs b/tests/ui/try_err.rs index df3a9dc5367..ab6a32dbe4d 100644 --- a/tests/ui/try_err.rs +++ b/tests/ui/try_err.rs @@ -2,6 +2,7 @@ // aux-build:macro_rules.rs #![deny(clippy::try_err)] +#![allow(clippy::unnecessary_wrap)] #[macro_use] extern crate macro_rules; diff --git a/tests/ui/try_err.stderr b/tests/ui/try_err.stderr index 3905ed2476b..ababa64e6d8 100644 --- a/tests/ui/try_err.stderr +++ b/tests/ui/try_err.stderr @@ -1,5 +1,5 @@ error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:18:9 + --> $DIR/try_err.rs:19:9 | LL | Err(err)?; | ^^^^^^^^^ help: try this: `return Err(err)` @@ -11,19 +11,19 @@ LL | #![deny(clippy::try_err)] | ^^^^^^^^^^^^^^^ error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:28:9 + --> $DIR/try_err.rs:29:9 | LL | Err(err)?; | ^^^^^^^^^ help: try this: `return Err(err.into())` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:48:17 + --> $DIR/try_err.rs:49:17 | LL | Err(err)?; | ^^^^^^^^^ help: try this: `return Err(err)` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:67:17 + --> $DIR/try_err.rs:68:17 | LL | Err(err)?; | ^^^^^^^^^ help: try this: `return Err(err.into())` diff --git a/tests/ui/unit_arg.rs b/tests/ui/unit_arg.rs index fec115ff29d..6bf704c09e0 100644 --- a/tests/ui/unit_arg.rs +++ b/tests/ui/unit_arg.rs @@ -4,6 +4,7 @@ unused_must_use, unused_variables, clippy::unused_unit, + clippy::unnecessary_wrap, clippy::or_fun_call )] diff --git a/tests/ui/unit_arg.stderr b/tests/ui/unit_arg.stderr index 90fee3aab23..c3a839a9bf8 100644 --- a/tests/ui/unit_arg.stderr +++ b/tests/ui/unit_arg.stderr @@ -1,5 +1,5 @@ error: passing a unit value to a function - --> $DIR/unit_arg.rs:29:5 + --> $DIR/unit_arg.rs:30:5 | LL | / foo({ LL | | 1; @@ -20,7 +20,7 @@ LL | foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:32:5 + --> $DIR/unit_arg.rs:33:5 | LL | foo(foo(1)); | ^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:33:5 + --> $DIR/unit_arg.rs:34:5 | LL | / foo({ LL | | foo(1); @@ -54,7 +54,7 @@ LL | foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:38:5 + --> $DIR/unit_arg.rs:39:5 | LL | / b.bar({ LL | | 1; @@ -74,7 +74,7 @@ LL | b.bar(()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:41:5 + --> $DIR/unit_arg.rs:42:5 | LL | taking_multiple_units(foo(0), foo(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -87,7 +87,7 @@ LL | taking_multiple_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:42:5 + --> $DIR/unit_arg.rs:43:5 | LL | / taking_multiple_units(foo(0), { LL | | foo(1); @@ -110,7 +110,7 @@ LL | taking_multiple_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:46:5 + --> $DIR/unit_arg.rs:47:5 | LL | / taking_multiple_units( LL | | { @@ -140,7 +140,7 @@ LL | foo(2); ... error: passing a unit value to a function - --> $DIR/unit_arg.rs:57:13 + --> $DIR/unit_arg.rs:58:13 | LL | None.or(Some(foo(2))); | ^^^^^^^^^^^^ @@ -154,7 +154,7 @@ LL | }); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:60:5 + --> $DIR/unit_arg.rs:61:5 | LL | foo(foo(())) | ^^^^^^^^^^^^ @@ -166,7 +166,7 @@ LL | foo(()) | error: passing a unit value to a function - --> $DIR/unit_arg.rs:93:5 + --> $DIR/unit_arg.rs:94:5 | LL | Some(foo(1)) | ^^^^^^^^^^^^ diff --git a/tests/ui/unnecessary_clone.rs b/tests/ui/unnecessary_clone.rs index e785ac02feb..3b1a6cf57c6 100644 --- a/tests/ui/unnecessary_clone.rs +++ b/tests/ui/unnecessary_clone.rs @@ -1,7 +1,7 @@ // does not test any rustfixable lints #![warn(clippy::clone_on_ref_ptr)] -#![allow(unused, clippy::redundant_clone)] +#![allow(unused, clippy::redundant_clone, clippy::unnecessary_wrap)] use std::cell::RefCell; use std::rc::{self, Rc}; diff --git a/tests/ui/useless_conversion.fixed b/tests/ui/useless_conversion.fixed index 8a9b0cd3cf0..45ee367d649 100644 --- a/tests/ui/useless_conversion.fixed +++ b/tests/ui/useless_conversion.fixed @@ -1,6 +1,7 @@ // run-rustfix #![deny(clippy::useless_conversion)] +#![allow(clippy::unnecessary_wrap)] fn test_generic(val: T) -> T { let _ = val; diff --git a/tests/ui/useless_conversion.rs b/tests/ui/useless_conversion.rs index 4faa1572973..e5bdffed20d 100644 --- a/tests/ui/useless_conversion.rs +++ b/tests/ui/useless_conversion.rs @@ -1,6 +1,7 @@ // run-rustfix #![deny(clippy::useless_conversion)] +#![allow(clippy::unnecessary_wrap)] fn test_generic(val: T) -> T { let _ = T::from(val); diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index 11c6efb25cc..26a33595031 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -1,5 +1,5 @@ error: useless conversion to the same type: `T` - --> $DIR/useless_conversion.rs:6:13 + --> $DIR/useless_conversion.rs:7:13 | LL | let _ = T::from(val); | ^^^^^^^^^^^^ help: consider removing `T::from()`: `val` @@ -11,61 +11,61 @@ LL | #![deny(clippy::useless_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: useless conversion to the same type: `T` - --> $DIR/useless_conversion.rs:7:5 + --> $DIR/useless_conversion.rs:8:5 | LL | val.into() | ^^^^^^^^^^ help: consider removing `.into()`: `val` error: useless conversion to the same type: `i32` - --> $DIR/useless_conversion.rs:19:22 + --> $DIR/useless_conversion.rs:20:22 | LL | let _: i32 = 0i32.into(); | ^^^^^^^^^^^ help: consider removing `.into()`: `0i32` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion.rs:60:21 + --> $DIR/useless_conversion.rs:61:21 | LL | let _: String = "foo".to_string().into(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `"foo".to_string()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion.rs:61:21 + --> $DIR/useless_conversion.rs:62:21 | LL | let _: String = From::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `From::from()`: `"foo".to_string()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion.rs:62:13 + --> $DIR/useless_conversion.rs:63:13 | LL | let _ = String::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `"foo".to_string()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion.rs:63:13 + --> $DIR/useless_conversion.rs:64:13 | LL | let _ = String::from(format!("A: {:04}", 123)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `format!("A: {:04}", 123)` error: useless conversion to the same type: `std::str::Lines` - --> $DIR/useless_conversion.rs:64:13 + --> $DIR/useless_conversion.rs:65:13 | LL | let _ = "".lines().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()` error: useless conversion to the same type: `std::vec::IntoIter` - --> $DIR/useless_conversion.rs:65:13 + --> $DIR/useless_conversion.rs:66:13 | LL | let _ = vec![1, 2, 3].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![1, 2, 3].into_iter()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion.rs:66:21 + --> $DIR/useless_conversion.rs:67:21 | LL | let _: String = format!("Hello {}", "world").into(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `format!("Hello {}", "world")` error: useless conversion to the same type: `i32` - --> $DIR/useless_conversion.rs:71:13 + --> $DIR/useless_conversion.rs:72:13 | LL | let _ = i32::from(a + b) * 3; | ^^^^^^^^^^^^^^^^ help: consider removing `i32::from()`: `(a + b)` diff --git a/tests/ui/wildcard_imports.fixed b/tests/ui/wildcard_imports.fixed index 287f8935327..14007e5f251 100644 --- a/tests/ui/wildcard_imports.fixed +++ b/tests/ui/wildcard_imports.fixed @@ -4,6 +4,7 @@ #![warn(clippy::wildcard_imports)] //#![allow(clippy::redundant_pub_crate)] #![allow(unused)] +#![allow(clippy::unnecessary_wrap)] #![warn(unused_imports)] extern crate wildcard_imports_helper; diff --git a/tests/ui/wildcard_imports.rs b/tests/ui/wildcard_imports.rs index 1f261159f4a..0e8631ca704 100644 --- a/tests/ui/wildcard_imports.rs +++ b/tests/ui/wildcard_imports.rs @@ -4,6 +4,7 @@ #![warn(clippy::wildcard_imports)] //#![allow(clippy::redundant_pub_crate)] #![allow(unused)] +#![allow(clippy::unnecessary_wrap)] #![warn(unused_imports)] extern crate wildcard_imports_helper; diff --git a/tests/ui/wildcard_imports.stderr b/tests/ui/wildcard_imports.stderr index 351988f31ea..66267dd27b8 100644 --- a/tests/ui/wildcard_imports.stderr +++ b/tests/ui/wildcard_imports.stderr @@ -1,5 +1,5 @@ error: usage of wildcard import - --> $DIR/wildcard_imports.rs:11:5 + --> $DIR/wildcard_imports.rs:12:5 | LL | use crate::fn_mod::*; | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` @@ -7,85 +7,85 @@ LL | use crate::fn_mod::*; = note: `-D clippy::wildcard-imports` implied by `-D warnings` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:12:5 + --> $DIR/wildcard_imports.rs:13:5 | LL | use crate::mod_mod::*; | ^^^^^^^^^^^^^^^^^ help: try: `crate::mod_mod::inner_mod` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:13:5 + --> $DIR/wildcard_imports.rs:14:5 | LL | use crate::multi_fn_mod::*; | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:15:5 + --> $DIR/wildcard_imports.rs:16:5 | LL | use crate::struct_mod::*; | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::struct_mod::{A, inner_struct_mod}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:19:5 + --> $DIR/wildcard_imports.rs:20:5 | LL | use wildcard_imports_helper::inner::inner_for_self_import::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:20:5 + --> $DIR/wildcard_imports.rs:21:5 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:91:13 + --> $DIR/wildcard_imports.rs:92:13 | LL | use crate::fn_mod::*; | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:97:75 + --> $DIR/wildcard_imports.rs:98:75 | LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; | ^ help: try: `inner_extern_foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:98:13 + --> $DIR/wildcard_imports.rs:99:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:109:20 + --> $DIR/wildcard_imports.rs:110:20 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^ help: try: `inner::inner_foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:109:30 + --> $DIR/wildcard_imports.rs:110:30 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^^ help: try: `inner2::inner_bar` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:116:13 + --> $DIR/wildcard_imports.rs:117:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:145:9 + --> $DIR/wildcard_imports.rs:146:9 | LL | use crate::in_fn_test::*; | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:154:9 + --> $DIR/wildcard_imports.rs:155:9 | LL | use crate:: in_fn_test:: * ; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:155:9 + --> $DIR/wildcard_imports.rs:156:9 | LL | use crate:: fn_mod:: | _________^ @@ -93,31 +93,31 @@ LL | | *; | |_________^ help: try: `crate:: fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:166:13 + --> $DIR/wildcard_imports.rs:167:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:201:17 + --> $DIR/wildcard_imports.rs:202:17 | LL | use super::*; | ^^^^^^^^ help: try: `super::insidefoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:209:13 + --> $DIR/wildcard_imports.rs:210:13 | LL | use super_imports::*; | ^^^^^^^^^^^^^^^^ help: try: `super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:218:17 + --> $DIR/wildcard_imports.rs:219:17 | LL | use super::super::*; | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:227:13 + --> $DIR/wildcard_imports.rs:228:13 | LL | use super::super::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` -- cgit 1.4.1-3-g733a5 From 86331a46e4030ddea41e1f9f9b8c313f58ce1eb1 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Mon, 2 Nov 2020 23:59:47 +0900 Subject: Update stderr files --- tests/ui/doc_errors.stderr | 14 +++++------ tests/ui/drop_ref.stderr | 36 ++++++++++++++-------------- tests/ui/manual_unwrap_or.fixed | 2 +- tests/ui/manual_unwrap_or.rs | 2 +- tests/ui/map_err.rs | 1 + tests/ui/map_err.stderr | 2 +- tests/ui/or_fun_call.stderr | 6 ++--- tests/ui/redundant_pattern_matching.stderr | 38 +++++++++++++++--------------- tests/ui/result_unit_error.rs | 1 + tests/ui/result_unit_error.stderr | 8 +++---- 10 files changed, 56 insertions(+), 54 deletions(-) (limited to 'tests') diff --git a/tests/ui/doc_errors.stderr b/tests/ui/doc_errors.stderr index c7b616e2897..b5a81419dae 100644 --- a/tests/ui/doc_errors.stderr +++ b/tests/ui/doc_errors.stderr @@ -1,5 +1,5 @@ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:7:1 + --> $DIR/doc_errors.rs:8:1 | LL | / pub fn pub_fn_missing_errors_header() -> Result<(), ()> { LL | | unimplemented!(); @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::missing-errors-doc` implied by `-D warnings` error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:11:1 + --> $DIR/doc_errors.rs:12:1 | LL | / pub async fn async_pub_fn_missing_errors_header() -> Result<(), ()> { LL | | unimplemented!(); @@ -17,7 +17,7 @@ LL | | } | |_^ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:16:1 + --> $DIR/doc_errors.rs:17:1 | LL | / pub fn pub_fn_returning_io_result() -> io::Result<()> { LL | | unimplemented!(); @@ -25,7 +25,7 @@ LL | | } | |_^ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:21:1 + --> $DIR/doc_errors.rs:22:1 | LL | / pub async fn async_pub_fn_returning_io_result() -> io::Result<()> { LL | | unimplemented!(); @@ -33,7 +33,7 @@ LL | | } | |_^ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:51:5 + --> $DIR/doc_errors.rs:52:5 | LL | / pub fn pub_method_missing_errors_header() -> Result<(), ()> { LL | | unimplemented!(); @@ -41,7 +41,7 @@ LL | | } | |_____^ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:56:5 + --> $DIR/doc_errors.rs:57:5 | LL | / pub async fn async_pub_method_missing_errors_header() -> Result<(), ()> { LL | | unimplemented!(); @@ -49,7 +49,7 @@ LL | | } | |_____^ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:85:5 + --> $DIR/doc_errors.rs:86:5 | LL | fn trait_method_missing_errors_header() -> Result<(), ()>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/drop_ref.stderr b/tests/ui/drop_ref.stderr index 7974bf56d44..10087cb4820 100644 --- a/tests/ui/drop_ref.stderr +++ b/tests/ui/drop_ref.stderr @@ -1,108 +1,108 @@ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:10:5 + --> $DIR/drop_ref.rs:11:5 | LL | drop(&SomeStruct); | ^^^^^^^^^^^^^^^^^ | = note: `-D clippy::drop-ref` implied by `-D warnings` note: argument has type `&SomeStruct` - --> $DIR/drop_ref.rs:10:10 + --> $DIR/drop_ref.rs:11:10 | LL | drop(&SomeStruct); | ^^^^^^^^^^^ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:13:5 + --> $DIR/drop_ref.rs:14:5 | LL | drop(&owned1); | ^^^^^^^^^^^^^ | note: argument has type `&SomeStruct` - --> $DIR/drop_ref.rs:13:10 + --> $DIR/drop_ref.rs:14:10 | LL | drop(&owned1); | ^^^^^^^ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:14:5 + --> $DIR/drop_ref.rs:15:5 | LL | drop(&&owned1); | ^^^^^^^^^^^^^^ | note: argument has type `&&SomeStruct` - --> $DIR/drop_ref.rs:14:10 + --> $DIR/drop_ref.rs:15:10 | LL | drop(&&owned1); | ^^^^^^^^ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:15:5 + --> $DIR/drop_ref.rs:16:5 | LL | drop(&mut owned1); | ^^^^^^^^^^^^^^^^^ | note: argument has type `&mut SomeStruct` - --> $DIR/drop_ref.rs:15:10 + --> $DIR/drop_ref.rs:16:10 | LL | drop(&mut owned1); | ^^^^^^^^^^^ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:19:5 + --> $DIR/drop_ref.rs:20:5 | LL | drop(reference1); | ^^^^^^^^^^^^^^^^ | note: argument has type `&SomeStruct` - --> $DIR/drop_ref.rs:19:10 + --> $DIR/drop_ref.rs:20:10 | LL | drop(reference1); | ^^^^^^^^^^ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:22:5 + --> $DIR/drop_ref.rs:23:5 | LL | drop(reference2); | ^^^^^^^^^^^^^^^^ | note: argument has type `&mut SomeStruct` - --> $DIR/drop_ref.rs:22:10 + --> $DIR/drop_ref.rs:23:10 | LL | drop(reference2); | ^^^^^^^^^^ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:25:5 + --> $DIR/drop_ref.rs:26:5 | LL | drop(reference3); | ^^^^^^^^^^^^^^^^ | note: argument has type `&SomeStruct` - --> $DIR/drop_ref.rs:25:10 + --> $DIR/drop_ref.rs:26:10 | LL | drop(reference3); | ^^^^^^^^^^ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:30:5 + --> $DIR/drop_ref.rs:31:5 | LL | drop(&val); | ^^^^^^^^^^ | note: argument has type `&T` - --> $DIR/drop_ref.rs:30:10 + --> $DIR/drop_ref.rs:31:10 | LL | drop(&val); | ^^^^ error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. - --> $DIR/drop_ref.rs:38:5 + --> $DIR/drop_ref.rs:39:5 | LL | std::mem::drop(&SomeStruct); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: argument has type `&SomeStruct` - --> $DIR/drop_ref.rs:38:20 + --> $DIR/drop_ref.rs:39:20 | LL | std::mem::drop(&SomeStruct); | ^^^^^^^^^^^ diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index 5aa5a43cb92..740b2f66728 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -1,6 +1,6 @@ // run-rustfix #![allow(dead_code)] -#![allow(unused_variables)] +#![allow(unused_variables, clippy::unnecessary_wrap)] fn option_unwrap_or() { // int case diff --git a/tests/ui/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs index df534031f54..6750662c58c 100644 --- a/tests/ui/manual_unwrap_or.rs +++ b/tests/ui/manual_unwrap_or.rs @@ -1,6 +1,6 @@ // run-rustfix #![allow(dead_code)] -#![allow(unused_variables)] +#![allow(unused_variables, clippy::unnecessary_wrap)] fn option_unwrap_or() { // int case diff --git a/tests/ui/map_err.rs b/tests/ui/map_err.rs index 617b6422872..231562507a8 100644 --- a/tests/ui/map_err.rs +++ b/tests/ui/map_err.rs @@ -1,4 +1,5 @@ #![warn(clippy::map_err_ignore)] +#![allow(clippy::unnecessary_wrap)] use std::convert::TryFrom; use std::error::Error; use std::fmt; diff --git a/tests/ui/map_err.stderr b/tests/ui/map_err.stderr index 7273f460380..390d7ce2e4e 100644 --- a/tests/ui/map_err.stderr +++ b/tests/ui/map_err.stderr @@ -1,5 +1,5 @@ error: `map_err(|_|...` ignores the original error - --> $DIR/map_err.rs:22:32 + --> $DIR/map_err.rs:23:32 | LL | println!("{:?}", x.map_err(|_| Errors::Ignored)); | ^^^ diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index 8a7b5574c84..a29e8fb58f5 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -61,19 +61,19 @@ LL | without_default.unwrap_or(Foo::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(Foo::new)` error: use of `or_insert` followed by a function call - --> $DIR/or_fun_call.rs:62:19 + --> $DIR/or_fun_call.rs:63:19 | LL | map.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)` error: use of `or_insert` followed by a function call - --> $DIR/or_fun_call.rs:65:21 + --> $DIR/or_fun_call.rs:66:21 | LL | btree.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:68:21 + --> $DIR/or_fun_call.rs:69:21 | LL | let _ = stringy.unwrap_or("".to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "".to_owned())` diff --git a/tests/ui/redundant_pattern_matching.stderr b/tests/ui/redundant_pattern_matching.stderr index 5341e81836a..aeb309f5ba1 100644 --- a/tests/ui/redundant_pattern_matching.stderr +++ b/tests/ui/redundant_pattern_matching.stderr @@ -19,19 +19,19 @@ LL | if let Err(_) = Err::(42) {} | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:21:15 + --> $DIR/redundant_pattern_matching.rs:22:15 | LL | while let Ok(_) = Ok::(10) {} | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:23:15 + --> $DIR/redundant_pattern_matching.rs:24:15 | LL | while let Err(_) = Ok::(10) {} | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:33:5 + --> $DIR/redundant_pattern_matching.rs:34:5 | LL | / match Ok::(42) { LL | | Ok(_) => true, @@ -40,7 +40,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:38:5 + --> $DIR/redundant_pattern_matching.rs:39:5 | LL | / match Ok::(42) { LL | | Ok(_) => false, @@ -49,7 +49,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_err()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:43:5 + --> $DIR/redundant_pattern_matching.rs:44:5 | LL | / match Err::(42) { LL | | Ok(_) => false, @@ -58,7 +58,7 @@ LL | | }; | |_____^ help: try this: `Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:48:5 + --> $DIR/redundant_pattern_matching.rs:49:5 | LL | / match Err::(42) { LL | | Ok(_) => true, @@ -67,73 +67,73 @@ LL | | }; | |_____^ help: try this: `Err::(42).is_ok()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:53:20 + --> $DIR/redundant_pattern_matching.rs:54:20 | LL | let _ = if let Ok(_) = Ok::(4) { true } else { false }; | -------^^^^^--------------------- help: try this: `if Ok::(4).is_ok()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:59:20 + --> $DIR/redundant_pattern_matching.rs:60:20 | LL | let _ = if let Ok(_) = gen_res() { | -------^^^^^------------ help: try this: `if gen_res().is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:61:19 + --> $DIR/redundant_pattern_matching.rs:62:19 | LL | } else if let Err(_) = gen_res() { | -------^^^^^^------------ help: try this: `if gen_res().is_err()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:84:19 + --> $DIR/redundant_pattern_matching.rs:85:19 | LL | while let Some(_) = r#try!(result_opt()) {} | ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:85:16 + --> $DIR/redundant_pattern_matching.rs:86:16 | LL | if let Some(_) = r#try!(result_opt()) {} | -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:91:12 + --> $DIR/redundant_pattern_matching.rs:92:12 | LL | if let Some(_) = m!() {} | -------^^^^^^^------- help: try this: `if m!().is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:92:15 + --> $DIR/redundant_pattern_matching.rs:93:15 | LL | while let Some(_) = m!() {} | ----------^^^^^^^------- help: try this: `while m!().is_some()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:110:12 + --> $DIR/redundant_pattern_matching.rs:111:12 | LL | if let Ok(_) = Ok::(42) {} | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:112:12 + --> $DIR/redundant_pattern_matching.rs:113:12 | LL | if let Err(_) = Err::(42) {} | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:114:15 + --> $DIR/redundant_pattern_matching.rs:115:15 | LL | while let Ok(_) = Ok::(10) {} | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:116:15 + --> $DIR/redundant_pattern_matching.rs:117:15 | LL | while let Err(_) = Ok::(10) {} | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:118:5 + --> $DIR/redundant_pattern_matching.rs:119:5 | LL | / match Ok::(42) { LL | | Ok(_) => true, @@ -142,7 +142,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:123:5 + --> $DIR/redundant_pattern_matching.rs:124:5 | LL | / match Err::(42) { LL | | Ok(_) => false, diff --git a/tests/ui/result_unit_error.rs b/tests/ui/result_unit_error.rs index a66f581b215..1b4a702377e 100644 --- a/tests/ui/result_unit_error.rs +++ b/tests/ui/result_unit_error.rs @@ -1,3 +1,4 @@ +#![allow(clippy::unnecessary_wrap)] #[warn(clippy::result_unit_err)] #[allow(unused)] diff --git a/tests/ui/result_unit_error.stderr b/tests/ui/result_unit_error.stderr index b8230032491..12901b354f9 100644 --- a/tests/ui/result_unit_error.stderr +++ b/tests/ui/result_unit_error.stderr @@ -1,5 +1,5 @@ error: this returns a `Result<_, ()> - --> $DIR/result_unit_error.rs:4:1 + --> $DIR/result_unit_error.rs:5:1 | LL | pub fn returns_unit_error() -> Result { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | pub fn returns_unit_error() -> Result { = help: use a custom Error type instead error: this returns a `Result<_, ()> - --> $DIR/result_unit_error.rs:13:5 + --> $DIR/result_unit_error.rs:14:5 | LL | fn get_that_error(&self) -> Result; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | fn get_that_error(&self) -> Result; = help: use a custom Error type instead error: this returns a `Result<_, ()> - --> $DIR/result_unit_error.rs:15:5 + --> $DIR/result_unit_error.rs:16:5 | LL | fn get_this_one_too(&self) -> Result { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL | fn get_this_one_too(&self) -> Result { = help: use a custom Error type instead error: this returns a `Result<_, ()> - --> $DIR/result_unit_error.rs:33:5 + --> $DIR/result_unit_error.rs:34:5 | LL | pub fn unit_error(&self) -> Result { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From 4c8d248190819f09753d4f6a9f89e3804e232ae7 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sat, 14 Nov 2020 18:54:16 +0900 Subject: Update stderr files --- tests/ui/map_flatten.stderr | 12 ++++++------ tests/ui/option_option.stderr | 2 +- tests/ui/or_fun_call.stderr | 8 ++++---- tests/ui/try_err.stderr | 12 ++++++------ 4 files changed, 17 insertions(+), 17 deletions(-) (limited to 'tests') diff --git a/tests/ui/map_flatten.stderr b/tests/ui/map_flatten.stderr index d4e27f9aa07..756e6e818ad 100644 --- a/tests/ui/map_flatten.stderr +++ b/tests/ui/map_flatten.stderr @@ -1,5 +1,5 @@ error: called `map(..).flatten()` on an `Iterator` - --> $DIR/map_flatten.rs:15:46 + --> $DIR/map_flatten.rs:16:46 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id)` @@ -7,31 +7,31 @@ LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().coll = note: `-D clippy::map-flatten` implied by `-D warnings` error: called `map(..).flatten()` on an `Iterator` - --> $DIR/map_flatten.rs:16:46 + --> $DIR/map_flatten.rs:17:46 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_ref)` error: called `map(..).flatten()` on an `Iterator` - --> $DIR/map_flatten.rs:17:46 + --> $DIR/map_flatten.rs:18:46 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_closure)` error: called `map(..).flatten()` on an `Iterator` - --> $DIR/map_flatten.rs:18:46 + --> $DIR/map_flatten.rs:19:46 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(|x| x.checked_add(1))` error: called `map(..).flatten()` on an `Iterator` - --> $DIR/map_flatten.rs:21:46 + --> $DIR/map_flatten.rs:22:46 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `.flat_map(|x| 0..x)` error: called `map(..).flatten()` on an `Option` - --> $DIR/map_flatten.rs:24:39 + --> $DIR/map_flatten.rs:25:39 | LL | let _: Option<_> = (Some(Some(1))).map(|x| x).flatten(); | ^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `.and_then(|x| x)` diff --git a/tests/ui/option_option.stderr b/tests/ui/option_option.stderr index 8ace8338fcf..ad7f081c713 100644 --- a/tests/ui/option_option.stderr +++ b/tests/ui/option_option.stderr @@ -59,7 +59,7 @@ LL | Struct { x: Option> }, | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:78:14 + --> $DIR/option_option.rs:76:14 | LL | foo: Option>>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index a29e8fb58f5..fb8bf339828 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -79,25 +79,25 @@ LL | let _ = stringy.unwrap_or("".to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "".to_owned())` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:76:21 + --> $DIR/or_fun_call.rs:77:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| map[&1])` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:78:21 + --> $DIR/or_fun_call.rs:79:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| map[&1])` error: use of `or` followed by a function call - --> $DIR/or_fun_call.rs:102:35 + --> $DIR/or_fun_call.rs:103:35 | LL | let _ = Some("a".to_string()).or(Some("b".to_string())); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some("b".to_string()))` error: use of `or` followed by a function call - --> $DIR/or_fun_call.rs:106:10 + --> $DIR/or_fun_call.rs:107:10 | LL | .or(Some(Bar(b, Duration::from_secs(2)))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some(Bar(b, Duration::from_secs(2))))` diff --git a/tests/ui/try_err.stderr b/tests/ui/try_err.stderr index ababa64e6d8..2c01d37192e 100644 --- a/tests/ui/try_err.stderr +++ b/tests/ui/try_err.stderr @@ -29,7 +29,7 @@ LL | Err(err)?; | ^^^^^^^^^ help: try this: `return Err(err.into())` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:86:23 + --> $DIR/try_err.rs:87:23 | LL | Err(_) => Err(1)?, | ^^^^^^^ help: try this: `return Err(1)` @@ -40,7 +40,7 @@ LL | try_validation!(Ok::<_, i32>(5)); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:101:23 + --> $DIR/try_err.rs:102:23 | LL | Err(_) => Err(ret_one!())?, | ^^^^^^^^^^^^^^^^ help: try this: `return Err(ret_one!())` @@ -51,25 +51,25 @@ LL | try_validation_in_macro!(Ok::<_, i32>(5)); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:140:9 + --> $DIR/try_err.rs:141:9 | LL | Err(foo!())?; | ^^^^^^^^^^^^ help: try this: `return Err(foo!())` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:147:9 + --> $DIR/try_err.rs:148:9 | LL | Err(io::ErrorKind::WriteZero)? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Err(io::ErrorKind::WriteZero.into()))` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:149:9 + --> $DIR/try_err.rs:150:9 | LL | Err(io::Error::new(io::ErrorKind::InvalidInput, "error"))? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput, "error")))` error: returning an `Err(_)` with the `?` operator - --> $DIR/try_err.rs:157:9 + --> $DIR/try_err.rs:158:9 | LL | Err(io::ErrorKind::NotFound)? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Some(Err(io::ErrorKind::NotFound.into())))` -- cgit 1.4.1-3-g733a5 From 4e5c02e8980d16feeee953f112f940c598180ddc Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sat, 14 Nov 2020 19:25:54 +0900 Subject: Ignore trait implementations --- clippy_lints/src/unnecessary_wrap.rs | 20 +++++++++----------- clippy_lints/src/utils/paths.rs | 1 - tests/ui/unnecessary_wrap.rs | 14 ++++++++++++++ 3 files changed, 23 insertions(+), 12 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs index ec6c823a4ec..2960ffc5352 100644 --- a/clippy_lints/src/unnecessary_wrap.rs +++ b/clippy_lints/src/unnecessary_wrap.rs @@ -1,11 +1,11 @@ use crate::utils::{ - in_macro, is_type_diagnostic_item, match_path, match_qpath, paths, return_ty, snippet, span_lint_and_then, - trait_ref_of_method, visitors::find_all_ret_expressions, + in_macro, is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_then, + visitors::find_all_ret_expressions, }; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; -use rustc_hir::{Body, ExprKind, FnDecl, HirId}; +use rustc_hir::{Body, ExprKind, FnDecl, HirId, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -63,14 +63,6 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { span: Span, hir_id: HirId, ) { - if_chain! { - if let Some(trait_ref) = trait_ref_of_method(cx, hir_id); - if match_path(trait_ref.path, &paths::PARTIAL_ORD); - then { - return; - } - } - match fn_kind { FnKind::ItemFn(.., visibility, _) | FnKind::Method(.., Some(visibility), _) => { if visibility.node.is_pub() { @@ -81,6 +73,12 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { _ => (), } + if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { + if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), ..} | ItemKind::Trait(..)) { + return; + } + } + let (return_type, path) = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(option_type)) { ("Option", &paths::OPTION_SOME) } else if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)) { diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 97e01f445ff..2be5ff93f86 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -81,7 +81,6 @@ pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to pub const PARKING_LOT_MUTEX_GUARD: [&str; 2] = ["parking_lot", "MutexGuard"]; pub const PARKING_LOT_RWLOCK_READ_GUARD: [&str; 2] = ["parking_lot", "RwLockReadGuard"]; pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 2] = ["parking_lot", "RwLockWriteGuard"]; -pub const PARTIAL_ORD: [&str; 3] = ["std", "cmp", "PartialOrd"]; pub const PATH: [&str; 3] = ["std", "path", "Path"]; pub const PATH_BUF: [&str; 3] = ["std", "path", "PathBuf"]; pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"]; diff --git a/tests/ui/unnecessary_wrap.rs b/tests/ui/unnecessary_wrap.rs index 618c452065b..11208690428 100644 --- a/tests/ui/unnecessary_wrap.rs +++ b/tests/ui/unnecessary_wrap.rs @@ -95,6 +95,20 @@ impl A { } } +trait B { + // trait impls are not linted + fn func13() -> Option { + Some(1) + } +} + +impl A for B { + // trait impls are not linted + fn func13() -> Option { + Some(0) + } +} + fn main() { // method calls are not linted func1(true, true); -- cgit 1.4.1-3-g733a5 From 1f577c030049d974b7982f46e90d2bf96e665ea1 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sat, 14 Nov 2020 19:39:41 +0900 Subject: Fix embarrassing grammatical error --- tests/ui/unnecessary_wrap.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/ui/unnecessary_wrap.rs b/tests/ui/unnecessary_wrap.rs index 11208690428..58178e2e04b 100644 --- a/tests/ui/unnecessary_wrap.rs +++ b/tests/ui/unnecessary_wrap.rs @@ -102,7 +102,7 @@ trait B { } } -impl A for B { +impl B for A { // trait impls are not linted fn func13() -> Option { Some(0) -- cgit 1.4.1-3-g733a5 From c7445d7f2c3eece2b9056d05ea92fb1d1112b3ca Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Wed, 18 Nov 2020 01:01:22 +0900 Subject: Pluralize lint name --- CHANGELOG.md | 2 +- clippy_lints/src/lib.rs | 10 +-- clippy_lints/src/unnecessary_wrap.rs | 143 ------------------------------ clippy_lints/src/unnecessary_wraps.rs | 143 ++++++++++++++++++++++++++++++ src/lintlist/mod.rs | 4 +- tests/ui/derive_ord_xor_partial_ord.rs | 2 +- tests/ui/doc_errors.rs | 2 +- tests/ui/drop_ref.rs | 2 +- tests/ui/forget_ref.rs | 2 +- tests/ui/let_underscore_must_use.rs | 2 +- tests/ui/manual_unwrap_or.fixed | 2 +- tests/ui/manual_unwrap_or.rs | 2 +- tests/ui/map_err.rs | 2 +- tests/ui/map_flatten.fixed | 2 +- tests/ui/map_flatten.rs | 2 +- tests/ui/needless_lifetimes.rs | 2 +- tests/ui/option_map_unit_fn_fixable.fixed | 2 +- tests/ui/option_map_unit_fn_fixable.rs | 2 +- tests/ui/option_option.rs | 2 +- tests/ui/or_fun_call.fixed | 2 +- tests/ui/or_fun_call.rs | 2 +- tests/ui/panic_in_result_fn.rs | 2 +- tests/ui/question_mark.fixed | 2 +- tests/ui/question_mark.rs | 2 +- tests/ui/redundant_pattern_matching.fixed | 2 +- tests/ui/redundant_pattern_matching.rs | 2 +- tests/ui/result_unit_error.rs | 2 +- tests/ui/try_err.fixed | 2 +- tests/ui/try_err.rs | 2 +- tests/ui/unit_arg.rs | 2 +- tests/ui/unnecessary_clone.rs | 2 +- tests/ui/unnecessary_wrap.rs | 116 ------------------------ tests/ui/unnecessary_wrap.stderr | 106 ---------------------- tests/ui/unnecessary_wraps.rs | 116 ++++++++++++++++++++++++ tests/ui/unnecessary_wraps.stderr | 106 ++++++++++++++++++++++ tests/ui/useless_conversion.fixed | 2 +- tests/ui/useless_conversion.rs | 2 +- tests/ui/wildcard_imports.fixed | 2 +- tests/ui/wildcard_imports.rs | 2 +- 39 files changed, 403 insertions(+), 403 deletions(-) delete mode 100644 clippy_lints/src/unnecessary_wrap.rs create mode 100644 clippy_lints/src/unnecessary_wraps.rs delete mode 100644 tests/ui/unnecessary_wrap.rs delete mode 100644 tests/ui/unnecessary_wrap.stderr create mode 100644 tests/ui/unnecessary_wraps.rs create mode 100644 tests/ui/unnecessary_wraps.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 02b862d3196..64f67680b6c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2008,7 +2008,7 @@ Released 2018-09-13 [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation [`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by [`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap -[`unnecessary_wrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wrap +[`unnecessary_wraps`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps [`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern [`unneeded_wildcard_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_wildcard_pattern [`unnested_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 2d1f75391bb..f0c1cb8d6e5 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -323,7 +323,7 @@ mod unicode; mod unit_return_expecting_ord; mod unnamed_address; mod unnecessary_sort_by; -mod unnecessary_wrap; +mod unnecessary_wraps; mod unnested_or_patterns; mod unsafe_removed_from_name; mod unused_io_amount; @@ -893,7 +893,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &unnamed_address::FN_ADDRESS_COMPARISONS, &unnamed_address::VTABLE_ADDRESS_COMPARISONS, &unnecessary_sort_by::UNNECESSARY_SORT_BY, - &unnecessary_wrap::UNNECESSARY_WRAP, + &unnecessary_wraps::UNNECESSARY_WRAPS, &unnested_or_patterns::UNNESTED_OR_PATTERNS, &unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, &unused_io_amount::UNUSED_IO_AMOUNT, @@ -1066,7 +1066,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box redundant_clone::RedundantClone); store.register_late_pass(|| box slow_vector_initialization::SlowVectorInit); store.register_late_pass(|| box unnecessary_sort_by::UnnecessarySortBy); - store.register_late_pass(|| box unnecessary_wrap::UnnecessaryWrap); + store.register_late_pass(|| box unnecessary_wraps::UnnecessaryWraps); store.register_late_pass(|| box types::RefToMut); store.register_late_pass(|| box assertions_on_constants::AssertionsOnConstants); store.register_late_pass(|| box missing_const_for_fn::MissingConstForFn); @@ -1574,7 +1574,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), - LintId::of(&unnecessary_wrap::UNNECESSARY_WRAP), + LintId::of(&unnecessary_wraps::UNNECESSARY_WRAPS), LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), LintId::of(&unused_unit::UNUSED_UNIT), @@ -1779,7 +1779,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::UNNECESSARY_CAST), LintId::of(&types::VEC_BOX), LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), - LintId::of(&unnecessary_wrap::UNNECESSARY_WRAP), + LintId::of(&unnecessary_wraps::UNNECESSARY_WRAPS), 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/unnecessary_wrap.rs b/clippy_lints/src/unnecessary_wrap.rs deleted file mode 100644 index 2960ffc5352..00000000000 --- a/clippy_lints/src/unnecessary_wrap.rs +++ /dev/null @@ -1,143 +0,0 @@ -use crate::utils::{ - in_macro, is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_then, - visitors::find_all_ret_expressions, -}; -use if_chain::if_chain; -use rustc_errors::Applicability; -use rustc_hir::intravisit::FnKind; -use rustc_hir::{Body, ExprKind, FnDecl, HirId, ItemKind, Node}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::subst::GenericArgKind; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::Span; - -declare_clippy_lint! { - /// **What it does:** Checks for private functions that only return `Ok` or `Some`. - /// - /// **Why is this bad?** It is not meaningful to wrap values when no `None` or `Err` is returned. - /// - /// **Known problems:** Since this lint changes function type signature, you may need to - /// adjust some code at callee side. - /// - /// **Example:** - /// - /// ```rust - /// fn get_cool_number(a: bool, b: bool) -> Option { - /// if a && b { - /// return Some(50); - /// } - /// if a { - /// Some(0) - /// } else { - /// Some(10) - /// } - /// } - /// ``` - /// Use instead: - /// ```rust - /// fn get_cool_number(a: bool, b: bool) -> i32 { - /// if a && b { - /// return 50; - /// } - /// if a { - /// 0 - /// } else { - /// 10 - /// } - /// } - /// ``` - pub UNNECESSARY_WRAP, - complexity, - "functions that only return `Ok` or `Some`" -} - -declare_lint_pass!(UnnecessaryWrap => [UNNECESSARY_WRAP]); - -impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap { - fn check_fn( - &mut self, - cx: &LateContext<'tcx>, - fn_kind: FnKind<'tcx>, - fn_decl: &FnDecl<'tcx>, - body: &Body<'tcx>, - span: Span, - hir_id: HirId, - ) { - match fn_kind { - FnKind::ItemFn(.., visibility, _) | FnKind::Method(.., Some(visibility), _) => { - if visibility.node.is_pub() { - return; - } - }, - FnKind::Closure(..) => return, - _ => (), - } - - if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { - if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), ..} | ItemKind::Trait(..)) { - return; - } - } - - let (return_type, path) = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(option_type)) { - ("Option", &paths::OPTION_SOME) - } else if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)) { - ("Result", &paths::RESULT_OK) - } else { - return; - }; - - let mut suggs = Vec::new(); - let can_sugg = find_all_ret_expressions(cx, &body.value, |ret_expr| { - if_chain! { - if !in_macro(ret_expr.span); - if let ExprKind::Call(ref func, ref args) = ret_expr.kind; - if let ExprKind::Path(ref qpath) = func.kind; - if match_qpath(qpath, path); - if args.len() == 1; - then { - suggs.push((ret_expr.span, snippet(cx, args[0].span.source_callsite(), "..").to_string())); - true - } else { - false - } - } - }); - - if can_sugg && !suggs.is_empty() { - span_lint_and_then( - cx, - UNNECESSARY_WRAP, - span, - format!( - "this function's return value is unnecessarily wrapped by `{}`", - return_type - ) - .as_str(), - |diag| { - let inner_ty = return_ty(cx, hir_id) - .walk() - .skip(1) // skip `std::option::Option` or `std::result::Result` - .take(1) // take the first outermost inner type - .filter_map(|inner| match inner.unpack() { - GenericArgKind::Type(inner_ty) => Some(inner_ty.to_string()), - _ => None, - }); - inner_ty.for_each(|inner_ty| { - diag.span_suggestion( - fn_decl.output.span(), - format!("remove `{}` from the return type...", return_type).as_str(), - inner_ty, - Applicability::MaybeIncorrect, - ); - }); - diag.multipart_suggestion( - "...and change the returning expressions", - suggs, - Applicability::MachineApplicable, - ); - }, - ); - } - } -} diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs new file mode 100644 index 00000000000..25ecc7a82f1 --- /dev/null +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -0,0 +1,143 @@ +use crate::utils::{ + in_macro, is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_then, + visitors::find_all_ret_expressions, +}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{Body, ExprKind, FnDecl, HirId, ItemKind, Node}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::subst::GenericArgKind; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for private functions that only return `Ok` or `Some`. + /// + /// **Why is this bad?** It is not meaningful to wrap values when no `None` or `Err` is returned. + /// + /// **Known problems:** Since this lint changes function type signature, you may need to + /// adjust some code at callee side. + /// + /// **Example:** + /// + /// ```rust + /// fn get_cool_number(a: bool, b: bool) -> Option { + /// if a && b { + /// return Some(50); + /// } + /// if a { + /// Some(0) + /// } else { + /// Some(10) + /// } + /// } + /// ``` + /// Use instead: + /// ```rust + /// fn get_cool_number(a: bool, b: bool) -> i32 { + /// if a && b { + /// return 50; + /// } + /// if a { + /// 0 + /// } else { + /// 10 + /// } + /// } + /// ``` + pub UNNECESSARY_WRAPS, + complexity, + "functions that only return `Ok` or `Some`" +} + +declare_lint_pass!(UnnecessaryWraps => [UNNECESSARY_WRAPS]); + +impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + fn_kind: FnKind<'tcx>, + fn_decl: &FnDecl<'tcx>, + body: &Body<'tcx>, + span: Span, + hir_id: HirId, + ) { + match fn_kind { + FnKind::ItemFn(.., visibility, _) | FnKind::Method(.., Some(visibility), _) => { + if visibility.node.is_pub() { + return; + } + }, + FnKind::Closure(..) => return, + _ => (), + } + + if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { + if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), ..} | ItemKind::Trait(..)) { + return; + } + } + + let (return_type, path) = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(option_type)) { + ("Option", &paths::OPTION_SOME) + } else if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym!(result_type)) { + ("Result", &paths::RESULT_OK) + } else { + return; + }; + + let mut suggs = Vec::new(); + let can_sugg = find_all_ret_expressions(cx, &body.value, |ret_expr| { + if_chain! { + if !in_macro(ret_expr.span); + if let ExprKind::Call(ref func, ref args) = ret_expr.kind; + if let ExprKind::Path(ref qpath) = func.kind; + if match_qpath(qpath, path); + if args.len() == 1; + then { + suggs.push((ret_expr.span, snippet(cx, args[0].span.source_callsite(), "..").to_string())); + true + } else { + false + } + } + }); + + if can_sugg && !suggs.is_empty() { + span_lint_and_then( + cx, + UNNECESSARY_WRAPS, + span, + format!( + "this function's return value is unnecessarily wrapped by `{}`", + return_type + ) + .as_str(), + |diag| { + let inner_ty = return_ty(cx, hir_id) + .walk() + .skip(1) // skip `std::option::Option` or `std::result::Result` + .take(1) // take the first outermost inner type + .filter_map(|inner| match inner.unpack() { + GenericArgKind::Type(inner_ty) => Some(inner_ty.to_string()), + _ => None, + }); + inner_ty.for_each(|inner_ty| { + diag.span_suggestion( + fn_decl.output.span(), + format!("remove `{}` from the return type...", return_type).as_str(), + inner_ty, + Applicability::MaybeIncorrect, + ); + }); + diag.multipart_suggestion( + "...and change the returning expressions", + suggs, + Applicability::MachineApplicable, + ); + }, + ); + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 4a0cdc5d82f..a2edd6cd0bd 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2609,11 +2609,11 @@ vec![ module: "unwrap", }, Lint { - name: "unnecessary_wrap", + name: "unnecessary_wraps", group: "complexity", desc: "functions that only return `Ok` or `Some`", deprecation: None, - module: "unnecessary_wrap", + module: "unnecessary_wraps", }, Lint { name: "unneeded_field_pattern", diff --git a/tests/ui/derive_ord_xor_partial_ord.rs b/tests/ui/derive_ord_xor_partial_ord.rs index 78ec1727fc1..6f12d36d777 100644 --- a/tests/ui/derive_ord_xor_partial_ord.rs +++ b/tests/ui/derive_ord_xor_partial_ord.rs @@ -1,5 +1,5 @@ #![warn(clippy::derive_ord_xor_partial_ord)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] use std::cmp::Ordering; diff --git a/tests/ui/doc_errors.rs b/tests/ui/doc_errors.rs index 77df7f176f0..c77a74a58f2 100644 --- a/tests/ui/doc_errors.rs +++ b/tests/ui/doc_errors.rs @@ -1,7 +1,7 @@ // edition:2018 #![warn(clippy::missing_errors_doc)] #![allow(clippy::result_unit_err)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] use std::io; diff --git a/tests/ui/drop_ref.rs b/tests/ui/drop_ref.rs index ba12e763821..e1a15c609fd 100644 --- a/tests/ui/drop_ref.rs +++ b/tests/ui/drop_ref.rs @@ -1,7 +1,7 @@ #![warn(clippy::drop_ref)] #![allow(clippy::toplevel_ref_arg)] #![allow(clippy::map_err_ignore)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] use std::mem::drop; diff --git a/tests/ui/forget_ref.rs b/tests/ui/forget_ref.rs index a9c2a92ce6b..c49e6756a6c 100644 --- a/tests/ui/forget_ref.rs +++ b/tests/ui/forget_ref.rs @@ -1,6 +1,6 @@ #![warn(clippy::forget_ref)] #![allow(clippy::toplevel_ref_arg)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] use std::mem::forget; diff --git a/tests/ui/let_underscore_must_use.rs b/tests/ui/let_underscore_must_use.rs index e3800cda1b1..a842e872a37 100644 --- a/tests/ui/let_underscore_must_use.rs +++ b/tests/ui/let_underscore_must_use.rs @@ -1,5 +1,5 @@ #![warn(clippy::let_underscore_must_use)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] // Debug implementations can fire this lint, // so we shouldn't lint external macros diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index 740b2f66728..81d903c15d3 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -1,6 +1,6 @@ // run-rustfix #![allow(dead_code)] -#![allow(unused_variables, clippy::unnecessary_wrap)] +#![allow(unused_variables, clippy::unnecessary_wraps)] fn option_unwrap_or() { // int case diff --git a/tests/ui/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs index 6750662c58c..16105d379c3 100644 --- a/tests/ui/manual_unwrap_or.rs +++ b/tests/ui/manual_unwrap_or.rs @@ -1,6 +1,6 @@ // run-rustfix #![allow(dead_code)] -#![allow(unused_variables, clippy::unnecessary_wrap)] +#![allow(unused_variables, clippy::unnecessary_wraps)] fn option_unwrap_or() { // int case diff --git a/tests/ui/map_err.rs b/tests/ui/map_err.rs index 231562507a8..05b9949f102 100644 --- a/tests/ui/map_err.rs +++ b/tests/ui/map_err.rs @@ -1,5 +1,5 @@ #![warn(clippy::map_err_ignore)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] use std::convert::TryFrom; use std::error::Error; use std::fmt; diff --git a/tests/ui/map_flatten.fixed b/tests/ui/map_flatten.fixed index b4a51837b2f..773b5914439 100644 --- a/tests/ui/map_flatten.fixed +++ b/tests/ui/map_flatten.fixed @@ -4,7 +4,7 @@ #![allow(clippy::let_underscore_drop)] #![allow(clippy::missing_docs_in_private_items)] #![allow(clippy::map_identity)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] fn main() { // mapping to Option on Iterator diff --git a/tests/ui/map_flatten.rs b/tests/ui/map_flatten.rs index e83cc46bda2..578bd877267 100644 --- a/tests/ui/map_flatten.rs +++ b/tests/ui/map_flatten.rs @@ -4,7 +4,7 @@ #![allow(clippy::let_underscore_drop)] #![allow(clippy::missing_docs_in_private_items)] #![allow(clippy::map_identity)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] fn main() { // mapping to Option on Iterator diff --git a/tests/ui/needless_lifetimes.rs b/tests/ui/needless_lifetimes.rs index e5973bbef8d..44972c8c639 100644 --- a/tests/ui/needless_lifetimes.rs +++ b/tests/ui/needless_lifetimes.rs @@ -1,5 +1,5 @@ #![warn(clippy::needless_lifetimes)] -#![allow(dead_code, clippy::needless_pass_by_value, clippy::unnecessary_wrap)] +#![allow(dead_code, clippy::needless_pass_by_value, clippy::unnecessary_wraps)] fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} diff --git a/tests/ui/option_map_unit_fn_fixable.fixed b/tests/ui/option_map_unit_fn_fixable.fixed index de2e9155906..7d29445e66c 100644 --- a/tests/ui/option_map_unit_fn_fixable.fixed +++ b/tests/ui/option_map_unit_fn_fixable.fixed @@ -2,7 +2,7 @@ #![warn(clippy::option_map_unit_fn)] #![allow(unused)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] fn do_nothing(_: T) {} diff --git a/tests/ui/option_map_unit_fn_fixable.rs b/tests/ui/option_map_unit_fn_fixable.rs index f0887c8a4bc..b6f834f686f 100644 --- a/tests/ui/option_map_unit_fn_fixable.rs +++ b/tests/ui/option_map_unit_fn_fixable.rs @@ -2,7 +2,7 @@ #![warn(clippy::option_map_unit_fn)] #![allow(unused)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] fn do_nothing(_: T) {} diff --git a/tests/ui/option_option.rs b/tests/ui/option_option.rs index 557d29dff67..6859ba8e5bb 100644 --- a/tests/ui/option_option.rs +++ b/tests/ui/option_option.rs @@ -1,5 +1,5 @@ #![deny(clippy::option_option)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] fn input(_: Option>) {} diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index e1735bc88f5..2a63318c8c7 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -2,7 +2,7 @@ #![warn(clippy::or_fun_call)] #![allow(dead_code)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] use std::collections::BTreeMap; use std::collections::HashMap; diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index a6abd2e8b34..026ef437caa 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -2,7 +2,7 @@ #![warn(clippy::or_fun_call)] #![allow(dead_code)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] use std::collections::BTreeMap; use std::collections::HashMap; diff --git a/tests/ui/panic_in_result_fn.rs b/tests/ui/panic_in_result_fn.rs index be4e85d05a7..3d3c19a1be5 100644 --- a/tests/ui/panic_in_result_fn.rs +++ b/tests/ui/panic_in_result_fn.rs @@ -1,5 +1,5 @@ #![warn(clippy::panic_in_result_fn)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] struct A; diff --git a/tests/ui/question_mark.fixed b/tests/ui/question_mark.fixed index df3c98a0aa5..0b5746cb522 100644 --- a/tests/ui/question_mark.fixed +++ b/tests/ui/question_mark.fixed @@ -1,6 +1,6 @@ // run-rustfix #![allow(unreachable_code)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] fn some_func(a: Option) -> Option { a?; diff --git a/tests/ui/question_mark.rs b/tests/ui/question_mark.rs index 62b3e96b65a..0f0825c9334 100644 --- a/tests/ui/question_mark.rs +++ b/tests/ui/question_mark.rs @@ -1,6 +1,6 @@ // run-rustfix #![allow(unreachable_code)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] fn some_func(a: Option) -> Option { if a.is_none() { diff --git a/tests/ui/redundant_pattern_matching.fixed b/tests/ui/redundant_pattern_matching.fixed index 2e7ba8bc04d..aa20512296a 100644 --- a/tests/ui/redundant_pattern_matching.fixed +++ b/tests/ui/redundant_pattern_matching.fixed @@ -7,7 +7,7 @@ unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro, - clippy::unnecessary_wrap, + clippy::unnecessary_wraps, deprecated )] diff --git a/tests/ui/redundant_pattern_matching.rs b/tests/ui/redundant_pattern_matching.rs index ea0e18f5970..d76f9c288ff 100644 --- a/tests/ui/redundant_pattern_matching.rs +++ b/tests/ui/redundant_pattern_matching.rs @@ -7,7 +7,7 @@ unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro, - clippy::unnecessary_wrap, + clippy::unnecessary_wraps, deprecated )] diff --git a/tests/ui/result_unit_error.rs b/tests/ui/result_unit_error.rs index 1b4a702377e..5e57c752b5a 100644 --- a/tests/ui/result_unit_error.rs +++ b/tests/ui/result_unit_error.rs @@ -1,4 +1,4 @@ -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] #[warn(clippy::result_unit_err)] #[allow(unused)] diff --git a/tests/ui/try_err.fixed b/tests/ui/try_err.fixed index 19458ddf68e..652b611208b 100644 --- a/tests/ui/try_err.fixed +++ b/tests/ui/try_err.fixed @@ -2,7 +2,7 @@ // aux-build:macro_rules.rs #![deny(clippy::try_err)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] #[macro_use] extern crate macro_rules; diff --git a/tests/ui/try_err.rs b/tests/ui/try_err.rs index ab6a32dbe4d..6bd479657b7 100644 --- a/tests/ui/try_err.rs +++ b/tests/ui/try_err.rs @@ -2,7 +2,7 @@ // aux-build:macro_rules.rs #![deny(clippy::try_err)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] #[macro_use] extern crate macro_rules; diff --git a/tests/ui/unit_arg.rs b/tests/ui/unit_arg.rs index 6bf704c09e0..9ad16d36509 100644 --- a/tests/ui/unit_arg.rs +++ b/tests/ui/unit_arg.rs @@ -4,7 +4,7 @@ unused_must_use, unused_variables, clippy::unused_unit, - clippy::unnecessary_wrap, + clippy::unnecessary_wraps, clippy::or_fun_call )] diff --git a/tests/ui/unnecessary_clone.rs b/tests/ui/unnecessary_clone.rs index 3b1a6cf57c6..6770a7fac90 100644 --- a/tests/ui/unnecessary_clone.rs +++ b/tests/ui/unnecessary_clone.rs @@ -1,7 +1,7 @@ // does not test any rustfixable lints #![warn(clippy::clone_on_ref_ptr)] -#![allow(unused, clippy::redundant_clone, clippy::unnecessary_wrap)] +#![allow(unused, clippy::redundant_clone, clippy::unnecessary_wraps)] use std::cell::RefCell; use std::rc::{self, Rc}; diff --git a/tests/ui/unnecessary_wrap.rs b/tests/ui/unnecessary_wrap.rs deleted file mode 100644 index 58178e2e04b..00000000000 --- a/tests/ui/unnecessary_wrap.rs +++ /dev/null @@ -1,116 +0,0 @@ -#![warn(clippy::unnecessary_wrap)] -#![allow(clippy::no_effect)] -#![allow(clippy::needless_return)] -#![allow(clippy::if_same_then_else)] -#![allow(dead_code)] - -// should be linted -fn func1(a: bool, b: bool) -> Option { - if a && b { - return Some(42); - } - if a { - Some(-1); - Some(2) - } else { - return Some(1337); - } -} - -// should be linted -fn func2(a: bool, b: bool) -> Option { - if a && b { - return Some(10); - } - if a { - Some(20) - } else { - Some(30) - } -} - -// public fns should not be linted -pub fn func3(a: bool) -> Option { - if a { - Some(1) - } else { - Some(1) - } -} - -// should not be linted -fn func4(a: bool) -> Option { - if a { - Some(1) - } else { - None - } -} - -// should be linted -fn func5() -> Option { - Some(1) -} - -// should not be linted -fn func6() -> Option { - None -} - -// should be linted -fn func7() -> Result { - Ok(1) -} - -// should not be linted -fn func8(a: bool) -> Result { - if a { - Ok(1) - } else { - Err(()) - } -} - -// should not be linted -fn func9(a: bool) -> Result { - Err(()) -} - -// should not be linted -fn func10() -> Option<()> { - unimplemented!() -} - -struct A; - -impl A { - // should not be linted - pub fn func11() -> Option { - Some(1) - } - - // should be linted - fn func12() -> Option { - Some(1) - } -} - -trait B { - // trait impls are not linted - fn func13() -> Option { - Some(1) - } -} - -impl B for A { - // trait impls are not linted - fn func13() -> Option { - Some(0) - } -} - -fn main() { - // method calls are not linted - func1(true, true); - func2(true, true); -} diff --git a/tests/ui/unnecessary_wrap.stderr b/tests/ui/unnecessary_wrap.stderr deleted file mode 100644 index 5f21b74bc76..00000000000 --- a/tests/ui/unnecessary_wrap.stderr +++ /dev/null @@ -1,106 +0,0 @@ -error: this function's return value is unnecessarily wrapped by `Option` - --> $DIR/unnecessary_wrap.rs:8:1 - | -LL | / fn func1(a: bool, b: bool) -> Option { -LL | | if a && b { -LL | | return Some(42); -LL | | } -... | -LL | | } -LL | | } - | |_^ - | - = note: `-D clippy::unnecessary-wrap` implied by `-D warnings` -help: remove `Option` from the return type... - | -LL | fn func1(a: bool, b: bool) -> i32 { - | ^^^ -help: ...and change the returning expressions - | -LL | return 42; -LL | } -LL | if a { -LL | Some(-1); -LL | 2 -LL | } else { - ... - -error: this function's return value is unnecessarily wrapped by `Option` - --> $DIR/unnecessary_wrap.rs:21:1 - | -LL | / fn func2(a: bool, b: bool) -> Option { -LL | | if a && b { -LL | | return Some(10); -LL | | } -... | -LL | | } -LL | | } - | |_^ - | -help: remove `Option` from the return type... - | -LL | fn func2(a: bool, b: bool) -> i32 { - | ^^^ -help: ...and change the returning expressions - | -LL | return 10; -LL | } -LL | if a { -LL | 20 -LL | } else { -LL | 30 - | - -error: this function's return value is unnecessarily wrapped by `Option` - --> $DIR/unnecessary_wrap.rs:51:1 - | -LL | / fn func5() -> Option { -LL | | Some(1) -LL | | } - | |_^ - | -help: remove `Option` from the return type... - | -LL | fn func5() -> i32 { - | ^^^ -help: ...and change the returning expressions - | -LL | 1 - | - -error: this function's return value is unnecessarily wrapped by `Result` - --> $DIR/unnecessary_wrap.rs:61:1 - | -LL | / fn func7() -> Result { -LL | | Ok(1) -LL | | } - | |_^ - | -help: remove `Result` from the return type... - | -LL | fn func7() -> i32 { - | ^^^ -help: ...and change the returning expressions - | -LL | 1 - | - -error: this function's return value is unnecessarily wrapped by `Option` - --> $DIR/unnecessary_wrap.rs:93:5 - | -LL | / fn func12() -> Option { -LL | | Some(1) -LL | | } - | |_____^ - | -help: remove `Option` from the return type... - | -LL | fn func12() -> i32 { - | ^^^ -help: ...and change the returning expressions - | -LL | 1 - | - -error: aborting due to 5 previous errors - diff --git a/tests/ui/unnecessary_wraps.rs b/tests/ui/unnecessary_wraps.rs new file mode 100644 index 00000000000..a53dec8f91a --- /dev/null +++ b/tests/ui/unnecessary_wraps.rs @@ -0,0 +1,116 @@ +#![warn(clippy::unnecessary_wraps)] +#![allow(clippy::no_effect)] +#![allow(clippy::needless_return)] +#![allow(clippy::if_same_then_else)] +#![allow(dead_code)] + +// should be linted +fn func1(a: bool, b: bool) -> Option { + if a && b { + return Some(42); + } + if a { + Some(-1); + Some(2) + } else { + return Some(1337); + } +} + +// should be linted +fn func2(a: bool, b: bool) -> Option { + if a && b { + return Some(10); + } + if a { + Some(20) + } else { + Some(30) + } +} + +// public fns should not be linted +pub fn func3(a: bool) -> Option { + if a { + Some(1) + } else { + Some(1) + } +} + +// should not be linted +fn func4(a: bool) -> Option { + if a { + Some(1) + } else { + None + } +} + +// should be linted +fn func5() -> Option { + Some(1) +} + +// should not be linted +fn func6() -> Option { + None +} + +// should be linted +fn func7() -> Result { + Ok(1) +} + +// should not be linted +fn func8(a: bool) -> Result { + if a { + Ok(1) + } else { + Err(()) + } +} + +// should not be linted +fn func9(a: bool) -> Result { + Err(()) +} + +// should not be linted +fn func10() -> Option<()> { + unimplemented!() +} + +struct A; + +impl A { + // should not be linted + pub fn func11() -> Option { + Some(1) + } + + // should be linted + fn func12() -> Option { + Some(1) + } +} + +trait B { + // trait impls are not linted + fn func13() -> Option { + Some(1) + } +} + +impl B for A { + // trait impls are not linted + fn func13() -> Option { + Some(0) + } +} + +fn main() { + // method calls are not linted + func1(true, true); + func2(true, true); +} diff --git a/tests/ui/unnecessary_wraps.stderr b/tests/ui/unnecessary_wraps.stderr new file mode 100644 index 00000000000..410f054b8ef --- /dev/null +++ b/tests/ui/unnecessary_wraps.stderr @@ -0,0 +1,106 @@ +error: this function's return value is unnecessarily wrapped by `Option` + --> $DIR/unnecessary_wraps.rs:8:1 + | +LL | / fn func1(a: bool, b: bool) -> Option { +LL | | if a && b { +LL | | return Some(42); +LL | | } +... | +LL | | } +LL | | } + | |_^ + | + = note: `-D clippy::unnecessary-wraps` implied by `-D warnings` +help: remove `Option` from the return type... + | +LL | fn func1(a: bool, b: bool) -> i32 { + | ^^^ +help: ...and change the returning expressions + | +LL | return 42; +LL | } +LL | if a { +LL | Some(-1); +LL | 2 +LL | } else { + ... + +error: this function's return value is unnecessarily wrapped by `Option` + --> $DIR/unnecessary_wraps.rs:21:1 + | +LL | / fn func2(a: bool, b: bool) -> Option { +LL | | if a && b { +LL | | return Some(10); +LL | | } +... | +LL | | } +LL | | } + | |_^ + | +help: remove `Option` from the return type... + | +LL | fn func2(a: bool, b: bool) -> i32 { + | ^^^ +help: ...and change the returning expressions + | +LL | return 10; +LL | } +LL | if a { +LL | 20 +LL | } else { +LL | 30 + | + +error: this function's return value is unnecessarily wrapped by `Option` + --> $DIR/unnecessary_wraps.rs:51:1 + | +LL | / fn func5() -> Option { +LL | | Some(1) +LL | | } + | |_^ + | +help: remove `Option` from the return type... + | +LL | fn func5() -> i32 { + | ^^^ +help: ...and change the returning expressions + | +LL | 1 + | + +error: this function's return value is unnecessarily wrapped by `Result` + --> $DIR/unnecessary_wraps.rs:61:1 + | +LL | / fn func7() -> Result { +LL | | Ok(1) +LL | | } + | |_^ + | +help: remove `Result` from the return type... + | +LL | fn func7() -> i32 { + | ^^^ +help: ...and change the returning expressions + | +LL | 1 + | + +error: this function's return value is unnecessarily wrapped by `Option` + --> $DIR/unnecessary_wraps.rs:93:5 + | +LL | / fn func12() -> Option { +LL | | Some(1) +LL | | } + | |_____^ + | +help: remove `Option` from the return type... + | +LL | fn func12() -> i32 { + | ^^^ +help: ...and change the returning expressions + | +LL | 1 + | + +error: aborting due to 5 previous errors + diff --git a/tests/ui/useless_conversion.fixed b/tests/ui/useless_conversion.fixed index 45ee367d649..03977de9455 100644 --- a/tests/ui/useless_conversion.fixed +++ b/tests/ui/useless_conversion.fixed @@ -1,7 +1,7 @@ // run-rustfix #![deny(clippy::useless_conversion)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] fn test_generic(val: T) -> T { let _ = val; diff --git a/tests/ui/useless_conversion.rs b/tests/ui/useless_conversion.rs index e5bdffed20d..f6e094c1661 100644 --- a/tests/ui/useless_conversion.rs +++ b/tests/ui/useless_conversion.rs @@ -1,7 +1,7 @@ // run-rustfix #![deny(clippy::useless_conversion)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] fn test_generic(val: T) -> T { let _ = T::from(val); diff --git a/tests/ui/wildcard_imports.fixed b/tests/ui/wildcard_imports.fixed index 14007e5f251..ee9c9045fff 100644 --- a/tests/ui/wildcard_imports.fixed +++ b/tests/ui/wildcard_imports.fixed @@ -4,7 +4,7 @@ #![warn(clippy::wildcard_imports)] //#![allow(clippy::redundant_pub_crate)] #![allow(unused)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] #![warn(unused_imports)] extern crate wildcard_imports_helper; diff --git a/tests/ui/wildcard_imports.rs b/tests/ui/wildcard_imports.rs index 0e8631ca704..efaa8f9ef66 100644 --- a/tests/ui/wildcard_imports.rs +++ b/tests/ui/wildcard_imports.rs @@ -4,7 +4,7 @@ #![warn(clippy::wildcard_imports)] //#![allow(clippy::redundant_pub_crate)] #![allow(unused)] -#![allow(clippy::unnecessary_wrap)] +#![allow(clippy::unnecessary_wraps)] #![warn(unused_imports)] extern crate wildcard_imports_helper; -- cgit 1.4.1-3-g733a5 From 5a8396887714fb75f44eae2a3775b1b2a12f38ae Mon Sep 17 00:00:00 2001 From: Christiaan Dirkx Date: Tue, 17 Nov 2020 22:34:39 +0100 Subject: Change `redundant_pattern_matching` to also lint `std::task::Poll` Suggest using utility methods `is_pending` and `is_ready`. --- clippy_lints/src/matches.rs | 33 ++++- clippy_lints/src/utils/paths.rs | 2 + tests/ui/redundant_pattern_matching.fixed | 110 ---------------- tests/ui/redundant_pattern_matching.rs | 128 ------------------ tests/ui/redundant_pattern_matching.stderr | 154 ---------------------- tests/ui/redundant_pattern_matching_option.fixed | 8 +- tests/ui/redundant_pattern_matching_option.rs | 8 +- tests/ui/redundant_pattern_matching_option.stderr | 38 +++--- tests/ui/redundant_pattern_matching_poll.fixed | 73 ++++++++++ tests/ui/redundant_pattern_matching_poll.rs | 88 +++++++++++++ tests/ui/redundant_pattern_matching_poll.stderr | 128 ++++++++++++++++++ tests/ui/redundant_pattern_matching_result.fixed | 109 +++++++++++++++ tests/ui/redundant_pattern_matching_result.rs | 127 ++++++++++++++++++ tests/ui/redundant_pattern_matching_result.stderr | 154 ++++++++++++++++++++++ 14 files changed, 732 insertions(+), 428 deletions(-) delete mode 100644 tests/ui/redundant_pattern_matching.fixed delete mode 100644 tests/ui/redundant_pattern_matching.rs delete mode 100644 tests/ui/redundant_pattern_matching.stderr create mode 100644 tests/ui/redundant_pattern_matching_poll.fixed create mode 100644 tests/ui/redundant_pattern_matching_poll.rs create mode 100644 tests/ui/redundant_pattern_matching_poll.stderr create mode 100644 tests/ui/redundant_pattern_matching_result.fixed create mode 100644 tests/ui/redundant_pattern_matching_result.rs create mode 100644 tests/ui/redundant_pattern_matching_result.stderr (limited to 'tests') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index c6dca54e250..af59917e801 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -411,8 +411,8 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Lint for redundant pattern matching over `Result` or - /// `Option` + /// **What it does:** Lint for redundant pattern matching over `Result`, `Option` or + /// `std::task::Poll` /// /// **Why is this bad?** It's more concise and clear to just use the proper /// utility function @@ -422,10 +422,13 @@ declare_clippy_lint! { /// **Example:** /// /// ```rust + /// # use std::task::Poll; /// if let Ok(_) = Ok::(42) {} /// if let Err(_) = Err::(42) {} /// if let None = None::<()> {} /// if let Some(_) = Some(42) {} + /// if let Poll::Pending = Poll::Pending::<()> {} + /// if let Poll::Ready(_) = Poll::Ready(42) {} /// match Ok::(42) { /// Ok(_) => true, /// Err(_) => false, @@ -435,10 +438,13 @@ declare_clippy_lint! { /// The more idiomatic use would be: /// /// ```rust + /// # use std::task::Poll; /// if Ok::(42).is_ok() {} /// if Err::(42).is_err() {} /// if None::<()>.is_none() {} /// if Some(42).is_some() {} + /// if Poll::Pending::<()>.is_pending() {} + /// if Poll::Ready(42).is_ready() {} /// Ok::(42).is_ok(); /// ``` pub REDUNDANT_PATTERN_MATCHING, @@ -1538,6 +1544,8 @@ mod redundant_pattern_match { "is_err()" } else if match_qpath(path, &paths::OPTION_SOME) { "is_some()" + } else if match_qpath(path, &paths::POLL_READY) { + "is_ready()" } else { return; } @@ -1545,7 +1553,15 @@ mod redundant_pattern_match { return; } }, - PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE) => "is_none()", + PatKind::Path(ref path) => { + if match_qpath(path, &paths::OPTION_NONE) { + "is_none()" + } else if match_qpath(path, &paths::POLL_PENDING) { + "is_pending()" + } else { + return; + } + }, _ => return, }; @@ -1628,6 +1644,17 @@ mod redundant_pattern_match { "is_some()", "is_none()", ) + .or_else(|| { + find_good_method_for_match( + arms, + path_left, + path_right, + &paths::POLL_READY, + &paths::POLL_PENDING, + "is_ready()", + "is_pending()", + ) + }) } else { None } diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 2be5ff93f86..e20f146f145 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -86,6 +86,8 @@ pub const PATH_BUF: [&str; 3] = ["std", "path", "PathBuf"]; pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"]; pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"]; pub const 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"]; pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"]; pub const PTR_NULL: [&str; 3] = ["core", "ptr", "null"]; pub const PTR_NULL_MUT: [&str; 3] = ["core", "ptr", "null_mut"]; diff --git a/tests/ui/redundant_pattern_matching.fixed b/tests/ui/redundant_pattern_matching.fixed deleted file mode 100644 index aa20512296a..00000000000 --- a/tests/ui/redundant_pattern_matching.fixed +++ /dev/null @@ -1,110 +0,0 @@ -// run-rustfix - -#![warn(clippy::all)] -#![warn(clippy::redundant_pattern_matching)] -#![allow( - clippy::unit_arg, - unused_must_use, - clippy::needless_bool, - clippy::match_like_matches_macro, - clippy::unnecessary_wraps, - deprecated -)] - -fn main() { - let result: Result = Err(5); - if result.is_ok() {} - - if Ok::(42).is_ok() {} - - if Err::(42).is_err() {} - - while Ok::(10).is_ok() {} - - while Ok::(10).is_err() {} - - if Ok::(42).is_ok() {} - - if Err::(42).is_err() {} - - if let Ok(x) = Ok::(42) { - println!("{}", x); - } - - Ok::(42).is_ok(); - - Ok::(42).is_err(); - - Err::(42).is_err(); - - Err::(42).is_ok(); - - let _ = if Ok::(4).is_ok() { true } else { false }; - - issue5504(); - issue6067(); - issue6065(); - - let _ = if gen_res().is_ok() { - 1 - } else if gen_res().is_err() { - 2 - } else { - 3 - }; -} - -fn gen_res() -> Result<(), ()> { - Ok(()) -} - -macro_rules! m { - () => { - Some(42u32) - }; -} - -fn issue5504() { - fn result_opt() -> Result, i32> { - Err(42) - } - - fn try_result_opt() -> Result { - while r#try!(result_opt()).is_some() {} - if r#try!(result_opt()).is_some() {} - Ok(42) - } - - try_result_opt(); - - if m!().is_some() {} - while m!().is_some() {} -} - -fn issue6065() { - macro_rules! if_let_in_macro { - ($pat:pat, $x:expr) => { - if let Some($pat) = $x {} - }; - } - - // shouldn't be linted - if_let_in_macro!(_, Some(42)); -} - -// Methods that are unstable const should not be suggested within a const context, see issue #5697. -// However, in Rust 1.48.0 the methods `is_ok` and `is_err` of `Result` were stabilized as const, -// so the following should be linted. -const fn issue6067() { - if Ok::(42).is_ok() {} - - if Err::(42).is_err() {} - - while Ok::(10).is_ok() {} - - while Ok::(10).is_err() {} - - Ok::(42).is_ok(); - - Err::(42).is_err(); -} diff --git a/tests/ui/redundant_pattern_matching.rs b/tests/ui/redundant_pattern_matching.rs deleted file mode 100644 index d76f9c288ff..00000000000 --- a/tests/ui/redundant_pattern_matching.rs +++ /dev/null @@ -1,128 +0,0 @@ -// run-rustfix - -#![warn(clippy::all)] -#![warn(clippy::redundant_pattern_matching)] -#![allow( - clippy::unit_arg, - unused_must_use, - clippy::needless_bool, - clippy::match_like_matches_macro, - clippy::unnecessary_wraps, - deprecated -)] - -fn main() { - let result: Result = Err(5); - if let Ok(_) = &result {} - - if let Ok(_) = Ok::(42) {} - - if let Err(_) = Err::(42) {} - - while let Ok(_) = Ok::(10) {} - - while let Err(_) = Ok::(10) {} - - if Ok::(42).is_ok() {} - - if Err::(42).is_err() {} - - if let Ok(x) = Ok::(42) { - println!("{}", x); - } - - match Ok::(42) { - Ok(_) => true, - Err(_) => false, - }; - - match Ok::(42) { - Ok(_) => false, - Err(_) => true, - }; - - match Err::(42) { - Ok(_) => false, - Err(_) => true, - }; - - match Err::(42) { - Ok(_) => true, - Err(_) => false, - }; - - let _ = if let Ok(_) = Ok::(4) { true } else { false }; - - issue5504(); - issue6067(); - issue6065(); - - let _ = if let Ok(_) = gen_res() { - 1 - } else if let Err(_) = gen_res() { - 2 - } else { - 3 - }; -} - -fn gen_res() -> Result<(), ()> { - Ok(()) -} - -macro_rules! m { - () => { - Some(42u32) - }; -} - -fn issue5504() { - fn result_opt() -> Result, i32> { - Err(42) - } - - fn try_result_opt() -> Result { - while let Some(_) = r#try!(result_opt()) {} - if let Some(_) = r#try!(result_opt()) {} - Ok(42) - } - - try_result_opt(); - - if let Some(_) = m!() {} - while let Some(_) = m!() {} -} - -fn issue6065() { - macro_rules! if_let_in_macro { - ($pat:pat, $x:expr) => { - if let Some($pat) = $x {} - }; - } - - // shouldn't be linted - if_let_in_macro!(_, Some(42)); -} - -// Methods that are unstable const should not be suggested within a const context, see issue #5697. -// However, in Rust 1.48.0 the methods `is_ok` and `is_err` of `Result` were stabilized as const, -// so the following should be linted. -const fn issue6067() { - if let Ok(_) = Ok::(42) {} - - if let Err(_) = Err::(42) {} - - while let Ok(_) = Ok::(10) {} - - while let Err(_) = Ok::(10) {} - - match Ok::(42) { - Ok(_) => true, - Err(_) => false, - }; - - match Err::(42) { - Ok(_) => false, - Err(_) => true, - }; -} diff --git a/tests/ui/redundant_pattern_matching.stderr b/tests/ui/redundant_pattern_matching.stderr deleted file mode 100644 index aeb309f5ba1..00000000000 --- a/tests/ui/redundant_pattern_matching.stderr +++ /dev/null @@ -1,154 +0,0 @@ -error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:16:12 - | -LL | if let Ok(_) = &result {} - | -------^^^^^---------- help: try this: `if result.is_ok()` - | - = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` - -error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:18:12 - | -LL | if let Ok(_) = Ok::(42) {} - | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` - -error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:20:12 - | -LL | if let Err(_) = Err::(42) {} - | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` - -error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:22:15 - | -LL | while let Ok(_) = Ok::(10) {} - | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` - -error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:24:15 - | -LL | while let Err(_) = Ok::(10) {} - | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` - -error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:34:5 - | -LL | / match Ok::(42) { -LL | | Ok(_) => true, -LL | | Err(_) => false, -LL | | }; - | |_____^ help: try this: `Ok::(42).is_ok()` - -error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:39:5 - | -LL | / match Ok::(42) { -LL | | Ok(_) => false, -LL | | Err(_) => true, -LL | | }; - | |_____^ help: try this: `Ok::(42).is_err()` - -error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:44:5 - | -LL | / match Err::(42) { -LL | | Ok(_) => false, -LL | | Err(_) => true, -LL | | }; - | |_____^ help: try this: `Err::(42).is_err()` - -error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:49:5 - | -LL | / match Err::(42) { -LL | | Ok(_) => true, -LL | | Err(_) => false, -LL | | }; - | |_____^ help: try this: `Err::(42).is_ok()` - -error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:54:20 - | -LL | let _ = if let Ok(_) = Ok::(4) { true } else { false }; - | -------^^^^^--------------------- help: try this: `if Ok::(4).is_ok()` - -error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:60:20 - | -LL | let _ = if let Ok(_) = gen_res() { - | -------^^^^^------------ help: try this: `if gen_res().is_ok()` - -error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:62:19 - | -LL | } else if let Err(_) = gen_res() { - | -------^^^^^^------------ help: try this: `if gen_res().is_err()` - -error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:85:19 - | -LL | while let Some(_) = r#try!(result_opt()) {} - | ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()` - -error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:86:16 - | -LL | if let Some(_) = r#try!(result_opt()) {} - | -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()` - -error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:92:12 - | -LL | if let Some(_) = m!() {} - | -------^^^^^^^------- help: try this: `if m!().is_some()` - -error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching.rs:93:15 - | -LL | while let Some(_) = m!() {} - | ----------^^^^^^^------- help: try this: `while m!().is_some()` - -error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:111:12 - | -LL | if let Ok(_) = Ok::(42) {} - | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` - -error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:113:12 - | -LL | if let Err(_) = Err::(42) {} - | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` - -error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:115:15 - | -LL | while let Ok(_) = Ok::(10) {} - | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` - -error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:117:15 - | -LL | while let Err(_) = Ok::(10) {} - | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` - -error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching.rs:119:5 - | -LL | / match Ok::(42) { -LL | | Ok(_) => true, -LL | | Err(_) => false, -LL | | }; - | |_____^ help: try this: `Ok::(42).is_ok()` - -error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching.rs:124:5 - | -LL | / match Err::(42) { -LL | | Ok(_) => false, -LL | | Err(_) => true, -LL | | }; - | |_____^ help: try this: `Err::(42).is_err()` - -error: aborting due to 22 previous errors - diff --git a/tests/ui/redundant_pattern_matching_option.fixed b/tests/ui/redundant_pattern_matching_option.fixed index 499b975b2bb..bc369dd2491 100644 --- a/tests/ui/redundant_pattern_matching_option.fixed +++ b/tests/ui/redundant_pattern_matching_option.fixed @@ -2,13 +2,7 @@ #![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] -#![allow( - clippy::unit_arg, - unused_must_use, - clippy::needless_bool, - clippy::match_like_matches_macro, - deprecated -)] +#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)] fn main() { if None::<()>.is_none() {} diff --git a/tests/ui/redundant_pattern_matching_option.rs b/tests/ui/redundant_pattern_matching_option.rs index 2a98435e790..d7616a72913 100644 --- a/tests/ui/redundant_pattern_matching_option.rs +++ b/tests/ui/redundant_pattern_matching_option.rs @@ -2,13 +2,7 @@ #![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] -#![allow( - clippy::unit_arg, - unused_must_use, - clippy::needless_bool, - clippy::match_like_matches_macro, - deprecated -)] +#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)] fn main() { if let None = None::<()> {} diff --git a/tests/ui/redundant_pattern_matching_option.stderr b/tests/ui/redundant_pattern_matching_option.stderr index eebb3448491..7ddfbe503a2 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:14:12 + --> $DIR/redundant_pattern_matching_option.rs:8: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:16:12 + --> $DIR/redundant_pattern_matching_option.rs:10: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:18:12 + --> $DIR/redundant_pattern_matching_option.rs:12: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:24:15 + --> $DIR/redundant_pattern_matching_option.rs:18: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:26:15 + --> $DIR/redundant_pattern_matching_option.rs:20: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:28:15 + --> $DIR/redundant_pattern_matching_option.rs:22: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:31:15 + --> $DIR/redundant_pattern_matching_option.rs:25: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:39:5 + --> $DIR/redundant_pattern_matching_option.rs:33: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:44:5 + --> $DIR/redundant_pattern_matching_option.rs:38: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:49:13 + --> $DIR/redundant_pattern_matching_option.rs:43: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:55:20 + --> $DIR/redundant_pattern_matching_option.rs:49:20 | LL | let x = 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:60:20 + --> $DIR/redundant_pattern_matching_option.rs:54: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:62:19 + --> $DIR/redundant_pattern_matching_option.rs:56: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:83:12 + --> $DIR/redundant_pattern_matching_option.rs:77: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:85:12 + --> $DIR/redundant_pattern_matching_option.rs:79: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:87:15 + --> $DIR/redundant_pattern_matching_option.rs:81: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:89:15 + --> $DIR/redundant_pattern_matching_option.rs:83: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:91:5 + --> $DIR/redundant_pattern_matching_option.rs:85: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:96:5 + --> $DIR/redundant_pattern_matching_option.rs:90: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 new file mode 100644 index 00000000000..564c427f063 --- /dev/null +++ b/tests/ui/redundant_pattern_matching_poll.fixed @@ -0,0 +1,73 @@ +// run-rustfix + +#![warn(clippy::all)] +#![warn(clippy::redundant_pattern_matching)] +#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)] + +use std::task::Poll::{self, Pending, Ready}; + +fn main() { + if Pending::<()>.is_pending() {} + + if Ready(42).is_ready() {} + + if Ready(42).is_ready() { + foo(); + } else { + bar(); + } + + while Ready(42).is_ready() {} + + while Ready(42).is_pending() {} + + while Pending::<()>.is_pending() {} + + if Pending::.is_pending() {} + + if Ready(42).is_ready() {} + + Ready(42).is_ready(); + + Pending::<()>.is_pending(); + + let _ = Pending::<()>.is_pending(); + + let poll = Ready(false); + let x = if poll.is_ready() { true } else { false }; + takes_poll(x); + + poll_const(); + + let _ = if gen_poll().is_ready() { + 1 + } else if gen_poll().is_pending() { + 2 + } else { + 3 + }; +} + +fn gen_poll() -> Poll<()> { + Pending +} + +fn takes_poll(_: bool) {} + +fn foo() {} + +fn bar() {} + +const fn poll_const() { + if Ready(42).is_ready() {} + + if Pending::<()>.is_pending() {} + + while Ready(42).is_ready() {} + + while Pending::<()>.is_pending() {} + + Ready(42).is_ready(); + + Pending::<()>.is_pending(); +} diff --git a/tests/ui/redundant_pattern_matching_poll.rs b/tests/ui/redundant_pattern_matching_poll.rs new file mode 100644 index 00000000000..d453d4184af --- /dev/null +++ b/tests/ui/redundant_pattern_matching_poll.rs @@ -0,0 +1,88 @@ +// run-rustfix + +#![warn(clippy::all)] +#![warn(clippy::redundant_pattern_matching)] +#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)] + +use std::task::Poll::{self, Pending, Ready}; + +fn main() { + if let Pending = Pending::<()> {} + + if let Ready(_) = Ready(42) {} + + if let Ready(_) = Ready(42) { + foo(); + } else { + bar(); + } + + while let Ready(_) = Ready(42) {} + + while let Pending = Ready(42) {} + + while let Pending = Pending::<()> {} + + if Pending::.is_pending() {} + + if Ready(42).is_ready() {} + + match Ready(42) { + Ready(_) => true, + Pending => false, + }; + + match Pending::<()> { + Ready(_) => false, + Pending => true, + }; + + let _ = match Pending::<()> { + Ready(_) => false, + Pending => true, + }; + + let poll = Ready(false); + let x = if let Ready(_) = poll { true } else { false }; + takes_poll(x); + + poll_const(); + + let _ = if let Ready(_) = gen_poll() { + 1 + } else if let Pending = gen_poll() { + 2 + } else { + 3 + }; +} + +fn gen_poll() -> Poll<()> { + Pending +} + +fn takes_poll(_: bool) {} + +fn foo() {} + +fn bar() {} + +const fn poll_const() { + if let Ready(_) = Ready(42) {} + + if let Pending = Pending::<()> {} + + while let Ready(_) = Ready(42) {} + + while let Pending = Pending::<()> {} + + match Ready(42) { + Ready(_) => true, + Pending => false, + }; + + match Pending::<()> { + Ready(_) => false, + Pending => true, + }; +} diff --git a/tests/ui/redundant_pattern_matching_poll.stderr b/tests/ui/redundant_pattern_matching_poll.stderr new file mode 100644 index 00000000000..42e5d6f41fe --- /dev/null +++ b/tests/ui/redundant_pattern_matching_poll.stderr @@ -0,0 +1,128 @@ +error: redundant pattern matching, consider using `is_pending()` + --> $DIR/redundant_pattern_matching_poll.rs:10:12 + | +LL | if let Pending = Pending::<()> {} + | -------^^^^^^^---------------- help: try this: `if Pending::<()>.is_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:12: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:14: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:20: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:22: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:24: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:30:5 + | +LL | / match Ready(42) { +LL | | Ready(_) => true, +LL | | Pending => false, +LL | | }; + | |_____^ help: try this: `Ready(42).is_ready()` + +error: redundant pattern matching, consider using `is_pending()` + --> $DIR/redundant_pattern_matching_poll.rs:35:5 + | +LL | / match Pending::<()> { +LL | | Ready(_) => false, +LL | | Pending => true, +LL | | }; + | |_____^ help: try this: `Pending::<()>.is_pending()` + +error: redundant pattern matching, consider using `is_pending()` + --> $DIR/redundant_pattern_matching_poll.rs:40:13 + | +LL | let _ = match Pending::<()> { + | _____________^ +LL | | Ready(_) => false, +LL | | Pending => true, +LL | | }; + | |_____^ help: try this: `Pending::<()>.is_pending()` + +error: redundant pattern matching, consider using `is_ready()` + --> $DIR/redundant_pattern_matching_poll.rs:46:20 + | +LL | let x = 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:51: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:53: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:71: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:73: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:75: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:77: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:79:5 + | +LL | / match Ready(42) { +LL | | Ready(_) => true, +LL | | Pending => false, +LL | | }; + | |_____^ help: try this: `Ready(42).is_ready()` + +error: redundant pattern matching, consider using `is_pending()` + --> $DIR/redundant_pattern_matching_poll.rs:84:5 + | +LL | / match Pending::<()> { +LL | | Ready(_) => false, +LL | | Pending => true, +LL | | }; + | |_____^ help: try this: `Pending::<()>.is_pending()` + +error: aborting due to 18 previous errors + diff --git a/tests/ui/redundant_pattern_matching_result.fixed b/tests/ui/redundant_pattern_matching_result.fixed new file mode 100644 index 00000000000..e94c5704b48 --- /dev/null +++ b/tests/ui/redundant_pattern_matching_result.fixed @@ -0,0 +1,109 @@ +// run-rustfix + +#![warn(clippy::all)] +#![warn(clippy::redundant_pattern_matching)] +#![allow( + unused_must_use, + clippy::needless_bool, + clippy::match_like_matches_macro, + clippy::unnecessary_wraps, + deprecated +)] + +fn main() { + let result: Result = Err(5); + if result.is_ok() {} + + if Ok::(42).is_ok() {} + + if Err::(42).is_err() {} + + while Ok::(10).is_ok() {} + + while Ok::(10).is_err() {} + + if Ok::(42).is_ok() {} + + if Err::(42).is_err() {} + + if let Ok(x) = Ok::(42) { + println!("{}", x); + } + + Ok::(42).is_ok(); + + Ok::(42).is_err(); + + Err::(42).is_err(); + + Err::(42).is_ok(); + + let _ = if Ok::(4).is_ok() { true } else { false }; + + issue5504(); + issue6067(); + issue6065(); + + let _ = if gen_res().is_ok() { + 1 + } else if gen_res().is_err() { + 2 + } else { + 3 + }; +} + +fn gen_res() -> Result<(), ()> { + Ok(()) +} + +macro_rules! m { + () => { + Some(42u32) + }; +} + +fn issue5504() { + fn result_opt() -> Result, i32> { + Err(42) + } + + fn try_result_opt() -> Result { + while r#try!(result_opt()).is_some() {} + if r#try!(result_opt()).is_some() {} + Ok(42) + } + + try_result_opt(); + + if m!().is_some() {} + while m!().is_some() {} +} + +fn issue6065() { + macro_rules! if_let_in_macro { + ($pat:pat, $x:expr) => { + if let Some($pat) = $x {} + }; + } + + // shouldn't be linted + if_let_in_macro!(_, Some(42)); +} + +// Methods that are unstable const should not be suggested within a const context, see issue #5697. +// However, in Rust 1.48.0 the methods `is_ok` and `is_err` of `Result` were stabilized as const, +// so the following should be linted. +const fn issue6067() { + if Ok::(42).is_ok() {} + + if Err::(42).is_err() {} + + while Ok::(10).is_ok() {} + + while Ok::(10).is_err() {} + + Ok::(42).is_ok(); + + Err::(42).is_err(); +} diff --git a/tests/ui/redundant_pattern_matching_result.rs b/tests/ui/redundant_pattern_matching_result.rs new file mode 100644 index 00000000000..5d175294232 --- /dev/null +++ b/tests/ui/redundant_pattern_matching_result.rs @@ -0,0 +1,127 @@ +// run-rustfix + +#![warn(clippy::all)] +#![warn(clippy::redundant_pattern_matching)] +#![allow( + unused_must_use, + clippy::needless_bool, + clippy::match_like_matches_macro, + clippy::unnecessary_wraps, + deprecated +)] + +fn main() { + let result: Result = Err(5); + if let Ok(_) = &result {} + + if let Ok(_) = Ok::(42) {} + + if let Err(_) = Err::(42) {} + + while let Ok(_) = Ok::(10) {} + + while let Err(_) = Ok::(10) {} + + if Ok::(42).is_ok() {} + + if Err::(42).is_err() {} + + if let Ok(x) = Ok::(42) { + println!("{}", x); + } + + match Ok::(42) { + Ok(_) => true, + Err(_) => false, + }; + + match Ok::(42) { + Ok(_) => false, + Err(_) => true, + }; + + match Err::(42) { + Ok(_) => false, + Err(_) => true, + }; + + match Err::(42) { + Ok(_) => true, + Err(_) => false, + }; + + let _ = if let Ok(_) = Ok::(4) { true } else { false }; + + issue5504(); + issue6067(); + issue6065(); + + let _ = if let Ok(_) = gen_res() { + 1 + } else if let Err(_) = gen_res() { + 2 + } else { + 3 + }; +} + +fn gen_res() -> Result<(), ()> { + Ok(()) +} + +macro_rules! m { + () => { + Some(42u32) + }; +} + +fn issue5504() { + fn result_opt() -> Result, i32> { + Err(42) + } + + fn try_result_opt() -> Result { + while let Some(_) = r#try!(result_opt()) {} + if let Some(_) = r#try!(result_opt()) {} + Ok(42) + } + + try_result_opt(); + + if let Some(_) = m!() {} + while let Some(_) = m!() {} +} + +fn issue6065() { + macro_rules! if_let_in_macro { + ($pat:pat, $x:expr) => { + if let Some($pat) = $x {} + }; + } + + // shouldn't be linted + if_let_in_macro!(_, Some(42)); +} + +// Methods that are unstable const should not be suggested within a const context, see issue #5697. +// However, in Rust 1.48.0 the methods `is_ok` and `is_err` of `Result` were stabilized as const, +// so the following should be linted. +const fn issue6067() { + if let Ok(_) = Ok::(42) {} + + if let Err(_) = Err::(42) {} + + while let Ok(_) = Ok::(10) {} + + while let Err(_) = Ok::(10) {} + + match Ok::(42) { + Ok(_) => true, + Err(_) => false, + }; + + match Err::(42) { + Ok(_) => false, + Err(_) => true, + }; +} diff --git a/tests/ui/redundant_pattern_matching_result.stderr b/tests/ui/redundant_pattern_matching_result.stderr new file mode 100644 index 00000000000..d6a46babb77 --- /dev/null +++ b/tests/ui/redundant_pattern_matching_result.stderr @@ -0,0 +1,154 @@ +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_result.rs:15:12 + | +LL | if let Ok(_) = &result {} + | -------^^^^^---------- help: try this: `if result.is_ok()` + | + = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_result.rs:17:12 + | +LL | if let Ok(_) = Ok::(42) {} + | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching_result.rs:19:12 + | +LL | if let Err(_) = Err::(42) {} + | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_result.rs:21:15 + | +LL | while let Ok(_) = Ok::(10) {} + | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching_result.rs:23:15 + | +LL | while let Err(_) = Ok::(10) {} + | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_result.rs:33:5 + | +LL | / match Ok::(42) { +LL | | Ok(_) => true, +LL | | Err(_) => false, +LL | | }; + | |_____^ help: try this: `Ok::(42).is_ok()` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching_result.rs:38:5 + | +LL | / match Ok::(42) { +LL | | Ok(_) => false, +LL | | Err(_) => true, +LL | | }; + | |_____^ help: try this: `Ok::(42).is_err()` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching_result.rs:43:5 + | +LL | / match Err::(42) { +LL | | Ok(_) => false, +LL | | Err(_) => true, +LL | | }; + | |_____^ help: try this: `Err::(42).is_err()` + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_result.rs:48:5 + | +LL | / match Err::(42) { +LL | | Ok(_) => true, +LL | | Err(_) => false, +LL | | }; + | |_____^ help: try this: `Err::(42).is_ok()` + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_result.rs:53:20 + | +LL | let _ = if let Ok(_) = Ok::(4) { true } else { false }; + | -------^^^^^--------------------- help: try this: `if Ok::(4).is_ok()` + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_result.rs:59:20 + | +LL | let _ = if let Ok(_) = gen_res() { + | -------^^^^^------------ help: try this: `if gen_res().is_ok()` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching_result.rs:61:19 + | +LL | } else if let Err(_) = gen_res() { + | -------^^^^^^------------ help: try this: `if gen_res().is_err()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching_result.rs:84:19 + | +LL | while let Some(_) = r#try!(result_opt()) {} + | ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching_result.rs:85:16 + | +LL | if let Some(_) = r#try!(result_opt()) {} + | -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching_result.rs:91:12 + | +LL | if let Some(_) = m!() {} + | -------^^^^^^^------- help: try this: `if m!().is_some()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching_result.rs:92:15 + | +LL | while let Some(_) = m!() {} + | ----------^^^^^^^------- help: try this: `while m!().is_some()` + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_result.rs:110:12 + | +LL | if let Ok(_) = Ok::(42) {} + | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching_result.rs:112:12 + | +LL | if let Err(_) = Err::(42) {} + | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_result.rs:114:15 + | +LL | while let Ok(_) = Ok::(10) {} + | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching_result.rs:116:15 + | +LL | while let Err(_) = Ok::(10) {} + | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_result.rs:118:5 + | +LL | / match Ok::(42) { +LL | | Ok(_) => true, +LL | | Err(_) => false, +LL | | }; + | |_____^ help: try this: `Ok::(42).is_ok()` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching_result.rs:123:5 + | +LL | / match Err::(42) { +LL | | Ok(_) => false, +LL | | Err(_) => true, +LL | | }; + | |_____^ help: try this: `Err::(42).is_err()` + +error: aborting due to 22 previous errors + -- cgit 1.4.1-3-g733a5 From 6494bd0bac72b621fc4db64d68c94c8199e97afe Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Wed, 18 Nov 2020 12:36:47 +0900 Subject: Revert "Add `rustfmt::skip` as a work around" This reverts commit 0e803417f997ba35c0045704dd347e64c2a1786c. Fixed by https://github.com/rust-lang/rustfmt/issues/4528. --- tests/ui/cast_ref_to_mut.rs | 4 ---- tests/ui/cast_ref_to_mut.stderr | 6 +++--- 2 files changed, 3 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/tests/ui/cast_ref_to_mut.rs b/tests/ui/cast_ref_to_mut.rs index 0ede958d170..089e5cfabe4 100644 --- a/tests/ui/cast_ref_to_mut.rs +++ b/tests/ui/cast_ref_to_mut.rs @@ -2,10 +2,6 @@ #![allow(clippy::no_effect)] extern "C" { - #[rustfmt::skip] - // TODO: This `rustfmt::skip` is a work around of #6336 because - // the following comments are checked by rustfmt for some reason. - // // N.B., mutability can be easily incorrect in FFI calls -- as // in C, the default is mutable pointers. fn ffi(c: *mut u8); diff --git a/tests/ui/cast_ref_to_mut.stderr b/tests/ui/cast_ref_to_mut.stderr index d36aa0e00ee..aacd99437d9 100644 --- a/tests/ui/cast_ref_to_mut.stderr +++ b/tests/ui/cast_ref_to_mut.stderr @@ -1,5 +1,5 @@ error: casting `&T` to `&mut T` may cause undefined behavior, consider instead using an `UnsafeCell` - --> $DIR/cast_ref_to_mut.rs:22:9 + --> $DIR/cast_ref_to_mut.rs:18:9 | LL | (*(a as *const _ as *mut String)).push_str(" world"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,13 +7,13 @@ LL | (*(a as *const _ as *mut String)).push_str(" world"); = note: `-D clippy::cast-ref-to-mut` implied by `-D warnings` error: casting `&T` to `&mut T` may cause undefined behavior, consider instead using an `UnsafeCell` - --> $DIR/cast_ref_to_mut.rs:23:9 + --> $DIR/cast_ref_to_mut.rs:19:9 | LL | *(a as *const _ as *mut _) = String::from("Replaced"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `&T` to `&mut T` may cause undefined behavior, consider instead using an `UnsafeCell` - --> $DIR/cast_ref_to_mut.rs:24:9 + --> $DIR/cast_ref_to_mut.rs:20:9 | LL | *(a as *const _ as *mut String) += " world"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From dd4e471b3fb701312c2a3fabfba0011f239d4760 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 20 Nov 2020 09:37:47 +0100 Subject: Properly deprecate panic_params lint --- clippy_lints/src/deprecated_lints.rs | 5 +++++ clippy_lints/src/lib.rs | 4 ++++ src/lintlist/mod.rs | 7 ------- tests/ui/deprecated.rs | 1 + tests/ui/deprecated.stderr | 8 +++++++- 5 files changed, 17 insertions(+), 8 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index 461c6e31d3e..1c3285ed701 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -181,3 +181,8 @@ declare_deprecated_lint! { pub TEMPORARY_CSTRING_AS_PTR, "this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr`" } + +declare_deprecated_lint! { + pub PANIC_PARAMS, + "this lint has been uplifted to rustc and is now called `panic_fmt`" +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 19bf67d80c4..ff5d4846995 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -495,6 +495,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: "clippy::temporary_cstring_as_ptr", "this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr`", ); + store.register_removed( + "clippy::panic_params", + "this lint has been uplifted to rustc and is now called `panic_fmt`", + ); // end deprecated lints, do not remove this comment, it’s used in `update_lints` // begin register lints, do not remove this comment, it’s used in `update_lints` diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 702f9d86de6..213e5758659 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1831,13 +1831,6 @@ vec![ deprecation: None, module: "panic_in_result_fn", }, - Lint { - name: "panic_params", - group: "style", - desc: "missing parameters in `panic!` calls", - deprecation: None, - module: "panic_unimplemented", - }, Lint { name: "panicking_unwrap", group: "correctness", diff --git a/tests/ui/deprecated.rs b/tests/ui/deprecated.rs index 56755596c97..4cbc5630d75 100644 --- a/tests/ui/deprecated.rs +++ b/tests/ui/deprecated.rs @@ -10,5 +10,6 @@ #[warn(clippy::regex_macro)] #[warn(clippy::drop_bounds)] #[warn(clippy::temporary_cstring_as_ptr)] +#[warn(clippy::panic_params)] fn main() {} diff --git a/tests/ui/deprecated.stderr b/tests/ui/deprecated.stderr index 37b726fc00f..a348d01d734 100644 --- a/tests/ui/deprecated.stderr +++ b/tests/ui/deprecated.stderr @@ -72,11 +72,17 @@ error: lint `clippy::temporary_cstring_as_ptr` has been removed: `this lint has LL | #[warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: lint `clippy::panic_params` has been removed: `this lint has been uplifted to rustc and is now called `panic_fmt`` + --> $DIR/deprecated.rs:13:8 + | +LL | #[warn(clippy::panic_params)] + | ^^^^^^^^^^^^^^^^^^^^ + error: lint `clippy::str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon` --> $DIR/deprecated.rs:1:8 | LL | #[warn(clippy::str_to_string)] | ^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 13 previous errors +error: aborting due to 14 previous errors -- cgit 1.4.1-3-g733a5 From e30bb7661d5cb547c59deb7ff700d53ddc219a11 Mon Sep 17 00:00:00 2001 From: oliver Date: Sun, 22 Nov 2020 00:50:09 -0400 Subject: update --- tests/ui/manual_ok_or.fixed | 2 +- tests/ui/manual_ok_or.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/ui/manual_ok_or.fixed b/tests/ui/manual_ok_or.fixed index b42e94bd727..887a97d7a01 100644 --- a/tests/ui/manual_ok_or.fixed +++ b/tests/ui/manual_ok_or.fixed @@ -28,7 +28,7 @@ fn main() { // not applicable, or side isn't `Result::Err` foo.map_or(Ok::(1), |v| Ok(v)); - // not applicatble, expr is not a `Result` value + // not applicable, expr is not a `Result` value foo.map_or(42, |v| v); // TODO patterns not covered yet diff --git a/tests/ui/manual_ok_or.rs b/tests/ui/manual_ok_or.rs index e5a6056fbf5..3c99872f502 100644 --- a/tests/ui/manual_ok_or.rs +++ b/tests/ui/manual_ok_or.rs @@ -32,7 +32,7 @@ fn main() { // not applicable, or side isn't `Result::Err` foo.map_or(Ok::(1), |v| Ok(v)); - // not applicatble, expr is not a `Result` value + // not applicable, expr is not a `Result` value foo.map_or(42, |v| v); // TODO patterns not covered yet -- cgit 1.4.1-3-g733a5 From a8c47440b4ac59239f11838cfc7426b2adef3685 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 10 Nov 2020 09:19:30 -0600 Subject: Use article_and_description for missing docs --- clippy_lints/src/missing_doc.rs | 64 ++++++++++++++++--------------- tests/ui/missing-doc-crate-missing.stderr | 2 +- tests/ui/missing-doc-impl.stderr | 12 +++--- 3 files changed, 40 insertions(+), 38 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index 009e3d8937e..f2c0ab1222c 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -2,7 +2,7 @@ // *rustc*'s // [`missing_doc`]. // -// [`missing_doc`]: https://github.com/rust-lang/rust/blob/d6d05904697d89099b55da3331155392f1db9c00/src/librustc_lint/builtin.rs#L246 +// [`missing_doc`]: https://github.com/rust-lang/rust/blob/cf9cf7c923eb01146971429044f216a3ca905e06/compiler/rustc_lint/src/builtin.rs#L415 // use crate::utils::span_lint; @@ -70,7 +70,14 @@ impl MissingDoc { } } - fn check_missing_docs_attrs(&self, cx: &LateContext<'_>, attrs: &[ast::Attribute], sp: Span, desc: &'static str) { + fn check_missing_docs_attrs( + &self, + cx: &LateContext<'_>, + attrs: &[ast::Attribute], + sp: Span, + article: &'static str, + desc: &'static str, + ) { // If we're building a test harness, then warning about // documentation is probably not really relevant right now. if cx.sess().opts.test { @@ -94,7 +101,7 @@ impl MissingDoc { cx, MISSING_DOCS_IN_PRIVATE_ITEMS, sp, - &format!("missing documentation for {}", desc), + &format!("missing documentation for {} {}", article, desc), ); } } @@ -120,13 +127,11 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { } fn check_crate(&mut self, cx: &LateContext<'tcx>, krate: &'tcx hir::Crate<'_>) { - self.check_missing_docs_attrs(cx, &krate.item.attrs, krate.item.span, "crate"); + self.check_missing_docs_attrs(cx, &krate.item.attrs, krate.item.span, "the", "crate"); } fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'_>) { - let desc = match it.kind { - hir::ItemKind::Const(..) => "a constant", - hir::ItemKind::Enum(..) => "an enum", + match it.kind { hir::ItemKind::Fn(..) => { // ignore main() if it.ident.name == sym::main { @@ -136,16 +141,17 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { return; } } - "a function" }, - hir::ItemKind::Mod(..) => "a module", - hir::ItemKind::Static(..) => "a static", - hir::ItemKind::Struct(..) => "a struct", - hir::ItemKind::Trait(..) => "a trait", - hir::ItemKind::TraitAlias(..) => "a trait alias", - hir::ItemKind::TyAlias(..) => "a type alias", - hir::ItemKind::Union(..) => "a union", - hir::ItemKind::OpaqueTy(..) => "an existential type", + hir::ItemKind::Const(..) + | hir::ItemKind::Enum(..) + | hir::ItemKind::Mod(..) + | hir::ItemKind::Static(..) + | hir::ItemKind::Struct(..) + | hir::ItemKind::Trait(..) + | hir::ItemKind::TraitAlias(..) + | hir::ItemKind::TyAlias(..) + | hir::ItemKind::Union(..) + | hir::ItemKind::OpaqueTy(..) => {}, hir::ItemKind::ExternCrate(..) | hir::ItemKind::ForeignMod(..) | hir::ItemKind::GlobalAsm(..) @@ -153,17 +159,17 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { | hir::ItemKind::Use(..) => return, }; - self.check_missing_docs_attrs(cx, &it.attrs, it.span, desc); + let def_id = cx.tcx.hir().local_def_id(it.hir_id); + let (article, desc) = cx.tcx.article_and_description(def_id.to_def_id()); + + self.check_missing_docs_attrs(cx, &it.attrs, it.span, article, desc); } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx hir::TraitItem<'_>) { - let desc = match trait_item.kind { - hir::TraitItemKind::Const(..) => "an associated constant", - hir::TraitItemKind::Fn(..) => "a trait method", - hir::TraitItemKind::Type(..) => "an associated type", - }; + let def_id = cx.tcx.hir().local_def_id(trait_item.hir_id); + let (article, desc) = cx.tcx.article_and_description(def_id.to_def_id()); - self.check_missing_docs_attrs(cx, &trait_item.attrs, trait_item.span, desc); + self.check_missing_docs_attrs(cx, &trait_item.attrs, trait_item.span, article, desc); } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { @@ -178,21 +184,17 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { }, } - let desc = match impl_item.kind { - hir::ImplItemKind::Const(..) => "an associated constant", - hir::ImplItemKind::Fn(..) => "a method", - hir::ImplItemKind::TyAlias(_) => "an associated type", - }; - self.check_missing_docs_attrs(cx, &impl_item.attrs, impl_item.span, desc); + let (article, desc) = cx.tcx.article_and_description(def_id.to_def_id()); + self.check_missing_docs_attrs(cx, &impl_item.attrs, impl_item.span, article, desc); } fn check_struct_field(&mut self, cx: &LateContext<'tcx>, sf: &'tcx hir::StructField<'_>) { if !sf.is_positional() { - self.check_missing_docs_attrs(cx, &sf.attrs, sf.span, "a struct field"); + self.check_missing_docs_attrs(cx, &sf.attrs, sf.span, "a", "struct field"); } } fn check_variant(&mut self, cx: &LateContext<'tcx>, v: &'tcx hir::Variant<'_>) { - self.check_missing_docs_attrs(cx, &v.attrs, v.span, "a variant"); + self.check_missing_docs_attrs(cx, &v.attrs, v.span, "a", "variant"); } } diff --git a/tests/ui/missing-doc-crate-missing.stderr b/tests/ui/missing-doc-crate-missing.stderr index da46f988636..d56c5cc4c3a 100644 --- a/tests/ui/missing-doc-crate-missing.stderr +++ b/tests/ui/missing-doc-crate-missing.stderr @@ -1,4 +1,4 @@ -error: missing documentation for crate +error: missing documentation for the crate --> $DIR/missing-doc-crate-missing.rs:1:1 | LL | / #![warn(clippy::missing_docs_in_private_items)] diff --git a/tests/ui/missing-doc-impl.stderr b/tests/ui/missing-doc-impl.stderr index 9656a39abce..7e10404ca00 100644 --- a/tests/ui/missing-doc-impl.stderr +++ b/tests/ui/missing-doc-impl.stderr @@ -51,13 +51,13 @@ LL | | fn foo_with_impl(&self) {} LL | | } | |_^ -error: missing documentation for a trait method +error: missing documentation for an associated function --> $DIR/missing-doc-impl.rs:39:5 | LL | fn foo(&self); | ^^^^^^^^^^^^^^ -error: missing documentation for a trait method +error: missing documentation for an associated function --> $DIR/missing-doc-impl.rs:40:5 | LL | fn foo_with_impl(&self) {} @@ -75,25 +75,25 @@ error: missing documentation for an associated type LL | type AssociatedTypeDef = Self; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: missing documentation for a method +error: missing documentation for an associated function --> $DIR/missing-doc-impl.rs:62:5 | LL | pub fn foo() {} | ^^^^^^^^^^^^^^^ -error: missing documentation for a method +error: missing documentation for an associated function --> $DIR/missing-doc-impl.rs:63:5 | LL | fn bar() {} | ^^^^^^^^^^^ -error: missing documentation for a method +error: missing documentation for an associated function --> $DIR/missing-doc-impl.rs:67:5 | LL | pub fn foo() {} | ^^^^^^^^^^^^^^^ -error: missing documentation for a method +error: missing documentation for an associated function --> $DIR/missing-doc-impl.rs:70:5 | LL | fn foo2() {} -- cgit 1.4.1-3-g733a5 From a39a93faeb4969fac62e896138cdf72377fa5502 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Sun, 22 Nov 2020 19:21:01 -0600 Subject: Disable unnecessary_cast for cfg-dependant types --- clippy_lints/src/types.rs | 10 +++++++++- tests/ui/unnecessary_cast.rs | 3 +++ 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index f0e10e374e1..840adbbc57a 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -8,6 +8,7 @@ use if_chain::if_chain; use rustc_ast::{FloatTy, IntTy, LitFloatType, LitIntType, LitKind, UintTy}; use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir as hir; +use rustc_hir::def::Res; use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::{ BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericBounds, GenericParamKind, HirId, @@ -1632,7 +1633,14 @@ impl<'tcx> LateLintPass<'tcx> for Casts { if expr.span.from_expansion() { return; } - if let ExprKind::Cast(ref ex, _) = expr.kind { + if let ExprKind::Cast(ref ex, cast_to) = expr.kind { + if let TyKind::Path(QPath::Resolved(_, path)) = cast_to.kind { + if let Res::Def(_, def_id) = path.res { + if cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr) { + return; + } + } + } let (cast_from, cast_to) = (cx.typeck_results().expr_ty(ex), cx.typeck_results().expr_ty(expr)); lint_fn_to_numeric_cast(cx, expr, ex, cast_from, cast_to); if let Some(lit) = get_numeric_literal(ex) { diff --git a/tests/ui/unnecessary_cast.rs b/tests/ui/unnecessary_cast.rs index df9b227eeb3..e8f2fb46665 100644 --- a/tests/ui/unnecessary_cast.rs +++ b/tests/ui/unnecessary_cast.rs @@ -20,4 +20,7 @@ fn main() { foo!(a, i32); foo!(b, f32); foo!(c, f64); + + // do not lint cast to cfg-dependant type + 1 as std::os::raw::c_char; } -- cgit 1.4.1-3-g733a5 From dc075b426675e61cc46089046cf41d85edd79aaa Mon Sep 17 00:00:00 2001 From: Christiaan Dirkx Date: Wed, 25 Nov 2020 02:01:05 +0100 Subject: Change `redundant_pattern_matching` to also lint `std::net::IpAddr` Suggest using utility methods `is_ipv4` and `is_ipv6`. --- clippy_lints/src/matches.rs | 25 ++++- clippy_lints/src/utils/paths.rs | 2 + tests/ui/redundant_pattern_matching_ipaddr.fixed | 73 ++++++++++++ tests/ui/redundant_pattern_matching_ipaddr.rs | 91 +++++++++++++++ tests/ui/redundant_pattern_matching_ipaddr.stderr | 130 ++++++++++++++++++++++ tests/ui/redundant_pattern_matching_option.fixed | 5 +- tests/ui/redundant_pattern_matching_option.rs | 5 +- tests/ui/redundant_pattern_matching_option.stderr | 18 +-- tests/ui/redundant_pattern_matching_poll.fixed | 5 +- tests/ui/redundant_pattern_matching_poll.rs | 5 +- tests/ui/redundant_pattern_matching_poll.stderr | 18 +-- 11 files changed, 341 insertions(+), 36 deletions(-) create mode 100644 tests/ui/redundant_pattern_matching_ipaddr.fixed create mode 100644 tests/ui/redundant_pattern_matching_ipaddr.rs create mode 100644 tests/ui/redundant_pattern_matching_ipaddr.stderr (limited to 'tests') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index af59917e801..a14b63faf78 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -411,8 +411,8 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Lint for redundant pattern matching over `Result`, `Option` or - /// `std::task::Poll` + /// **What it does:** Lint for redundant pattern matching over `Result`, `Option`, + /// `std::task::Poll` or `std::net::IpAddr` /// /// **Why is this bad?** It's more concise and clear to just use the proper /// utility function @@ -423,12 +423,15 @@ declare_clippy_lint! { /// /// ```rust /// # use std::task::Poll; + /// # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; /// if let Ok(_) = Ok::(42) {} /// if let Err(_) = Err::(42) {} /// if let None = None::<()> {} /// if let Some(_) = Some(42) {} /// if let Poll::Pending = Poll::Pending::<()> {} /// if let Poll::Ready(_) = Poll::Ready(42) {} + /// if let IpAddr::V4(_) = IpAddr::V4(Ipv4Addr::LOCALHOST) {} + /// if let IpAddr::V6(_) = IpAddr::V6(Ipv6Addr::LOCALHOST) {} /// match Ok::(42) { /// Ok(_) => true, /// Err(_) => false, @@ -439,12 +442,15 @@ declare_clippy_lint! { /// /// ```rust /// # use std::task::Poll; + /// # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; /// if Ok::(42).is_ok() {} /// if Err::(42).is_err() {} /// if None::<()>.is_none() {} /// if Some(42).is_some() {} /// if Poll::Pending::<()>.is_pending() {} /// if Poll::Ready(42).is_ready() {} + /// if IpAddr::V4(Ipv4Addr::LOCALHOST).is_ipv4() {} + /// if IpAddr::V6(Ipv6Addr::LOCALHOST).is_ipv6() {} /// Ok::(42).is_ok(); /// ``` pub REDUNDANT_PATTERN_MATCHING, @@ -1546,6 +1552,10 @@ mod redundant_pattern_match { "is_some()" } else if match_qpath(path, &paths::POLL_READY) { "is_ready()" + } else if match_qpath(path, &paths::IPADDR_V4) { + "is_ipv4()" + } else if match_qpath(path, &paths::IPADDR_V6) { + "is_ipv6()" } else { return; } @@ -1626,6 +1636,17 @@ mod redundant_pattern_match { "is_ok()", "is_err()", ) + .or_else(|| { + find_good_method_for_match( + arms, + path_left, + path_right, + &paths::IPADDR_V4, + &paths::IPADDR_V6, + "is_ipv4()", + "is_ipv6()", + ) + }) } else { None } diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 829e9a2989c..61aeabb7ba7 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -58,6 +58,8 @@ pub const INTO: [&str; 3] = ["core", "convert", "Into"]; pub const INTO_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "IntoIterator"]; pub const IO_READ: [&str; 3] = ["std", "io", "Read"]; pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"]; +pub const IPADDR_V4: [&str; 4] = ["std", "net", "IpAddr", "V4"]; +pub const IPADDR_V6: [&str; 4] = ["std", "net", "IpAddr", "V6"]; pub const ITERATOR: [&str; 5] = ["core", "iter", "traits", "iterator", "Iterator"]; pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"]; pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"]; diff --git a/tests/ui/redundant_pattern_matching_ipaddr.fixed b/tests/ui/redundant_pattern_matching_ipaddr.fixed new file mode 100644 index 00000000000..acc8de5f41e --- /dev/null +++ b/tests/ui/redundant_pattern_matching_ipaddr.fixed @@ -0,0 +1,73 @@ +// run-rustfix + +#![warn(clippy::all)] +#![warn(clippy::redundant_pattern_matching)] +#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)] + +use std::net::{ + IpAddr::{self, V4, V6}, + Ipv4Addr, Ipv6Addr, +}; + +fn main() { + let ipaddr: IpAddr = V4(Ipv4Addr::LOCALHOST); + if ipaddr.is_ipv4() {} + + if V4(Ipv4Addr::LOCALHOST).is_ipv4() {} + + if V6(Ipv6Addr::LOCALHOST).is_ipv6() {} + + while V4(Ipv4Addr::LOCALHOST).is_ipv4() {} + + while V6(Ipv6Addr::LOCALHOST).is_ipv6() {} + + if V4(Ipv4Addr::LOCALHOST).is_ipv4() {} + + if V6(Ipv6Addr::LOCALHOST).is_ipv6() {} + + if let V4(ipaddr) = V4(Ipv4Addr::LOCALHOST) { + println!("{}", ipaddr); + } + + V4(Ipv4Addr::LOCALHOST).is_ipv4(); + + V4(Ipv4Addr::LOCALHOST).is_ipv6(); + + V6(Ipv6Addr::LOCALHOST).is_ipv6(); + + V6(Ipv6Addr::LOCALHOST).is_ipv4(); + + let _ = if V4(Ipv4Addr::LOCALHOST).is_ipv4() { + true + } else { + false + }; + + ipaddr_const(); + + let _ = if gen_ipaddr().is_ipv4() { + 1 + } else if gen_ipaddr().is_ipv6() { + 2 + } else { + 3 + }; +} + +fn gen_ipaddr() -> IpAddr { + V4(Ipv4Addr::LOCALHOST) +} + +const fn ipaddr_const() { + if V4(Ipv4Addr::LOCALHOST).is_ipv4() {} + + if V6(Ipv6Addr::LOCALHOST).is_ipv6() {} + + while V4(Ipv4Addr::LOCALHOST).is_ipv4() {} + + while V6(Ipv6Addr::LOCALHOST).is_ipv6() {} + + V4(Ipv4Addr::LOCALHOST).is_ipv4(); + + V6(Ipv6Addr::LOCALHOST).is_ipv6(); +} diff --git a/tests/ui/redundant_pattern_matching_ipaddr.rs b/tests/ui/redundant_pattern_matching_ipaddr.rs new file mode 100644 index 00000000000..678d91ce93a --- /dev/null +++ b/tests/ui/redundant_pattern_matching_ipaddr.rs @@ -0,0 +1,91 @@ +// run-rustfix + +#![warn(clippy::all)] +#![warn(clippy::redundant_pattern_matching)] +#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)] + +use std::net::{ + IpAddr::{self, V4, V6}, + Ipv4Addr, Ipv6Addr, +}; + +fn main() { + let ipaddr: IpAddr = V4(Ipv4Addr::LOCALHOST); + if let V4(_) = &ipaddr {} + + if let V4(_) = V4(Ipv4Addr::LOCALHOST) {} + + if let V6(_) = V6(Ipv6Addr::LOCALHOST) {} + + while let V4(_) = V4(Ipv4Addr::LOCALHOST) {} + + while let V6(_) = V6(Ipv6Addr::LOCALHOST) {} + + if V4(Ipv4Addr::LOCALHOST).is_ipv4() {} + + if V6(Ipv6Addr::LOCALHOST).is_ipv6() {} + + if let V4(ipaddr) = V4(Ipv4Addr::LOCALHOST) { + println!("{}", ipaddr); + } + + match V4(Ipv4Addr::LOCALHOST) { + V4(_) => true, + V6(_) => false, + }; + + match V4(Ipv4Addr::LOCALHOST) { + V4(_) => false, + V6(_) => true, + }; + + match V6(Ipv6Addr::LOCALHOST) { + V4(_) => false, + V6(_) => true, + }; + + match V6(Ipv6Addr::LOCALHOST) { + V4(_) => true, + V6(_) => false, + }; + + let _ = if let V4(_) = V4(Ipv4Addr::LOCALHOST) { + true + } else { + false + }; + + ipaddr_const(); + + let _ = if let V4(_) = gen_ipaddr() { + 1 + } else if let V6(_) = gen_ipaddr() { + 2 + } else { + 3 + }; +} + +fn gen_ipaddr() -> IpAddr { + V4(Ipv4Addr::LOCALHOST) +} + +const fn ipaddr_const() { + if let V4(_) = V4(Ipv4Addr::LOCALHOST) {} + + if let V6(_) = V6(Ipv6Addr::LOCALHOST) {} + + while let V4(_) = V4(Ipv4Addr::LOCALHOST) {} + + while let V6(_) = V6(Ipv6Addr::LOCALHOST) {} + + match V4(Ipv4Addr::LOCALHOST) { + V4(_) => true, + V6(_) => false, + }; + + match V6(Ipv6Addr::LOCALHOST) { + V4(_) => false, + V6(_) => true, + }; +} diff --git a/tests/ui/redundant_pattern_matching_ipaddr.stderr b/tests/ui/redundant_pattern_matching_ipaddr.stderr new file mode 100644 index 00000000000..caf458cd862 --- /dev/null +++ b/tests/ui/redundant_pattern_matching_ipaddr.stderr @@ -0,0 +1,130 @@ +error: redundant pattern matching, consider using `is_ipv4()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:14:12 + | +LL | if let V4(_) = &ipaddr {} + | -------^^^^^---------- help: try this: `if ipaddr.is_ipv4()` + | + = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` + +error: redundant pattern matching, consider using `is_ipv4()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:16:12 + | +LL | if let V4(_) = V4(Ipv4Addr::LOCALHOST) {} + | -------^^^^^-------------------------- help: try this: `if V4(Ipv4Addr::LOCALHOST).is_ipv4()` + +error: redundant pattern matching, consider using `is_ipv6()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:18:12 + | +LL | if let V6(_) = V6(Ipv6Addr::LOCALHOST) {} + | -------^^^^^-------------------------- help: try this: `if V6(Ipv6Addr::LOCALHOST).is_ipv6()` + +error: redundant pattern matching, consider using `is_ipv4()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:20:15 + | +LL | while let V4(_) = V4(Ipv4Addr::LOCALHOST) {} + | ----------^^^^^-------------------------- help: try this: `while V4(Ipv4Addr::LOCALHOST).is_ipv4()` + +error: redundant pattern matching, consider using `is_ipv6()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:22:15 + | +LL | while let V6(_) = V6(Ipv6Addr::LOCALHOST) {} + | ----------^^^^^-------------------------- help: try this: `while V6(Ipv6Addr::LOCALHOST).is_ipv6()` + +error: redundant pattern matching, consider using `is_ipv4()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:32:5 + | +LL | / match V4(Ipv4Addr::LOCALHOST) { +LL | | V4(_) => true, +LL | | V6(_) => false, +LL | | }; + | |_____^ help: try this: `V4(Ipv4Addr::LOCALHOST).is_ipv4()` + +error: redundant pattern matching, consider using `is_ipv6()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:37:5 + | +LL | / match V4(Ipv4Addr::LOCALHOST) { +LL | | V4(_) => false, +LL | | V6(_) => true, +LL | | }; + | |_____^ help: try this: `V4(Ipv4Addr::LOCALHOST).is_ipv6()` + +error: redundant pattern matching, consider using `is_ipv6()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:42:5 + | +LL | / match V6(Ipv6Addr::LOCALHOST) { +LL | | V4(_) => false, +LL | | V6(_) => true, +LL | | }; + | |_____^ help: try this: `V6(Ipv6Addr::LOCALHOST).is_ipv6()` + +error: redundant pattern matching, consider using `is_ipv4()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:47:5 + | +LL | / match V6(Ipv6Addr::LOCALHOST) { +LL | | V4(_) => true, +LL | | V6(_) => false, +LL | | }; + | |_____^ help: try this: `V6(Ipv6Addr::LOCALHOST).is_ipv4()` + +error: redundant pattern matching, consider using `is_ipv4()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:52:20 + | +LL | let _ = if let V4(_) = V4(Ipv4Addr::LOCALHOST) { + | -------^^^^^-------------------------- help: try this: `if V4(Ipv4Addr::LOCALHOST).is_ipv4()` + +error: redundant pattern matching, consider using `is_ipv4()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:60:20 + | +LL | let _ = if let V4(_) = gen_ipaddr() { + | -------^^^^^--------------- help: try this: `if gen_ipaddr().is_ipv4()` + +error: redundant pattern matching, consider using `is_ipv6()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:62:19 + | +LL | } else if let V6(_) = gen_ipaddr() { + | -------^^^^^--------------- help: try this: `if gen_ipaddr().is_ipv6()` + +error: redundant pattern matching, consider using `is_ipv4()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:74:12 + | +LL | if let V4(_) = V4(Ipv4Addr::LOCALHOST) {} + | -------^^^^^-------------------------- help: try this: `if V4(Ipv4Addr::LOCALHOST).is_ipv4()` + +error: redundant pattern matching, consider using `is_ipv6()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:76:12 + | +LL | if let V6(_) = V6(Ipv6Addr::LOCALHOST) {} + | -------^^^^^-------------------------- help: try this: `if V6(Ipv6Addr::LOCALHOST).is_ipv6()` + +error: redundant pattern matching, consider using `is_ipv4()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:78:15 + | +LL | while let V4(_) = V4(Ipv4Addr::LOCALHOST) {} + | ----------^^^^^-------------------------- help: try this: `while V4(Ipv4Addr::LOCALHOST).is_ipv4()` + +error: redundant pattern matching, consider using `is_ipv6()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:80:15 + | +LL | while let V6(_) = V6(Ipv6Addr::LOCALHOST) {} + | ----------^^^^^-------------------------- help: try this: `while V6(Ipv6Addr::LOCALHOST).is_ipv6()` + +error: redundant pattern matching, consider using `is_ipv4()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:82:5 + | +LL | / match V4(Ipv4Addr::LOCALHOST) { +LL | | V4(_) => true, +LL | | V6(_) => false, +LL | | }; + | |_____^ help: try this: `V4(Ipv4Addr::LOCALHOST).is_ipv4()` + +error: redundant pattern matching, consider using `is_ipv6()` + --> $DIR/redundant_pattern_matching_ipaddr.rs:87:5 + | +LL | / match V6(Ipv6Addr::LOCALHOST) { +LL | | V4(_) => false, +LL | | V6(_) => true, +LL | | }; + | |_____^ help: try this: `V6(Ipv6Addr::LOCALHOST).is_ipv6()` + +error: aborting due to 18 previous errors + diff --git a/tests/ui/redundant_pattern_matching_option.fixed b/tests/ui/redundant_pattern_matching_option.fixed index bc369dd2491..66f580a0a68 100644 --- a/tests/ui/redundant_pattern_matching_option.fixed +++ b/tests/ui/redundant_pattern_matching_option.fixed @@ -37,8 +37,7 @@ fn main() { let _ = None::<()>.is_none(); let opt = Some(false); - let x = if opt.is_some() { true } else { false }; - takes_bool(x); + let _ = if opt.is_some() { true } else { false }; issue6067(); @@ -55,8 +54,6 @@ fn gen_opt() -> Option<()> { None } -fn takes_bool(_: bool) {} - fn foo() {} fn bar() {} diff --git a/tests/ui/redundant_pattern_matching_option.rs b/tests/ui/redundant_pattern_matching_option.rs index d7616a72913..f18b27b8b95 100644 --- a/tests/ui/redundant_pattern_matching_option.rs +++ b/tests/ui/redundant_pattern_matching_option.rs @@ -46,8 +46,7 @@ fn main() { }; let opt = Some(false); - let x = if let Some(_) = opt { true } else { false }; - takes_bool(x); + let _ = if let Some(_) = opt { true } else { false }; issue6067(); @@ -64,8 +63,6 @@ fn gen_opt() -> Option<()> { None } -fn takes_bool(_: bool) {} - fn foo() {} fn bar() {} diff --git a/tests/ui/redundant_pattern_matching_option.stderr b/tests/ui/redundant_pattern_matching_option.stderr index 7ddfbe503a2..58482a0ab70 100644 --- a/tests/ui/redundant_pattern_matching_option.stderr +++ b/tests/ui/redundant_pattern_matching_option.stderr @@ -73,47 +73,47 @@ LL | | }; error: redundant pattern matching, consider using `is_some()` --> $DIR/redundant_pattern_matching_option.rs:49:20 | -LL | let x = if let Some(_) = opt { true } else { false }; +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:54:20 + --> $DIR/redundant_pattern_matching_option.rs:53: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:56:19 + --> $DIR/redundant_pattern_matching_option.rs:55: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:77:12 + --> $DIR/redundant_pattern_matching_option.rs:74: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:79:12 + --> $DIR/redundant_pattern_matching_option.rs:76: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:81:15 + --> $DIR/redundant_pattern_matching_option.rs:78: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:83:15 + --> $DIR/redundant_pattern_matching_option.rs:80: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:85:5 + --> $DIR/redundant_pattern_matching_option.rs:82: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:90:5 + --> $DIR/redundant_pattern_matching_option.rs:87: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 564c427f063..465aa80dac2 100644 --- a/tests/ui/redundant_pattern_matching_poll.fixed +++ b/tests/ui/redundant_pattern_matching_poll.fixed @@ -34,8 +34,7 @@ fn main() { let _ = Pending::<()>.is_pending(); let poll = Ready(false); - let x = if poll.is_ready() { true } else { false }; - takes_poll(x); + let _ = if poll.is_ready() { true } else { false }; poll_const(); @@ -52,8 +51,6 @@ fn gen_poll() -> Poll<()> { Pending } -fn takes_poll(_: bool) {} - fn foo() {} fn bar() {} diff --git a/tests/ui/redundant_pattern_matching_poll.rs b/tests/ui/redundant_pattern_matching_poll.rs index d453d4184af..7891ff353b1 100644 --- a/tests/ui/redundant_pattern_matching_poll.rs +++ b/tests/ui/redundant_pattern_matching_poll.rs @@ -43,8 +43,7 @@ fn main() { }; let poll = Ready(false); - let x = if let Ready(_) = poll { true } else { false }; - takes_poll(x); + let _ = if let Ready(_) = poll { true } else { false }; poll_const(); @@ -61,8 +60,6 @@ fn gen_poll() -> Poll<()> { Pending } -fn takes_poll(_: bool) {} - fn foo() {} fn bar() {} diff --git a/tests/ui/redundant_pattern_matching_poll.stderr b/tests/ui/redundant_pattern_matching_poll.stderr index 42e5d6f41fe..5ffc6c47c90 100644 --- a/tests/ui/redundant_pattern_matching_poll.stderr +++ b/tests/ui/redundant_pattern_matching_poll.stderr @@ -67,47 +67,47 @@ LL | | }; error: redundant pattern matching, consider using `is_ready()` --> $DIR/redundant_pattern_matching_poll.rs:46:20 | -LL | let x = if let Ready(_) = poll { true } else { false }; +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:51:20 + --> $DIR/redundant_pattern_matching_poll.rs:50: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:53:19 + --> $DIR/redundant_pattern_matching_poll.rs:52: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:71:12 + --> $DIR/redundant_pattern_matching_poll.rs:68: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:73:12 + --> $DIR/redundant_pattern_matching_poll.rs:70: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:75:15 + --> $DIR/redundant_pattern_matching_poll.rs:72: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:77:15 + --> $DIR/redundant_pattern_matching_poll.rs:74: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:79:5 + --> $DIR/redundant_pattern_matching_poll.rs:76: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:84:5 + --> $DIR/redundant_pattern_matching_poll.rs:81:5 | LL | / match Pending::<()> { LL | | Ready(_) => false, -- cgit 1.4.1-3-g733a5 From aaa43250455c19ae54c821518c42219f6460038c Mon Sep 17 00:00:00 2001 From: Suyash458 Date: Wed, 21 Oct 2020 18:17:34 +0530 Subject: add support for minimum supported rust version. add configuration option for minimum supported rust version add msrv attribute to some lints listed in #6097 add tests --- clippy_lints/src/lib.rs | 23 ++++++-- clippy_lints/src/manual_non_exhaustive.rs | 33 ++++++++++- clippy_lints/src/manual_strip.rs | 37 ++++++++++-- clippy_lints/src/matches.rs | 39 +++++++++++-- clippy_lints/src/methods/mod.rs | 51 +++++++++++++--- clippy_lints/src/utils/attrs.rs | 17 ++++++ clippy_lints/src/utils/conf.rs | 2 + clippy_lints/src/utils/mod.rs | 49 ++++++++++++++++ tests/ui-toml/invalid_min_rust_version/clippy.toml | 1 + .../invalid_min_rust_version.rs | 3 + .../invalid_min_rust_version.stderr | 4 ++ tests/ui-toml/min_rust_version/clippy.toml | 1 + tests/ui-toml/min_rust_version/min_rust_version.rs | 68 ++++++++++++++++++++++ .../toml_unknown_key/conf_unknown_key.stderr | 2 +- tests/ui/min_rust_version_attr.rs | 51 ++++++++++++++++ tests/ui/min_rust_version_invalid_attr.rs | 4 ++ tests/ui/min_rust_version_invalid_attr.stderr | 8 +++ tests/ui/min_rust_version_no_patch.rs | 14 +++++ tests/ui/min_rust_version_outer_attr.rs | 4 ++ tests/ui/min_rust_version_outer_attr.stderr | 8 +++ 20 files changed, 390 insertions(+), 29 deletions(-) create mode 100644 tests/ui-toml/invalid_min_rust_version/clippy.toml create mode 100644 tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.rs create mode 100644 tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.stderr create mode 100644 tests/ui-toml/min_rust_version/clippy.toml create mode 100644 tests/ui-toml/min_rust_version/min_rust_version.rs create mode 100644 tests/ui/min_rust_version_attr.rs create mode 100644 tests/ui/min_rust_version_invalid_attr.rs create mode 100644 tests/ui/min_rust_version_invalid_attr.stderr create mode 100644 tests/ui/min_rust_version_no_patch.rs create mode 100644 tests/ui/min_rust_version_outer_attr.rs create mode 100644 tests/ui/min_rust_version_outer_attr.stderr (limited to 'tests') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 7e8cbd00c22..866dae110cc 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -44,6 +44,7 @@ extern crate rustc_target; extern crate rustc_trait_selection; extern crate rustc_typeck; +use crate::utils::parse_msrv; use rustc_data_structures::fx::FxHashSet; use rustc_lint::LintId; use rustc_session::Session; @@ -933,7 +934,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &zero_div_zero::ZERO_DIVIDED_BY_ZERO, ]); // end register lints, do not remove this comment, it’s used in `update_lints` - store.register_late_pass(|| box await_holding_invalid::AwaitHolding); store.register_late_pass(|| box serde_api::SerdeAPI); store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); @@ -969,7 +969,23 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box strings::StringAdd); store.register_late_pass(|| box implicit_return::ImplicitReturn); store.register_late_pass(|| box implicit_saturating_sub::ImplicitSaturatingSub); - store.register_late_pass(|| box methods::Methods); + + let parsed_msrv = conf.msrv.as_ref().and_then(|s| { + parse_msrv(s, None, None).or_else(|| { + sess.err(&format!("error reading Clippy's configuration file. `{}` is not a valid Rust version", s)); + None + }) + }); + + let msrv = parsed_msrv.clone(); + store.register_late_pass(move || box methods::Methods::new(msrv.clone())); + let msrv = parsed_msrv.clone(); + store.register_late_pass(move || box matches::Matches::new(msrv.clone())); + let msrv = parsed_msrv.clone(); + store.register_early_pass(move || box manual_non_exhaustive::ManualNonExhaustive::new(msrv.clone())); + let msrv = parsed_msrv; + store.register_late_pass(move || box manual_strip::ManualStrip::new(msrv.clone())); + store.register_late_pass(|| box map_clone::MapClone); store.register_late_pass(|| box map_err_ignore::MapErrIgnore); store.register_late_pass(|| box shadow::Shadow); @@ -983,7 +999,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box types::Casts); let type_complexity_threshold = conf.type_complexity_threshold; store.register_late_pass(move || box types::TypeComplexity::new(type_complexity_threshold)); - store.register_late_pass(|| box matches::Matches::default()); store.register_late_pass(|| box minmax::MinMaxPass); store.register_late_pass(|| box open_options::OpenOptions); store.register_late_pass(|| box zero_div_zero::ZeroDiv); @@ -1144,7 +1159,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box if_let_mutex::IfLetMutex); store.register_late_pass(|| box mut_mutex_lock::MutMutexLock); store.register_late_pass(|| box match_on_vec_items::MatchOnVecItems); - store.register_early_pass(|| box manual_non_exhaustive::ManualNonExhaustive); store.register_late_pass(|| box manual_async_fn::ManualAsyncFn); store.register_early_pass(|| box redundant_field_names::RedundantFieldNames); store.register_late_pass(|| box vec_resize_to_zero::VecResizeToZero); @@ -1166,7 +1180,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box manual_ok_or::ManualOkOr); store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); - store.register_late_pass(|| box manual_strip::ManualStrip); store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem); let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::>(); store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods)); diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index a1450b0d5fe..4762ba16ac7 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -1,11 +1,20 @@ -use crate::utils::{snippet_opt, span_lint_and_then}; +use crate::utils::{get_inner_attr, meets_msrv, snippet_opt, span_lint_and_then}; use if_chain::if_chain; use rustc_ast::ast::{Attribute, Item, ItemKind, StructField, Variant, VariantData, VisibilityKind}; use rustc_attr as attr; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Span}; +use semver::{Version, VersionReq}; + +const MANUAL_NON_EXHAUSTIVE_MSRV: Version = Version { + major: 1, + minor: 40, + patch: 0, + pre: Vec::new(), + build: Vec::new(), +}; declare_clippy_lint! { /// **What it does:** Checks for manual implementations of the non-exhaustive pattern. @@ -55,10 +64,26 @@ declare_clippy_lint! { "manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]" } -declare_lint_pass!(ManualNonExhaustive => [MANUAL_NON_EXHAUSTIVE]); +#[derive(Clone)] +pub struct ManualNonExhaustive { + msrv: Option, +} + +impl ManualNonExhaustive { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(ManualNonExhaustive => [MANUAL_NON_EXHAUSTIVE]); impl EarlyLintPass for ManualNonExhaustive { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + if !meets_msrv(self.msrv.as_ref(), &MANUAL_NON_EXHAUSTIVE_MSRV) { + return; + } + match &item.kind { ItemKind::Enum(def, _) => { check_manual_non_exhaustive_enum(cx, item, &def.variants); @@ -73,6 +98,8 @@ impl EarlyLintPass for ManualNonExhaustive { _ => {}, } } + + extract_msrv_attr!(EarlyContext); } fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants: &[Variant]) { diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 4afb0ab3bad..446641ca114 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -1,21 +1,31 @@ use crate::consts::{constant, Constant}; use crate::utils::usage::mutated_variables; use crate::utils::{ - eq_expr_value, higher, match_def_path, multispan_sugg, paths, qpath_res, snippet, span_lint_and_then, + eq_expr_value, get_inner_attr, higher, match_def_path, meets_msrv, multispan_sugg, paths, qpath_res, snippet, + span_lint_and_then, }; use if_chain::if_chain; -use rustc_ast::ast::LitKind; +use rustc_ast::ast::{Attribute, LitKind}; use rustc_hir::def::Res; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::BinOpKind; use rustc_hir::{BorrowKind, Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::ty; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Spanned; use rustc_span::Span; +use semver::{Version, VersionReq}; + +const MANUAL_STRIP_MSRV: Version = Version { + major: 1, + minor: 45, + patch: 0, + pre: Vec::new(), + build: Vec::new(), +}; declare_clippy_lint! { /// **What it does:** @@ -51,7 +61,18 @@ declare_clippy_lint! { "suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing" } -declare_lint_pass!(ManualStrip => [MANUAL_STRIP]); +pub struct ManualStrip { + msrv: Option, +} + +impl ManualStrip { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(ManualStrip => [MANUAL_STRIP]); #[derive(Clone, Copy, Debug, Eq, PartialEq)] enum StripKind { @@ -61,6 +82,10 @@ enum StripKind { impl<'tcx> LateLintPass<'tcx> for ManualStrip { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if !meets_msrv(self.msrv.as_ref(), &MANUAL_STRIP_MSRV) { + return; + } + if_chain! { if let Some((cond, then, _)) = higher::if_block(&expr); if let ExprKind::MethodCall(_, _, [target_arg, pattern], _) = cond.kind; @@ -114,6 +139,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { } } } + + extract_msrv_attr!(LateContext); } // Returns `Some(arg)` if `expr` matches `arg.len()` and `None` otherwise. diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index af59917e801..5c38abbd9c6 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -2,14 +2,14 @@ use crate::consts::{constant, miri_to_const, Constant}; use crate::utils::sugg::Sugg; use crate::utils::usage::is_unused; use crate::utils::{ - expr_block, get_arg_name, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of, is_refutable, - is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, multispan_sugg, remove_blocks, snippet, - snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, - span_lint_and_then, + expr_block, get_arg_name, get_inner_attr, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of, + is_refutable, is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, meets_msrv, multispan_sugg, + remove_blocks, snippet, snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note, + span_lint_and_sugg, span_lint_and_then, }; use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash}; use if_chain::if_chain; -use rustc_ast::ast::LitKind; +use rustc_ast::ast::{Attribute, LitKind}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::def::CtorKind; @@ -23,6 +23,7 @@ use rustc_middle::ty::{self, Ty, TyS}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::{Span, Spanned}; use rustc_span::{sym, Symbol}; +use semver::{Version, VersionReq}; use std::cmp::Ordering; use std::collections::hash_map::Entry; use std::collections::Bound; @@ -527,9 +528,20 @@ declare_clippy_lint! { #[derive(Default)] pub struct Matches { + msrv: Option, infallible_destructuring_match_linted: bool, } +impl Matches { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { + msrv, + ..Matches::default() + } + } +} + impl_lint_pass!(Matches => [ SINGLE_MATCH, MATCH_REF_PATS, @@ -549,6 +561,14 @@ impl_lint_pass!(Matches => [ MATCH_SAME_ARMS, ]); +const MATCH_LIKE_MATCHES_MACRO_MSRV: Version = Version { + major: 1, + minor: 42, + patch: 0, + pre: Vec::new(), + build: Vec::new(), +}; + impl<'tcx> LateLintPass<'tcx> for Matches { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if in_external_macro(cx.sess(), expr.span) || in_macro(expr.span) { @@ -556,7 +576,12 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } redundant_pattern_match::check(cx, expr); - if !check_match_like_matches(cx, expr) { + + if meets_msrv(self.msrv.as_ref(), &MATCH_LIKE_MATCHES_MACRO_MSRV) { + if !check_match_like_matches(cx, expr) { + lint_match_arms(cx, expr); + } + } else { lint_match_arms(cx, expr); } @@ -640,6 +665,8 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } } } + + extract_msrv_attr!(LateContext); } #[rustfmt::skip] diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index fa174404365..6b478986067 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -12,6 +12,7 @@ use std::iter; use bind_instead_of_map::BindInsteadOfMap; use if_chain::if_chain; use rustc_ast::ast; +use rustc_ast::ast::Attribute; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; @@ -20,7 +21,7 @@ use rustc_lint::{LateContext, LateLintPass, Lint, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, TraitRef, Ty, TyS}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_span::symbol::{sym, SymbolStr}; @@ -28,12 +29,14 @@ use crate::consts::{constant, Constant}; use crate::utils::eager_or_lazy::is_lazyness_candidate; use crate::utils::usage::mutated_variables; use crate::utils::{ - contains_ty, get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, in_macro, - is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path, match_qpath, - match_trait_method, match_type, match_var, method_calls, method_chain_args, paths, remove_blocks, return_ty, - single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint, - span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, walk_ptrs_ty_depth, SpanlessEq, + contains_ty, get_arg_name, get_inner_attr, get_parent_expr, get_trait_def_id, has_iter_method, higher, + implements_trait, in_macro, is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, + match_def_path, match_qpath, match_trait_method, match_type, match_var, meets_msrv, method_calls, + method_chain_args, paths, remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability, + snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, + walk_ptrs_ty_depth, SpanlessEq, }; +use semver::{Version, VersionReq}; declare_clippy_lint! { /// **What it does:** Checks for `.unwrap()` calls on `Option`s and on `Result`s. @@ -1404,7 +1407,18 @@ declare_clippy_lint! { "use `.collect()` instead of `::from_iter()`" } -declare_lint_pass!(Methods => [ +pub struct Methods { + msrv: Option, +} + +impl Methods { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(Methods => [ UNWRAP_USED, EXPECT_USED, SHOULD_IMPLEMENT_TRAIT, @@ -1531,8 +1545,12 @@ impl<'tcx> LateLintPass<'tcx> for Methods { check_pointer_offset(cx, expr, arg_lists[0]) }, ["is_file", ..] => lint_filetype_is_file(cx, expr, arg_lists[0]), - ["map", "as_ref"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], false), - ["map", "as_mut"] => lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], true), + ["map", "as_ref"] => { + lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], false, self.msrv.as_ref()) + }, + ["map", "as_mut"] => { + lint_option_as_ref_deref(cx, expr, arg_lists[1], arg_lists[0], true, self.msrv.as_ref()) + }, ["unwrap_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "unwrap_or"), ["get_or_insert_with", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "get_or_insert"), ["ok_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "ok_or"), @@ -1738,6 +1756,8 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } } } + + extract_msrv_attr!(LateContext); } /// Checks for the `OR_FUN_CALL` lint. @@ -3453,6 +3473,14 @@ fn lint_suspicious_map(cx: &LateContext<'_>, expr: &hir::Expr<'_>) { ); } +const OPTION_AS_REF_DEREF_MSRV: Version = Version { + major: 1, + minor: 40, + patch: 0, + pre: Vec::new(), + build: Vec::new(), +}; + /// lint use of `_.as_ref().map(Deref::deref)` for `Option`s fn lint_option_as_ref_deref<'tcx>( cx: &LateContext<'tcx>, @@ -3460,7 +3488,12 @@ fn lint_option_as_ref_deref<'tcx>( as_ref_args: &[hir::Expr<'_>], map_args: &[hir::Expr<'_>], is_mut: bool, + msrv: Option<&VersionReq>, ) { + if !meets_msrv(msrv, &OPTION_AS_REF_DEREF_MSRV) { + return; + } + let same_mutability = |m| (is_mut && m == &hir::Mutability::Mut) || (!is_mut && m == &hir::Mutability::Not); let option_ty = cx.typeck_results().expr_ty(&as_ref_args[0]); diff --git a/clippy_lints/src/utils/attrs.rs b/clippy_lints/src/utils/attrs.rs index e6d41341a55..aed6ee5dc57 100644 --- a/clippy_lints/src/utils/attrs.rs +++ b/clippy_lints/src/utils/attrs.rs @@ -21,6 +21,7 @@ pub const BUILTIN_ATTRIBUTES: &[(&str, DeprecationStatus)] = &[ DeprecationStatus::Replaced("cognitive_complexity"), ), ("dump", DeprecationStatus::None), + ("msrv", DeprecationStatus::None), ]; pub struct LimitStack { @@ -123,6 +124,22 @@ fn parse_attrs(sess: &Session, attrs: &[ast::Attribute], name: &' } } +pub fn get_unique_inner_attr(sess: &Session, attrs: &[ast::Attribute], name: &'static str) -> Option { + let mut unique_attr = None; + for attr in get_attr(sess, attrs, name) { + match attr.style { + ast::AttrStyle::Inner if unique_attr.is_none() => unique_attr = Some(attr.clone()), + ast::AttrStyle::Inner => { + sess.span_err(attr.span, &format!("`{}` is defined multiple times", name)); + }, + ast::AttrStyle::Outer => { + sess.span_err(attr.span, &format!("`{}` cannot be an outer attribute", name)); + }, + } + } + unique_attr +} + /// Return true if the attributes contain any of `proc_macro`, /// `proc_macro_derive` or `proc_macro_attribute`, false otherwise pub fn is_proc_macro(sess: &Session, attrs: &[ast::Attribute]) -> bool { diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 0ac8fff69f0..fc6304118d9 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -106,6 +106,8 @@ macro_rules! define_Conf { pub use self::helpers::Conf; define_Conf! { + /// Lint: MANUAL_NON_EXHAUSTIVE, MANUAL_STRIP, OPTION_AS_REF_DEREF, MATCH_LIKE_MATCHES_MACRO. The minimum rust version that the project supports + (msrv, "msrv": Option, None), /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses (blacklisted_names, "blacklisted_names": Vec, ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()), /// Lint: COGNITIVE_COMPLEXITY. The maximum cognitive complexity a function can have diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index e9c71e23a67..f8e88512048 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -51,6 +51,7 @@ use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable}; +use rustc_session::Session; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::original_sp; use rustc_span::sym as rustc_sym; @@ -58,10 +59,58 @@ use rustc_span::symbol::{self, kw, Symbol}; use rustc_span::{BytePos, Pos, Span, DUMMY_SP}; use rustc_target::abi::Integer; use rustc_trait_selection::traits::query::normalize::AtExt; +use semver::{Version, VersionReq}; use smallvec::SmallVec; use crate::consts::{constant, Constant}; +pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Option { + if let Ok(version) = VersionReq::parse(msrv) { + return Some(version); + } else if let Some(sess) = sess { + if let Some(span) = span { + sess.span_err(span, &format!("`{}` is not a valid Rust version", msrv)); + } + } + None +} + +pub fn meets_msrv(msrv: Option<&VersionReq>, lint_msrv: &Version) -> bool { + msrv.map_or(true, |msrv| !msrv.matches(lint_msrv)) +} + +#[macro_export] +macro_rules! extract_msrv_attr { + (LateContext) => { + fn enter_lint_attrs(&mut self, cx: &rustc_lint::LateContext<'tcx>, attrs: &'tcx [Attribute]) { + match get_inner_attr(cx.sess(), attrs, "msrv") { + Some(msrv_attr) => { + if let Some(msrv) = msrv_attr.value_str() { + self.msrv = crate::utils::parse_msrv(&msrv.to_string(), Some(cx.sess()), Some(msrv_attr.span)); + } else { + cx.sess().span_err(msrv_attr.span, "bad clippy attribute"); + } + }, + _ => (), + } + } + }; + (EarlyContext) => { + fn enter_lint_attrs(&mut self, cx: &rustc_lint::EarlyContext<'tcx>, attrs: &'tcx [Attribute]) { + match get_inner_attr(cx.sess, attrs, "msrv") { + Some(msrv_attr) => { + if let Some(msrv) = msrv_attr.value_str() { + self.msrv = crate::utils::parse_msrv(&msrv.to_string(), Some(cx.sess), Some(msrv_attr.span)); + } else { + cx.sess.span_err(msrv_attr.span, "bad clippy attribute"); + } + }, + _ => (), + } + } + }; +} + /// Returns `true` if the two spans come from differing expansions (i.e., one is /// from a macro and one isn't). #[must_use] diff --git a/tests/ui-toml/invalid_min_rust_version/clippy.toml b/tests/ui-toml/invalid_min_rust_version/clippy.toml new file mode 100644 index 00000000000..088b12b2dac --- /dev/null +++ b/tests/ui-toml/invalid_min_rust_version/clippy.toml @@ -0,0 +1 @@ +msrv = "invalid.version" diff --git a/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.rs b/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.rs new file mode 100644 index 00000000000..2ebf28645e5 --- /dev/null +++ b/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.rs @@ -0,0 +1,3 @@ +#![allow(clippy::redundant_clone)] + +fn main() {} diff --git a/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.stderr b/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.stderr new file mode 100644 index 00000000000..e9d8fd2e0f5 --- /dev/null +++ b/tests/ui-toml/invalid_min_rust_version/invalid_min_rust_version.stderr @@ -0,0 +1,4 @@ +error: error reading Clippy's configuration file. `invalid.version` is not a valid Rust version + +error: aborting due to previous error + diff --git a/tests/ui-toml/min_rust_version/clippy.toml b/tests/ui-toml/min_rust_version/clippy.toml new file mode 100644 index 00000000000..8e17d8074c4 --- /dev/null +++ b/tests/ui-toml/min_rust_version/clippy.toml @@ -0,0 +1 @@ +msrv = "1.0.0" diff --git a/tests/ui-toml/min_rust_version/min_rust_version.rs b/tests/ui-toml/min_rust_version/min_rust_version.rs new file mode 100644 index 00000000000..bc41efa42a1 --- /dev/null +++ b/tests/ui-toml/min_rust_version/min_rust_version.rs @@ -0,0 +1,68 @@ +#![allow(clippy::redundant_clone)] +#![warn(clippy::manual_non_exhaustive)] + +use std::ops::Deref; + +mod enums { + enum E { + A, + B, + #[doc(hidden)] + _C, + } + + // user forgot to remove the marker + #[non_exhaustive] + enum Ep { + A, + B, + #[doc(hidden)] + _C, + } +} + +fn option_as_ref_deref() { + let mut opt = Some(String::from("123")); + + let _ = opt.as_ref().map(String::as_str); + let _ = opt.as_ref().map(|x| x.as_str()); + let _ = opt.as_mut().map(String::as_mut_str); + let _ = opt.as_mut().map(|x| x.as_mut_str()); +} + +fn match_like_matches() { + let _y = match Some(5) { + Some(0) => true, + _ => false, + }; +} + +fn match_same_arms() { + match (1, 2, 3) { + (1, .., 3) => 42, + (.., 3) => 42, //~ ERROR match arms have same body + _ => 0, + }; +} + +fn match_same_arms2() { + let _ = match Some(42) { + Some(_) => 24, + None => 24, //~ ERROR match arms have same body + }; +} + +fn manual_strip_msrv() { + let s = "hello, world!"; + if s.starts_with("hello, ") { + assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); + } +} + +fn main() { + option_as_ref_deref(); + match_like_matches(); + match_same_arms(); + match_same_arms2(); + manual_strip_msrv(); +} 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 a58e7e918e2..af3d9ecf6e8 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 `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`, `third-party` at line 5 column 1 +error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `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`, `third-party` at line 5 column 1 error: aborting due to previous error diff --git a/tests/ui/min_rust_version_attr.rs b/tests/ui/min_rust_version_attr.rs new file mode 100644 index 00000000000..8ed483a3ac6 --- /dev/null +++ b/tests/ui/min_rust_version_attr.rs @@ -0,0 +1,51 @@ +#![allow(clippy::redundant_clone)] +#![feature(custom_inner_attributes)] +#![clippy::msrv = "1.0.0"] + +use std::ops::Deref; + +fn option_as_ref_deref() { + let mut opt = Some(String::from("123")); + + let _ = opt.as_ref().map(String::as_str); + let _ = opt.as_ref().map(|x| x.as_str()); + let _ = opt.as_mut().map(String::as_mut_str); + let _ = opt.as_mut().map(|x| x.as_mut_str()); +} + +fn match_like_matches() { + let _y = match Some(5) { + Some(0) => true, + _ => false, + }; +} + +fn match_same_arms() { + match (1, 2, 3) { + (1, .., 3) => 42, + (.., 3) => 42, //~ ERROR match arms have same body + _ => 0, + }; +} + +fn match_same_arms2() { + let _ = match Some(42) { + Some(_) => 24, + None => 24, //~ ERROR match arms have same body + }; +} + +fn manual_strip_msrv() { + let s = "hello, world!"; + if s.starts_with("hello, ") { + assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); + } +} + +fn main() { + option_as_ref_deref(); + match_like_matches(); + match_same_arms(); + match_same_arms2(); + manual_strip_msrv(); +} diff --git a/tests/ui/min_rust_version_invalid_attr.rs b/tests/ui/min_rust_version_invalid_attr.rs new file mode 100644 index 00000000000..f20841891a7 --- /dev/null +++ b/tests/ui/min_rust_version_invalid_attr.rs @@ -0,0 +1,4 @@ +#![feature(custom_inner_attributes)] +#![clippy::msrv = "invalid.version"] + +fn main() {} diff --git a/tests/ui/min_rust_version_invalid_attr.stderr b/tests/ui/min_rust_version_invalid_attr.stderr new file mode 100644 index 00000000000..6ff88ca56f8 --- /dev/null +++ b/tests/ui/min_rust_version_invalid_attr.stderr @@ -0,0 +1,8 @@ +error: `invalid.version` is not a valid Rust version + --> $DIR/min_rust_version_invalid_attr.rs:2:1 + | +LL | #![clippy::msrv = "invalid.version"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/tests/ui/min_rust_version_no_patch.rs b/tests/ui/min_rust_version_no_patch.rs new file mode 100644 index 00000000000..515fe8f95e9 --- /dev/null +++ b/tests/ui/min_rust_version_no_patch.rs @@ -0,0 +1,14 @@ +#![allow(clippy::redundant_clone)] +#![feature(custom_inner_attributes)] +#![clippy::msrv = "^1.0"] + +fn manual_strip_msrv() { + let s = "hello, world!"; + if s.starts_with("hello, ") { + assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); + } +} + +fn main() { + manual_strip_msrv() +} diff --git a/tests/ui/min_rust_version_outer_attr.rs b/tests/ui/min_rust_version_outer_attr.rs new file mode 100644 index 00000000000..551948bd72e --- /dev/null +++ b/tests/ui/min_rust_version_outer_attr.rs @@ -0,0 +1,4 @@ +#![feature(custom_inner_attributes)] + +#[clippy::msrv = "invalid.version"] +fn main() {} diff --git a/tests/ui/min_rust_version_outer_attr.stderr b/tests/ui/min_rust_version_outer_attr.stderr new file mode 100644 index 00000000000..579ee7a87d2 --- /dev/null +++ b/tests/ui/min_rust_version_outer_attr.stderr @@ -0,0 +1,8 @@ +error: `msrv` cannot be an outer attribute + --> $DIR/min_rust_version_outer_attr.rs:3:1 + | +LL | #[clippy::msrv = "invalid.version"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From d06076c0c56a2b254b061d569d421b887a7c6bbe Mon Sep 17 00:00:00 2001 From: flip1995 Date: Wed, 25 Nov 2020 12:19:42 +0100 Subject: Add test for multiple defined msrv attrs --- tests/ui/min_rust_version_multiple_inner_attr.rs | 11 +++++++ .../ui/min_rust_version_multiple_inner_attr.stderr | 38 ++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 tests/ui/min_rust_version_multiple_inner_attr.rs create mode 100644 tests/ui/min_rust_version_multiple_inner_attr.stderr (limited to 'tests') diff --git a/tests/ui/min_rust_version_multiple_inner_attr.rs b/tests/ui/min_rust_version_multiple_inner_attr.rs new file mode 100644 index 00000000000..e882d5ccf91 --- /dev/null +++ b/tests/ui/min_rust_version_multiple_inner_attr.rs @@ -0,0 +1,11 @@ +#![feature(custom_inner_attributes)] +#![clippy::msrv = "1.40"] +#![clippy::msrv = "=1.35.0"] +#![clippy::msrv = "1.10.1"] + +mod foo { + #![clippy::msrv = "1"] + #![clippy::msrv = "1.0.0"] +} + +fn main() {} diff --git a/tests/ui/min_rust_version_multiple_inner_attr.stderr b/tests/ui/min_rust_version_multiple_inner_attr.stderr new file mode 100644 index 00000000000..e3ff6605cde --- /dev/null +++ b/tests/ui/min_rust_version_multiple_inner_attr.stderr @@ -0,0 +1,38 @@ +error: `msrv` is defined multiple times + --> $DIR/min_rust_version_multiple_inner_attr.rs:3:1 + | +LL | #![clippy::msrv = "=1.35.0"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first definition found here + --> $DIR/min_rust_version_multiple_inner_attr.rs:2:1 + | +LL | #![clippy::msrv = "1.40"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `msrv` is defined multiple times + --> $DIR/min_rust_version_multiple_inner_attr.rs:4:1 + | +LL | #![clippy::msrv = "1.10.1"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first definition found here + --> $DIR/min_rust_version_multiple_inner_attr.rs:2:1 + | +LL | #![clippy::msrv = "1.40"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `msrv` is defined multiple times + --> $DIR/min_rust_version_multiple_inner_attr.rs:8:5 + | +LL | #![clippy::msrv = "1.0.0"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first definition found here + --> $DIR/min_rust_version_multiple_inner_attr.rs:7:5 + | +LL | #![clippy::msrv = "1"] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + -- cgit 1.4.1-3-g733a5 From 2345ef5b1f89d9cac7a6697de08c36bfb771fe12 Mon Sep 17 00:00:00 2001 From: PunitLodha Date: Sat, 14 Nov 2020 19:21:33 +0530 Subject: added lints str_to_string and string_to_string --- clippy_lints/src/deprecated_lints.rs | 20 ------- clippy_lints/src/lib.rs | 22 +++----- clippy_lints/src/strings.rs | 100 ++++++++++++++++++++++++++++++++++- src/lintlist/mod.rs | 14 +++++ tests/ui/deprecated.rs | 2 - tests/ui/deprecated.stderr | 46 ++++++---------- tests/ui/deprecated_old.rs | 2 - tests/ui/deprecated_old.stderr | 30 ++++------- tests/ui/str_to_string.rs | 7 +++ tests/ui/str_to_string.stderr | 19 +++++++ tests/ui/string_to_string.rs | 7 +++ tests/ui/string_to_string.stderr | 11 ++++ 12 files changed, 189 insertions(+), 91 deletions(-) create mode 100644 tests/ui/str_to_string.rs create mode 100644 tests/ui/str_to_string.stderr create mode 100644 tests/ui/string_to_string.rs create mode 100644 tests/ui/string_to_string.stderr (limited to 'tests') diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index 1c3285ed701..bec0c9f93a0 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -51,26 +51,6 @@ declare_deprecated_lint! { "`Vec::as_mut_slice` has been stabilized in 1.7" } -declare_deprecated_lint! { - /// **What it does:** Nothing. This lint has been deprecated. - /// - /// **Deprecation reason:** This used to check for `.to_string()` method calls on values - /// of type `&str`. This is not unidiomatic and with specialization coming, `to_string` could be - /// specialized to be as efficient as `to_owned`. - pub STR_TO_STRING, - "using `str::to_string` is common even today and specialization will likely happen soon" -} - -declare_deprecated_lint! { - /// **What it does:** Nothing. This lint has been deprecated. - /// - /// **Deprecation reason:** This used to check for `.to_string()` method calls on values - /// of type `String`. This is not unidiomatic and with specialization coming, `to_string` could be - /// specialized to be as efficient as `clone`. - pub STRING_TO_STRING, - "using `string::to_string` is common even today and specialization will likely happen soon" -} - declare_deprecated_lint! { /// **What it does:** Nothing. This lint has been deprecated. /// diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 866dae110cc..67a3a3fcf48 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -441,14 +441,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: "clippy::unstable_as_mut_slice", "`Vec::as_mut_slice` has been stabilized in 1.7", ); - store.register_removed( - "clippy::str_to_string", - "using `str::to_string` is common even today and specialization will likely happen soon", - ); - store.register_removed( - "clippy::string_to_string", - "using `string::to_string` is common even today and specialization will likely happen soon", - ); store.register_removed( "clippy::misaligned_transmute", "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr", @@ -840,6 +832,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &strings::STRING_ADD_ASSIGN, &strings::STRING_FROM_UTF8_AS_BYTES, &strings::STRING_LIT_AS_BYTES, + &strings::STRING_TO_STRING, + &strings::STR_TO_STRING, &suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL, &suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL, &swap::ALMOST_SWAPPED, @@ -1186,6 +1180,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box asm_syntax::InlineAsmX86AttSyntax); store.register_early_pass(|| box asm_syntax::InlineAsmX86IntelSyntax); store.register_late_pass(|| box undropped_manually_drops::UndroppedManuallyDrops); + store.register_late_pass(|| box strings::StrToString); + store.register_late_pass(|| box strings::StringToString); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ @@ -1228,6 +1224,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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(&unwrap_in_result::UNWRAP_IN_RESULT), LintId::of(&verbose_file_reads::VERBOSE_FILE_READS), @@ -1943,14 +1941,6 @@ fn register_removed_non_tool_lints(store: &mut rustc_lint::LintStore) { "unstable_as_mut_slice", "`Vec::as_mut_slice` has been stabilized in 1.7", ); - store.register_removed( - "str_to_string", - "using `str::to_string` is common even today and specialization will likely happen soon", - ); - store.register_removed( - "string_to_string", - "using `string::to_string` is common even today and specialization will likely happen soon", - ); store.register_removed( "misaligned_transmute", "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr", diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index ede37624f71..42c45be3b45 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -2,6 +2,7 @@ use rustc_errors::Applicability; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, QPath}; 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::Spanned; use rustc_span::sym; @@ -11,7 +12,7 @@ use if_chain::if_chain; use crate::utils::SpanlessEq; use crate::utils::{ get_parent_expr, is_allowed, is_type_diagnostic_item, match_function_call, method_calls, paths, span_lint, - span_lint_and_sugg, + span_lint_and_help, span_lint_and_sugg, }; declare_clippy_lint! { @@ -289,3 +290,100 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { } } } + +declare_clippy_lint! { + /// **What it does:** This lint checks for `.to_string()` method calls on values of type `&str`. + /// + /// **Why is this bad?** The `to_string` method is also used on other types to convert them to a string. + /// When called on a `&str` it turns the `&str` into the owned variant `String`, which can be better + /// expressed with `.to_owned()`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // example code where clippy issues a warning + /// let _ = "str".to_string(); + /// ``` + /// Use instead: + /// ```rust + /// // example code which does not raise clippy warning + /// let _ = "str".to_owned(); + /// ``` + pub STR_TO_STRING, + restriction, + "using `to_string()` on a `&str`, which should be `to_owned()`" +} + +declare_lint_pass!(StrToString => [STR_TO_STRING]); + +impl LateLintPass<'_> for StrToString { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { + if_chain! { + if let ExprKind::MethodCall(path, _, args, _) = &expr.kind; + if path.ident.name == sym!(to_string); + let ty = cx.typeck_results().expr_ty(&args[0]); + if let ty::Ref(_, ty, ..) = ty.kind(); + if *ty.kind() == ty::Str; + then { + span_lint_and_help( + cx, + STR_TO_STRING, + expr.span, + "`to_string()` called on a `&str`", + None, + "consider using `.to_owned()`", + ); + } + } + } +} + +declare_clippy_lint! { + /// **What it does:** This lint checks for `.to_string()` method calls on values of type `String`. + /// + /// **Why is this bad?** The `to_string` method is also used on other types to convert them to a string. + /// When called on a `String` it only clones the `String`, which can be better expressed with `.clone()`. + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // example code where clippy issues a warning + /// let msg = String::from("Hello World"); + /// let _ = msg.to_string(); + /// ``` + /// Use instead: + /// ```rust + /// // example code which does not raise clippy warning + /// let msg = String::from("Hello World"); + /// let _ = msg.clone(); + /// ``` + pub STRING_TO_STRING, + restriction, + "using `to_string()` on a `String`, which should be `clone()`" +} + +declare_lint_pass!(StringToString => [STRING_TO_STRING]); + +impl LateLintPass<'_> for StringToString { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { + if_chain! { + if let ExprKind::MethodCall(path, _, args, _) = &expr.kind; + if path.ident.name == sym!(to_string); + let ty = cx.typeck_results().expr_ty(&args[0]); + if is_type_diagnostic_item(cx, ty, sym!(string_type)); + then { + span_lint_and_help( + cx, + STRING_TO_STRING, + expr.span, + "`to_string()` called on a `String`", + None, + "consider using `.clone()`", + ); + } + } + } +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 1d906d20ad4..a104f687bdf 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2237,6 +2237,13 @@ vec![ deprecation: None, module: "stable_sort_primitive", }, + Lint { + name: "str_to_string", + group: "restriction", + desc: "using `to_string()` on a `&str`, which should be `to_owned()`", + deprecation: None, + module: "strings", + }, Lint { name: "string_add", group: "restriction", @@ -2272,6 +2279,13 @@ vec![ deprecation: None, module: "strings", }, + Lint { + name: "string_to_string", + group: "restriction", + desc: "using `to_string()` on a `String`, which should be `clone()`", + deprecation: None, + module: "strings", + }, Lint { name: "struct_excessive_bools", group: "pedantic", diff --git a/tests/ui/deprecated.rs b/tests/ui/deprecated.rs index 4cbc5630d75..e1ee8dbca2c 100644 --- a/tests/ui/deprecated.rs +++ b/tests/ui/deprecated.rs @@ -1,5 +1,3 @@ -#[warn(clippy::str_to_string)] -#[warn(clippy::string_to_string)] #[warn(clippy::unstable_as_slice)] #[warn(clippy::unstable_as_mut_slice)] #[warn(clippy::misaligned_transmute)] diff --git a/tests/ui/deprecated.stderr b/tests/ui/deprecated.stderr index a348d01d734..edbb891afe0 100644 --- a/tests/ui/deprecated.stderr +++ b/tests/ui/deprecated.stderr @@ -1,88 +1,76 @@ -error: lint `clippy::str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon` - --> $DIR/deprecated.rs:1:8 - | -LL | #[warn(clippy::str_to_string)] - | ^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D renamed-and-removed-lints` implied by `-D warnings` - -error: lint `clippy::string_to_string` has been removed: `using `string::to_string` is common even today and specialization will likely happen soon` - --> $DIR/deprecated.rs:2:8 - | -LL | #[warn(clippy::string_to_string)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ - error: lint `clippy::unstable_as_slice` has been removed: ``Vec::as_slice` has been stabilized in 1.7` - --> $DIR/deprecated.rs:3:8 + --> $DIR/deprecated.rs:1:8 | LL | #[warn(clippy::unstable_as_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D renamed-and-removed-lints` implied by `-D warnings` error: lint `clippy::unstable_as_mut_slice` has been removed: ``Vec::as_mut_slice` has been stabilized in 1.7` - --> $DIR/deprecated.rs:4:8 + --> $DIR/deprecated.rs:2:8 | LL | #[warn(clippy::unstable_as_mut_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::misaligned_transmute` has been removed: `this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr` - --> $DIR/deprecated.rs:5:8 + --> $DIR/deprecated.rs:3:8 | LL | #[warn(clippy::misaligned_transmute)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::unused_collect` has been removed: ``collect` has been marked as #[must_use] in rustc and that covers all cases of this lint` - --> $DIR/deprecated.rs:6:8 + --> $DIR/deprecated.rs:4:8 | LL | #[warn(clippy::unused_collect)] | ^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::invalid_ref` has been removed: `superseded by rustc lint `invalid_value`` - --> $DIR/deprecated.rs:7:8 + --> $DIR/deprecated.rs:5:8 | LL | #[warn(clippy::invalid_ref)] | ^^^^^^^^^^^^^^^^^^^ error: lint `clippy::into_iter_on_array` has been removed: `this lint has been uplifted to rustc and is now called `array_into_iter`` - --> $DIR/deprecated.rs:8:8 + --> $DIR/deprecated.rs:6:8 | LL | #[warn(clippy::into_iter_on_array)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::unused_label` has been removed: `this lint has been uplifted to rustc and is now called `unused_labels`` - --> $DIR/deprecated.rs:9:8 + --> $DIR/deprecated.rs:7:8 | LL | #[warn(clippy::unused_label)] | ^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::regex_macro` has been removed: `the regex! macro has been removed from the regex crate in 2018` - --> $DIR/deprecated.rs:10:8 + --> $DIR/deprecated.rs:8:8 | LL | #[warn(clippy::regex_macro)] | ^^^^^^^^^^^^^^^^^^^ error: lint `clippy::drop_bounds` has been removed: `this lint has been uplifted to rustc and is now called `drop_bounds`` - --> $DIR/deprecated.rs:11:8 + --> $DIR/deprecated.rs:9:8 | LL | #[warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ error: lint `clippy::temporary_cstring_as_ptr` has been removed: `this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr`` - --> $DIR/deprecated.rs:12:8 + --> $DIR/deprecated.rs:10:8 | LL | #[warn(clippy::temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::panic_params` has been removed: `this lint has been uplifted to rustc and is now called `panic_fmt`` - --> $DIR/deprecated.rs:13:8 + --> $DIR/deprecated.rs:11:8 | LL | #[warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon` +error: lint `clippy::unstable_as_slice` has been removed: ``Vec::as_slice` has been stabilized in 1.7` --> $DIR/deprecated.rs:1:8 | -LL | #[warn(clippy::str_to_string)] - | ^^^^^^^^^^^^^^^^^^^^^ +LL | #[warn(clippy::unstable_as_slice)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 14 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/deprecated_old.rs b/tests/ui/deprecated_old.rs index 2e5c5b7ead1..e89dca4fcfd 100644 --- a/tests/ui/deprecated_old.rs +++ b/tests/ui/deprecated_old.rs @@ -1,5 +1,3 @@ -#[warn(str_to_string)] -#[warn(string_to_string)] #[warn(unstable_as_slice)] #[warn(unstable_as_mut_slice)] #[warn(misaligned_transmute)] diff --git a/tests/ui/deprecated_old.stderr b/tests/ui/deprecated_old.stderr index ff3e9e8fcf3..2fe1facf0c7 100644 --- a/tests/ui/deprecated_old.stderr +++ b/tests/ui/deprecated_old.stderr @@ -1,40 +1,28 @@ -error: lint `str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon` - --> $DIR/deprecated_old.rs:1:8 - | -LL | #[warn(str_to_string)] - | ^^^^^^^^^^^^^ - | - = note: `-D renamed-and-removed-lints` implied by `-D warnings` - -error: lint `string_to_string` has been removed: `using `string::to_string` is common even today and specialization will likely happen soon` - --> $DIR/deprecated_old.rs:2:8 - | -LL | #[warn(string_to_string)] - | ^^^^^^^^^^^^^^^^ - error: lint `unstable_as_slice` has been removed: ``Vec::as_slice` has been stabilized in 1.7` - --> $DIR/deprecated_old.rs:3:8 + --> $DIR/deprecated_old.rs:1:8 | LL | #[warn(unstable_as_slice)] | ^^^^^^^^^^^^^^^^^ + | + = note: `-D renamed-and-removed-lints` implied by `-D warnings` error: lint `unstable_as_mut_slice` has been removed: ``Vec::as_mut_slice` has been stabilized in 1.7` - --> $DIR/deprecated_old.rs:4:8 + --> $DIR/deprecated_old.rs:2:8 | LL | #[warn(unstable_as_mut_slice)] | ^^^^^^^^^^^^^^^^^^^^^ error: lint `misaligned_transmute` has been removed: `this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr` - --> $DIR/deprecated_old.rs:5:8 + --> $DIR/deprecated_old.rs:3:8 | LL | #[warn(misaligned_transmute)] | ^^^^^^^^^^^^^^^^^^^^ -error: lint `str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon` +error: lint `unstable_as_slice` has been removed: ``Vec::as_slice` has been stabilized in 1.7` --> $DIR/deprecated_old.rs:1:8 | -LL | #[warn(str_to_string)] - | ^^^^^^^^^^^^^ +LL | #[warn(unstable_as_slice)] + | ^^^^^^^^^^^^^^^^^ -error: aborting due to 6 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/str_to_string.rs b/tests/ui/str_to_string.rs new file mode 100644 index 00000000000..08f73402518 --- /dev/null +++ b/tests/ui/str_to_string.rs @@ -0,0 +1,7 @@ +#![warn(clippy::str_to_string)] + +fn main() { + let hello = "hello world".to_string(); + let msg = &hello[..]; + msg.to_string(); +} diff --git a/tests/ui/str_to_string.stderr b/tests/ui/str_to_string.stderr new file mode 100644 index 00000000000..b1f73eda5d2 --- /dev/null +++ b/tests/ui/str_to_string.stderr @@ -0,0 +1,19 @@ +error: `to_string()` called on a `&str` + --> $DIR/str_to_string.rs:4:17 + | +LL | let hello = "hello world".to_string(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::str-to-string` implied by `-D warnings` + = help: consider using `.to_owned()` + +error: `to_string()` called on a `&str` + --> $DIR/str_to_string.rs:6:5 + | +LL | msg.to_string(); + | ^^^^^^^^^^^^^^^ + | + = help: consider using `.to_owned()` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/string_to_string.rs b/tests/ui/string_to_string.rs new file mode 100644 index 00000000000..4c66855f709 --- /dev/null +++ b/tests/ui/string_to_string.rs @@ -0,0 +1,7 @@ +#![warn(clippy::string_to_string)] +#![allow(clippy::redundant_clone)] + +fn main() { + let mut message = String::from("Hello"); + let mut v = message.to_string(); +} diff --git a/tests/ui/string_to_string.stderr b/tests/ui/string_to_string.stderr new file mode 100644 index 00000000000..1ebd17999bd --- /dev/null +++ b/tests/ui/string_to_string.stderr @@ -0,0 +1,11 @@ +error: `to_string()` called on a `String` + --> $DIR/string_to_string.rs:6:17 + | +LL | let mut v = message.to_string(); + | ^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::string-to-string` implied by `-D warnings` + = help: consider using `.clone()` + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From 3b53de6b36081646315c0721638c4318c28b6982 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 21 Nov 2020 21:08:32 -0300 Subject: Fix rust-lang/rust#79255 - Incorrect try suggestion for unnecessary float literal cast ending in dot --- clippy_lints/src/types.rs | 2 +- tests/ui/unnecessary_cast_fixable.fixed | 2 ++ tests/ui/unnecessary_cast_fixable.rs | 2 ++ tests/ui/unnecessary_cast_fixable.stderr | 32 ++++++++++++++++++++++---------- 4 files changed, 27 insertions(+), 11 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index f0e10e374e1..f3cad66cbf3 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -1711,7 +1711,7 @@ fn show_unnecessary_cast(cx: &LateContext<'_>, expr: &Expr<'_>, literal_str: &st expr.span, &format!("casting {} literal to `{}` is unnecessary", literal_kind_name, cast_to), "try", - format!("{}_{}", literal_str, cast_to), + format!("{}_{}", literal_str.trim_end_matches('.'), cast_to), Applicability::MachineApplicable, ); } diff --git a/tests/ui/unnecessary_cast_fixable.fixed b/tests/ui/unnecessary_cast_fixable.fixed index 350da4965d1..7fbce58a82f 100644 --- a/tests/ui/unnecessary_cast_fixable.fixed +++ b/tests/ui/unnecessary_cast_fixable.fixed @@ -11,6 +11,8 @@ fn main() { let _ = -100_f32; let _ = -100_f64; let _ = -100_f64; + 100_f32; + 100_f64; // Should not trigger #[rustfmt::skip] let v = vec!(1); diff --git a/tests/ui/unnecessary_cast_fixable.rs b/tests/ui/unnecessary_cast_fixable.rs index ad2fb2e6289..a71363ea4d2 100644 --- a/tests/ui/unnecessary_cast_fixable.rs +++ b/tests/ui/unnecessary_cast_fixable.rs @@ -11,6 +11,8 @@ fn main() { let _ = -100 as f32; let _ = -100 as f64; let _ = -100_i32 as f64; + 100. as f32; + 100. as f64; // Should not trigger #[rustfmt::skip] let v = vec!(1); diff --git a/tests/ui/unnecessary_cast_fixable.stderr b/tests/ui/unnecessary_cast_fixable.stderr index 5a210fc8909..3695a8f819c 100644 --- a/tests/ui/unnecessary_cast_fixable.stderr +++ b/tests/ui/unnecessary_cast_fixable.stderr @@ -36,59 +36,71 @@ error: casting integer literal to `f64` is unnecessary LL | let _ = -100_i32 as f64; | ^^^^^^^^^^^^^^^ help: try: `-100_f64` +error: casting float literal to `f32` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:14:5 + | +LL | 100. as f32; + | ^^^^^^^^^^^ help: try: `100_f32` + +error: casting float literal to `f64` is unnecessary + --> $DIR/unnecessary_cast_fixable.rs:15:5 + | +LL | 100. as f64; + | ^^^^^^^^^^^ help: try: `100_f64` + error: casting integer literal to `u32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:25:5 + --> $DIR/unnecessary_cast_fixable.rs:27:5 | LL | 1 as u32; | ^^^^^^^^ help: try: `1_u32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:26:5 + --> $DIR/unnecessary_cast_fixable.rs:28:5 | LL | 0x10 as i32; | ^^^^^^^^^^^ help: try: `0x10_i32` error: casting integer literal to `usize` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:27:5 + --> $DIR/unnecessary_cast_fixable.rs:29:5 | LL | 0b10 as usize; | ^^^^^^^^^^^^^ help: try: `0b10_usize` error: casting integer literal to `u16` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:28:5 + --> $DIR/unnecessary_cast_fixable.rs:30:5 | LL | 0o73 as u16; | ^^^^^^^^^^^ help: try: `0o73_u16` error: casting integer literal to `u32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:29:5 + --> $DIR/unnecessary_cast_fixable.rs:31:5 | LL | 1_000_000_000 as u32; | ^^^^^^^^^^^^^^^^^^^^ help: try: `1_000_000_000_u32` error: casting float literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:31:5 + --> $DIR/unnecessary_cast_fixable.rs:33:5 | LL | 1.0 as f64; | ^^^^^^^^^^ help: try: `1.0_f64` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:32:5 + --> $DIR/unnecessary_cast_fixable.rs:34:5 | LL | 0.5 as f32; | ^^^^^^^^^^ help: try: `0.5_f32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:36:13 + --> $DIR/unnecessary_cast_fixable.rs:38:13 | LL | let _ = -1 as i32; | ^^^^^^^^^ help: try: `-1_i32` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:37:13 + --> $DIR/unnecessary_cast_fixable.rs:39:13 | LL | let _ = -1.0 as f32; | ^^^^^^^^^^^ help: try: `-1.0_f32` -error: aborting due to 15 previous errors +error: aborting due to 17 previous errors -- cgit 1.4.1-3-g733a5 From 82a7068007a1490b43a2eb4e70e0f70de384a9ae Mon Sep 17 00:00:00 2001 From: Markus Legner Date: Sat, 21 Nov 2020 12:28:53 +0100 Subject: Trigger modulo_one lint also on -1. --- clippy_lints/src/misc.rs | 30 ++++++++++++++++++-------- tests/ui/modulo_one.rs | 11 +++++++++- tests/ui/modulo_one.stderr | 54 +++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 80 insertions(+), 15 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 308e92057b7..f16feb9b1ba 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -18,7 +18,7 @@ use crate::utils::sugg::Sugg; use crate::utils::{ get_item_name, get_parent_expr, higher, implements_trait, in_constant, is_integer_const, iter_input_pats, last_path_segment, match_qpath, match_trait_method, paths, snippet, snippet_opt, span_lint, span_lint_and_sugg, - span_lint_and_then, span_lint_hir_and_then, SpanlessEq, + span_lint_and_then, span_lint_hir_and_then, SpanlessEq, unsext, }; declare_clippy_lint! { @@ -139,12 +139,14 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for getting the remainder of a division by one. + /// **What it does:** Checks for getting the remainder of a division by one or minus + /// one. /// - /// **Why is this bad?** The result can only ever be zero. No one will write - /// such code deliberately, unless trying to win an Underhanded Rust - /// Contest. Even for that contest, it's probably a bad idea. Use something more - /// underhanded. + /// **Why is this bad?** The result for a divisor of one can only ever be zero; for + /// minus one it can cause panic/overflow (if the left operand is the minimal value of + /// the respective integer type) or results in zero. No one will write such code + /// deliberately, unless trying to win an Underhanded Rust Contest. Even for that + /// contest, it's probably a bad idea. Use something more underhanded. /// /// **Known problems:** None. /// @@ -152,10 +154,11 @@ declare_clippy_lint! { /// ```rust /// # let x = 1; /// let a = x % 1; + /// let a = x % -1; /// ``` pub MODULO_ONE, correctness, - "taking a number modulo 1, which always returns 0" + "taking a number modulo +/-1, which can either panic/overflow or always returns 0" } declare_clippy_lint! { @@ -429,8 +432,17 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { } diag.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`"); }); - } else if op == BinOpKind::Rem && is_integer_const(cx, right, 1) { - span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0"); + } else if op == BinOpKind::Rem { + if is_integer_const(cx, right, 1) { + span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0"); + } + + if let ty::Int(ity) = cx.typeck_results().expr_ty(right).kind() { + if is_integer_const(cx, right, unsext(cx.tcx, -1, *ity)) { + span_lint(cx, MODULO_ONE, expr.span, + "any number modulo -1 will panic/overflow or result in 0"); + } + }; } }, _ => {}, diff --git a/tests/ui/modulo_one.rs b/tests/ui/modulo_one.rs index cc8c8e7cdae..678a312f66e 100644 --- a/tests/ui/modulo_one.rs +++ b/tests/ui/modulo_one.rs @@ -2,13 +2,22 @@ #![allow(clippy::no_effect, clippy::unnecessary_operation)] static STATIC_ONE: usize = 2 - 1; +static STATIC_NEG_ONE: i64 = 1 - 2; fn main() { 10 % 1; + 10 % -1; 10 % 2; + i32::MIN % (-1); // also caught by rustc const ONE: u32 = 1 * 1; + const NEG_ONE: i64 = 1 - 2; + const INT_MIN: i64 = i64::MIN; 2 % ONE; - 5 % STATIC_ONE; + 5 % STATIC_ONE; // NOT caught by lint + 2 % NEG_ONE; + 5 % STATIC_NEG_ONE; // NOT caught by lint + INT_MIN % NEG_ONE; // also caught by rustc + INT_MIN % STATIC_NEG_ONE; // ONLY caught by rustc } diff --git a/tests/ui/modulo_one.stderr b/tests/ui/modulo_one.stderr index 6bee68360b6..2b2c6997338 100644 --- a/tests/ui/modulo_one.stderr +++ b/tests/ui/modulo_one.stderr @@ -1,13 +1,45 @@ +error: this arithmetic operation will overflow + --> $DIR/modulo_one.rs:11:5 + | +LL | i32::MIN % (-1); // also caught by rustc + | ^^^^^^^^^^^^^^^ attempt to compute the remainder of `i32::MIN % -1_i32`, which would overflow + | + = note: `#[deny(arithmetic_overflow)]` on by default + +error: this arithmetic operation will overflow + --> $DIR/modulo_one.rs:21:5 + | +LL | INT_MIN % NEG_ONE; // also caught by rustc + | ^^^^^^^^^^^^^^^^^ attempt to compute the remainder of `i64::MIN % -1_i64`, which would overflow + +error: this arithmetic operation will overflow + --> $DIR/modulo_one.rs:22:5 + | +LL | INT_MIN % STATIC_NEG_ONE; // ONLY caught by rustc + | ^^^^^^^^^^^^^^^^^^^^^^^^ attempt to compute the remainder of `i64::MIN % -1_i64`, which would overflow + error: any number modulo 1 will be 0 - --> $DIR/modulo_one.rs:7:5 + --> $DIR/modulo_one.rs:8:5 | LL | 10 % 1; | ^^^^^^ | = note: `-D clippy::modulo-one` implied by `-D warnings` +error: any number modulo -1 will panic/overflow or result in 0 + --> $DIR/modulo_one.rs:9:5 + | +LL | 10 % -1; + | ^^^^^^^ + +error: any number modulo -1 will panic/overflow or result in 0 + --> $DIR/modulo_one.rs:11:5 + | +LL | i32::MIN % (-1); // also caught by rustc + | ^^^^^^^^^^^^^^^ + error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/modulo_one.rs:10:22 + --> $DIR/modulo_one.rs:13:22 | LL | const ONE: u32 = 1 * 1; | ^^^^^ @@ -15,16 +47,28 @@ LL | const ONE: u32 = 1 * 1; = note: `-D clippy::identity-op` implied by `-D warnings` error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/modulo_one.rs:10:22 + --> $DIR/modulo_one.rs:13:22 | LL | const ONE: u32 = 1 * 1; | ^^^^^ error: any number modulo 1 will be 0 - --> $DIR/modulo_one.rs:12:5 + --> $DIR/modulo_one.rs:17:5 | LL | 2 % ONE; | ^^^^^^^ -error: aborting due to 4 previous errors +error: any number modulo -1 will panic/overflow or result in 0 + --> $DIR/modulo_one.rs:19:5 + | +LL | 2 % NEG_ONE; + | ^^^^^^^^^^^ + +error: any number modulo -1 will panic/overflow or result in 0 + --> $DIR/modulo_one.rs:21:5 + | +LL | INT_MIN % NEG_ONE; // also caught by rustc + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 11 previous errors -- cgit 1.4.1-3-g733a5 From 0387981f2b5e41c982ec4a1b102f0c54997361ff Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sat, 17 Oct 2020 19:48:40 +0200 Subject: Add --no-deps command-line argument --- clippy_workspace_tests/path_dep/Cargo.toml | 3 ++ clippy_workspace_tests/path_dep/src/lib.rs | 6 +++ clippy_workspace_tests/subcrate/Cargo.toml | 3 ++ src/driver.rs | 37 +++++++++------- tests/dogfood.rs | 71 +++++++++++++++++++++++++++++- 5 files changed, 104 insertions(+), 16 deletions(-) create mode 100644 clippy_workspace_tests/path_dep/Cargo.toml create mode 100644 clippy_workspace_tests/path_dep/src/lib.rs (limited to 'tests') diff --git a/clippy_workspace_tests/path_dep/Cargo.toml b/clippy_workspace_tests/path_dep/Cargo.toml new file mode 100644 index 00000000000..85a91cd2dec --- /dev/null +++ b/clippy_workspace_tests/path_dep/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "path_dep" +version = "0.1.0" diff --git a/clippy_workspace_tests/path_dep/src/lib.rs b/clippy_workspace_tests/path_dep/src/lib.rs new file mode 100644 index 00000000000..35ce524f2b1 --- /dev/null +++ b/clippy_workspace_tests/path_dep/src/lib.rs @@ -0,0 +1,6 @@ +#![deny(clippy::empty_loop)] + +#[cfg(feature = "primary_package_test")] +pub fn lint_me() { + loop {} +} diff --git a/clippy_workspace_tests/subcrate/Cargo.toml b/clippy_workspace_tests/subcrate/Cargo.toml index 83ea5868160..45362c11b85 100644 --- a/clippy_workspace_tests/subcrate/Cargo.toml +++ b/clippy_workspace_tests/subcrate/Cargo.toml @@ -1,3 +1,6 @@ [package] name = "subcrate" version = "0.1.0" + +[dependencies] +path_dep = { path = "../path_dep" } diff --git a/src/driver.rs b/src/driver.rs index ef31c72481a..bbe9ce73936 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -277,27 +277,34 @@ pub fn main() { args.extend(vec!["--sysroot".into(), sys_root]); }; + let mut no_deps = false; + let clippy_args = env::var("CLIPPY_ARGS") + .unwrap_or_default() + .split("__CLIPPY_HACKERY__") + .filter_map(|s| match s { + "" => None, + "--no-deps" => { + no_deps = true; + None + }, + _ => Some(s.to_string()), + }) + .chain(vec!["--cfg".into(), r#"feature="cargo-clippy""#.into()]) + .collect::>(); + // this check ensures that dependencies are built but not linted and the final // crate is linted but not built - let clippy_enabled = env::var("CLIPPY_TESTS").map_or(false, |val| val == "true") - || arg_value(&orig_args, "--cap-lints", |val| val == "allow").is_none(); - - if clippy_enabled { - args.extend(vec!["--cfg".into(), r#"feature="cargo-clippy""#.into()]); - if let Ok(extra_args) = env::var("CLIPPY_ARGS") { - args.extend(extra_args.split("__CLIPPY_HACKERY__").filter_map(|s| { - if s.is_empty() { - None - } else { - Some(s.to_string()) - } - })); - } + let clippy_disabled = env::var("CLIPPY_TESTS").map_or(false, |val| val != "true") + || arg_value(&orig_args, "--cap-lints", |val| val == "allow").is_some() + || no_deps && env::var("CARGO_PRIMARY_PACKAGE").is_err(); + + if !clippy_disabled { + args.extend(clippy_args); } let mut clippy = ClippyCallbacks; let mut default = DefaultCallbacks; let callbacks: &mut (dyn rustc_driver::Callbacks + Send) = - if clippy_enabled { &mut clippy } else { &mut default }; + if clippy_disabled { &mut default } else { &mut clippy }; rustc_driver::RunCompiler::new(&args, callbacks).run() })) } diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 48e0478f169..b166a6b7c1f 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -3,7 +3,7 @@ #![feature(once_cell)] use std::lazy::SyncLazy; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::process::Command; mod cargo; @@ -41,12 +41,77 @@ fn dogfood_clippy() { #[test] fn dogfood_subprojects() { + fn test_no_deps_ignores_path_deps_in_workspaces() { + fn clean(cwd: &Path, target_dir: &Path) { + Command::new("cargo") + .current_dir(cwd) + .env("CARGO_TARGET_DIR", target_dir) + .arg("clean") + .args(&["-p", "subcrate"]) + .args(&["-p", "path_dep"]) + .output() + .unwrap(); + } + + if cargo::is_rustc_test_suite() { + return; + } + let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let target_dir = root.join("target").join("dogfood"); + let cwd = root.join("clippy_workspace_tests"); + + // Make sure we start with a clean state + clean(&cwd, &target_dir); + + // `path_dep` is a path dependency of `subcrate` that would trigger a denied lint. + // Make sure that with the `--no-deps` argument Clippy does not run on `path_dep`. + let output = Command::new(&*CLIPPY_PATH) + .current_dir(&cwd) + .env("CLIPPY_DOGFOOD", "1") + .env("CARGO_INCREMENTAL", "0") + .arg("clippy") + .args(&["-p", "subcrate"]) + .arg("--") + .arg("--no-deps") + .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir + .args(&["--cfg", r#"feature="primary_package_test""#]) + .output() + .unwrap(); + println!("status: {}", output.status); + println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); + println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); + + assert!(output.status.success()); + + // Make sure we start with a clean state + clean(&cwd, &target_dir); + + // Test that without the `--no-deps` argument, `path_dep` is linted. + let output = Command::new(&*CLIPPY_PATH) + .current_dir(&cwd) + .env("CLIPPY_DOGFOOD", "1") + .env("CARGO_INCREMENTAL", "0") + .arg("clippy") + .args(&["-p", "subcrate"]) + .arg("--") + .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir + .args(&["--cfg", r#"feature="primary_package_test""#]) + .output() + .unwrap(); + println!("status: {}", output.status); + println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); + println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); + + assert!(!output.status.success()); + } + // run clippy on remaining subprojects and fail the test if lint warnings are reported if cargo::is_rustc_test_suite() { return; } let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + // NOTE: `path_dep` crate is omitted on purpose here for d in &[ "clippy_workspace_tests", "clippy_workspace_tests/src", @@ -72,4 +137,8 @@ fn dogfood_subprojects() { assert!(output.status.success()); } + + // NOTE: Since tests run in parallel we can't run cargo commands on the same workspace at the + // same time, so we test this immediately after the dogfood for workspaces. + test_no_deps_ignores_path_deps_in_workspaces(); } -- cgit 1.4.1-3-g733a5 From af1cc5c91131e0ec30f0f34691a5e635350295a1 Mon Sep 17 00:00:00 2001 From: Ryan1729 Date: Sat, 7 Nov 2020 16:00:42 -0700 Subject: add suspicious_operation_groupings lint run `cargo dev new_lint --category correctness --name suspicious_chained_operators --pass early` add (currently failing) tests for suspicious_chained_operators add some tests to answer a question that came up during implementation write usage code for functions we'll need to find or create Complete left-right tracking TODO get it compiling with several `todo!` invocations. refactor to a set of incomplete functions that don't expect to be able to edit a `Span` create placeholder for `suggestion_with_swapped_ident` function and correct some comments add `inside_larger_boolean_expression` test fill out `get_ident` and `suggestion_with_swapped_ident` Implementi the `IdentIter` start on implementing the `IdentIter` handle the `ExprKind::Path` case in `IdentIter` on second thought, make the iterator type dynamic so we don't need an explicit type for each one we will need handle `ExprKind::MacCall` in `IdentIter` Try handling `box x` expressions restructure `IdentIter` set `self.done` when returning `None` Handle `ExprKind::Array` reduce duplication with a macro that we expect to use several more times handle ExprKind::Call add `new_p` convenience method handle `MethodCall` handle `Tup` and `Binary` handle `Unary` simplify by not returning an additional `Expr` from the `IdentIter` add cross product test against false positives rename suspicious_chained_operators to suspicious_operation_groupings within files For the record, the exact commands run were: find . -type f -name "*.md" -exec sed -i 's/suspicious_chained_operators/suspicious_operation_groupings/g' {} + find . -type f -name "*.rs" -exec sed -i 's/suspicious_chained_operators/suspicious_operation_groupings/g' {} + find . -type f -name "*.rs" -exec sed -i 's/SUSPICIOUS_CHAINED_OPERATORS/SUSPICIOUS_OPERATION_GROUPINGS/g' {} + find . -type f -name "*.rs" -exec sed -i 's/SuspiciousChainedOperators/SuspiciousOperationGroupings/g' {} + Also: rename file to match module name rename test file to match lint name start implementing `IdentDifference` creation add `IdentIter` utility use `ident_iter::IdentIter` fix bug in `suggestion_with_swapped_ident` add `inside_if_statements` test implement `Add` `todo`s register `SuspiciousOperationGroupings` lint pass fill in `chained_binops`, and fill in a stopgap version of `ident_difference_expr`, but then notice that the lint does not seem to ever be run in the tests run `cargo dev update_lints` and not that the `suspicious_operation_groupings` lint still does not seem to be run fix base index incrementing bug fix paired_identifiers bug, and remove ident from `Single` change help prefix and note our first successful lint messages! add odd_number_of_pairs test get the `non_boolean_operators` test passing, with two copies of the error message extract `is_useless_with_eq_exprs` so we can know when `eq_op` will already handle something add `not_caught_by_eq_op` tests since `s1.b * s1.b` was (reasonably) not caught by `eq_op` cover the case where the change should be made on either side of the expression with `not_caught_by_eq_op` tests produce the expected suggestion on the `not_caught_by_eq_op_middle_change_left` test confirm that the previous tests still pass and update references fix early continue bug and get `not_caught_by_eq_op_middle_change_right` passing note that `not_caught_by_eq_op_start` already passes fix bugs based on misunderstanding of what `Iterator::skip` does, and note that `not_caught_by_eq_op_end` now passes add several parens tests and make some of them pass handle parens inside `chained_binops_helper` and note that this makes several tests pass get `inside_larger_boolean_expression_with_unsorted_ops` test passing by extracting out `check_same_op_binops` function also run `cargo dev fmt` note that `inside_function_call` already passes add another `if_statement` test remove the matching op requirement, making `inside_larger_boolean_expression_with_unsorted_ops` pass prevent non-change suggestions from being emitted get the `Nested` tests passing, and remove apparently false note about eq_op add a test to justify comment in `ident_difference_expr_with_base_location` but find that the failure mode seems different than expected complete `todo` making `do_not_give_bad_suggestions_for_this_unusual_expr` pass and add some more tests that already pass add test to `eq_op` note that `inside_fn_with_similar_expression` already passes fix `inside_an_if_statement` and note that it already passes attempt to implement if statement extraction and notice that we don't seem to handle unary ops correctly add `maximum_unary_minus_right_tree` test and make it pass add two tests and note one of them passes filter out unary operations in several places, and find that the issue seems to be that we don't currently recognize the error in `multiple_comparison_types_and_unary_minus` even so. remove filtering that was causing bad suggestions remove tests that were deemed too much for now run `cargo dev fmt` correct eq_op post-merge fill out the description and delete debugging code run `cargo dev update_lints` update eq_op references add parens to work around rustfmt issue #3666 and run rustfmt https://github.com/rust-lang/rustfmt/issues/3666#issuecomment-714612257 update references after formatting fix dogfood issues fix multi-cursor edit fix missed dogfood error fix more dogfood pedantic issues, including function length even more nesting insert hidden definition of Vec3 so docs compile add spaces to second struct def reword test description comment Co-authored-by: llogiq add local `use BinOpKind::*;` Apply suggestions from code review Co-authored-by: llogiq switch `SUSPICIOUS_OPERATION_GROUPINGS` to a style lint run `cargo dev update_lints` put both usages of `op_types` in the same closure to satisfy `borrowck` fix compile error --- CHANGELOG.md | 1 + clippy_lints/src/eq_op.rs | 27 +- clippy_lints/src/lib.rs | 5 + clippy_lints/src/suspicious_operation_groupings.rs | 693 +++++++++++++++++++++ clippy_lints/src/utils/ast_utils.rs | 11 + clippy_lints/src/utils/ast_utils/ident_iter.rs | 45 ++ tests/ui/eq_op.rs | 9 + tests/ui/eq_op.stderr | 10 +- tests/ui/suspicious_operation_groupings.rs | 207 ++++++ tests/ui/suspicious_operation_groupings.stderr | 166 +++++ 10 files changed, 1150 insertions(+), 24 deletions(-) create mode 100644 clippy_lints/src/suspicious_operation_groupings.rs create mode 100644 clippy_lints/src/utils/ast_utils/ident_iter.rs create mode 100644 tests/ui/suspicious_operation_groupings.rs create mode 100644 tests/ui/suspicious_operation_groupings.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index b9e4b0e6704..e76a781f13b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2073,6 +2073,7 @@ Released 2018-09-13 [`suspicious_else_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_else_formatting [`suspicious_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_map [`suspicious_op_assign_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_op_assign_impl +[`suspicious_operation_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_operation_groupings [`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting [`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments [`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index 3201adbf9a0..6308f6e2e7e 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -1,10 +1,10 @@ use crate::utils::{ - eq_expr_value, higher, implements_trait, in_macro, is_copy, is_expn_of, multispan_sugg, snippet, span_lint, - span_lint_and_then, + ast_utils::is_useless_with_eq_exprs, eq_expr_value, higher, implements_trait, in_macro, is_copy, is_expn_of, + multispan_sugg, snippet, span_lint, span_lint_and_then, }; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{BinOp, BinOpKind, BorrowKind, Expr, ExprKind, StmtKind}; +use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -102,7 +102,7 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { if macro_with_not_op(&left.kind) || macro_with_not_op(&right.kind) { return; } - if is_valid_operator(op) && eq_expr_value(cx, left, right) { + if is_useless_with_eq_exprs(higher::binop(op.node)) && eq_expr_value(cx, left, right) { span_lint( cx, EQ_OP, @@ -245,22 +245,3 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { } } } - -fn is_valid_operator(op: BinOp) -> bool { - matches!( - op.node, - BinOpKind::Sub - | BinOpKind::Div - | BinOpKind::Eq - | BinOpKind::Lt - | BinOpKind::Le - | BinOpKind::Gt - | BinOpKind::Ge - | BinOpKind::Ne - | BinOpKind::And - | BinOpKind::Or - | BinOpKind::BitXor - | BinOpKind::BitAnd - | BinOpKind::BitOr - ) -} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 67a3a3fcf48..6eb5f6a7f48 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -308,6 +308,7 @@ mod single_component_path_imports; mod slow_vector_initialization; mod stable_sort_primitive; mod strings; +mod suspicious_operation_groupings; mod suspicious_trait_impl; mod swap; mod tabs_in_doc_comments; @@ -834,6 +835,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &strings::STRING_LIT_AS_BYTES, &strings::STRING_TO_STRING, &strings::STR_TO_STRING, + &suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS, &suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL, &suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL, &swap::ALMOST_SWAPPED, @@ -1066,6 +1068,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box types::UnitArg); store.register_late_pass(|| box double_comparison::DoubleComparisons); store.register_late_pass(|| box question_mark::QuestionMark); + store.register_early_pass(|| box suspicious_operation_groupings::SuspiciousOperationGroupings); store.register_late_pass(|| box suspicious_trait_impl::SuspiciousImpl); store.register_late_pass(|| box map_unit_fn::MapUnit); store.register_late_pass(|| box inherent_impl::MultipleInherentImpl::default()); @@ -1547,6 +1550,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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(&suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), LintId::of(&swap::ALMOST_SWAPPED), @@ -1698,6 +1702,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&returns::LET_AND_RETURN), LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), + LintId::of(&suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), 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), diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs new file mode 100644 index 00000000000..cccd24ccf94 --- /dev/null +++ b/clippy_lints/src/suspicious_operation_groupings.rs @@ -0,0 +1,693 @@ +use crate::utils::ast_utils::{eq_id, is_useless_with_eq_exprs, IdentIter}; +use crate::utils::{snippet_with_applicability, span_lint_and_sugg}; +use core::ops::{Add, AddAssign}; +use if_chain::if_chain; +use rustc_ast::ast::{BinOpKind, Expr, ExprKind, StmtKind}; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Spanned; +use rustc_span::symbol::Ident; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** + /// Checks for unlikely usages of binary operators that are almost + /// certainly typos and/or copy/paste errors, given the other usages + /// of binary operators nearby. + /// **Why is this bad?** + /// They are probably bugs and if they aren't then they look like bugs + /// and you should add a comment explaining why you are doing such an + /// odd set of operations. + /// **Known problems:** + /// There may be some false positives if you are trying to do something + /// unusual that happens to look like a typo. + /// + /// **Example:** + /// + /// ```rust + /// struct Vec3 { + /// x: f64, + /// y: f64, + /// z: f64, + /// } + /// + /// impl Eq for Vec3 {} + /// + /// impl PartialEq for Vec3 { + /// fn eq(&self, other: &Self) -> bool { + /// // This should trigger the lint because `self.x` is compared to `other.y` + /// self.x == other.y && self.y == other.y && self.z == other.z + /// } + /// } + /// ``` + /// Use instead: + /// ```rust + /// # struct Vec3 { + /// # x: f64, + /// # y: f64, + /// # z: f64, + /// # } + /// // same as above except: + /// impl PartialEq for Vec3 { + /// fn eq(&self, other: &Self) -> bool { + /// // Note we now compare other.x to self.x + /// self.x == other.x && self.y == other.y && self.z == other.z + /// } + /// } + /// ``` + pub SUSPICIOUS_OPERATION_GROUPINGS, + style, + "groupings of binary operations that look suspiciously like typos" +} + +declare_lint_pass!(SuspiciousOperationGroupings => [SUSPICIOUS_OPERATION_GROUPINGS]); + +impl EarlyLintPass for SuspiciousOperationGroupings { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + if expr.span.from_expansion() { + return; + } + + if let Some(binops) = extract_related_binops(&expr.kind) { + check_binops(cx, &binops.iter().collect::>()); + + let mut op_types = Vec::with_capacity(binops.len()); + // We could use a hashmap, etc. to avoid being O(n*m) here, but + // we want the lints to be emitted in a consistent order. Besides, + // m, (the number of distinct `BinOpKind`s in `binops`) + // will often be small, and does have an upper limit. + binops.iter().map(|b| b.op).for_each(|op| { + if !op_types.contains(&op) { + op_types.push(op); + } + }); + + for op_type in op_types { + let ops: Vec<_> = binops.iter().filter(|b| b.op == op_type).collect(); + + check_binops(cx, &ops); + } + } + } +} + +fn check_binops(cx: &EarlyContext<'_>, binops: &[&BinaryOp<'_>]) { + let binop_count = binops.len(); + if binop_count < 2 { + // Single binary operation expressions would likely be false + // positives. + return; + } + + let mut one_ident_difference_count = 0; + let mut no_difference_info = None; + let mut double_difference_info = None; + let mut expected_ident_loc = None; + + let mut paired_identifiers = FxHashSet::default(); + + for (i, BinaryOp { left, right, op, .. }) in binops.iter().enumerate() { + match ident_difference_expr(left, right) { + IdentDifference::NoDifference => { + if is_useless_with_eq_exprs(*op) { + // The `eq_op` lint should catch this in this case. + return; + } + + no_difference_info = Some(i); + }, + IdentDifference::Single(ident_loc) => { + one_ident_difference_count += 1; + if let Some(previous_expected) = expected_ident_loc { + if previous_expected != ident_loc { + // This expression doesn't match the form we're + // looking for. + return; + } + } else { + expected_ident_loc = Some(ident_loc); + } + + // If there was only a single difference, all other idents + // must have been the same, and thus were paired. + for id in skip_index(IdentIter::from(*left), ident_loc.index) { + paired_identifiers.insert(id); + } + }, + IdentDifference::Double(ident_loc1, ident_loc2) => { + double_difference_info = Some((i, ident_loc1, ident_loc2)); + }, + IdentDifference::Multiple | IdentDifference::NonIdent => { + // It's too hard to know whether this is a bug or not. + return; + }, + } + } + + let mut applicability = Applicability::MachineApplicable; + + if let Some(expected_loc) = expected_ident_loc { + match (no_difference_info, double_difference_info) { + (Some(i), None) => attempt_to_emit_no_difference_lint(cx, binops, i, expected_loc), + (None, Some((double_difference_index, ident_loc1, ident_loc2))) => { + if_chain! { + if one_ident_difference_count == binop_count - 1; + if let Some(binop) = binops.get(double_difference_index); + then { + let changed_loc = if ident_loc1 == expected_loc { + ident_loc2 + } else if ident_loc2 == expected_loc { + ident_loc1 + } else { + // This expression doesn't match the form we're + // looking for. + return; + }; + + if let Some(sugg) = ident_swap_sugg( + cx, + &paired_identifiers, + binop, + changed_loc, + &mut applicability, + ) { + emit_suggestion( + cx, + binop.span, + sugg, + applicability, + ); + } + } + } + }, + _ => {}, + } + } +} + +fn attempt_to_emit_no_difference_lint( + cx: &EarlyContext<'_>, + binops: &[&BinaryOp<'_>], + i: usize, + expected_loc: IdentLocation, +) { + if let Some(binop) = binops.get(i).cloned() { + // We need to try and figure out which identifier we should + // suggest using instead. Since there could be multiple + // replacement candidates in a given expression, and we're + // just taking the first one, we may get some bad lint + // messages. + let mut applicability = Applicability::MaybeIncorrect; + + // We assume that the correct ident is one used elsewhere in + // the other binops, in a place that there was a single + // difference between idents before. + let old_left_ident = get_ident(binop.left, expected_loc); + let old_right_ident = get_ident(binop.right, expected_loc); + + for b in skip_index(binops.iter(), i) { + if_chain! { + if let (Some(old_ident), Some(new_ident)) = + (old_left_ident, get_ident(b.left, expected_loc)); + if old_ident != new_ident; + if let Some(sugg) = suggestion_with_swapped_ident( + cx, + binop.left, + expected_loc, + new_ident, + &mut applicability, + ); + then { + emit_suggestion( + cx, + binop.span, + replace_left_sugg(cx, &binop, &sugg, &mut applicability), + applicability, + ); + return; + } + } + + if_chain! { + if let (Some(old_ident), Some(new_ident)) = + (old_right_ident, get_ident(b.right, expected_loc)); + if old_ident != new_ident; + if let Some(sugg) = suggestion_with_swapped_ident( + cx, + binop.right, + expected_loc, + new_ident, + &mut applicability, + ); + then { + emit_suggestion( + cx, + binop.span, + replace_right_sugg(cx, &binop, &sugg, &mut applicability), + applicability, + ); + return; + } + } + } + } +} + +fn emit_suggestion(cx: &EarlyContext<'_>, span: Span, sugg: String, applicability: Applicability) { + span_lint_and_sugg( + cx, + SUSPICIOUS_OPERATION_GROUPINGS, + span, + "This sequence of operators looks suspiciously like a bug.", + "I think you meant", + sugg, + applicability, + ) +} + +fn ident_swap_sugg( + cx: &EarlyContext<'_>, + paired_identifiers: &FxHashSet, + binop: &BinaryOp<'_>, + location: IdentLocation, + applicability: &mut Applicability, +) -> Option { + let left_ident = get_ident(&binop.left, location)?; + let right_ident = get_ident(&binop.right, location)?; + + let sugg = match ( + paired_identifiers.contains(&left_ident), + paired_identifiers.contains(&right_ident), + ) { + (true, true) | (false, false) => { + // We don't have a good guess of what ident should be + // used instead, in these cases. + *applicability = Applicability::MaybeIncorrect; + + // We arbitraily choose one side to suggest changing, + // since we don't have a better guess. If the user + // ends up duplicating a clause, the `logic_bug` lint + // should catch it. + + let right_suggestion = + suggestion_with_swapped_ident(cx, &binop.right, location, left_ident, applicability)?; + + replace_right_sugg(cx, binop, &right_suggestion, applicability) + }, + (false, true) => { + // We haven't seen a pair involving the left one, so + // it's probably what is wanted. + + let right_suggestion = + suggestion_with_swapped_ident(cx, &binop.right, location, left_ident, applicability)?; + + replace_right_sugg(cx, binop, &right_suggestion, applicability) + }, + (true, false) => { + // We haven't seen a pair involving the right one, so + // it's probably what is wanted. + let left_suggestion = suggestion_with_swapped_ident(cx, &binop.left, location, right_ident, applicability)?; + + replace_left_sugg(cx, binop, &left_suggestion, applicability) + }, + }; + + Some(sugg) +} + +fn replace_left_sugg( + cx: &EarlyContext<'_>, + binop: &BinaryOp<'_>, + left_suggestion: &str, + applicability: &mut Applicability, +) -> String { + format!( + "{} {} {}", + left_suggestion, + binop.op.to_string(), + snippet_with_applicability(cx, binop.right.span, "..", applicability), + ) +} + +fn replace_right_sugg( + cx: &EarlyContext<'_>, + binop: &BinaryOp<'_>, + right_suggestion: &str, + applicability: &mut Applicability, +) -> String { + format!( + "{} {} {}", + snippet_with_applicability(cx, binop.left.span, "..", applicability), + binop.op.to_string(), + right_suggestion, + ) +} + +#[derive(Clone, Debug)] +struct BinaryOp<'exprs> { + op: BinOpKind, + span: Span, + left: &'exprs Expr, + right: &'exprs Expr, +} + +impl BinaryOp<'exprs> { + fn new(op: BinOpKind, span: Span, (left, right): (&'exprs Expr, &'exprs Expr)) -> Self { + Self { op, span, left, right } + } +} + +fn strip_non_ident_wrappers(expr: &Expr) -> &Expr { + let mut output = expr; + loop { + output = match &output.kind { + ExprKind::Paren(ref inner) | ExprKind::Unary(_, ref inner) => inner, + _ => { + return output; + }, + }; + } +} + +fn extract_related_binops(kind: &ExprKind) -> Option>> { + append_opt_vecs(chained_binops(kind), if_statment_binops(kind)) +} + +fn if_statment_binops(kind: &ExprKind) -> Option>> { + match kind { + ExprKind::If(ref condition, _, _) => chained_binops(&condition.kind), + ExprKind::Paren(ref e) => if_statment_binops(&e.kind), + ExprKind::Block(ref block, _) => { + let mut output = None; + for stmt in &block.stmts { + match stmt.kind { + StmtKind::Expr(ref e) | StmtKind::Semi(ref e) => { + output = append_opt_vecs(output, if_statment_binops(&e.kind)); + }, + _ => {}, + } + } + output + }, + _ => None, + } +} + +fn append_opt_vecs(target_opt: Option>, source_opt: Option>) -> Option> { + match (target_opt, source_opt) { + (Some(mut target), Some(mut source)) => { + target.reserve(source.len()); + for op in source.drain(..) { + target.push(op); + } + Some(target) + }, + (Some(v), None) | (None, Some(v)) => Some(v), + (None, None) => None, + } +} + +fn chained_binops(kind: &ExprKind) -> Option>> { + match kind { + ExprKind::Binary(_, left_outer, right_outer) => chained_binops_helper(left_outer, right_outer), + ExprKind::Paren(ref e) | ExprKind::Unary(_, ref e) => chained_binops(&e.kind), + _ => None, + } +} + +fn chained_binops_helper(left_outer: &'expr Expr, right_outer: &'expr Expr) -> Option>> { + match (&left_outer.kind, &right_outer.kind) { + ( + ExprKind::Paren(ref left_e) | ExprKind::Unary(_, ref left_e), + ExprKind::Paren(ref right_e) | ExprKind::Unary(_, ref right_e), + ) => chained_binops_helper(left_e, right_e), + (ExprKind::Paren(ref left_e) | ExprKind::Unary(_, ref left_e), _) => chained_binops_helper(left_e, right_outer), + (_, ExprKind::Paren(ref right_e) | ExprKind::Unary(_, ref right_e)) => { + chained_binops_helper(left_outer, right_e) + }, + ( + ExprKind::Binary(Spanned { node: left_op, .. }, ref left_left, ref left_right), + ExprKind::Binary(Spanned { node: right_op, .. }, ref right_left, ref right_right), + ) => match ( + chained_binops_helper(left_left, left_right), + chained_binops_helper(right_left, right_right), + ) { + (Some(mut left_ops), Some(mut right_ops)) => { + left_ops.reserve(right_ops.len()); + for op in right_ops.drain(..) { + left_ops.push(op); + } + Some(left_ops) + }, + (Some(mut left_ops), _) => { + left_ops.push(BinaryOp::new(*right_op, right_outer.span, (right_left, right_right))); + Some(left_ops) + }, + (_, Some(mut right_ops)) => { + right_ops.insert(0, BinaryOp::new(*left_op, left_outer.span, (left_left, left_right))); + Some(right_ops) + }, + (None, None) => Some(vec![ + BinaryOp::new(*left_op, left_outer.span, (left_left, left_right)), + BinaryOp::new(*right_op, right_outer.span, (right_left, right_right)), + ]), + }, + _ => None, + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)] +struct IdentLocation { + index: usize, +} + +impl Add for IdentLocation { + type Output = IdentLocation; + + fn add(self, other: Self) -> Self::Output { + Self { + index: self.index + other.index, + } + } +} + +impl AddAssign for IdentLocation { + fn add_assign(&mut self, other: Self) { + *self = *self + other + } +} + +#[derive(Clone, Copy, Debug)] +enum IdentDifference { + NoDifference, + Single(IdentLocation), + Double(IdentLocation, IdentLocation), + Multiple, + NonIdent, +} + +impl Add for IdentDifference { + type Output = IdentDifference; + + fn add(self, other: Self) -> Self::Output { + match (self, other) { + (Self::NoDifference, output) | (output, Self::NoDifference) => output, + (Self::Multiple, _) + | (_, Self::Multiple) + | (Self::Double(_, _), Self::Single(_)) + | (Self::Single(_) | Self::Double(_, _), Self::Double(_, _)) => Self::Multiple, + (Self::NonIdent, _) | (_, Self::NonIdent) => Self::NonIdent, + (Self::Single(il1), Self::Single(il2)) => Self::Double(il1, il2), + } + } +} + +impl AddAssign for IdentDifference { + fn add_assign(&mut self, other: Self) { + *self = *self + other + } +} + +impl IdentDifference { + /// Returns true if learning about more differences will not change the value + /// of this `IdentDifference`, and false otherwise. + fn is_complete(&self) -> bool { + match self { + Self::NoDifference | Self::Single(_) | Self::Double(_, _) => false, + Self::Multiple | Self::NonIdent => true, + } + } +} + +fn ident_difference_expr(left: &Expr, right: &Expr) -> IdentDifference { + ident_difference_expr_with_base_location(left, right, IdentLocation::default()).0 +} + +fn ident_difference_expr_with_base_location( + left: &Expr, + right: &Expr, + mut base: IdentLocation, +) -> (IdentDifference, IdentLocation) { + // Ideally, this function should not use IdentIter because it should return + // early if the expressions have any non-ident differences. We want that early + // return because if without that restriction the lint would lead to false + // positives. + // + // But, we cannot (easily?) use a `rustc_ast::visit::Visitor`, since we need + // the two expressions to be walked in lockstep. And without a `Visitor`, we'd + // have to do all the AST traversal ourselves, which is a lot of work, since to + // do it properly we'd need to be able to handle more or less every possible + // AST node since `Item`s can be written inside `Expr`s. + // + // In practice, it seems likely that expressions, above a certain size, that + // happen to use the exact same idents in the exact same order, and which are + // not structured the same, would be rare. Therefore it seems likely that if + // we do only the first layer of matching ourselves and eventually fallback on + // IdentIter, then the output of this function will be almost always be correct + // in practice. + // + // If it turns out that problematic cases are more prelavent than we assume, + // then we should be able to change this function to do the correct traversal, + // without needing to change the rest of the code. + + #![allow(clippy::enum_glob_use)] + use ExprKind::*; + + match ( + &strip_non_ident_wrappers(left).kind, + &strip_non_ident_wrappers(right).kind, + ) { + (Yield(_), Yield(_)) + | (Try(_), Try(_)) + | (Paren(_), Paren(_)) + | (Repeat(_, _), Repeat(_, _)) + | (Struct(_, _, _), Struct(_, _, _)) + | (MacCall(_), MacCall(_)) + | (LlvmInlineAsm(_), LlvmInlineAsm(_)) + | (InlineAsm(_), InlineAsm(_)) + | (Ret(_), Ret(_)) + | (Continue(_), Continue(_)) + | (Break(_, _), Break(_, _)) + | (AddrOf(_, _, _), AddrOf(_, _, _)) + | (Path(_, _), Path(_, _)) + | (Range(_, _, _), Range(_, _, _)) + | (Index(_, _), Index(_, _)) + | (Field(_, _), Field(_, _)) + | (AssignOp(_, _, _), AssignOp(_, _, _)) + | (Assign(_, _, _), Assign(_, _, _)) + | (TryBlock(_), TryBlock(_)) + | (Await(_), Await(_)) + | (Async(_, _, _), Async(_, _, _)) + | (Block(_, _), Block(_, _)) + | (Closure(_, _, _, _, _, _), Closure(_, _, _, _, _, _)) + | (Match(_, _), Match(_, _)) + | (Loop(_, _), Loop(_, _)) + | (ForLoop(_, _, _, _), ForLoop(_, _, _, _)) + | (While(_, _, _), While(_, _, _)) + | (If(_, _, _), If(_, _, _)) + | (Let(_, _), Let(_, _)) + | (Type(_, _), Type(_, _)) + | (Cast(_, _), Cast(_, _)) + | (Lit(_), Lit(_)) + | (Unary(_, _), Unary(_, _)) + | (Binary(_, _, _), Binary(_, _, _)) + | (Tup(_), Tup(_)) + | (MethodCall(_, _, _), MethodCall(_, _, _)) + | (Call(_, _), Call(_, _)) + | (ConstBlock(_), ConstBlock(_)) + | (Array(_), Array(_)) + | (Box(_), Box(_)) => { + // keep going + }, + _ => { + return (IdentDifference::NonIdent, base); + }, + } + + let mut difference = IdentDifference::NoDifference; + + for (left_attr, right_attr) in left.attrs.iter().zip(right.attrs.iter()) { + let (new_difference, new_base) = + ident_difference_via_ident_iter_with_base_location(left_attr, right_attr, base); + base = new_base; + difference += new_difference; + if difference.is_complete() { + return (difference, base); + } + } + + let (new_difference, new_base) = ident_difference_via_ident_iter_with_base_location(left, right, base); + base = new_base; + difference += new_difference; + + (difference, base) +} + +fn ident_difference_via_ident_iter_with_base_location>( + left: Iterable, + right: Iterable, + mut base: IdentLocation, +) -> (IdentDifference, IdentLocation) { + // See the note in `ident_difference_expr_with_base_location` about `IdentIter` + let mut difference = IdentDifference::NoDifference; + + let mut left_iterator = left.into(); + let mut right_iterator = right.into(); + + loop { + match (left_iterator.next(), right_iterator.next()) { + (Some(left_ident), Some(right_ident)) => { + if !eq_id(left_ident, right_ident) { + difference += IdentDifference::Single(base); + if difference.is_complete() { + return (difference, base); + } + } + }, + (Some(_), None) | (None, Some(_)) => { + return (IdentDifference::NonIdent, base); + }, + (None, None) => { + return (difference, base); + }, + } + base += IdentLocation { index: 1 }; + } +} + +fn get_ident(expr: &Expr, location: IdentLocation) -> Option { + IdentIter::from(expr).nth(location.index) +} + +fn suggestion_with_swapped_ident( + cx: &EarlyContext<'_>, + expr: &Expr, + location: IdentLocation, + new_ident: Ident, + applicability: &mut Applicability, +) -> Option { + get_ident(expr, location).and_then(|current_ident| { + if eq_id(current_ident, new_ident) { + // We never want to suggest a non-change + return None; + } + + Some(format!( + "{}{}{}", + snippet_with_applicability(cx, expr.span.with_hi(current_ident.span.lo()), "..", applicability), + new_ident.to_string(), + snippet_with_applicability(cx, expr.span.with_lo(current_ident.span.hi()), "..", applicability), + )) + }) +} + +fn skip_index(iter: Iter, index: usize) -> impl Iterator +where + Iter: Iterator, +{ + iter.enumerate() + .filter_map(move |(i, a)| if i == index { None } else { Some(a) }) +} diff --git a/clippy_lints/src/utils/ast_utils.rs b/clippy_lints/src/utils/ast_utils.rs index fcf7a4b1367..31b4e25411b 100644 --- a/clippy_lints/src/utils/ast_utils.rs +++ b/clippy_lints/src/utils/ast_utils.rs @@ -10,6 +10,17 @@ use rustc_ast::{self as ast, *}; use rustc_span::symbol::Ident; use std::mem; +pub mod ident_iter; +pub use ident_iter::IdentIter; + +pub fn is_useless_with_eq_exprs(kind: BinOpKind) -> bool { + use BinOpKind::*; + matches!( + kind, + Sub | Div | Eq | Lt | Le | Gt | Ge | Ne | And | Or | BitXor | BitAnd | BitOr + ) +} + /// Checks if each element in the first slice is contained within the latter as per `eq_fn`. pub fn unordered_over(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool { left.len() == right.len() && left.iter().all(|l| right.iter().any(|r| eq_fn(l, r))) diff --git a/clippy_lints/src/utils/ast_utils/ident_iter.rs b/clippy_lints/src/utils/ast_utils/ident_iter.rs new file mode 100644 index 00000000000..eefcbabd835 --- /dev/null +++ b/clippy_lints/src/utils/ast_utils/ident_iter.rs @@ -0,0 +1,45 @@ +use core::iter::FusedIterator; +use rustc_ast::visit::{walk_attribute, walk_expr, Visitor}; +use rustc_ast::{Attribute, Expr}; +use rustc_span::symbol::Ident; + +pub struct IdentIter(std::vec::IntoIter); + +impl Iterator for IdentIter { + type Item = Ident; + + fn next(&mut self) -> Option { + self.0.next() + } +} + +impl FusedIterator for IdentIter {} + +impl From<&Expr> for IdentIter { + fn from(expr: &Expr) -> Self { + let mut visitor = IdentCollector::default(); + + walk_expr(&mut visitor, expr); + + IdentIter(visitor.0.into_iter()) + } +} + +impl From<&Attribute> for IdentIter { + fn from(attr: &Attribute) -> Self { + let mut visitor = IdentCollector::default(); + + walk_attribute(&mut visitor, attr); + + IdentIter(visitor.0.into_iter()) + } +} + +#[derive(Default)] +struct IdentCollector(Vec); + +impl Visitor<'_> for IdentCollector { + fn visit_ident(&mut self, ident: Ident) { + self.0.push(ident); + } +} diff --git a/tests/ui/eq_op.rs b/tests/ui/eq_op.rs index 4e09d19ea21..7ab23320db6 100644 --- a/tests/ui/eq_op.rs +++ b/tests/ui/eq_op.rs @@ -86,3 +86,12 @@ fn check_ignore_macro() { // checks if the lint ignores macros with `!` operator !bool_macro!(1) && !bool_macro!(""); } + +struct Nested { + inner: ((i32,), (i32,), (i32,)), +} + +fn check_nested(n1: &Nested, n2: &Nested) -> bool { + // `n2.inner.0.0` mistyped as `n1.inner.0.0` + (n1.inner.0).0 == (n1.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.2).0 +} diff --git a/tests/ui/eq_op.stderr b/tests/ui/eq_op.stderr index ad81b35a766..8ef658af8df 100644 --- a/tests/ui/eq_op.stderr +++ b/tests/ui/eq_op.stderr @@ -162,5 +162,13 @@ error: equal expressions as operands to `/` LL | const D: u32 = A / A; | ^^^^^ -error: aborting due to 27 previous errors +error: equal expressions as operands to `==` + --> $DIR/eq_op.rs:96:5 + | +LL | (n1.inner.0).0 == (n1.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.2).0 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(clippy::eq_op)]` on by default + +error: aborting due to 28 previous errors diff --git a/tests/ui/suspicious_operation_groupings.rs b/tests/ui/suspicious_operation_groupings.rs new file mode 100644 index 00000000000..dd6f4ec7bd9 --- /dev/null +++ b/tests/ui/suspicious_operation_groupings.rs @@ -0,0 +1,207 @@ +#![warn(clippy::suspicious_operation_groupings)] + +struct Vec3 { + x: f64, + y: f64, + z: f64, +} + +impl Eq for Vec3 {} + +impl PartialEq for Vec3 { + fn eq(&self, other: &Self) -> bool { + // This should trigger the lint because `self.x` is compared to `other.y` + self.x == other.y && self.y == other.y && self.z == other.z + } +} + +struct S { + a: i32, + b: i32, + c: i32, + d: i32, +} + +fn buggy_ab_cmp(s1: &S, s2: &S) -> bool { + // There's no `s1.b` + s1.a < s2.a && s1.a < s2.b +} + +struct SAOnly { + a: i32, +} + +impl S { + fn a(&self) -> i32 { + 0 + } +} + +fn do_not_give_bad_suggestions_for_this_unusual_expr(s1: &S, s2: &SAOnly) -> bool { + // This is superficially similar to `buggy_ab_cmp`, but we should not suggest + // `s2.b` since that is invalid. + s1.a < s2.a && s1.a() < s1.b +} + +fn do_not_give_bad_suggestions_for_this_macro_expr(s1: &S, s2: &SAOnly) -> bool { + macro_rules! s1 { + () => { + S { + a: 1, + b: 1, + c: 1, + d: 1, + } + }; + } + + // This is superficially similar to `buggy_ab_cmp`, but we should not suggest + // `s2.b` since that is invalid. + s1.a < s2.a && s1!().a < s1.b +} + +fn do_not_give_bad_suggestions_for_this_incorrect_expr(s1: &S, s2: &SAOnly) -> bool { + // There's two `s1.b`, but we should not suggest `s2.b` since that is invalid + s1.a < s2.a && s1.b < s1.b +} + +fn permissable(s1: &S, s2: &S) -> bool { + // Something like this seems like it might actually be what is desired. + s1.a == s2.b +} + +fn non_boolean_operators(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + s1.a * s2.a + s1.b * s2.b + s1.c * s2.b + s1.d * s2.d +} + +fn odd_number_of_pairs(s1: &S, s2: &S) -> i32 { + // There's no `s2.b` + s1.a * s2.a + s1.b * s2.c + s1.c * s2.c +} + +fn not_caught_by_eq_op_middle_change_left(s1: &S, s2: &S) -> i32 { + // There's no `s1.b` + s1.a * s2.a + s2.b * s2.b + s1.c * s2.c +} + +fn not_caught_by_eq_op_middle_change_right(s1: &S, s2: &S) -> i32 { + // There's no `s2.b` + s1.a * s2.a + s1.b * s1.b + s1.c * s2.c +} + +fn not_caught_by_eq_op_start(s1: &S, s2: &S) -> i32 { + // There's no `s2.a` + s1.a * s1.a + s1.b * s2.b + s1.c * s2.c +} + +fn not_caught_by_eq_op_end(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + s1.a * s2.a + s1.b * s2.b + s1.c * s1.c +} + +fn the_cross_product_should_not_lint(s1: &S, s2: &S) -> (i32, i32, i32) { + ( + s1.b * s2.c - s1.c * s2.b, + s1.c * s2.a - s1.a * s2.c, + s1.a * s2.b - s1.b * s2.a, + ) +} + +fn outer_parens_simple(s1: &S, s2: &S) -> i32 { + // There's no `s2.b` + (s1.a * s2.a + s1.b * s1.b) +} + +fn outer_parens(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + (s1.a * s2.a + s1.b * s2.b + s1.c * s2.b + s1.d * s2.d) +} + +fn inner_parens(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + (s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d) +} + +fn outer_and_some_inner_parens(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + ((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d)) +} + +fn all_parens_balanced_tree(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + (((s1.a * s2.a) + (s1.b * s2.b)) + ((s1.c * s2.b) + (s1.d * s2.d))) +} + +fn all_parens_left_tree(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + (((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b)) + (s1.d * s2.d)) +} + +fn all_parens_right_tree(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + ((s1.a * s2.a) + ((s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d))) +} + +fn inside_other_binop_expression(s1: &S, s2: &S) -> i32 { + // There's no `s1.b` + (s1.a * s2.a + s2.b * s2.b) / 2 +} + +fn inside_function_call(s1: &S, s2: &S) -> i32 { + // There's no `s1.b` + i32::swap_bytes(s1.a * s2.a + s2.b * s2.b) +} + +fn inside_larger_boolean_expression(s1: &S, s2: &S) -> bool { + // There's no `s1.c` + s1.a > 0 && s1.b > 0 && s1.d == s2.c && s1.d == s2.d +} + +fn inside_larger_boolean_expression_with_unsorted_ops(s1: &S, s2: &S) -> bool { + // There's no `s1.c` + s1.a > 0 && s1.d == s2.c && s1.b > 0 && s1.d == s2.d +} + +struct Nested { + inner: ((i32,), (i32,), (i32,)), +} + +fn changed_middle_ident(n1: &Nested, n2: &Nested) -> bool { + // There's no `n2.inner.2.0` + (n1.inner.0).0 == (n2.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.1).0 +} + +// `eq_op` should catch this one. +fn changed_initial_ident(n1: &Nested, n2: &Nested) -> bool { + // There's no `n2.inner.0.0` + (n1.inner.0).0 == (n1.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.2).0 +} + +fn inside_fn_with_similar_expression(s1: &S, s2: &S, strict: bool) -> bool { + if strict { + s1.a < s2.a && s1.b < s2.b + } else { + // There's no `s1.b` in this subexpression + s1.a <= s2.a && s1.a <= s2.b + } +} + +fn inside_an_if_statement(s1: &S, s2: &S) { + // There's no `s1.b` + if s1.a < s2.a && s1.a < s2.b { + s1.c = s2.c; + } +} + +fn maximum_unary_minus_right_tree(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + -(-(-s1.a * -s2.a) + (-(-s1.b * -s2.b) + -(-s1.c * -s2.b) + -(-s1.d * -s2.d))) +} + +fn unary_minus_and_an_if_expression(s1: &S, s2: &S) -> i32 { + // There's no `s1.b` + -(if -s1.a < -s2.a && -s1.a < -s2.b { s1.c } else { s2.a }) +} + +fn main() {} diff --git a/tests/ui/suspicious_operation_groupings.stderr b/tests/ui/suspicious_operation_groupings.stderr new file mode 100644 index 00000000000..ce7108217f1 --- /dev/null +++ b/tests/ui/suspicious_operation_groupings.stderr @@ -0,0 +1,166 @@ +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:14:9 + | +LL | self.x == other.y && self.y == other.y && self.z == other.z + | ^^^^^^^^^^^^^^^^^ help: I think you meant: `self.x == other.x` + | + = note: `-D clippy::suspicious-operation-groupings` implied by `-D warnings` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:14:9 + | +LL | self.x == other.y && self.y == other.y && self.z == other.z + | ^^^^^^^^^^^^^^^^^ help: I think you meant: `self.x == other.x` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:27:20 + | +LL | s1.a < s2.a && s1.a < s2.b + | ^^^^^^^^^^^ help: I think you meant: `s1.b < s2.b` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:75:33 + | +LL | s1.a * s2.a + s1.b * s2.b + s1.c * s2.b + s1.d * s2.d + | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:80:19 + | +LL | s1.a * s2.a + s1.b * s2.c + s1.c * s2.c + | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:80:19 + | +LL | s1.a * s2.a + s1.b * s2.c + s1.c * s2.c + | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:85:19 + | +LL | s1.a * s2.a + s2.b * s2.b + s1.c * s2.c + | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:90:19 + | +LL | s1.a * s2.a + s1.b * s1.b + s1.c * s2.c + | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:95:5 + | +LL | s1.a * s1.a + s1.b * s2.b + s1.c * s2.c + | ^^^^^^^^^^^ help: I think you meant: `s1.a * s2.a` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:100:33 + | +LL | s1.a * s2.a + s1.b * s2.b + s1.c * s1.c + | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:113:20 + | +LL | (s1.a * s2.a + s1.b * s1.b) + | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:118:34 + | +LL | (s1.a * s2.a + s1.b * s2.b + s1.c * s2.b + s1.d * s2.d) + | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:123:38 + | +LL | (s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d) + | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:128:39 + | +LL | ((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d)) + | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:133:42 + | +LL | (((s1.a * s2.a) + (s1.b * s2.b)) + ((s1.c * s2.b) + (s1.d * s2.d))) + | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:133:42 + | +LL | (((s1.a * s2.a) + (s1.b * s2.b)) + ((s1.c * s2.b) + (s1.d * s2.d))) + | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:138:40 + | +LL | (((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b)) + (s1.d * s2.d)) + | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:143:40 + | +LL | ((s1.a * s2.a) + ((s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d))) + | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:148:20 + | +LL | (s1.a * s2.a + s2.b * s2.b) / 2 + | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:153:35 + | +LL | i32::swap_bytes(s1.a * s2.a + s2.b * s2.b) + | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:158:29 + | +LL | s1.a > 0 && s1.b > 0 && s1.d == s2.c && s1.d == s2.d + | ^^^^^^^^^^^^ help: I think you meant: `s1.c == s2.c` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:163:17 + | +LL | s1.a > 0 && s1.d == s2.c && s1.b > 0 && s1.d == s2.d + | ^^^^^^^^^^^^ help: I think you meant: `s1.c == s2.c` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:172:77 + | +LL | (n1.inner.0).0 == (n2.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.1).0 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: I think you meant: `(n1.inner.2).0 == (n2.inner.2).0` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:186:25 + | +LL | s1.a <= s2.a && s1.a <= s2.b + | ^^^^^^^^^^^^ help: I think you meant: `s1.b <= s2.b` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:192:23 + | +LL | if s1.a < s2.a && s1.a < s2.b { + | ^^^^^^^^^^^ help: I think you meant: `s1.b < s2.b` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:199:48 + | +LL | -(-(-s1.a * -s2.a) + (-(-s1.b * -s2.b) + -(-s1.c * -s2.b) + -(-s1.d * -s2.d))) + | ^^^^^^^^^^^^^ help: I think you meant: `-s1.c * -s2.c` + +error: This sequence of operators looks suspiciously like a bug. + --> $DIR/suspicious_operation_groupings.rs:204:27 + | +LL | -(if -s1.a < -s2.a && -s1.a < -s2.b { s1.c } else { s2.a }) + | ^^^^^^^^^^^^^ help: I think you meant: `-s1.b < -s2.b` + +error: aborting due to 27 previous errors + -- cgit 1.4.1-3-g733a5 From 0e5aee1fc1251493c35a4344700798e9a586ef16 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sat, 28 Nov 2020 17:18:15 +0100 Subject: items_after_statements: don't lint when they a separated by trailing semicolons --- clippy_lints/src/items_after_statements.rs | 4 ++-- tests/ui/item_after_statement.rs | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/items_after_statements.rs b/clippy_lints/src/items_after_statements.rs index 8998fae09de..0927d218446 100644 --- a/clippy_lints/src/items_after_statements.rs +++ b/clippy_lints/src/items_after_statements.rs @@ -58,12 +58,12 @@ impl EarlyLintPass for ItemsAfterStatements { return; } - // skip initial items + // skip initial items and trailing semicolons let stmts = item .stmts .iter() .map(|stmt| &stmt.kind) - .skip_while(|s| matches!(**s, StmtKind::Item(..))); + .skip_while(|s| matches!(**s, StmtKind::Item(..) | StmtKind::Empty)); // lint on all further items for stmt in stmts { diff --git a/tests/ui/item_after_statement.rs b/tests/ui/item_after_statement.rs index 377e58e4417..d439ca1e4e1 100644 --- a/tests/ui/item_after_statement.rs +++ b/tests/ui/item_after_statement.rs @@ -37,3 +37,16 @@ fn mac() { b!(); println!("{}", a); } + +fn semicolon() { + struct S { + a: u32, + }; + impl S { + fn new(a: u32) -> Self { + Self { a } + } + } + + let _ = S::new(3); +} -- cgit 1.4.1-3-g733a5 From f7b2098e1c4f8e13ec2194f7f094f471b4056f97 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sun, 29 Nov 2020 01:55:15 +0900 Subject: Fix a false positive in `unnecessary_wraps` --- clippy_lints/src/methods/mod.rs | 2 +- clippy_lints/src/unnecessary_wraps.rs | 2 ++ tests/ui/unnecessary_wraps.rs | 7 +++++++ 3 files changed, 10 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 50dd760432d..004b8416fc1 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3878,7 +3878,7 @@ fn is_bool(ty: &hir::Ty<'_>) -> bool { } // Returns `true` if `expr` contains a return expression -fn contains_return(expr: &hir::Expr<'_>) -> bool { +pub(crate) fn contains_return(expr: &hir::Expr<'_>) -> bool { struct RetCallFinder { found: bool, } diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index 25ecc7a82f1..7b550c702cd 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -1,3 +1,4 @@ +use crate::methods::contains_return; use crate::utils::{ in_macro, is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_then, visitors::find_all_ret_expressions, @@ -95,6 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { if let ExprKind::Path(ref qpath) = func.kind; if match_qpath(qpath, path); if args.len() == 1; + if !contains_return(&args[0]); then { suggs.push((ret_expr.span, snippet(cx, args[0].span.source_callsite(), "..").to_string())); true diff --git a/tests/ui/unnecessary_wraps.rs b/tests/ui/unnecessary_wraps.rs index a53dec8f91a..a4570098d71 100644 --- a/tests/ui/unnecessary_wraps.rs +++ b/tests/ui/unnecessary_wraps.rs @@ -109,6 +109,13 @@ impl B for A { } } +fn issue_6384(s: &str) -> Option<&str> { + Some(match s { + "a" => "A", + _ => return None, + }) +} + fn main() { // method calls are not linted func1(true, true); -- cgit 1.4.1-3-g733a5 From 76f2c10fb6afb9071c56e8b6e410671573648ef4 Mon Sep 17 00:00:00 2001 From: Rajkumar Natarajan Date: Sat, 28 Nov 2020 12:19:57 -0500 Subject: issue_6357 update unreachable macro usage --- clippy_lints/src/panic_unimplemented.rs | 9 ++------- tests/ui/panicking_macros.stderr | 8 ++++---- 2 files changed, 6 insertions(+), 11 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/panic_unimplemented.rs b/clippy_lints/src/panic_unimplemented.rs index 31b03ecd101..359620cc079 100644 --- a/clippy_lints/src/panic_unimplemented.rs +++ b/clippy_lints/src/panic_unimplemented.rs @@ -66,7 +66,7 @@ declare_clippy_lint! { /// ``` pub UNREACHABLE, restriction, - "`unreachable!` should not be present in production code" + "usage of the `unreachable!` macro" } declare_lint_pass!(PanicUnimplemented => [UNIMPLEMENTED, UNREACHABLE, TODO, PANIC]); @@ -85,12 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { } else if is_expn_of(expr.span, "todo").is_some() { span_lint(cx, TODO, span, "`todo` should not be present in production code"); } else if is_expn_of(expr.span, "unreachable").is_some() { - span_lint( - cx, - UNREACHABLE, - span, - "`unreachable` should not be present in production code", - ); + span_lint(cx, UNREACHABLE, span, "usage of the `unreachable!` macro"); } else if is_expn_of(expr.span, "panic").is_some() { span_lint(cx, PANIC, span, "`panic` should not be present in production code"); } diff --git a/tests/ui/panicking_macros.stderr b/tests/ui/panicking_macros.stderr index 83234c0ed92..6028323a3c8 100644 --- a/tests/ui/panicking_macros.stderr +++ b/tests/ui/panicking_macros.stderr @@ -62,7 +62,7 @@ error: `unimplemented` should not be present in production code LL | unimplemented!("{} {}", "panic with", "multiple arguments"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: `unreachable` should not be present in production code +error: usage of the `unreachable!` macro --> $DIR/panicking_macros.rs:32:5 | LL | unreachable!(); @@ -70,7 +70,7 @@ LL | unreachable!(); | = note: `-D clippy::unreachable` implied by `-D warnings` -error: `unreachable` should not be present in production code +error: usage of the `unreachable!` macro --> $DIR/panicking_macros.rs:33:5 | LL | unreachable!("message"); @@ -78,7 +78,7 @@ LL | unreachable!("message"); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: `unreachable` should not be present in production code +error: usage of the `unreachable!` macro --> $DIR/panicking_macros.rs:34:5 | LL | unreachable!("{} {}", "panic with", "multiple arguments"); @@ -102,7 +102,7 @@ error: `unimplemented` should not be present in production code LL | unimplemented!(); | ^^^^^^^^^^^^^^^^^ -error: `unreachable` should not be present in production code +error: usage of the `unreachable!` macro --> $DIR/panicking_macros.rs:43:5 | LL | unreachable!(); -- cgit 1.4.1-3-g733a5 From 4e4b8319e83d1ec52253dd33c3d108b96fca9024 Mon Sep 17 00:00:00 2001 From: Suyash458 Date: Sat, 28 Nov 2020 19:54:28 +0530 Subject: fix msrv in test --- tests/ui/min_rust_version_no_patch.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/ui/min_rust_version_no_patch.rs b/tests/ui/min_rust_version_no_patch.rs index 515fe8f95e9..98fffe1e351 100644 --- a/tests/ui/min_rust_version_no_patch.rs +++ b/tests/ui/min_rust_version_no_patch.rs @@ -1,6 +1,6 @@ #![allow(clippy::redundant_clone)] #![feature(custom_inner_attributes)] -#![clippy::msrv = "^1.0"] +#![clippy::msrv = "1.0"] fn manual_strip_msrv() { let s = "hello, world!"; -- cgit 1.4.1-3-g733a5 From 61b29281e744b6af410e9256cc2e9369a3dc173a Mon Sep 17 00:00:00 2001 From: Suyash458 Date: Sun, 29 Nov 2020 17:08:56 +0530 Subject: add more tests for msrv --- tests/ui/min_rust_version_attr.rs | 38 ++++++++++++++++++++++++++++++++++- tests/ui/min_rust_version_attr.stderr | 37 ++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 tests/ui/min_rust_version_attr.stderr (limited to 'tests') diff --git a/tests/ui/min_rust_version_attr.rs b/tests/ui/min_rust_version_attr.rs index 8ed483a3ac6..1026cc40d3b 100644 --- a/tests/ui/min_rust_version_attr.rs +++ b/tests/ui/min_rust_version_attr.rs @@ -35,7 +35,7 @@ fn match_same_arms2() { }; } -fn manual_strip_msrv() { +pub fn manual_strip_msrv() { let s = "hello, world!"; if s.starts_with("hello, ") { assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); @@ -49,3 +49,39 @@ fn main() { match_same_arms2(); manual_strip_msrv(); } + +mod meets_msrv { + #![feature(custom_inner_attributes)] + #![clippy::msrv = "1.45.0"] + + fn main() { + let s = "hello, world!"; + if s.starts_with("hello, ") { + assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); + } + } +} + +mod just_under_msrv { + #![feature(custom_inner_attributes)] + #![clippy::msrv = "1.46.0"] + + fn main() { + let s = "hello, world!"; + if s.starts_with("hello, ") { + assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); + } + } +} + +mod just_above_msrv { + #![feature(custom_inner_attributes)] + #![clippy::msrv = "1.44.0"] + + fn main() { + let s = "hello, world!"; + if s.starts_with("hello, ") { + assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); + } + } +} diff --git a/tests/ui/min_rust_version_attr.stderr b/tests/ui/min_rust_version_attr.stderr new file mode 100644 index 00000000000..3e1af046e7a --- /dev/null +++ b/tests/ui/min_rust_version_attr.stderr @@ -0,0 +1,37 @@ +error: stripping a prefix manually + --> $DIR/min_rust_version_attr.rs:60:24 + | +LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::manual-strip` implied by `-D warnings` +note: the prefix was tested here + --> $DIR/min_rust_version_attr.rs:59:9 + | +LL | if s.starts_with("hello, ") { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: try using the `strip_prefix` method + | +LL | if let Some() = s.strip_prefix("hello, ") { +LL | assert_eq!(.to_uppercase(), "WORLD!"); + | + +error: stripping a prefix manually + --> $DIR/min_rust_version_attr.rs:72:24 + | +LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); + | ^^^^^^^^^^^^^^^^^^^^ + | +note: the prefix was tested here + --> $DIR/min_rust_version_attr.rs:71:9 + | +LL | if s.starts_with("hello, ") { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: try using the `strip_prefix` method + | +LL | if let Some() = s.strip_prefix("hello, ") { +LL | assert_eq!(.to_uppercase(), "WORLD!"); + | + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From 2838b044875b84bb9dc515ba3aa0c1b9772d870b Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Sun, 8 Nov 2020 13:39:46 +0100 Subject: add internal-lints feature to enable clippys internal lints (off by default) --- .github/workflows/clippy_bors.yml | 4 +- Cargo.toml | 5 +- clippy_lints/Cargo.toml | 2 + clippy_lints/src/lib.rs | 28 ++++-- clippy_lints/src/utils/diagnostics.rs | 4 +- clippy_lints/src/utils/mod.rs | 1 + clippy_lints/src/utils/paths.rs | 4 + tests/compile-test.rs | 15 ++- tests/dogfood.rs | 47 +++++++--- .../ui-internal/collapsible_span_lint_calls.fixed | 91 +++++++++++++++++++ tests/ui-internal/collapsible_span_lint_calls.rs | 101 +++++++++++++++++++++ .../ui-internal/collapsible_span_lint_calls.stderr | 49 ++++++++++ tests/ui-internal/custom_ice_message.rs | 10 ++ tests/ui-internal/custom_ice_message.stderr | 13 +++ tests/ui-internal/default_lint.rs | 27 ++++++ tests/ui-internal/default_lint.stderr | 21 +++++ tests/ui-internal/invalid_paths.rs | 23 +++++ tests/ui-internal/invalid_paths.stderr | 16 ++++ tests/ui-internal/lint_without_lint_pass.rs | 44 +++++++++ tests/ui-internal/lint_without_lint_pass.stderr | 21 +++++ tests/ui-internal/match_type_on_diag_item.rs | 50 ++++++++++ tests/ui-internal/match_type_on_diag_item.stderr | 33 +++++++ tests/ui-internal/outer_expn_data.fixed | 28 ++++++ tests/ui-internal/outer_expn_data.rs | 28 ++++++ tests/ui-internal/outer_expn_data.stderr | 15 +++ tests/ui/collapsible_span_lint_calls.fixed | 91 ------------------- tests/ui/collapsible_span_lint_calls.rs | 101 --------------------- tests/ui/collapsible_span_lint_calls.stderr | 49 ---------- tests/ui/custom_ice_message.rs | 10 -- tests/ui/custom_ice_message.stderr | 13 --- tests/ui/default_lint.rs | 27 ------ tests/ui/default_lint.stderr | 21 ----- tests/ui/invalid_paths.rs | 23 ----- tests/ui/invalid_paths.stderr | 16 ---- tests/ui/lint_without_lint_pass.rs | 44 --------- tests/ui/lint_without_lint_pass.stderr | 21 ----- tests/ui/match_type_on_diag_item.rs | 50 ---------- tests/ui/match_type_on_diag_item.stderr | 33 ------- tests/ui/outer_expn_data.fixed | 28 ------ tests/ui/outer_expn_data.rs | 28 ------ tests/ui/outer_expn_data.stderr | 15 --- 41 files changed, 654 insertions(+), 596 deletions(-) create mode 100644 tests/ui-internal/collapsible_span_lint_calls.fixed create mode 100644 tests/ui-internal/collapsible_span_lint_calls.rs create mode 100644 tests/ui-internal/collapsible_span_lint_calls.stderr create mode 100644 tests/ui-internal/custom_ice_message.rs create mode 100644 tests/ui-internal/custom_ice_message.stderr create mode 100644 tests/ui-internal/default_lint.rs create mode 100644 tests/ui-internal/default_lint.stderr create mode 100644 tests/ui-internal/invalid_paths.rs create mode 100644 tests/ui-internal/invalid_paths.stderr create mode 100644 tests/ui-internal/lint_without_lint_pass.rs create mode 100644 tests/ui-internal/lint_without_lint_pass.stderr create mode 100644 tests/ui-internal/match_type_on_diag_item.rs create mode 100644 tests/ui-internal/match_type_on_diag_item.stderr create mode 100644 tests/ui-internal/outer_expn_data.fixed create mode 100644 tests/ui-internal/outer_expn_data.rs create mode 100644 tests/ui-internal/outer_expn_data.stderr delete mode 100644 tests/ui/collapsible_span_lint_calls.fixed delete mode 100644 tests/ui/collapsible_span_lint_calls.rs delete mode 100644 tests/ui/collapsible_span_lint_calls.stderr delete mode 100644 tests/ui/custom_ice_message.rs delete mode 100644 tests/ui/custom_ice_message.stderr delete mode 100644 tests/ui/default_lint.rs delete mode 100644 tests/ui/default_lint.stderr delete mode 100644 tests/ui/invalid_paths.rs delete mode 100644 tests/ui/invalid_paths.stderr delete mode 100644 tests/ui/lint_without_lint_pass.rs delete mode 100644 tests/ui/lint_without_lint_pass.stderr delete mode 100644 tests/ui/match_type_on_diag_item.rs delete mode 100644 tests/ui/match_type_on_diag_item.stderr delete mode 100644 tests/ui/outer_expn_data.fixed delete mode 100644 tests/ui/outer_expn_data.rs delete mode 100644 tests/ui/outer_expn_data.stderr (limited to 'tests') diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index 7509d90c6c2..11c1eeac1cf 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -132,10 +132,10 @@ jobs: run: cargo build --features deny-warnings - name: Test - run: cargo test --features deny-warnings + run: cargo test --features deny-warnings --features internal-lints - name: Test clippy_lints - run: cargo test --features deny-warnings + run: cargo test --features deny-warnings --features internal-lints working-directory: clippy_lints - name: Test rustc_tools_util diff --git a/Cargo.toml b/Cargo.toml index 1ddcd18598d..a765390c603 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ path = "src/driver.rs" clippy_lints = { version = "0.0.212", path = "clippy_lints" } # end automatic update semver = "0.11" -rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util"} +rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util" } tempfile = { version = "3.1.0", optional = true } [dev-dependencies] @@ -49,8 +49,9 @@ derive-new = "0.5" rustc-workspace-hack = "1.0.0" [build-dependencies] -rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util"} +rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util" } [features] deny-warnings = [] integration = ["tempfile"] +internal-lints = ["clippy_lints/internal-lints"] diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index d9471d25197..969249cc446 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -36,3 +36,5 @@ syn = { version = "1", features = ["full"] } [features] deny-warnings = [] +# build clippy with internal lints enabled, off by default +internal-lints = [] diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 6eb5f6a7f48..a58f7eb3666 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -904,14 +904,23 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &unwrap_in_result::UNWRAP_IN_RESULT, &use_self::USE_SELF, &useless_conversion::USELESS_CONVERSION, + #[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::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, &vec::USELESS_VEC, &vec_resize_to_zero::VEC_RESIZE_TO_ZERO, @@ -932,11 +941,14 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: // end register lints, do not remove this comment, it’s used in `update_lints` store.register_late_pass(|| box await_holding_invalid::AwaitHolding); store.register_late_pass(|| box serde_api::SerdeAPI); - store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); - store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default()); - store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass); - store.register_late_pass(|| box utils::internal_lints::InvalidPaths); - store.register_late_pass(|| box utils::inspector::DeepCodeInspector); + #[cfg(feature = "internal-lints")] + { + store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); + store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default()); + store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass); + store.register_late_pass(|| box utils::internal_lints::InvalidPaths); + store.register_late_pass(|| box utils::inspector::DeepCodeInspector); + } store.register_late_pass(|| box utils::author::Author); let vec_box_size_threshold = conf.vec_box_size_threshold; store.register_late_pass(move || box types::Types::new(vec_box_size_threshold)); @@ -1122,6 +1134,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box literal_representation::LiteralDigitGrouping); let literal_representation_threshold = conf.literal_representation_threshold; store.register_early_pass(move || box literal_representation::DecimalLiteralRepresentation::new(literal_representation_threshold)); + #[cfg(feature = "internal-lints")] store.register_early_pass(|| box utils::internal_lints::ClippyLintsInternal); let enum_variant_name_threshold = conf.enum_variant_name_threshold; store.register_early_pass(move || box enum_variants::EnumVariantNames::new(enum_variant_name_threshold)); @@ -1136,6 +1149,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box large_const_arrays::LargeConstArrays::new(array_size_threshold)); store.register_late_pass(|| box floating_point_arithmetic::FloatingPointArithmetic); store.register_early_pass(|| box as_conversions::AsConversions); + #[cfg(feature = "internal-lints")] store.register_early_pass(|| box utils::internal_lints::ProduceIce); store.register_late_pass(|| box let_underscore::LetUnderscore); store.register_late_pass(|| box atomic_ordering::AtomicOrdering); @@ -1152,6 +1166,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box dereference::Dereferencing); store.register_late_pass(|| box option_if_let_else::OptionIfLetElse); store.register_late_pass(|| box future_not_send::FutureNotSend); + #[cfg(feature = "internal-lints")] store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls); store.register_late_pass(|| box if_let_mutex::IfLetMutex); store.register_late_pass(|| box mut_mutex_lock::MutMutexLock); @@ -1177,6 +1192,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box manual_ok_or::ManualOkOr); store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); + #[cfg(feature = "internal-lints")] store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem); let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::>(); store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods)); @@ -1317,7 +1333,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&wildcard_imports::ENUM_GLOB_USE), LintId::of(&wildcard_imports::WILDCARD_IMPORTS), ]); - + #[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), diff --git a/clippy_lints/src/utils/diagnostics.rs b/clippy_lints/src/utils/diagnostics.rs index 0a58231558e..a7a6b5855b7 100644 --- a/clippy_lints/src/utils/diagnostics.rs +++ b/clippy_lints/src/utils/diagnostics.rs @@ -186,7 +186,9 @@ pub fn span_lint_hir_and_then( /// | /// = note: `-D fold-any` implied by `-D warnings` /// ``` -#[allow(clippy::collapsible_span_lint_calls)] + +#[allow(clippy::unknown_clippy_lints)] +#[cfg_attr(feature = "internal-lints", allow(clippy::collapsible_span_lint_calls))] pub fn span_lint_and_sugg<'a, T: LintContext>( cx: &'a T, lint: &'static Lint, diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 850abc3bae7..63f14c592bd 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -14,6 +14,7 @@ pub mod eager_or_lazy; pub mod higher; mod hir_utils; pub mod inspector; +#[cfg(feature = "internal-lints")] pub mod internal_lints; pub mod numeric_literal; pub mod paths; diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 61aeabb7ba7..16e6a016c9e 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -31,6 +31,7 @@ pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"]; pub const DOUBLE_ENDED_ITERATOR: [&str; 4] = ["core", "iter", "traits", "DoubleEndedIterator"]; pub const DROP: [&str; 3] = ["core", "mem", "drop"]; pub const DURATION: [&str; 3] = ["core", "time", "Duration"]; +#[cfg(feature = "internal-lints")] pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"]; pub const EXIT: [&str; 3] = ["std", "process", "exit"]; pub const F32_EPSILON: [&str; 4] = ["core", "f32", "", "EPSILON"]; @@ -61,8 +62,10 @@ pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"]; pub const IPADDR_V4: [&str; 4] = ["std", "net", "IpAddr", "V4"]; pub const IPADDR_V6: [&str; 4] = ["std", "net", "IpAddr", "V6"]; pub const ITERATOR: [&str; 5] = ["core", "iter", "traits", "iterator", "Iterator"]; +#[cfg(feature = "internal-lints")] pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"]; pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"]; +#[cfg(feature = "internal-lints")] pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"]; pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"]; pub const MEM_FORGET: [&str; 3] = ["core", "mem", "forget"]; @@ -133,6 +136,7 @@ pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "", "ends_with"]; pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"]; pub const STR_LEN: [&str; 4] = ["core", "str", "", "len"]; pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "", "starts_with"]; +#[cfg(feature = "internal-lints")] pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]; pub const TO_OWNED: [&str; 3] = ["alloc", "borrow", "ToOwned"]; pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"]; diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 0e8f7683103..ec3af94b9ca 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -12,6 +12,9 @@ use std::path::{Path, PathBuf}; mod cargo; +// whether to run internal tests or not +const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal-lints"); + fn host_lib() -> PathBuf { option_env!("HOST_LIBS").map_or(cargo::CARGO_TARGET_DIR.join(env!("PROFILE")), PathBuf::from) } @@ -96,6 +99,16 @@ fn run_mode(cfg: &mut compiletest::Config) { compiletest::run_tests(&cfg); } +fn run_internal_tests(cfg: &mut compiletest::Config) { + // only run internal tests with the internal-tests feature + if !RUN_INTERNAL_TESTS { + return; + } + cfg.mode = TestMode::Ui; + cfg.src_base = Path::new("tests").join("ui-internal"); + compiletest::run_tests(&cfg); +} + fn run_ui_toml(config: &mut compiletest::Config) { fn run_tests(config: &compiletest::Config, mut tests: Vec) -> Result { let mut result = true; @@ -199,7 +212,6 @@ fn run_ui_cargo(config: &mut compiletest::Config) { Some("main.rs") => {}, _ => continue, } - let paths = compiletest::common::TestPaths { file: file_path, base: config.src_base.clone(), @@ -253,4 +265,5 @@ fn compile_test() { run_mode(&mut config); run_ui_toml(&mut config); run_ui_cargo(&mut config); + run_internal_tests(&mut config); } diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 48e0478f169..eae25adf839 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -18,20 +18,39 @@ fn dogfood_clippy() { } let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - let output = Command::new(&*CLIPPY_PATH) - .current_dir(root_dir) - .env("CLIPPY_DOGFOOD", "1") - .env("CARGO_INCREMENTAL", "0") - .arg("clippy-preview") - .arg("--all-targets") - .arg("--all-features") - .arg("--") - .args(&["-D", "clippy::all"]) - .args(&["-D", "clippy::internal"]) - .args(&["-D", "clippy::pedantic"]) - .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir - .output() - .unwrap(); + let output = if cfg!(feature = "internal-lints") { + // with internal lints and internal warnings + Command::new(&*CLIPPY_PATH) + .current_dir(root_dir) + .env("CLIPPY_DOGFOOD", "1") + .env("CARGO_INCREMENTAL", "0") + .arg("clippy-preview") + .arg("--all-targets") + .arg("--all-features") + .args(&["--features", "internal-lints"]) + .arg("--") + .args(&["-D", "clippy::all"]) + .args(&["-D", "clippy::pedantic"]) + .args(&["-D", "clippy::internal"]) + .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir + .output() + .unwrap() + } else { + // without internal lints or warnings + Command::new(&*CLIPPY_PATH) + .current_dir(root_dir) + .env("CLIPPY_DOGFOOD", "1") + .env("CARGO_INCREMENTAL", "0") + .arg("clippy-preview") + .arg("--all-targets") + .arg("--all-features") + .arg("--") + .args(&["-D", "clippy::all"]) + .args(&["-D", "clippy::pedantic"]) + .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir + .output() + .unwrap() + }; println!("status: {}", output.status); println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); diff --git a/tests/ui-internal/collapsible_span_lint_calls.fixed b/tests/ui-internal/collapsible_span_lint_calls.fixed new file mode 100644 index 00000000000..e588c23345e --- /dev/null +++ b/tests/ui-internal/collapsible_span_lint_calls.fixed @@ -0,0 +1,91 @@ +// run-rustfix +#![deny(clippy::internal)] +#![feature(rustc_private)] + +extern crate rustc_ast; +extern crate rustc_errors; +extern crate rustc_lint; +extern crate rustc_session; +extern crate rustc_span; + +use rustc_ast::ast::Expr; +use rustc_errors::{Applicability, DiagnosticBuilder}; +use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; + +#[allow(unused_variables)] +pub fn span_lint_and_then<'a, T: LintContext, F>(cx: &'a T, lint: &'static Lint, sp: Span, msg: &str, f: F) +where + F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>), +{ +} + +#[allow(unused_variables)] +fn span_lint_and_help<'a, T: LintContext>( + cx: &'a T, + lint: &'static Lint, + span: Span, + msg: &str, + option_span: Option, + help: &str, +) { +} + +#[allow(unused_variables)] +fn span_lint_and_note<'a, T: LintContext>( + cx: &'a T, + lint: &'static Lint, + span: Span, + msg: &str, + note_span: Option, + note: &str, +) { +} + +#[allow(unused_variables)] +fn span_lint_and_sugg<'a, T: LintContext>( + cx: &'a T, + lint: &'static Lint, + sp: Span, + msg: &str, + help: &str, + sugg: String, + applicability: Applicability, +) { +} + +declare_tool_lint! { + pub clippy::TEST_LINT, + Warn, + "", + report_in_external_macro: true +} + +declare_lint_pass!(Pass => [TEST_LINT]); + +impl EarlyLintPass for Pass { + fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) { + let lint_msg = "lint message"; + let help_msg = "help message"; + let note_msg = "note message"; + let sugg = "new_call()"; + let predicate = true; + + span_lint_and_sugg(cx, TEST_LINT, expr.span, lint_msg, help_msg, sugg.to_string(), Applicability::MachineApplicable); + span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg); + span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg); + span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg); + span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg); + + // This expr shouldn't trigger this lint. + span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + db.note(note_msg); + if predicate { + db.note(note_msg); + } + }) + } +} + +fn main() {} diff --git a/tests/ui-internal/collapsible_span_lint_calls.rs b/tests/ui-internal/collapsible_span_lint_calls.rs new file mode 100644 index 00000000000..d5dd3bb562b --- /dev/null +++ b/tests/ui-internal/collapsible_span_lint_calls.rs @@ -0,0 +1,101 @@ +// run-rustfix +#![deny(clippy::internal)] +#![feature(rustc_private)] + +extern crate rustc_ast; +extern crate rustc_errors; +extern crate rustc_lint; +extern crate rustc_session; +extern crate rustc_span; + +use rustc_ast::ast::Expr; +use rustc_errors::{Applicability, DiagnosticBuilder}; +use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; + +#[allow(unused_variables)] +pub fn span_lint_and_then<'a, T: LintContext, F>(cx: &'a T, lint: &'static Lint, sp: Span, msg: &str, f: F) +where + F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>), +{ +} + +#[allow(unused_variables)] +fn span_lint_and_help<'a, T: LintContext>( + cx: &'a T, + lint: &'static Lint, + span: Span, + msg: &str, + option_span: Option, + help: &str, +) { +} + +#[allow(unused_variables)] +fn span_lint_and_note<'a, T: LintContext>( + cx: &'a T, + lint: &'static Lint, + span: Span, + msg: &str, + note_span: Option, + note: &str, +) { +} + +#[allow(unused_variables)] +fn span_lint_and_sugg<'a, T: LintContext>( + cx: &'a T, + lint: &'static Lint, + sp: Span, + msg: &str, + help: &str, + sugg: String, + applicability: Applicability, +) { +} + +declare_tool_lint! { + pub clippy::TEST_LINT, + Warn, + "", + report_in_external_macro: true +} + +declare_lint_pass!(Pass => [TEST_LINT]); + +impl EarlyLintPass for Pass { + fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) { + let lint_msg = "lint message"; + let help_msg = "help message"; + let note_msg = "note message"; + let sugg = "new_call()"; + let predicate = true; + + span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + db.span_suggestion(expr.span, help_msg, sugg.to_string(), Applicability::MachineApplicable); + }); + span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + db.span_help(expr.span, help_msg); + }); + span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + db.help(help_msg); + }); + span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + db.span_note(expr.span, note_msg); + }); + span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + db.note(note_msg); + }); + + // This expr shouldn't trigger this lint. + span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { + db.note(note_msg); + if predicate { + db.note(note_msg); + } + }) + } +} + +fn main() {} diff --git a/tests/ui-internal/collapsible_span_lint_calls.stderr b/tests/ui-internal/collapsible_span_lint_calls.stderr new file mode 100644 index 00000000000..874d4a9f255 --- /dev/null +++ b/tests/ui-internal/collapsible_span_lint_calls.stderr @@ -0,0 +1,49 @@ +error: this call is collapsible + --> $DIR/collapsible_span_lint_calls.rs:75:9 + | +LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { +LL | | db.span_suggestion(expr.span, help_msg, sugg.to_string(), Applicability::MachineApplicable); +LL | | }); + | |__________^ help: collapse into: `span_lint_and_sugg(cx, TEST_LINT, expr.span, lint_msg, help_msg, sugg.to_string(), Applicability::MachineApplicable)` + | +note: the lint level is defined here + --> $DIR/collapsible_span_lint_calls.rs:2:9 + | +LL | #![deny(clippy::internal)] + | ^^^^^^^^^^^^^^^^ + = note: `#[deny(clippy::collapsible_span_lint_calls)]` implied by `#[deny(clippy::internal)]` + +error: this call is collapsible + --> $DIR/collapsible_span_lint_calls.rs:78:9 + | +LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { +LL | | db.span_help(expr.span, help_msg); +LL | | }); + | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg)` + +error: this call is collapsible + --> $DIR/collapsible_span_lint_calls.rs:81:9 + | +LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { +LL | | db.help(help_msg); +LL | | }); + | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg)` + +error: this call is collspible + --> $DIR/collapsible_span_lint_calls.rs:84:9 + | +LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { +LL | | db.span_note(expr.span, note_msg); +LL | | }); + | |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg)` + +error: this call is collspible + --> $DIR/collapsible_span_lint_calls.rs:87:9 + | +LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { +LL | | db.note(note_msg); +LL | | }); + | |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg)` + +error: aborting due to 5 previous errors + diff --git a/tests/ui-internal/custom_ice_message.rs b/tests/ui-internal/custom_ice_message.rs new file mode 100644 index 00000000000..5b30c9d5721 --- /dev/null +++ b/tests/ui-internal/custom_ice_message.rs @@ -0,0 +1,10 @@ +// rustc-env:RUST_BACKTRACE=0 +// normalize-stderr-test: "Clippy version: .*" -> "Clippy version: foo" +// normalize-stderr-test: "internal_lints.rs:\d*:\d*" -> "internal_lints.rs" +// normalize-stderr-test: "', .*clippy_lints" -> "', clippy_lints" + +#![deny(clippy::internal)] + +fn it_looks_like_you_are_trying_to_kill_clippy() {} + +fn main() {} diff --git a/tests/ui-internal/custom_ice_message.stderr b/tests/ui-internal/custom_ice_message.stderr new file mode 100644 index 00000000000..a1b8e2ee162 --- /dev/null +++ b/tests/ui-internal/custom_ice_message.stderr @@ -0,0 +1,13 @@ +thread 'rustc' panicked at 'Would you like some help with that?', clippy_lints/src/utils/internal_lints.rs +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace + +error: internal compiler error: unexpected panic + +note: the compiler unexpectedly panicked. this is a bug. + +note: we would appreciate a bug report: https://github.com/rust-lang/rust-clippy/issues/new + +note: Clippy version: foo + +query stack during panic: +end of query stack diff --git a/tests/ui-internal/default_lint.rs b/tests/ui-internal/default_lint.rs new file mode 100644 index 00000000000..053faae02ce --- /dev/null +++ b/tests/ui-internal/default_lint.rs @@ -0,0 +1,27 @@ +#![deny(clippy::internal)] +#![feature(rustc_private)] + +#[macro_use] +extern crate rustc_middle; +#[macro_use] +extern crate rustc_session; +extern crate rustc_lint; + +declare_tool_lint! { + pub clippy::TEST_LINT, + Warn, + "", + report_in_external_macro: true +} + +declare_tool_lint! { + pub clippy::TEST_LINT_DEFAULT, + Warn, + "default lint description", + report_in_external_macro: true +} + +declare_lint_pass!(Pass => [TEST_LINT]); +declare_lint_pass!(Pass2 => [TEST_LINT_DEFAULT]); + +fn main() {} diff --git a/tests/ui-internal/default_lint.stderr b/tests/ui-internal/default_lint.stderr new file mode 100644 index 00000000000..5c5836a7d29 --- /dev/null +++ b/tests/ui-internal/default_lint.stderr @@ -0,0 +1,21 @@ +error: the lint `TEST_LINT_DEFAULT` has the default lint description + --> $DIR/default_lint.rs:17:1 + | +LL | / declare_tool_lint! { +LL | | pub clippy::TEST_LINT_DEFAULT, +LL | | Warn, +LL | | "default lint description", +LL | | report_in_external_macro: true +LL | | } + | |_^ + | +note: the lint level is defined here + --> $DIR/default_lint.rs:1:9 + | +LL | #![deny(clippy::internal)] + | ^^^^^^^^^^^^^^^^ + = note: `#[deny(clippy::default_lint)]` implied by `#[deny(clippy::internal)]` + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error + diff --git a/tests/ui-internal/invalid_paths.rs b/tests/ui-internal/invalid_paths.rs new file mode 100644 index 00000000000..01e28ae5e9d --- /dev/null +++ b/tests/ui-internal/invalid_paths.rs @@ -0,0 +1,23 @@ +#![warn(clippy::internal)] + +mod paths { + // Good path + pub const ANY_TRAIT: [&str; 3] = ["std", "any", "Any"]; + + // Path to method on inherent impl of a primitive type + pub const F32_EPSILON: [&str; 4] = ["core", "f32", "", "EPSILON"]; + + // Path to method on inherent impl + pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"]; + + // Path with empty segment + pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"]; + + // Path with bad crate + pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"]; + + // Path with bad module + pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"]; +} + +fn main() {} diff --git a/tests/ui-internal/invalid_paths.stderr b/tests/ui-internal/invalid_paths.stderr new file mode 100644 index 00000000000..bd69d661b71 --- /dev/null +++ b/tests/ui-internal/invalid_paths.stderr @@ -0,0 +1,16 @@ +error: invalid path + --> $DIR/invalid_paths.rs:17:5 + | +LL | pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::clippy-lints-internal` implied by `-D warnings` + +error: invalid path + --> $DIR/invalid_paths.rs:20:5 + | +LL | pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui-internal/lint_without_lint_pass.rs b/tests/ui-internal/lint_without_lint_pass.rs new file mode 100644 index 00000000000..beaef79a340 --- /dev/null +++ b/tests/ui-internal/lint_without_lint_pass.rs @@ -0,0 +1,44 @@ +#![deny(clippy::internal)] +#![feature(rustc_private)] + +#[macro_use] +extern crate rustc_middle; +#[macro_use] +extern crate rustc_session; +extern crate rustc_lint; +use rustc_lint::LintPass; + +declare_tool_lint! { + pub clippy::TEST_LINT, + Warn, + "", + report_in_external_macro: true +} + +declare_tool_lint! { + pub clippy::TEST_LINT_REGISTERED, + Warn, + "", + report_in_external_macro: true +} + +declare_tool_lint! { + pub clippy::TEST_LINT_REGISTERED_ONLY_IMPL, + Warn, + "", + report_in_external_macro: true +} + +pub struct Pass; +impl LintPass for Pass { + fn name(&self) -> &'static str { + "TEST_LINT" + } +} + +declare_lint_pass!(Pass2 => [TEST_LINT_REGISTERED]); + +pub struct Pass3; +impl_lint_pass!(Pass3 => [TEST_LINT_REGISTERED_ONLY_IMPL]); + +fn main() {} diff --git a/tests/ui-internal/lint_without_lint_pass.stderr b/tests/ui-internal/lint_without_lint_pass.stderr new file mode 100644 index 00000000000..1257dae96d7 --- /dev/null +++ b/tests/ui-internal/lint_without_lint_pass.stderr @@ -0,0 +1,21 @@ +error: the lint `TEST_LINT` is not added to any `LintPass` + --> $DIR/lint_without_lint_pass.rs:11:1 + | +LL | / declare_tool_lint! { +LL | | pub clippy::TEST_LINT, +LL | | Warn, +LL | | "", +LL | | report_in_external_macro: true +LL | | } + | |_^ + | +note: the lint level is defined here + --> $DIR/lint_without_lint_pass.rs:1:9 + | +LL | #![deny(clippy::internal)] + | ^^^^^^^^^^^^^^^^ + = note: `#[deny(clippy::lint_without_lint_pass)]` implied by `#[deny(clippy::internal)]` + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error + diff --git a/tests/ui-internal/match_type_on_diag_item.rs b/tests/ui-internal/match_type_on_diag_item.rs new file mode 100644 index 00000000000..fe950b0aa7c --- /dev/null +++ b/tests/ui-internal/match_type_on_diag_item.rs @@ -0,0 +1,50 @@ +#![deny(clippy::internal)] +#![feature(rustc_private)] + +extern crate rustc_hir; +extern crate rustc_lint; +extern crate rustc_middle; +#[macro_use] +extern crate rustc_session; +use rustc_hir::Expr; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::Ty; + +mod paths { + pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"]; +} + +mod utils { + use super::*; + + pub fn match_type(_cx: &LateContext<'_>, _ty: Ty<'_>, _path: &[&str]) -> bool { + false + } +} + +use utils::match_type; + +declare_lint! { + pub TEST_LINT, + Warn, + "" +} + +declare_lint_pass!(Pass => [TEST_LINT]); + +static OPTION: [&str; 3] = ["core", "option", "Option"]; + +impl<'tcx> LateLintPass<'tcx> for Pass { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr) { + let ty = cx.typeck_results().expr_ty(expr); + + let _ = match_type(cx, ty, &paths::VEC); + let _ = match_type(cx, ty, &OPTION); + let _ = match_type(cx, ty, &["core", "result", "Result"]); + + let rc_path = &["alloc", "rc", "Rc"]; + let _ = utils::match_type(cx, ty, rc_path); + } +} + +fn main() {} diff --git a/tests/ui-internal/match_type_on_diag_item.stderr b/tests/ui-internal/match_type_on_diag_item.stderr new file mode 100644 index 00000000000..82465dbaf6e --- /dev/null +++ b/tests/ui-internal/match_type_on_diag_item.stderr @@ -0,0 +1,33 @@ +error: usage of `utils::match_type()` on a type diagnostic item + --> $DIR/match_type_on_diag_item.rs:41:17 + | +LL | let _ = match_type(cx, ty, &paths::VEC); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::vec_type)` + | +note: the lint level is defined here + --> $DIR/match_type_on_diag_item.rs:1:9 + | +LL | #![deny(clippy::internal)] + | ^^^^^^^^^^^^^^^^ + = note: `#[deny(clippy::match_type_on_diagnostic_item)]` implied by `#[deny(clippy::internal)]` + +error: usage of `utils::match_type()` on a type diagnostic item + --> $DIR/match_type_on_diag_item.rs:42:17 + | +LL | let _ = match_type(cx, ty, &OPTION); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::option_type)` + +error: usage of `utils::match_type()` on a type diagnostic item + --> $DIR/match_type_on_diag_item.rs:43:17 + | +LL | let _ = match_type(cx, ty, &["core", "result", "Result"]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::result_type)` + +error: usage of `utils::match_type()` on a type diagnostic item + --> $DIR/match_type_on_diag_item.rs:46:17 + | +LL | let _ = utils::match_type(cx, ty, rc_path); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::Rc)` + +error: aborting due to 4 previous errors + diff --git a/tests/ui-internal/outer_expn_data.fixed b/tests/ui-internal/outer_expn_data.fixed new file mode 100644 index 00000000000..b0b3498f057 --- /dev/null +++ b/tests/ui-internal/outer_expn_data.fixed @@ -0,0 +1,28 @@ +// run-rustfix + +#![deny(clippy::internal)] +#![feature(rustc_private)] + +extern crate rustc_hir; +extern crate rustc_lint; +extern crate rustc_middle; +#[macro_use] +extern crate rustc_session; +use rustc_hir::Expr; +use rustc_lint::{LateContext, LateLintPass}; + +declare_lint! { + pub TEST_LINT, + Warn, + "" +} + +declare_lint_pass!(Pass => [TEST_LINT]); + +impl<'tcx> LateLintPass<'tcx> for Pass { + fn check_expr(&mut self, _cx: &LateContext<'tcx>, expr: &'tcx Expr) { + let _ = expr.span.ctxt().outer_expn_data(); + } +} + +fn main() {} diff --git a/tests/ui-internal/outer_expn_data.rs b/tests/ui-internal/outer_expn_data.rs new file mode 100644 index 00000000000..55a3fed00d0 --- /dev/null +++ b/tests/ui-internal/outer_expn_data.rs @@ -0,0 +1,28 @@ +// run-rustfix + +#![deny(clippy::internal)] +#![feature(rustc_private)] + +extern crate rustc_hir; +extern crate rustc_lint; +extern crate rustc_middle; +#[macro_use] +extern crate rustc_session; +use rustc_hir::Expr; +use rustc_lint::{LateContext, LateLintPass}; + +declare_lint! { + pub TEST_LINT, + Warn, + "" +} + +declare_lint_pass!(Pass => [TEST_LINT]); + +impl<'tcx> LateLintPass<'tcx> for Pass { + fn check_expr(&mut self, _cx: &LateContext<'tcx>, expr: &'tcx Expr) { + let _ = expr.span.ctxt().outer_expn().expn_data(); + } +} + +fn main() {} diff --git a/tests/ui-internal/outer_expn_data.stderr b/tests/ui-internal/outer_expn_data.stderr new file mode 100644 index 00000000000..56b6ce1f78e --- /dev/null +++ b/tests/ui-internal/outer_expn_data.stderr @@ -0,0 +1,15 @@ +error: usage of `outer_expn().expn_data()` + --> $DIR/outer_expn_data.rs:24:34 + | +LL | let _ = expr.span.ctxt().outer_expn().expn_data(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `outer_expn_data()` + | +note: the lint level is defined here + --> $DIR/outer_expn_data.rs:3:9 + | +LL | #![deny(clippy::internal)] + | ^^^^^^^^^^^^^^^^ + = note: `#[deny(clippy::outer_expn_expn_data)]` implied by `#[deny(clippy::internal)]` + +error: aborting due to previous error + diff --git a/tests/ui/collapsible_span_lint_calls.fixed b/tests/ui/collapsible_span_lint_calls.fixed deleted file mode 100644 index e588c23345e..00000000000 --- a/tests/ui/collapsible_span_lint_calls.fixed +++ /dev/null @@ -1,91 +0,0 @@ -// run-rustfix -#![deny(clippy::internal)] -#![feature(rustc_private)] - -extern crate rustc_ast; -extern crate rustc_errors; -extern crate rustc_lint; -extern crate rustc_session; -extern crate rustc_span; - -use rustc_ast::ast::Expr; -use rustc_errors::{Applicability, DiagnosticBuilder}; -use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Span; - -#[allow(unused_variables)] -pub fn span_lint_and_then<'a, T: LintContext, F>(cx: &'a T, lint: &'static Lint, sp: Span, msg: &str, f: F) -where - F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>), -{ -} - -#[allow(unused_variables)] -fn span_lint_and_help<'a, T: LintContext>( - cx: &'a T, - lint: &'static Lint, - span: Span, - msg: &str, - option_span: Option, - help: &str, -) { -} - -#[allow(unused_variables)] -fn span_lint_and_note<'a, T: LintContext>( - cx: &'a T, - lint: &'static Lint, - span: Span, - msg: &str, - note_span: Option, - note: &str, -) { -} - -#[allow(unused_variables)] -fn span_lint_and_sugg<'a, T: LintContext>( - cx: &'a T, - lint: &'static Lint, - sp: Span, - msg: &str, - help: &str, - sugg: String, - applicability: Applicability, -) { -} - -declare_tool_lint! { - pub clippy::TEST_LINT, - Warn, - "", - report_in_external_macro: true -} - -declare_lint_pass!(Pass => [TEST_LINT]); - -impl EarlyLintPass for Pass { - fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) { - let lint_msg = "lint message"; - let help_msg = "help message"; - let note_msg = "note message"; - let sugg = "new_call()"; - let predicate = true; - - span_lint_and_sugg(cx, TEST_LINT, expr.span, lint_msg, help_msg, sugg.to_string(), Applicability::MachineApplicable); - span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg); - span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg); - span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg); - span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg); - - // This expr shouldn't trigger this lint. - span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { - db.note(note_msg); - if predicate { - db.note(note_msg); - } - }) - } -} - -fn main() {} diff --git a/tests/ui/collapsible_span_lint_calls.rs b/tests/ui/collapsible_span_lint_calls.rs deleted file mode 100644 index d5dd3bb562b..00000000000 --- a/tests/ui/collapsible_span_lint_calls.rs +++ /dev/null @@ -1,101 +0,0 @@ -// run-rustfix -#![deny(clippy::internal)] -#![feature(rustc_private)] - -extern crate rustc_ast; -extern crate rustc_errors; -extern crate rustc_lint; -extern crate rustc_session; -extern crate rustc_span; - -use rustc_ast::ast::Expr; -use rustc_errors::{Applicability, DiagnosticBuilder}; -use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Span; - -#[allow(unused_variables)] -pub fn span_lint_and_then<'a, T: LintContext, F>(cx: &'a T, lint: &'static Lint, sp: Span, msg: &str, f: F) -where - F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>), -{ -} - -#[allow(unused_variables)] -fn span_lint_and_help<'a, T: LintContext>( - cx: &'a T, - lint: &'static Lint, - span: Span, - msg: &str, - option_span: Option, - help: &str, -) { -} - -#[allow(unused_variables)] -fn span_lint_and_note<'a, T: LintContext>( - cx: &'a T, - lint: &'static Lint, - span: Span, - msg: &str, - note_span: Option, - note: &str, -) { -} - -#[allow(unused_variables)] -fn span_lint_and_sugg<'a, T: LintContext>( - cx: &'a T, - lint: &'static Lint, - sp: Span, - msg: &str, - help: &str, - sugg: String, - applicability: Applicability, -) { -} - -declare_tool_lint! { - pub clippy::TEST_LINT, - Warn, - "", - report_in_external_macro: true -} - -declare_lint_pass!(Pass => [TEST_LINT]); - -impl EarlyLintPass for Pass { - fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) { - let lint_msg = "lint message"; - let help_msg = "help message"; - let note_msg = "note message"; - let sugg = "new_call()"; - let predicate = true; - - span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { - db.span_suggestion(expr.span, help_msg, sugg.to_string(), Applicability::MachineApplicable); - }); - span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { - db.span_help(expr.span, help_msg); - }); - span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { - db.help(help_msg); - }); - span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { - db.span_note(expr.span, note_msg); - }); - span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { - db.note(note_msg); - }); - - // This expr shouldn't trigger this lint. - span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { - db.note(note_msg); - if predicate { - db.note(note_msg); - } - }) - } -} - -fn main() {} diff --git a/tests/ui/collapsible_span_lint_calls.stderr b/tests/ui/collapsible_span_lint_calls.stderr deleted file mode 100644 index 874d4a9f255..00000000000 --- a/tests/ui/collapsible_span_lint_calls.stderr +++ /dev/null @@ -1,49 +0,0 @@ -error: this call is collapsible - --> $DIR/collapsible_span_lint_calls.rs:75:9 - | -LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { -LL | | db.span_suggestion(expr.span, help_msg, sugg.to_string(), Applicability::MachineApplicable); -LL | | }); - | |__________^ help: collapse into: `span_lint_and_sugg(cx, TEST_LINT, expr.span, lint_msg, help_msg, sugg.to_string(), Applicability::MachineApplicable)` - | -note: the lint level is defined here - --> $DIR/collapsible_span_lint_calls.rs:2:9 - | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::collapsible_span_lint_calls)]` implied by `#[deny(clippy::internal)]` - -error: this call is collapsible - --> $DIR/collapsible_span_lint_calls.rs:78:9 - | -LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { -LL | | db.span_help(expr.span, help_msg); -LL | | }); - | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg)` - -error: this call is collapsible - --> $DIR/collapsible_span_lint_calls.rs:81:9 - | -LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { -LL | | db.help(help_msg); -LL | | }); - | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg)` - -error: this call is collspible - --> $DIR/collapsible_span_lint_calls.rs:84:9 - | -LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { -LL | | db.span_note(expr.span, note_msg); -LL | | }); - | |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg)` - -error: this call is collspible - --> $DIR/collapsible_span_lint_calls.rs:87:9 - | -LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { -LL | | db.note(note_msg); -LL | | }); - | |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg)` - -error: aborting due to 5 previous errors - diff --git a/tests/ui/custom_ice_message.rs b/tests/ui/custom_ice_message.rs deleted file mode 100644 index 5b30c9d5721..00000000000 --- a/tests/ui/custom_ice_message.rs +++ /dev/null @@ -1,10 +0,0 @@ -// rustc-env:RUST_BACKTRACE=0 -// normalize-stderr-test: "Clippy version: .*" -> "Clippy version: foo" -// normalize-stderr-test: "internal_lints.rs:\d*:\d*" -> "internal_lints.rs" -// normalize-stderr-test: "', .*clippy_lints" -> "', clippy_lints" - -#![deny(clippy::internal)] - -fn it_looks_like_you_are_trying_to_kill_clippy() {} - -fn main() {} diff --git a/tests/ui/custom_ice_message.stderr b/tests/ui/custom_ice_message.stderr deleted file mode 100644 index a1b8e2ee162..00000000000 --- a/tests/ui/custom_ice_message.stderr +++ /dev/null @@ -1,13 +0,0 @@ -thread 'rustc' panicked at 'Would you like some help with that?', clippy_lints/src/utils/internal_lints.rs -note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace - -error: internal compiler error: unexpected panic - -note: the compiler unexpectedly panicked. this is a bug. - -note: we would appreciate a bug report: https://github.com/rust-lang/rust-clippy/issues/new - -note: Clippy version: foo - -query stack during panic: -end of query stack diff --git a/tests/ui/default_lint.rs b/tests/ui/default_lint.rs deleted file mode 100644 index 053faae02ce..00000000000 --- a/tests/ui/default_lint.rs +++ /dev/null @@ -1,27 +0,0 @@ -#![deny(clippy::internal)] -#![feature(rustc_private)] - -#[macro_use] -extern crate rustc_middle; -#[macro_use] -extern crate rustc_session; -extern crate rustc_lint; - -declare_tool_lint! { - pub clippy::TEST_LINT, - Warn, - "", - report_in_external_macro: true -} - -declare_tool_lint! { - pub clippy::TEST_LINT_DEFAULT, - Warn, - "default lint description", - report_in_external_macro: true -} - -declare_lint_pass!(Pass => [TEST_LINT]); -declare_lint_pass!(Pass2 => [TEST_LINT_DEFAULT]); - -fn main() {} diff --git a/tests/ui/default_lint.stderr b/tests/ui/default_lint.stderr deleted file mode 100644 index 5c5836a7d29..00000000000 --- a/tests/ui/default_lint.stderr +++ /dev/null @@ -1,21 +0,0 @@ -error: the lint `TEST_LINT_DEFAULT` has the default lint description - --> $DIR/default_lint.rs:17:1 - | -LL | / declare_tool_lint! { -LL | | pub clippy::TEST_LINT_DEFAULT, -LL | | Warn, -LL | | "default lint description", -LL | | report_in_external_macro: true -LL | | } - | |_^ - | -note: the lint level is defined here - --> $DIR/default_lint.rs:1:9 - | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::default_lint)]` implied by `#[deny(clippy::internal)]` - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to previous error - diff --git a/tests/ui/invalid_paths.rs b/tests/ui/invalid_paths.rs deleted file mode 100644 index 01e28ae5e9d..00000000000 --- a/tests/ui/invalid_paths.rs +++ /dev/null @@ -1,23 +0,0 @@ -#![warn(clippy::internal)] - -mod paths { - // Good path - pub const ANY_TRAIT: [&str; 3] = ["std", "any", "Any"]; - - // Path to method on inherent impl of a primitive type - pub const F32_EPSILON: [&str; 4] = ["core", "f32", "", "EPSILON"]; - - // Path to method on inherent impl - pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"]; - - // Path with empty segment - pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"]; - - // Path with bad crate - pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"]; - - // Path with bad module - pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"]; -} - -fn main() {} diff --git a/tests/ui/invalid_paths.stderr b/tests/ui/invalid_paths.stderr deleted file mode 100644 index bd69d661b71..00000000000 --- a/tests/ui/invalid_paths.stderr +++ /dev/null @@ -1,16 +0,0 @@ -error: invalid path - --> $DIR/invalid_paths.rs:17:5 - | -LL | pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::clippy-lints-internal` implied by `-D warnings` - -error: invalid path - --> $DIR/invalid_paths.rs:20:5 - | -LL | pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 2 previous errors - diff --git a/tests/ui/lint_without_lint_pass.rs b/tests/ui/lint_without_lint_pass.rs deleted file mode 100644 index beaef79a340..00000000000 --- a/tests/ui/lint_without_lint_pass.rs +++ /dev/null @@ -1,44 +0,0 @@ -#![deny(clippy::internal)] -#![feature(rustc_private)] - -#[macro_use] -extern crate rustc_middle; -#[macro_use] -extern crate rustc_session; -extern crate rustc_lint; -use rustc_lint::LintPass; - -declare_tool_lint! { - pub clippy::TEST_LINT, - Warn, - "", - report_in_external_macro: true -} - -declare_tool_lint! { - pub clippy::TEST_LINT_REGISTERED, - Warn, - "", - report_in_external_macro: true -} - -declare_tool_lint! { - pub clippy::TEST_LINT_REGISTERED_ONLY_IMPL, - Warn, - "", - report_in_external_macro: true -} - -pub struct Pass; -impl LintPass for Pass { - fn name(&self) -> &'static str { - "TEST_LINT" - } -} - -declare_lint_pass!(Pass2 => [TEST_LINT_REGISTERED]); - -pub struct Pass3; -impl_lint_pass!(Pass3 => [TEST_LINT_REGISTERED_ONLY_IMPL]); - -fn main() {} diff --git a/tests/ui/lint_without_lint_pass.stderr b/tests/ui/lint_without_lint_pass.stderr deleted file mode 100644 index 1257dae96d7..00000000000 --- a/tests/ui/lint_without_lint_pass.stderr +++ /dev/null @@ -1,21 +0,0 @@ -error: the lint `TEST_LINT` is not added to any `LintPass` - --> $DIR/lint_without_lint_pass.rs:11:1 - | -LL | / declare_tool_lint! { -LL | | pub clippy::TEST_LINT, -LL | | Warn, -LL | | "", -LL | | report_in_external_macro: true -LL | | } - | |_^ - | -note: the lint level is defined here - --> $DIR/lint_without_lint_pass.rs:1:9 - | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::lint_without_lint_pass)]` implied by `#[deny(clippy::internal)]` - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to previous error - diff --git a/tests/ui/match_type_on_diag_item.rs b/tests/ui/match_type_on_diag_item.rs deleted file mode 100644 index fe950b0aa7c..00000000000 --- a/tests/ui/match_type_on_diag_item.rs +++ /dev/null @@ -1,50 +0,0 @@ -#![deny(clippy::internal)] -#![feature(rustc_private)] - -extern crate rustc_hir; -extern crate rustc_lint; -extern crate rustc_middle; -#[macro_use] -extern crate rustc_session; -use rustc_hir::Expr; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::Ty; - -mod paths { - pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"]; -} - -mod utils { - use super::*; - - pub fn match_type(_cx: &LateContext<'_>, _ty: Ty<'_>, _path: &[&str]) -> bool { - false - } -} - -use utils::match_type; - -declare_lint! { - pub TEST_LINT, - Warn, - "" -} - -declare_lint_pass!(Pass => [TEST_LINT]); - -static OPTION: [&str; 3] = ["core", "option", "Option"]; - -impl<'tcx> LateLintPass<'tcx> for Pass { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr) { - let ty = cx.typeck_results().expr_ty(expr); - - let _ = match_type(cx, ty, &paths::VEC); - let _ = match_type(cx, ty, &OPTION); - let _ = match_type(cx, ty, &["core", "result", "Result"]); - - let rc_path = &["alloc", "rc", "Rc"]; - let _ = utils::match_type(cx, ty, rc_path); - } -} - -fn main() {} diff --git a/tests/ui/match_type_on_diag_item.stderr b/tests/ui/match_type_on_diag_item.stderr deleted file mode 100644 index 82465dbaf6e..00000000000 --- a/tests/ui/match_type_on_diag_item.stderr +++ /dev/null @@ -1,33 +0,0 @@ -error: usage of `utils::match_type()` on a type diagnostic item - --> $DIR/match_type_on_diag_item.rs:41:17 - | -LL | let _ = match_type(cx, ty, &paths::VEC); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::vec_type)` - | -note: the lint level is defined here - --> $DIR/match_type_on_diag_item.rs:1:9 - | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::match_type_on_diagnostic_item)]` implied by `#[deny(clippy::internal)]` - -error: usage of `utils::match_type()` on a type diagnostic item - --> $DIR/match_type_on_diag_item.rs:42:17 - | -LL | let _ = match_type(cx, ty, &OPTION); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::option_type)` - -error: usage of `utils::match_type()` on a type diagnostic item - --> $DIR/match_type_on_diag_item.rs:43:17 - | -LL | let _ = match_type(cx, ty, &["core", "result", "Result"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::result_type)` - -error: usage of `utils::match_type()` on a type diagnostic item - --> $DIR/match_type_on_diag_item.rs:46:17 - | -LL | let _ = utils::match_type(cx, ty, rc_path); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::Rc)` - -error: aborting due to 4 previous errors - diff --git a/tests/ui/outer_expn_data.fixed b/tests/ui/outer_expn_data.fixed deleted file mode 100644 index b0b3498f057..00000000000 --- a/tests/ui/outer_expn_data.fixed +++ /dev/null @@ -1,28 +0,0 @@ -// run-rustfix - -#![deny(clippy::internal)] -#![feature(rustc_private)] - -extern crate rustc_hir; -extern crate rustc_lint; -extern crate rustc_middle; -#[macro_use] -extern crate rustc_session; -use rustc_hir::Expr; -use rustc_lint::{LateContext, LateLintPass}; - -declare_lint! { - pub TEST_LINT, - Warn, - "" -} - -declare_lint_pass!(Pass => [TEST_LINT]); - -impl<'tcx> LateLintPass<'tcx> for Pass { - fn check_expr(&mut self, _cx: &LateContext<'tcx>, expr: &'tcx Expr) { - let _ = expr.span.ctxt().outer_expn_data(); - } -} - -fn main() {} diff --git a/tests/ui/outer_expn_data.rs b/tests/ui/outer_expn_data.rs deleted file mode 100644 index 55a3fed00d0..00000000000 --- a/tests/ui/outer_expn_data.rs +++ /dev/null @@ -1,28 +0,0 @@ -// run-rustfix - -#![deny(clippy::internal)] -#![feature(rustc_private)] - -extern crate rustc_hir; -extern crate rustc_lint; -extern crate rustc_middle; -#[macro_use] -extern crate rustc_session; -use rustc_hir::Expr; -use rustc_lint::{LateContext, LateLintPass}; - -declare_lint! { - pub TEST_LINT, - Warn, - "" -} - -declare_lint_pass!(Pass => [TEST_LINT]); - -impl<'tcx> LateLintPass<'tcx> for Pass { - fn check_expr(&mut self, _cx: &LateContext<'tcx>, expr: &'tcx Expr) { - let _ = expr.span.ctxt().outer_expn().expn_data(); - } -} - -fn main() {} diff --git a/tests/ui/outer_expn_data.stderr b/tests/ui/outer_expn_data.stderr deleted file mode 100644 index 56b6ce1f78e..00000000000 --- a/tests/ui/outer_expn_data.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: usage of `outer_expn().expn_data()` - --> $DIR/outer_expn_data.rs:24:34 - | -LL | let _ = expr.span.ctxt().outer_expn().expn_data(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `outer_expn_data()` - | -note: the lint level is defined here - --> $DIR/outer_expn_data.rs:3:9 - | -LL | #![deny(clippy::internal)] - | ^^^^^^^^^^^^^^^^ - = note: `#[deny(clippy::outer_expn_expn_data)]` implied by `#[deny(clippy::internal)]` - -error: aborting due to previous error - -- cgit 1.4.1-3-g733a5 From 28dec3b708c3e0d5e45b6c70f054860cbd53d624 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 24 Nov 2020 20:37:07 -0600 Subject: Add collapsible_match lint --- CHANGELOG.md | 1 + clippy_lints/src/collapsible_match.rs | 172 +++++++++++++++++++++ clippy_lints/src/lib.rs | 5 + tests/ui/collapsible_match.rs | 278 ++++++++++++++++++++++++++++++++++ tests/ui/collapsible_match.stderr | 237 +++++++++++++++++++++++++++++ 5 files changed, 693 insertions(+) create mode 100644 clippy_lints/src/collapsible_match.rs create mode 100644 tests/ui/collapsible_match.rs create mode 100644 tests/ui/collapsible_match.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index e76a781f13b..e65e7cc639f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1770,6 +1770,7 @@ Released 2018-09-13 [`cmp_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_owned [`cognitive_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity [`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if +[`collapsible_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match [`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain [`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty [`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator diff --git a/clippy_lints/src/collapsible_match.rs b/clippy_lints/src/collapsible_match.rs new file mode 100644 index 00000000000..a34ba2d00a8 --- /dev/null +++ b/clippy_lints/src/collapsible_match.rs @@ -0,0 +1,172 @@ +use crate::utils::visitors::LocalUsedVisitor; +use crate::utils::{span_lint_and_then, SpanlessEq}; +use if_chain::if_chain; +use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; +use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, QPath, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{DefIdTree, TyCtxt}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{MultiSpan, Span}; + +declare_clippy_lint! { + /// **What it does:** Finds nested `match` or `if let` expressions where the patterns may be "collapsed" together + /// without adding any branches. + /// + /// Note that this lint is not intended to find _all_ cases where nested match patterns can be merged, but only + /// cases where merging would most likely make the code more readable. + /// + /// **Why is this bad?** It is unnecessarily verbose and complex. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// fn func(opt: Option>) { + /// let n = match opt { + /// Some(n) => match n { + /// Ok(n) => n, + /// _ => return, + /// } + /// None => return, + /// }; + /// } + /// ``` + /// Use instead: + /// ```rust + /// fn func(opt: Option>) { + /// let n = match opt { + /// Some(Ok(n)) => n, + /// _ => return, + /// }; + /// } + /// ``` + pub COLLAPSIBLE_MATCH, + style, + "Nested `match` or `if let` expressions where the patterns may be \"collapsed\" together." +} + +declare_lint_pass!(CollapsibleMatch => [COLLAPSIBLE_MATCH]); + +impl<'tcx> LateLintPass<'tcx> for CollapsibleMatch { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { + if let ExprKind::Match(_expr, arms, _source) = expr.kind { + if let Some(wild_arm) = arms.iter().rfind(|arm| arm_is_wild_like(arm, cx.tcx)) { + for arm in arms { + check_arm(arm, wild_arm, cx); + } + } + } + } +} + +fn check_arm(arm: &Arm<'_>, wild_outer_arm: &Arm<'_>, cx: &LateContext<'_>) { + if_chain! { + let expr = strip_singleton_blocks(arm.body); + if let ExprKind::Match(expr_in, arms_inner, _) = expr.kind; + // the outer arm pattern and the inner match + if expr_in.span.ctxt() == arm.pat.span.ctxt(); + // there must be no more than two arms in the inner match for this lint + if arms_inner.len() == 2; + // no if guards on the inner match + if arms_inner.iter().all(|arm| arm.guard.is_none()); + // match expression must be a local binding + // match { .. } + if let ExprKind::Path(QPath::Resolved(None, path)) = expr_in.kind; + if let Res::Local(binding_id) = path.res; + // one of the branches must be "wild-like" + if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| arm_is_wild_like(arm_inner, cx.tcx)); + let (wild_inner_arm, non_wild_inner_arm) = + (&arms_inner[wild_inner_arm_idx], &arms_inner[1 - wild_inner_arm_idx]); + if !pat_contains_or(non_wild_inner_arm.pat); + // the binding must come from the pattern of the containing match arm + // .... => match { .. } + if let Some(binding_span) = find_pat_binding(arm.pat, binding_id); + // the "wild-like" branches must be equal + if SpanlessEq::new(cx).eq_expr(wild_inner_arm.body, wild_outer_arm.body); + // the binding must not be used in the if guard + if !matches!(arm.guard, Some(Guard::If(guard)) if LocalUsedVisitor::new(binding_id).check_expr(guard)); + // ...or anywhere in the inner match + if !arms_inner.iter().any(|arm| LocalUsedVisitor::new(binding_id).check_arm(arm)); + then { + span_lint_and_then( + cx, + COLLAPSIBLE_MATCH, + expr.span, + "Unnecessary nested match", + |diag| { + let mut help_span = MultiSpan::from_spans(vec![binding_span, non_wild_inner_arm.pat.span]); + help_span.push_span_label(binding_span, "Replace this binding".into()); + help_span.push_span_label(non_wild_inner_arm.pat.span, "with this pattern".into()); + diag.span_help(help_span, "The outer pattern can be modified to include the inner pattern."); + }, + ); + } + } +} + +fn strip_singleton_blocks<'hir>(mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> { + while let ExprKind::Block(block, _) = expr.kind { + match (block.stmts, block.expr) { + ([stmt], None) => match stmt.kind { + StmtKind::Expr(e) | StmtKind::Semi(e) => expr = e, + _ => break, + }, + ([], Some(e)) => expr = e, + _ => break, + } + } + expr +} + +/// A "wild-like" pattern is wild ("_") or `None`. +/// For this lint to apply, both the outer and inner match expressions +/// must have "wild-like" branches that can be combined. +fn arm_is_wild_like(arm: &Arm<'_>, tcx: TyCtxt<'_>) -> bool { + if arm.guard.is_some() { + return false; + } + match arm.pat.kind { + PatKind::Binding(..) | PatKind::Wild => true, + PatKind::Path(QPath::Resolved(None, path)) if is_none_ctor(path.res, tcx) => true, + _ => false, + } +} + +fn find_pat_binding(pat: &Pat<'_>, hir_id: HirId) -> Option { + let mut span = None; + pat.walk_short(|p| match &p.kind { + // ignore OR patterns + PatKind::Or(_) => false, + PatKind::Binding(_bm, _, _ident, _) => { + let found = p.hir_id == hir_id; + if found { + span = Some(p.span); + } + !found + }, + _ => true, + }); + span +} + +fn pat_contains_or(pat: &Pat<'_>) -> bool { + let mut result = false; + pat.walk(|p| { + let is_or = matches!(p.kind, PatKind::Or(_)); + result |= is_or; + !is_or + }); + result +} + +fn is_none_ctor(res: Res, tcx: TyCtxt<'_>) -> bool { + if let Some(none_id) = tcx.lang_items().option_none_variant() { + if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = res { + if let Some(variant_id) = tcx.parent(id) { + return variant_id == none_id; + } + } + } + false +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 6eb5f6a7f48..ca190986cdb 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -172,6 +172,7 @@ mod cargo_common_metadata; mod checked_conversions; mod cognitive_complexity; mod collapsible_if; +mod collapsible_match; mod comparison_chain; mod copies; mod copy_iterator; @@ -531,6 +532,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &checked_conversions::CHECKED_CONVERSIONS, &cognitive_complexity::COGNITIVE_COMPLEXITY, &collapsible_if::COLLAPSIBLE_IF, + &collapsible_match::COLLAPSIBLE_MATCH, &comparison_chain::COMPARISON_CHAIN, &copies::IFS_SAME_COND, &copies::IF_SAME_THEN_ELSE, @@ -960,6 +962,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box len_zero::LenZero); store.register_late_pass(|| box attrs::Attributes); store.register_late_pass(|| box blocks_in_if_conditions::BlocksInIfConditions); + store.register_late_pass(|| box collapsible_match::CollapsibleMatch); store.register_late_pass(|| box unicode::Unicode); store.register_late_pass(|| box unit_return_expecting_ord::UnitReturnExpectingOrd); store.register_late_pass(|| box strings::StringAdd); @@ -1351,6 +1354,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&booleans::NONMINIMAL_BOOL), LintId::of(&bytecount::NAIVE_BYTECOUNT), 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), @@ -1617,6 +1621,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&blacklisted_name::BLACKLISTED_NAME), LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), 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), diff --git a/tests/ui/collapsible_match.rs b/tests/ui/collapsible_match.rs new file mode 100644 index 00000000000..75640b70ff0 --- /dev/null +++ b/tests/ui/collapsible_match.rs @@ -0,0 +1,278 @@ +#![warn(clippy::collapsible_match)] +#![allow(clippy::needless_return, clippy::no_effect, clippy::single_match)] + +fn lint_cases(opt_opt: Option>, res_opt: Result, String>) { + // match without block + match res_opt { + Ok(val) => match val { + Some(n) => foo(n), + _ => return, + }, + _ => return, + } + + // match with block + match res_opt { + Ok(val) => match val { + Some(n) => foo(n), + _ => return, + }, + _ => return, + } + + // if let, if let + if let Ok(val) = res_opt { + if let Some(n) = val { + take(n); + } + } + + // if let else, if let else + if let Ok(val) = res_opt { + if let Some(n) = val { + take(n); + } else { + return; + } + } else { + return; + } + + // if let, match + if let Ok(val) = res_opt { + match val { + Some(n) => foo(n), + _ => (), + } + } + + // match, if let + match res_opt { + Ok(val) => { + if let Some(n) = val { + take(n); + } + }, + _ => {}, + } + + // if let else, match + if let Ok(val) = res_opt { + match val { + Some(n) => foo(n), + _ => return, + } + } else { + return; + } + + // match, if let else + match res_opt { + Ok(val) => { + if let Some(n) = val { + take(n); + } else { + return; + } + }, + _ => return, + } + + // None in inner match same as outer wild branch + match res_opt { + Ok(val) => match val { + Some(n) => foo(n), + None => return, + }, + _ => return, + } + + // None in outer match same as inner wild branch + match opt_opt { + Some(val) => match val { + Some(n) => foo(n), + _ => return, + }, + None => return, + } + + // if guards on outer match + { + match res_opt { + Ok(val) if make() => match val { + Some(n) => foo(n), + _ => return, + }, + _ => return, + } + match res_opt { + Ok(val) => match val { + Some(n) => foo(n), + _ => return, + }, + _ if make() => return, + _ => return, + } + } + + // macro + { + macro_rules! mac { + ($outer:expr => $pat:pat, $e:expr => $inner_pat:pat, $then:expr) => { + match $outer { + $pat => match $e { + $inner_pat => $then, + _ => return, + }, + _ => return, + } + }; + } + // Lint this since the patterns are not defined by the macro. + // Allows the lint to work on if_chain! for example. + // Fixing the lint requires knowledge of the specific macro, but we optimistically assume that + // there is still a better way to write this. + mac!(res_opt => Ok(val), val => Some(n), foo(n)); + } +} + +fn negative_cases(res_opt: Result, String>, res_res: Result, String>) { + // no wild pattern in outer match + match res_opt { + Ok(val) => match val { + Some(n) => foo(n), + _ => return, + }, + Err(_) => return, + } + + // inner branch is not wild or None + match res_res { + Ok(val) => match val { + Ok(n) => foo(n), + Err(_) => return, + }, + _ => return, + } + + // statement before inner match + match res_opt { + Ok(val) => { + "hi buddy"; + match val { + Some(n) => foo(n), + _ => return, + } + }, + _ => return, + } + + // statement after inner match + match res_opt { + Ok(val) => { + match val { + Some(n) => foo(n), + _ => return, + } + "hi buddy"; + }, + _ => return, + } + + // wild branches do not match + match res_opt { + Ok(val) => match val { + Some(n) => foo(n), + _ => { + "sup"; + return; + }, + }, + _ => return, + } + + // binding used in if guard + match res_opt { + Ok(val) if val.is_some() => match val { + Some(n) => foo(n), + _ => return, + }, + _ => return, + } + + // binding used in inner match body + match res_opt { + Ok(val) => match val { + Some(_) => take(val), + _ => return, + }, + _ => return, + } + + // if guard on inner match + { + match res_opt { + Ok(val) => match val { + Some(n) if make() => foo(n), + _ => return, + }, + _ => return, + } + match res_opt { + Ok(val) => match val { + _ => make(), + _ if make() => return, + }, + _ => return, + } + } + + // differing macro contexts + { + macro_rules! mac { + ($val:ident) => { + match $val { + Some(n) => foo(n), + _ => return, + } + }; + } + match res_opt { + Ok(val) => mac!(val), + _ => return, + } + } + + // OR pattern + enum E { + A(T), + B(T), + C(T), + }; + match make::>>() { + E::A(val) | E::B(val) => match val { + Some(n) => foo(n), + _ => return, + }, + _ => return, + } + match make::>>() { + Some(val) => match val { + E::A(val) | E::B(val) => foo(val), + _ => return, + }, + _ => return, + } +} + +fn make() -> T { + unimplemented!() +} + +fn foo(t: T) -> U { + unimplemented!() +} + +fn take(t: T) {} + +fn main() {} diff --git a/tests/ui/collapsible_match.stderr b/tests/ui/collapsible_match.stderr new file mode 100644 index 00000000000..a9e4f911914 --- /dev/null +++ b/tests/ui/collapsible_match.stderr @@ -0,0 +1,237 @@ +error: Unnecessary nested match + --> $DIR/collapsible_match.rs:7:20 + | +LL | Ok(val) => match val { + | ____________________^ +LL | | Some(n) => foo(n), +LL | | _ => return, +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 + | +LL | Ok(val) => match val { + | ^^^ Replace this binding +LL | Some(n) => foo(n), + | ^^^^^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match.rs:16:20 + | +LL | Ok(val) => match val { + | ____________________^ +LL | | Some(n) => foo(n), +LL | | _ => return, +LL | | }, + | |_________^ + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match.rs:16:12 + | +LL | Ok(val) => match val { + | ^^^ Replace this binding +LL | Some(n) => foo(n), + | ^^^^^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match.rs:25:9 + | +LL | / if let Some(n) = val { +LL | | take(n); +LL | | } + | |_________^ + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match.rs:24:15 + | +LL | if let Ok(val) = res_opt { + | ^^^ Replace this binding +LL | if let Some(n) = val { + | ^^^^^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match.rs:32:9 + | +LL | / if let Some(n) = val { +LL | | take(n); +LL | | } else { +LL | | return; +LL | | } + | |_________^ + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match.rs:31:15 + | +LL | if let Ok(val) = res_opt { + | ^^^ Replace this binding +LL | if let Some(n) = val { + | ^^^^^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match.rs:43:9 + | +LL | / match val { +LL | | Some(n) => foo(n), +LL | | _ => (), +LL | | } + | |_________^ + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match.rs:42:15 + | +LL | if let Ok(val) = res_opt { + | ^^^ Replace this binding +LL | match val { +LL | Some(n) => foo(n), + | ^^^^^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match.rs:52:13 + | +LL | / if let Some(n) = val { +LL | | take(n); +LL | | } + | |_____________^ + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match.rs:51:12 + | +LL | Ok(val) => { + | ^^^ Replace this binding +LL | if let Some(n) = val { + | ^^^^^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match.rs:61:9 + | +LL | / match val { +LL | | Some(n) => foo(n), +LL | | _ => return, +LL | | } + | |_________^ + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match.rs:60:15 + | +LL | if let Ok(val) = res_opt { + | ^^^ Replace this binding +LL | match val { +LL | Some(n) => foo(n), + | ^^^^^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match.rs:72:13 + | +LL | / if let Some(n) = val { +LL | | take(n); +LL | | } else { +LL | | return; +LL | | } + | |_____________^ + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match.rs:71:12 + | +LL | Ok(val) => { + | ^^^ Replace this binding +LL | if let Some(n) = val { + | ^^^^^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match.rs:83:20 + | +LL | Ok(val) => match val { + | ____________________^ +LL | | Some(n) => foo(n), +LL | | None => return, +LL | | }, + | |_________^ + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match.rs:83:12 + | +LL | Ok(val) => match val { + | ^^^ Replace this binding +LL | Some(n) => foo(n), + | ^^^^^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match.rs:92:22 + | +LL | Some(val) => match val { + | ______________________^ +LL | | Some(n) => foo(n), +LL | | _ => return, +LL | | }, + | |_________^ + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match.rs:92:14 + | +LL | Some(val) => match val { + | ^^^ Replace this binding +LL | Some(n) => foo(n), + | ^^^^^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match.rs:102:34 + | +LL | Ok(val) if make() => match val { + | __________________________________^ +LL | | Some(n) => foo(n), +LL | | _ => return, +LL | | }, + | |_____________^ + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match.rs:102:16 + | +LL | Ok(val) if make() => match val { + | ^^^ Replace this binding +LL | Some(n) => foo(n), + | ^^^^^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match.rs:109:24 + | +LL | Ok(val) => match val { + | ________________________^ +LL | | Some(n) => foo(n), +LL | | _ => return, +LL | | }, + | |_____________^ + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match.rs:109:16 + | +LL | Ok(val) => match val { + | ^^^ Replace this binding +LL | Some(n) => foo(n), + | ^^^^^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match.rs:123:29 + | +LL | $pat => match $e { + | _____________________________^ +LL | | $inner_pat => $then, +LL | | _ => return, +LL | | }, + | |_____________________^ +... +LL | mac!(res_opt => Ok(val), val => Some(n), foo(n)); + | ------------------------------------------------- in this macro invocation + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match.rs:135:28 + | +LL | mac!(res_opt => Ok(val), val => Some(n), foo(n)); + | ^^^ ^^^^^^^ with this pattern + | | + | Replace this binding + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 13 previous errors + -- cgit 1.4.1-3-g733a5 From 252083f7e02a3a9174bb39821fd20356ada3dd4a Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Mon, 16 Nov 2020 12:44:05 +0100 Subject: address review comments and rebase ci: always build with internal lints group up internal lints in lib.rs dogfood: we already pass --all-features, no need to enable internal-lints again --- .github/workflows/clippy_bors.yml | 7 ------ clippy_dev/src/lib.rs | 6 ++--- clippy_lints/src/lib.rs | 25 ++++++++---------- tests/dogfood.rs | 53 +++++++++++++++------------------------ 4 files changed, 33 insertions(+), 58 deletions(-) (limited to 'tests') diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index a8b4925176c..784463fe0df 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -128,13 +128,6 @@ jobs: SYSROOT=$(rustc --print sysroot) echo "$SYSROOT/bin" >> $GITHUB_PATH - - name: Build - run: cargo build --features deny-warnings - - # compiletest would panic due to "Found multiple rlibs for crate `clippy_lints`" - - name: clean rlibs - run: rm -f ./target/debug/deps/libclippy_lints* - - name: Build with internal lints run: cargo build --features deny-warnings,internal-lints diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index 1453ac7efa3..f51c45e9eb5 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -160,13 +160,11 @@ pub fn gen_register_lint_list<'a>( l.module, l.name.to_uppercase() ) - }) - .collect::>(); + }); 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() - .collect::>(); + .sorted(); let mut lint_list = vec![header]; lint_list.extend(internal_lints); lint_list.extend(other_lints); diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index fed7da3ee4f..8fbd44528b1 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -939,17 +939,23 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &zero_div_zero::ZERO_DIVIDED_BY_ZERO, ]); // end register lints, do not remove this comment, it’s used in `update_lints` - store.register_late_pass(|| box await_holding_invalid::AwaitHolding); - store.register_late_pass(|| box serde_api::SerdeAPI); + + // all the internal lints #[cfg(feature = "internal-lints")] { + store.register_early_pass(|| box utils::internal_lints::ClippyLintsInternal); + store.register_early_pass(|| box utils::internal_lints::ProduceIce); + store.register_late_pass(|| box utils::inspector::DeepCodeInspector); + store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls); store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); + store.register_late_pass(|| box utils::internal_lints::InvalidPaths); store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default()); + store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem); store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass); - store.register_late_pass(|| box utils::internal_lints::InvalidPaths); - store.register_late_pass(|| box utils::inspector::DeepCodeInspector); } store.register_late_pass(|| box utils::author::Author); + store.register_late_pass(|| box await_holding_invalid::AwaitHolding); + store.register_late_pass(|| box serde_api::SerdeAPI); let vec_box_size_threshold = conf.vec_box_size_threshold; store.register_late_pass(move || box types::Types::new(vec_box_size_threshold)); store.register_late_pass(|| box booleans::NonminimalBool); @@ -1134,8 +1140,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box literal_representation::LiteralDigitGrouping); let literal_representation_threshold = conf.literal_representation_threshold; store.register_early_pass(move || box literal_representation::DecimalLiteralRepresentation::new(literal_representation_threshold)); - #[cfg(feature = "internal-lints")] - store.register_early_pass(|| box utils::internal_lints::ClippyLintsInternal); let enum_variant_name_threshold = conf.enum_variant_name_threshold; store.register_early_pass(move || box enum_variants::EnumVariantNames::new(enum_variant_name_threshold)); store.register_early_pass(|| box tabs_in_doc_comments::TabsInDocComments); @@ -1149,8 +1153,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box large_const_arrays::LargeConstArrays::new(array_size_threshold)); store.register_late_pass(|| box floating_point_arithmetic::FloatingPointArithmetic); store.register_early_pass(|| box as_conversions::AsConversions); - #[cfg(feature = "internal-lints")] - store.register_early_pass(|| box utils::internal_lints::ProduceIce); store.register_late_pass(|| box let_underscore::LetUnderscore); store.register_late_pass(|| box atomic_ordering::AtomicOrdering); store.register_early_pass(|| box single_component_path_imports::SingleComponentPathImports); @@ -1166,8 +1168,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box dereference::Dereferencing); store.register_late_pass(|| box option_if_let_else::OptionIfLetElse); store.register_late_pass(|| box future_not_send::FutureNotSend); - #[cfg(feature = "internal-lints")] - store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls); store.register_late_pass(|| box if_let_mutex::IfLetMutex); store.register_late_pass(|| box mut_mutex_lock::MutMutexLock); store.register_late_pass(|| box match_on_vec_items::MatchOnVecItems); @@ -1175,7 +1175,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box redundant_field_names::RedundantFieldNames); store.register_late_pass(|| box vec_resize_to_zero::VecResizeToZero); store.register_late_pass(|| box panic_in_result_fn::PanicInResultFn); - let single_char_binding_names_threshold = conf.single_char_binding_names_threshold; store.register_early_pass(move || box non_expressive_names::NonExpressiveNames { single_char_binding_names_threshold, @@ -1192,8 +1191,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box manual_ok_or::ManualOkOr); store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); - #[cfg(feature = "internal-lints")] - store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem); let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::>(); store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods)); store.register_early_pass(|| box asm_syntax::InlineAsmX86AttSyntax); @@ -1202,7 +1199,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box strings::StrToString); store.register_late_pass(|| box strings::StringToString); - store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), LintId::of(&arithmetic::INTEGER_ARITHMETIC), @@ -1333,6 +1329,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&wildcard_imports::ENUM_GLOB_USE), LintId::of(&wildcard_imports::WILDCARD_IMPORTS), ]); + #[cfg(feature = "internal-lints")] store.register_group(true, "clippy::internal", Some("clippy_internal"), vec![ LintId::of(&utils::internal_lints::CLIPPY_LINTS_INTERNAL), diff --git a/tests/dogfood.rs b/tests/dogfood.rs index eae25adf839..a6163a83d76 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -18,39 +18,26 @@ fn dogfood_clippy() { } let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - let output = if cfg!(feature = "internal-lints") { - // with internal lints and internal warnings - Command::new(&*CLIPPY_PATH) - .current_dir(root_dir) - .env("CLIPPY_DOGFOOD", "1") - .env("CARGO_INCREMENTAL", "0") - .arg("clippy-preview") - .arg("--all-targets") - .arg("--all-features") - .args(&["--features", "internal-lints"]) - .arg("--") - .args(&["-D", "clippy::all"]) - .args(&["-D", "clippy::pedantic"]) - .args(&["-D", "clippy::internal"]) - .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir - .output() - .unwrap() - } else { - // without internal lints or warnings - Command::new(&*CLIPPY_PATH) - .current_dir(root_dir) - .env("CLIPPY_DOGFOOD", "1") - .env("CARGO_INCREMENTAL", "0") - .arg("clippy-preview") - .arg("--all-targets") - .arg("--all-features") - .arg("--") - .args(&["-D", "clippy::all"]) - .args(&["-D", "clippy::pedantic"]) - .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir - .output() - .unwrap() - }; + let mut command = Command::new(&*CLIPPY_PATH); + command + .current_dir(root_dir) + .env("CLIPPY_DOGFOOD", "1") + .env("CARGO_INCREMENTAL", "0") + .arg("clippy-preview") + .arg("--all-targets") + .arg("--all-features") + .arg("--") + .args(&["-D", "clippy::all"]) + .args(&["-D", "clippy::pedantic"]) + .arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir + + // internal lints only exist if we build with the internal-lints feature + if cfg!(feature = "internal-lints") { + command.args(&["-D", "clippy::internal"]); + } + + let output = command.output().unwrap(); + println!("status: {}", output.status); println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); -- cgit 1.4.1-3-g733a5 From f059febe85f49fd7b432a24542321f9b948a49de Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 13 Nov 2020 12:13:50 -0600 Subject: Add redundant else lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 4 + clippy_lints/src/redundant_else.rs | 135 ++++++++++++++++++++++++++++++++ tests/ui/redundant_else.rs | 154 +++++++++++++++++++++++++++++++++++++ tests/ui/redundant_else.stderr | 80 +++++++++++++++++++ 5 files changed, 374 insertions(+) create mode 100644 clippy_lints/src/redundant_else.rs create mode 100644 tests/ui/redundant_else.rs create mode 100644 tests/ui/redundant_else.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index e76a781f13b..c3351793c66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2023,6 +2023,7 @@ Released 2018-09-13 [`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure [`redundant_closure_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_call [`redundant_closure_for_method_calls`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_for_method_calls +[`redundant_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_else [`redundant_field_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names [`redundant_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern [`redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 6eb5f6a7f48..66895a866ee 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -293,6 +293,7 @@ mod question_mark; mod ranges; mod redundant_clone; mod redundant_closure_call; +mod redundant_else; mod redundant_field_names; mod redundant_pub_crate; mod redundant_static_lifetimes; @@ -810,6 +811,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &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_static_lifetimes::REDUNDANT_STATIC_LIFETIMES, @@ -1113,6 +1115,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box items_after_statements::ItemsAfterStatements); store.register_early_pass(|| box precedence::Precedence); store.register_early_pass(|| box needless_continue::NeedlessContinue); + store.register_early_pass(|| box redundant_else::RedundantElse); store.register_late_pass(|| box create_dir::CreateDir); store.register_early_pass(|| box needless_arbitrary_self_type::NeedlessArbitrarySelfType); store.register_early_pass(|| box redundant_static_lifetimes::RedundantStaticLifetimes); @@ -1294,6 +1297,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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(&shadow::SHADOW_UNRELATED), LintId::of(&strings::STRING_ADD_ASSIGN), diff --git a/clippy_lints/src/redundant_else.rs b/clippy_lints/src/redundant_else.rs new file mode 100644 index 00000000000..3d585cd27a3 --- /dev/null +++ b/clippy_lints/src/redundant_else.rs @@ -0,0 +1,135 @@ +use crate::utils::span_lint_and_help; +use rustc_ast::ast::{Block, Expr, ExprKind, Stmt, StmtKind}; +use rustc_ast::visit::{walk_expr, Visitor}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for `else` blocks that can be removed without changing semantics. + /// + /// **Why is this bad?** The `else` block adds unnecessary indentation and verbosity. + /// + /// **Known problems:** Some may prefer to keep the `else` block for clarity. + /// + /// **Example:** + /// + /// ```rust + /// fn my_func(count: u32) { + /// if count == 0 { + /// print!("Nothing to do"); + /// return; + /// } else { + /// print!("Moving on..."); + /// } + /// } + /// ``` + /// Use instead: + /// ```rust + /// fn my_func(count: u32) { + /// if count == 0 { + /// print!("Nothing to do"); + /// return; + /// } + /// print!("Moving on..."); + /// } + /// ``` + pub REDUNDANT_ELSE, + pedantic, + "`else` branch that can be removed without changing semantics" +} + +declare_lint_pass!(RedundantElse => [REDUNDANT_ELSE]); + +impl EarlyLintPass for RedundantElse { + fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &Stmt) { + if in_external_macro(cx.sess, stmt.span) { + return; + } + // Only look at expressions that are a whole statement + let expr: &Expr = match &stmt.kind { + StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr, + _ => return, + }; + // if else + let (mut then, mut els): (&Block, &Expr) = match &expr.kind { + ExprKind::If(_, then, Some(els)) => (then, els), + _ => return, + }; + loop { + if !BreakVisitor::default().check_block(then) { + // then block does not always break + return; + } + match &els.kind { + // else if else + ExprKind::If(_, next_then, Some(next_els)) => { + then = next_then; + els = next_els; + continue; + }, + // else if without else + ExprKind::If(..) => return, + // done + _ => break, + } + } + span_lint_and_help( + cx, + REDUNDANT_ELSE, + els.span, + "redundant else block", + None, + "remove the `else` block and move the contents out", + ); + } +} + +/// Call `check` functions to check if an expression always breaks control flow +#[derive(Default)] +struct BreakVisitor { + is_break: bool, +} + +impl<'ast> Visitor<'ast> for BreakVisitor { + fn visit_block(&mut self, block: &'ast Block) { + self.is_break = match block.stmts.as_slice() { + [.., last] => self.check_stmt(last), + _ => false, + }; + } + + fn visit_expr(&mut self, expr: &'ast Expr) { + self.is_break = match expr.kind { + ExprKind::Break(..) | ExprKind::Continue(..) | ExprKind::Ret(..) => true, + ExprKind::Match(_, ref arms) => arms.iter().all(|arm| self.check_expr(&arm.body)), + ExprKind::If(_, ref then, Some(ref els)) => self.check_block(then) && self.check_expr(els), + ExprKind::If(_, _, None) + // ignore loops for simplicity + | ExprKind::While(..) | ExprKind::ForLoop(..) | ExprKind::Loop(..) => false, + _ => { + walk_expr(self, expr); + return; + }, + }; + } +} + +impl BreakVisitor { + fn check(&mut self, item: T, visit: fn(&mut Self, T)) -> bool { + visit(self, item); + std::mem::replace(&mut self.is_break, false) + } + + fn check_block(&mut self, block: &Block) -> bool { + self.check(block, Self::visit_block) + } + + fn check_expr(&mut self, expr: &Expr) -> bool { + self.check(expr, Self::visit_expr) + } + + fn check_stmt(&mut self, stmt: &Stmt) -> bool { + self.check(stmt, Self::visit_stmt) + } +} diff --git a/tests/ui/redundant_else.rs b/tests/ui/redundant_else.rs new file mode 100644 index 00000000000..737c8a9f8db --- /dev/null +++ b/tests/ui/redundant_else.rs @@ -0,0 +1,154 @@ +#![warn(clippy::redundant_else)] +#![allow(clippy::needless_return)] + +fn main() { + loop { + // break + if foo() { + println!("Love your neighbor;"); + break; + } else { + println!("yet don't pull down your hedge."); + } + // continue + if foo() { + println!("He that lies down with Dogs,"); + continue; + } else { + println!("shall rise up with fleas."); + } + // match block + if foo() { + match foo() { + 1 => break, + _ => return, + } + } else { + println!("You may delay, but time will not."); + } + } + // else if + if foo() { + return; + } else if foo() { + return; + } else { + println!("A fat kitchen makes a lean will."); + } + // let binding outside of block + let _ = { + if foo() { + return; + } else { + 1 + } + }; + // else if with let binding outside of block + let _ = { + if foo() { + return; + } else if foo() { + return; + } else { + 2 + } + }; + // inside if let + let _ = if let Some(1) = foo() { + let _ = 1; + if foo() { + return; + } else { + 1 + } + } else { + 1 + }; + + // + // non-lint cases + // + + // sanity check + if foo() { + let _ = 1; + } else { + println!("Who is wise? He that learns from every one."); + } + // else if without else + if foo() { + return; + } else if foo() { + foo() + }; + // nested if return + if foo() { + if foo() { + return; + } + } else { + foo() + }; + // match with non-breaking branch + if foo() { + match foo() { + 1 => foo(), + _ => return, + } + } else { + println!("Three may keep a secret, if two of them are dead."); + } + // let binding + let _ = if foo() { + return; + } else { + 1 + }; + // assign + let a; + a = if foo() { + return; + } else { + 1 + }; + // assign-op + a += if foo() { + return; + } else { + 1 + }; + // if return else if else + if foo() { + return; + } else if foo() { + 1 + } else { + 2 + }; + // if else if return else + if foo() { + 1 + } else if foo() { + return; + } else { + 2 + }; + // else if with let binding + let _ = if foo() { + return; + } else if foo() { + return; + } else { + 2 + }; + // inside function call + Box::new(if foo() { + return; + } else { + 1 + }); +} + +fn foo() -> T { + unimplemented!("I'm not Santa Claus") +} diff --git a/tests/ui/redundant_else.stderr b/tests/ui/redundant_else.stderr new file mode 100644 index 00000000000..9000cdc814b --- /dev/null +++ b/tests/ui/redundant_else.stderr @@ -0,0 +1,80 @@ +error: redundant else block + --> $DIR/redundant_else.rs:10:16 + | +LL | } else { + | ________________^ +LL | | println!("yet don't pull down your hedge."); +LL | | } + | |_________^ + | + = note: `-D clippy::redundant-else` implied by `-D warnings` + = help: remove the `else` block and move the contents out + +error: redundant else block + --> $DIR/redundant_else.rs:17:16 + | +LL | } else { + | ________________^ +LL | | println!("shall rise up with fleas."); +LL | | } + | |_________^ + | + = help: remove the `else` block and move the contents out + +error: redundant else block + --> $DIR/redundant_else.rs:26:16 + | +LL | } else { + | ________________^ +LL | | println!("You may delay, but time will not."); +LL | | } + | |_________^ + | + = help: remove the `else` block and move the contents out + +error: redundant else block + --> $DIR/redundant_else.rs:35:12 + | +LL | } else { + | ____________^ +LL | | println!("A fat kitchen makes a lean will."); +LL | | } + | |_____^ + | + = help: remove the `else` block and move the contents out + +error: redundant else block + --> $DIR/redundant_else.rs:42:16 + | +LL | } else { + | ________________^ +LL | | 1 +LL | | } + | |_________^ + | + = help: remove the `else` block and move the contents out + +error: redundant else block + --> $DIR/redundant_else.rs:52:16 + | +LL | } else { + | ________________^ +LL | | 2 +LL | | } + | |_________^ + | + = help: remove the `else` block and move the contents out + +error: redundant else block + --> $DIR/redundant_else.rs:61:16 + | +LL | } else { + | ________________^ +LL | | 1 +LL | | } + | |_________^ + | + = help: remove the `else` block and move the contents out + +error: aborting due to 7 previous errors + -- cgit 1.4.1-3-g733a5 From 0e207888391fb8b55fa75d19259812b6cb97a75c Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Sun, 29 Nov 2020 18:21:21 -0600 Subject: Split tests --- tests/ui/collapsible_match.rs | 39 ------------------------ tests/ui/collapsible_match.stderr | 60 +------------------------------------ tests/ui/collapsible_match2.rs | 53 +++++++++++++++++++++++++++++++++ tests/ui/collapsible_match2.stderr | 61 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 115 insertions(+), 98 deletions(-) create mode 100644 tests/ui/collapsible_match2.rs create mode 100644 tests/ui/collapsible_match2.stderr (limited to 'tests') diff --git a/tests/ui/collapsible_match.rs b/tests/ui/collapsible_match.rs index 75640b70ff0..a83e6c77b12 100644 --- a/tests/ui/collapsible_match.rs +++ b/tests/ui/collapsible_match.rs @@ -95,45 +95,6 @@ fn lint_cases(opt_opt: Option>, res_opt: Result, String> }, None => return, } - - // if guards on outer match - { - match res_opt { - Ok(val) if make() => match val { - Some(n) => foo(n), - _ => return, - }, - _ => return, - } - match res_opt { - Ok(val) => match val { - Some(n) => foo(n), - _ => return, - }, - _ if make() => return, - _ => return, - } - } - - // macro - { - macro_rules! mac { - ($outer:expr => $pat:pat, $e:expr => $inner_pat:pat, $then:expr) => { - match $outer { - $pat => match $e { - $inner_pat => $then, - _ => return, - }, - _ => return, - } - }; - } - // Lint this since the patterns are not defined by the macro. - // Allows the lint to work on if_chain! for example. - // Fixing the lint requires knowledge of the specific macro, but we optimistically assume that - // there is still a better way to write this. - mac!(res_opt => Ok(val), val => Some(n), foo(n)); - } } fn negative_cases(res_opt: Result, String>, res_res: Result, String>) { diff --git a/tests/ui/collapsible_match.stderr b/tests/ui/collapsible_match.stderr index a9e4f911914..63ac6a1613d 100644 --- a/tests/ui/collapsible_match.stderr +++ b/tests/ui/collapsible_match.stderr @@ -175,63 +175,5 @@ LL | Some(val) => match val { LL | Some(n) => foo(n), | ^^^^^^^ with this pattern -error: Unnecessary nested match - --> $DIR/collapsible_match.rs:102:34 - | -LL | Ok(val) if make() => match val { - | __________________________________^ -LL | | Some(n) => foo(n), -LL | | _ => return, -LL | | }, - | |_____________^ - | -help: The outer pattern can be modified to include the inner pattern. - --> $DIR/collapsible_match.rs:102:16 - | -LL | Ok(val) if make() => match val { - | ^^^ Replace this binding -LL | Some(n) => foo(n), - | ^^^^^^^ with this pattern - -error: Unnecessary nested match - --> $DIR/collapsible_match.rs:109:24 - | -LL | Ok(val) => match val { - | ________________________^ -LL | | Some(n) => foo(n), -LL | | _ => return, -LL | | }, - | |_____________^ - | -help: The outer pattern can be modified to include the inner pattern. - --> $DIR/collapsible_match.rs:109:16 - | -LL | Ok(val) => match val { - | ^^^ Replace this binding -LL | Some(n) => foo(n), - | ^^^^^^^ with this pattern - -error: Unnecessary nested match - --> $DIR/collapsible_match.rs:123:29 - | -LL | $pat => match $e { - | _____________________________^ -LL | | $inner_pat => $then, -LL | | _ => return, -LL | | }, - | |_____________________^ -... -LL | mac!(res_opt => Ok(val), val => Some(n), foo(n)); - | ------------------------------------------------- in this macro invocation - | -help: The outer pattern can be modified to include the inner pattern. - --> $DIR/collapsible_match.rs:135:28 - | -LL | mac!(res_opt => Ok(val), val => Some(n), foo(n)); - | ^^^ ^^^^^^^ with this pattern - | | - | Replace this binding - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 13 previous errors +error: aborting due to 10 previous errors diff --git a/tests/ui/collapsible_match2.rs b/tests/ui/collapsible_match2.rs new file mode 100644 index 00000000000..d571ac4ab69 --- /dev/null +++ b/tests/ui/collapsible_match2.rs @@ -0,0 +1,53 @@ +#![warn(clippy::collapsible_match)] +#![allow(clippy::needless_return, clippy::no_effect, clippy::single_match)] + +fn lint_cases(opt_opt: Option>, res_opt: Result, String>) { + // if guards on outer match + { + match res_opt { + Ok(val) if make() => match val { + Some(n) => foo(n), + _ => return, + }, + _ => return, + } + match res_opt { + Ok(val) => match val { + Some(n) => foo(n), + _ => return, + }, + _ if make() => return, + _ => return, + } + } + + // macro + { + macro_rules! mac { + ($outer:expr => $pat:pat, $e:expr => $inner_pat:pat, $then:expr) => { + match $outer { + $pat => match $e { + $inner_pat => $then, + _ => return, + }, + _ => return, + } + }; + } + // Lint this since the patterns are not defined by the macro. + // Allows the lint to work on if_chain! for example. + // Fixing the lint requires knowledge of the specific macro, but we optimistically assume that + // there is still a better way to write this. + mac!(res_opt => Ok(val), val => Some(n), foo(n)); + } +} + +fn make() -> T { + unimplemented!() +} + +fn foo(t: T) -> U { + unimplemented!() +} + +fn main() {} diff --git a/tests/ui/collapsible_match2.stderr b/tests/ui/collapsible_match2.stderr new file mode 100644 index 00000000000..490d82d12cd --- /dev/null +++ b/tests/ui/collapsible_match2.stderr @@ -0,0 +1,61 @@ +error: Unnecessary nested match + --> $DIR/collapsible_match2.rs:8:34 + | +LL | Ok(val) if make() => match val { + | __________________________________^ +LL | | Some(n) => foo(n), +LL | | _ => return, +LL | | }, + | |_____________^ + | + = note: `-D clippy::collapsible-match` implied by `-D warnings` +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match2.rs:8:16 + | +LL | Ok(val) if make() => match val { + | ^^^ Replace this binding +LL | Some(n) => foo(n), + | ^^^^^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match2.rs:15:24 + | +LL | Ok(val) => match val { + | ________________________^ +LL | | Some(n) => foo(n), +LL | | _ => return, +LL | | }, + | |_____________^ + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match2.rs:15:16 + | +LL | Ok(val) => match val { + | ^^^ Replace this binding +LL | Some(n) => foo(n), + | ^^^^^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match2.rs:29:29 + | +LL | $pat => match $e { + | _____________________________^ +LL | | $inner_pat => $then, +LL | | _ => return, +LL | | }, + | |_____________________^ +... +LL | mac!(res_opt => Ok(val), val => Some(n), foo(n)); + | ------------------------------------------------- in this macro invocation + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match2.rs:41:28 + | +LL | mac!(res_opt => Ok(val), val => Some(n), foo(n)); + | ^^^ ^^^^^^^ with this pattern + | | + | Replace this binding + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 3 previous errors + -- cgit 1.4.1-3-g733a5 From 8135ab8a22f4aa6ad071574798397aa131377ca2 Mon Sep 17 00:00:00 2001 From: Ricky Date: Thu, 3 Dec 2020 16:11:52 -0500 Subject: Moved map_err_ignore to restriction and updated help message --- clippy_lints/src/map_err_ignore.rs | 4 ++-- tests/ui/map_err.stderr | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/map_err_ignore.rs b/clippy_lints/src/map_err_ignore.rs index 5298e16a04d..324a11f140a 100644 --- a/clippy_lints/src/map_err_ignore.rs +++ b/clippy_lints/src/map_err_ignore.rs @@ -99,7 +99,7 @@ declare_clippy_lint! { /// } /// ``` pub MAP_ERR_IGNORE, - pedantic, + restriction, "`map_err` should not ignore the original error" } @@ -135,7 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for MapErrIgnore { body_span, "`map_err(|_|...` ignores the original error", None, - "Consider wrapping the error in an enum variant", + "Consider wrapping the error in an enum variant for more error context, or using a named wildcard (`.map_err(|_ignored| ...`) to intentionally ignore the error", ); } } diff --git a/tests/ui/map_err.stderr b/tests/ui/map_err.stderr index 390d7ce2e4e..8193f7cfb8e 100644 --- a/tests/ui/map_err.stderr +++ b/tests/ui/map_err.stderr @@ -5,7 +5,7 @@ LL | println!("{:?}", x.map_err(|_| Errors::Ignored)); | ^^^ | = note: `-D clippy::map-err-ignore` implied by `-D warnings` - = help: Consider wrapping the error in an enum variant + = help: Consider wrapping the error in an enum variant for more error context, or using a named wildcard (`.map_err(|_ignored| ...`) to intentionally ignore the error error: aborting due to previous error -- cgit 1.4.1-3-g733a5 From 4bc33d37225db3dd3ab68bb53e855fcf794047db Mon Sep 17 00:00:00 2001 From: Ricky Date: Thu, 3 Dec 2020 17:49:27 -0500 Subject: Update the stderr message in ui tests --- tests/ui/map_err.stderr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/ui/map_err.stderr b/tests/ui/map_err.stderr index 8193f7cfb8e..8ee2941790d 100644 --- a/tests/ui/map_err.stderr +++ b/tests/ui/map_err.stderr @@ -1,11 +1,11 @@ -error: `map_err(|_|...` ignores the original error +error: `map_err(|_|...` wildcard pattern discards the original error --> $DIR/map_err.rs:23:32 | LL | println!("{:?}", x.map_err(|_| Errors::Ignored)); | ^^^ | = note: `-D clippy::map-err-ignore` implied by `-D warnings` - = help: Consider wrapping the error in an enum variant for more error context, or using a named wildcard (`.map_err(|_ignored| ...`) to intentionally ignore the error + = help: Consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`) error: aborting due to previous error -- cgit 1.4.1-3-g733a5 From b6113066429bc3108f62b920ccbfc79accfef2dd Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 27 Nov 2020 22:44:02 -0300 Subject: Add lint unsafe_sizeof_count_copies --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 + clippy_lints/src/unsafe_sizeof_count_copies.rs | 98 ++++++++++++++++++ clippy_lints/src/utils/paths.rs | 4 + tests/ui/unsafe_sizeof_count_copies.rs | 54 ++++++++++ tests/ui/unsafe_sizeof_count_copies.stderr | 131 +++++++++++++++++++++++++ 6 files changed, 293 insertions(+) create mode 100644 clippy_lints/src/unsafe_sizeof_count_copies.rs create mode 100644 tests/ui/unsafe_sizeof_count_copies.rs create mode 100644 tests/ui/unsafe_sizeof_count_copies.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index e65e7cc639f..e0f3b82ad25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2124,6 +2124,7 @@ Released 2018-09-13 [`unreadable_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal [`unsafe_derive_deserialize`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_derive_deserialize [`unsafe_removed_from_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_removed_from_name +[`unsafe_sizeof_count_copies`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_sizeof_count_copies [`unsafe_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_vector_initialization [`unseparated_literal_suffix`]: https://rust-lang.github.io/rust-clippy/master/index.html#unseparated_literal_suffix [`unsound_collection_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsound_collection_transmute diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 167e5b6b87f..1bce0130b40 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -329,6 +329,7 @@ mod unnecessary_sort_by; mod unnecessary_wraps; mod unnested_or_patterns; mod unsafe_removed_from_name; +mod unsafe_sizeof_count_copies; mod unused_io_amount; mod unused_self; mod unused_unit; @@ -916,6 +917,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &unnecessary_wraps::UNNECESSARY_WRAPS, &unnested_or_patterns::UNNESTED_OR_PATTERNS, &unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, + &unsafe_sizeof_count_copies::UNSAFE_SIZEOF_COUNT_COPIES, &unused_io_amount::UNUSED_IO_AMOUNT, &unused_self::UNUSED_SELF, &unused_unit::UNUSED_UNIT, @@ -998,6 +1000,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box matches::Matches::new(msrv)); store.register_early_pass(move || box manual_non_exhaustive::ManualNonExhaustive::new(msrv)); store.register_late_pass(move || box manual_strip::ManualStrip::new(msrv)); + store.register_late_pass(|| box unsafe_sizeof_count_copies::UnsafeSizeofCountCopies); store.register_late_pass(|| box map_clone::MapClone); store.register_late_pass(|| box map_err_ignore::MapErrIgnore); store.register_late_pass(|| box shadow::Shadow); @@ -1605,6 +1608,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), LintId::of(&unnecessary_wraps::UNNECESSARY_WRAPS), LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), + LintId::of(&unsafe_sizeof_count_copies::UNSAFE_SIZEOF_COUNT_COPIES), LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), LintId::of(&unused_unit::UNUSED_UNIT), LintId::of(&unwrap::PANICKING_UNWRAP), @@ -1883,6 +1887,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), + LintId::of(&unsafe_sizeof_count_copies::UNSAFE_SIZEOF_COUNT_COPIES), 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/unsafe_sizeof_count_copies.rs b/clippy_lints/src/unsafe_sizeof_count_copies.rs new file mode 100644 index 00000000000..2422df8feba --- /dev/null +++ b/clippy_lints/src/unsafe_sizeof_count_copies.rs @@ -0,0 +1,98 @@ +//! Lint on unsafe memory copying that use the `size_of` of the pointee type instead of a pointee +//! count + +use crate::utils::{match_def_path, paths, span_lint_and_help}; +use if_chain::if_chain; +use rustc_hir::BinOpKind; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{Ty as TyM, TyS}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Detects expressions where + /// size_of:: is used as the count argument to unsafe + /// memory copying functions like ptr::copy and + /// ptr::copy_nonoverlapping where T is the pointee type + /// of the pointers used + /// + /// **Why is this bad?** These functions expect a count + /// of T and not a number of bytes, which can lead to + /// copying the incorrect amount of bytes, which can + /// result in Undefined Behaviour + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,no_run + /// # use std::ptr::copy_nonoverlapping; + /// # use std::mem::size_of; + /// + /// const SIZE: usize = 128; + /// let x = [2u8; SIZE]; + /// let mut y = [2u8; SIZE]; + /// unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; + /// ``` + pub UNSAFE_SIZEOF_COUNT_COPIES, + correctness, + "unsafe memory copying using a byte count instead of a count of T" +} + +declare_lint_pass!(UnsafeSizeofCountCopies => [UNSAFE_SIZEOF_COUNT_COPIES]); + +fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option> { + match &expr.kind { + ExprKind::Call(ref count_func, _func_args) => { + if_chain! { + if let ExprKind::Path(ref count_func_qpath) = count_func.kind; + if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id(); + if match_def_path(cx, def_id, &paths::MEM_SIZE_OF) + || match_def_path(cx, def_id, &paths::MEM_SIZE_OF_VAL); + then { + cx.typeck_results().node_substs(count_func.hir_id).types().next() + } else { + None + } + } + }, + ExprKind::Binary(op, left, right) if BinOpKind::Mul == op.node || BinOpKind::Div == op.node => { + get_size_of_ty(cx, &*left).or_else(|| get_size_of_ty(cx, &*right)) + }, + _ => None, + } +} + +impl<'tcx> LateLintPass<'tcx> for UnsafeSizeofCountCopies { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + // Find calls to ptr::copy and copy_nonoverlapping + if let ExprKind::Call(ref func, ref func_args) = expr.kind; + 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::COPY_NONOVERLAPPING) + || match_def_path(cx, def_id, &paths::COPY); + + // Get the pointee type + let _substs = cx.typeck_results().node_substs(func.hir_id); + if let Some(pointee_ty) = cx.typeck_results().node_substs(func.hir_id).types().next(); + + // Find a size_of call in the count parameter expression and + // check that it's the same type + if let [_src, _dest, count] = &**func_args; + if let Some(ty_used_for_size_of) = get_size_of_ty(cx, count); + if TyS::same_type(pointee_ty, ty_used_for_size_of); + then { + span_lint_and_help( + cx, + UNSAFE_SIZEOF_COUNT_COPIES, + expr.span, + "unsafe memory copying using a byte count (Multiplied by size_of::) \ + instead of a count of T", + None, + "use a count of elements instead of a count of bytes for the count parameter, \ + it already gets multiplied by the size of the pointed to type" + ); + } + }; + } +} diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 16e6a016c9e..fe763d4bfbb 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -20,6 +20,8 @@ pub const CLONE_TRAIT: [&str; 3] = ["core", "clone", "Clone"]; pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"]; pub const CMP_MAX: [&str; 3] = ["core", "cmp", "max"]; pub const CMP_MIN: [&str; 3] = ["core", "cmp", "min"]; +pub const COPY: [&str; 3] = ["core", "intrinsics", "copy_nonoverlapping"]; +pub const COPY_NONOVERLAPPING: [&str; 3] = ["core", "intrinsics", "copy"]; pub const COW: [&str; 3] = ["alloc", "borrow", "Cow"]; pub const CSTRING_AS_C_STR: [&str; 5] = ["std", "ffi", "c_str", "CString", "as_c_str"]; pub const DEFAULT_TRAIT: [&str; 3] = ["core", "default", "Default"]; @@ -73,6 +75,8 @@ pub const MEM_MANUALLY_DROP: [&str; 4] = ["core", "mem", "manually_drop", "Manua pub const MEM_MAYBEUNINIT: [&str; 4] = ["core", "mem", "maybe_uninit", "MaybeUninit"]; pub const MEM_MAYBEUNINIT_UNINIT: [&str; 5] = ["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"]; pub const MEM_REPLACE: [&str; 3] = ["core", "mem", "replace"]; +pub const MEM_SIZE_OF: [&str; 3] = ["core", "mem", "size_of"]; +pub const MEM_SIZE_OF_VAL: [&str; 3] = ["core", "mem", "size_of_val"]; pub const MUTEX_GUARD: [&str; 4] = ["std", "sync", "mutex", "MutexGuard"]; pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"]; pub const OPS_MODULE: [&str; 2] = ["core", "ops"]; diff --git a/tests/ui/unsafe_sizeof_count_copies.rs b/tests/ui/unsafe_sizeof_count_copies.rs new file mode 100644 index 00000000000..0077ed07fce --- /dev/null +++ b/tests/ui/unsafe_sizeof_count_copies.rs @@ -0,0 +1,54 @@ +#![warn(clippy::unsafe_sizeof_count_copies)] + +use std::mem::{size_of, size_of_val}; +use std::ptr::{copy, copy_nonoverlapping}; + +fn main() { + const SIZE: usize = 128; + const HALF_SIZE: usize = SIZE / 2; + const DOUBLE_SIZE: usize = SIZE * 2; + let mut x = [2u8; SIZE]; + let mut y = [2u8; SIZE]; + + // Count is size_of (Should trigger the lint) + unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; + + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; + + // Count expression involving multiplication of size_of (Should trigger the lint) + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * SIZE) }; + + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * SIZE) }; + + // Count expression involving nested multiplications of size_of (Should trigger the lint) + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * HALF_SIZE * 2) }; + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) }; + + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE * HALF_SIZE) }; + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * HALF_SIZE * 2) }; + + // Count expression involving divisions of size_of (Should trigger the lint) + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * DOUBLE_SIZE / 2) }; + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / 2 * size_of_val(&x[0])) }; + + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * DOUBLE_SIZE / 2) }; + + // No size_of calls (Should not trigger the lint) + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), SIZE) }; + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), SIZE) }; + + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), SIZE) }; + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), SIZE) }; + + // Different types for pointee and size_of (Should not trigger the lint) + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() / 2 * SIZE) }; + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&0u16) / 2 * SIZE) }; + + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() / 2 * SIZE) }; + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&0u16) / 2 * SIZE) }; +} diff --git a/tests/ui/unsafe_sizeof_count_copies.stderr b/tests/ui/unsafe_sizeof_count_copies.stderr new file mode 100644 index 00000000000..6804df8cdfc --- /dev/null +++ b/tests/ui/unsafe_sizeof_count_copies.stderr @@ -0,0 +1,131 @@ +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:14:14 + | +LL | unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unsafe-sizeof-count-copies` implied by `-D warnings` + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:15:14 + | +LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:17:14 + | +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:18:14 + | +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:21:14 + | +LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:22:14 + | +LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:24:14 + | +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:25:14 + | +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:28:14 + | +LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * HALF_SIZE * 2) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:29:14 + | +LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:31:14 + | +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE * HALF_SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:32:14 + | +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * HALF_SIZE * 2) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:35:14 + | +LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * DOUBLE_SIZE / 2) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:36:14 + | +LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / 2 * size_of_val(&x[0])) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:38:14 + | +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:39:14 + | +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * DOUBLE_SIZE / 2) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: aborting due to 16 previous errors + -- cgit 1.4.1-3-g733a5 From 0f954babef41b16e26b900d3858ceac4005a4506 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 29 Nov 2020 01:47:32 -0300 Subject: Make the unsafe_sizeof_count_copies lint find copy_{from,to} method calls --- clippy_lints/src/unsafe_sizeof_count_copies.rs | 66 +++++++++++++++++++------- tests/ui/unsafe_sizeof_count_copies.rs | 5 ++ tests/ui/unsafe_sizeof_count_copies.stderr | 60 +++++++++++++++++------ 3 files changed, 99 insertions(+), 32 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/unsafe_sizeof_count_copies.rs b/clippy_lints/src/unsafe_sizeof_count_copies.rs index 2422df8feba..5df7d72564e 100644 --- a/clippy_lints/src/unsafe_sizeof_count_copies.rs +++ b/clippy_lints/src/unsafe_sizeof_count_copies.rs @@ -6,7 +6,7 @@ use if_chain::if_chain; use rustc_hir::BinOpKind; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{Ty as TyM, TyS}; +use rustc_middle::ty::{self, Ty, TyS, TypeAndMut}; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -40,7 +40,7 @@ declare_clippy_lint! { declare_lint_pass!(UnsafeSizeofCountCopies => [UNSAFE_SIZEOF_COUNT_COPIES]); -fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option> { +fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option> { match &expr.kind { ExprKind::Call(ref count_func, _func_args) => { if_chain! { @@ -62,35 +62,65 @@ fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option, expr: &'tcx Expr<'_>) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> { + if_chain! { + // Find calls to ptr::copy and copy_nonoverlapping + if let ExprKind::Call(ref func, ref args) = expr.kind; + if let [_src, _dest, count] = &**args; + 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::COPY_NONOVERLAPPING) + || match_def_path(cx, def_id, &paths::COPY); + + // Get the pointee type + if let Some(pointee_ty) = cx.typeck_results().node_substs(func.hir_id).types().next(); + then { + return Some((pointee_ty, count)); + } + }; + if_chain! { + // Find calls to copy_{from,to}{,_nonoverlapping} + if let ExprKind::MethodCall(ref method_path, _, ref args, _) = expr.kind; + if let [ptr_self, _, count] = &**args; + let method_ident = method_path.ident.as_str(); + if method_ident== "copy_to" || method_ident == "copy_from" + || method_ident == "copy_to_nonoverlapping" || method_ident == "copy_from_nonoverlapping"; + + // Get the pointee type + if let ty::RawPtr(TypeAndMut { ty: pointee_ty, mutbl:_mutability }) = + cx.typeck_results().expr_ty(ptr_self).kind(); + then { + return Some((pointee_ty, count)); + } + }; + None +} + impl<'tcx> LateLintPass<'tcx> for UnsafeSizeofCountCopies { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - // Find calls to ptr::copy and copy_nonoverlapping - if let ExprKind::Call(ref func, ref func_args) = expr.kind; - 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::COPY_NONOVERLAPPING) - || match_def_path(cx, def_id, &paths::COPY); + const HELP_MSG: &str = "use a count of elements instead of a count of bytes \ + for the count parameter, it already gets multiplied by the size of the pointed to type"; + + const LINT_MSG: &str = "unsafe memory copying using a byte count \ + (Multiplied by size_of::) instead of a count of T"; - // Get the pointee type - let _substs = cx.typeck_results().node_substs(func.hir_id); - if let Some(pointee_ty) = cx.typeck_results().node_substs(func.hir_id).types().next(); + if_chain! { + // Find calls to unsafe copy functions and get + // the pointee type and count parameter expression + if let Some((pointee_ty, count_expr)) = get_pointee_ty_and_count_expr(cx, expr); // Find a size_of call in the count parameter expression and // check that it's the same type - if let [_src, _dest, count] = &**func_args; - if let Some(ty_used_for_size_of) = get_size_of_ty(cx, count); + if let Some(ty_used_for_size_of) = get_size_of_ty(cx, count_expr); if TyS::same_type(pointee_ty, ty_used_for_size_of); then { span_lint_and_help( cx, UNSAFE_SIZEOF_COUNT_COPIES, expr.span, - "unsafe memory copying using a byte count (Multiplied by size_of::) \ - instead of a count of T", + LINT_MSG, None, - "use a count of elements instead of a count of bytes for the count parameter, \ - it already gets multiplied by the size of the pointed to type" + HELP_MSG ); } }; diff --git a/tests/ui/unsafe_sizeof_count_copies.rs b/tests/ui/unsafe_sizeof_count_copies.rs index 0077ed07fce..0bb22314cc0 100644 --- a/tests/ui/unsafe_sizeof_count_copies.rs +++ b/tests/ui/unsafe_sizeof_count_copies.rs @@ -14,6 +14,11 @@ fn main() { unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; + unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; + unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::()) }; + unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; + unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::()) }; + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; diff --git a/tests/ui/unsafe_sizeof_count_copies.stderr b/tests/ui/unsafe_sizeof_count_copies.stderr index 6804df8cdfc..14ca04617c2 100644 --- a/tests/ui/unsafe_sizeof_count_copies.stderr +++ b/tests/ui/unsafe_sizeof_count_copies.stderr @@ -18,13 +18,45 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T --> $DIR/unsafe_sizeof_count_copies.rs:17:14 | +LL | unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:18:14 + | +LL | unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:19:14 + | +LL | unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:20:14 + | +LL | unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:22:14 + | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:18:14 + --> $DIR/unsafe_sizeof_count_copies.rs:23:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -32,7 +64,7 @@ LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:21:14 + --> $DIR/unsafe_sizeof_count_copies.rs:26:14 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -40,7 +72,7 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::( = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:22:14 + --> $DIR/unsafe_sizeof_count_copies.rs:27:14 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -48,7 +80,7 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:24:14 + --> $DIR/unsafe_sizeof_count_copies.rs:29:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -56,7 +88,7 @@ LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:25:14 + --> $DIR/unsafe_sizeof_count_copies.rs:30:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -64,7 +96,7 @@ LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * SIZE) }; = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:28:14 + --> $DIR/unsafe_sizeof_count_copies.rs:33:14 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * HALF_SIZE * 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -72,7 +104,7 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::( = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:29:14 + --> $DIR/unsafe_sizeof_count_copies.rs:34:14 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -80,7 +112,7 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * si = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:31:14 + --> $DIR/unsafe_sizeof_count_copies.rs:36:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE * HALF_SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -88,7 +120,7 @@ LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE * HALF = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:32:14 + --> $DIR/unsafe_sizeof_count_copies.rs:37:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * HALF_SIZE * 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -96,7 +128,7 @@ LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * HALF_SIZ = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:35:14 + --> $DIR/unsafe_sizeof_count_copies.rs:40:14 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * DOUBLE_SIZE / 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -104,7 +136,7 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::( = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:36:14 + --> $DIR/unsafe_sizeof_count_copies.rs:41:14 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / 2 * size_of_val(&x[0])) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -112,7 +144,7 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:38:14 + --> $DIR/unsafe_sizeof_count_copies.rs:43:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -120,12 +152,12 @@ LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:39:14 + --> $DIR/unsafe_sizeof_count_copies.rs:44:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * DOUBLE_SIZE / 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: aborting due to 16 previous errors +error: aborting due to 20 previous errors -- cgit 1.4.1-3-g733a5 From 1b80990fe01646868f85245f608203e23f64184a Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 29 Nov 2020 14:23:59 -0300 Subject: Make the unsafe_sizeof_count_copies lint work with more functions Specifically: - find std::ptr::write_bytes - find std::ptr::swap_nonoverlapping - find std::ptr::slice_from_raw_parts - find std::ptr::slice_from_raw_parts_mut - pointer_primitive::write_bytes --- clippy_lints/src/unsafe_sizeof_count_copies.rs | 42 ++++++--- clippy_lints/src/utils/paths.rs | 4 + tests/ui/unsafe_sizeof_count_copies.rs | 12 ++- tests/ui/unsafe_sizeof_count_copies.stderr | 122 ++++++++++++++++--------- 4 files changed, 126 insertions(+), 54 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/unsafe_sizeof_count_copies.rs b/clippy_lints/src/unsafe_sizeof_count_copies.rs index 5df7d72564e..8a4538091e7 100644 --- a/clippy_lints/src/unsafe_sizeof_count_copies.rs +++ b/clippy_lints/src/unsafe_sizeof_count_copies.rs @@ -41,8 +41,8 @@ declare_clippy_lint! { declare_lint_pass!(UnsafeSizeofCountCopies => [UNSAFE_SIZEOF_COUNT_COPIES]); fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option> { - match &expr.kind { - ExprKind::Call(ref count_func, _func_args) => { + match expr.kind { + ExprKind::Call(count_func, _func_args) => { if_chain! { if let ExprKind::Path(ref count_func_qpath) = count_func.kind; if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id(); @@ -56,7 +56,7 @@ fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option { - get_size_of_ty(cx, &*left).or_else(|| get_size_of_ty(cx, &*right)) + get_size_of_ty(cx, left).or_else(|| get_size_of_ty(cx, right)) }, _ => None, } @@ -64,13 +64,16 @@ fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option, expr: &'tcx Expr<'_>) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> { if_chain! { - // Find calls to ptr::copy and copy_nonoverlapping - if let ExprKind::Call(ref func, ref args) = expr.kind; - if let [_src, _dest, count] = &**args; + // Find calls to ptr::{copy, copy_nonoverlapping} + // and ptr::{swap_nonoverlapping, write_bytes}, + if let ExprKind::Call(func, args) = expr.kind; + if let [_, _, count] = args; 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::COPY_NONOVERLAPPING) - || match_def_path(cx, def_id, &paths::COPY); + || match_def_path(cx, def_id, &paths::COPY) + || match_def_path(cx, def_id, &paths::WRITE_BYTES) + || match_def_path(cx, def_id, &paths::PTR_SWAP_NONOVERLAPPING); // Get the pointee type if let Some(pointee_ty) = cx.typeck_results().node_substs(func.hir_id).types().next(); @@ -79,11 +82,11 @@ fn get_pointee_ty_and_count_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) - } }; if_chain! { - // Find calls to copy_{from,to}{,_nonoverlapping} - if let ExprKind::MethodCall(ref method_path, _, ref args, _) = expr.kind; - if let [ptr_self, _, count] = &**args; + // Find calls to copy_{from,to}{,_nonoverlapping} and write_bytes methods + if let ExprKind::MethodCall(method_path, _, args, _) = expr.kind; + if let [ptr_self, _, count] = args; let method_ident = method_path.ident.as_str(); - if method_ident== "copy_to" || method_ident == "copy_from" + if method_ident == "write_bytes" || method_ident == "copy_to" || method_ident == "copy_from" || method_ident == "copy_to_nonoverlapping" || method_ident == "copy_from_nonoverlapping"; // Get the pointee type @@ -93,6 +96,21 @@ fn get_pointee_ty_and_count_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) - return Some((pointee_ty, count)); } }; + if_chain! { + // Find calls to ptr::copy and copy_nonoverlapping + if let ExprKind::Call(func, args) = expr.kind; + if let [_data, count] = args; + 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::PTR_SLICE_FROM_RAW_PARTS) + || match_def_path(cx, def_id, &paths::PTR_SLICE_FROM_RAW_PARTS_MUT); + + // Get the pointee type + if let Some(pointee_ty) = cx.typeck_results().node_substs(func.hir_id).types().next(); + then { + return Some((pointee_ty, count)); + } + }; None } @@ -102,7 +120,7 @@ impl<'tcx> LateLintPass<'tcx> for UnsafeSizeofCountCopies { for the count parameter, it already gets multiplied by the size of the pointed to type"; const LINT_MSG: &str = "unsafe memory copying using a byte count \ - (Multiplied by size_of::) instead of a count of T"; + (multiplied by size_of/size_of_val::) instead of a count of T"; if_chain! { // Find calls to unsafe copy functions and get diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index fe763d4bfbb..87c020a99db 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -104,6 +104,9 @@ pub const POLL_READY: [&str; 5] = ["core", "task", "poll", "Poll", "Ready"]; pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"]; pub const PTR_NULL: [&str; 3] = ["core", "ptr", "null"]; pub const PTR_NULL_MUT: [&str; 3] = ["core", "ptr", "null_mut"]; +pub const PTR_SLICE_FROM_RAW_PARTS: [&str; 3] = ["core", "ptr", "slice_from_raw_parts"]; +pub const PTR_SLICE_FROM_RAW_PARTS_MUT: [&str; 3] = ["core", "ptr", "slice_from_raw_parts_mut"]; +pub const PTR_SWAP_NONOVERLAPPING: [&str; 3] = ["core", "ptr", "swap_nonoverlapping"]; pub const PUSH_STR: [&str; 4] = ["alloc", "string", "String", "push_str"]; pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"]; pub const RC: [&str; 3] = ["alloc", "rc", "Rc"]; @@ -158,3 +161,4 @@ 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"]; +pub const WRITE_BYTES: [&str; 3] = ["core", "intrinsics", "write_bytes"]; diff --git a/tests/ui/unsafe_sizeof_count_copies.rs b/tests/ui/unsafe_sizeof_count_copies.rs index 0bb22314cc0..6aed8c31f7e 100644 --- a/tests/ui/unsafe_sizeof_count_copies.rs +++ b/tests/ui/unsafe_sizeof_count_copies.rs @@ -1,7 +1,9 @@ #![warn(clippy::unsafe_sizeof_count_copies)] use std::mem::{size_of, size_of_val}; -use std::ptr::{copy, copy_nonoverlapping}; +use std::ptr::{ + copy, copy_nonoverlapping, slice_from_raw_parts, slice_from_raw_parts_mut, swap_nonoverlapping, write_bytes, +}; fn main() { const SIZE: usize = 128; @@ -22,6 +24,14 @@ fn main() { unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; + unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() * SIZE) }; + unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::() * SIZE) }; + + unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::() * SIZE) }; + + unsafe { slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE) }; + unsafe { slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE) }; + // Count expression involving multiplication of size_of (Should trigger the lint) unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * SIZE) }; diff --git a/tests/ui/unsafe_sizeof_count_copies.stderr b/tests/ui/unsafe_sizeof_count_copies.stderr index 14ca04617c2..6f491bc4e4a 100644 --- a/tests/ui/unsafe_sizeof_count_copies.stderr +++ b/tests/ui/unsafe_sizeof_count_copies.stderr @@ -1,5 +1,5 @@ -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:14:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:16:14 | LL | unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,157 +7,197 @@ LL | unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of: = note: `-D clippy::unsafe-sizeof-count-copies` implied by `-D warnings` = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:15:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:17:14 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:17:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:19:14 | LL | unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:18:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:20:14 | LL | unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::()) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:19:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:21:14 | LL | unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:20:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:22:14 | LL | unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::()) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:22:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:24:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:23:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:25:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:26:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:27:14 + | +LL | unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:28:14 + | +LL | unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:30:14 + | +LL | unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:32:14 + | +LL | unsafe { slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:33:14 + | +LL | unsafe { slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type + +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:36:14 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:27:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:37:14 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:29:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:39:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:30:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:40:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:33:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:43:14 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * HALF_SIZE * 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:34:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:44:14 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:36:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:46:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE * HALF_SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:37:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:47:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * HALF_SIZE * 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:40:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:50:14 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * DOUBLE_SIZE / 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:41:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:51:14 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / 2 * size_of_val(&x[0])) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:43:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:53:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (Multiplied by size_of::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:44:14 +error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T + --> $DIR/unsafe_sizeof_count_copies.rs:54:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * DOUBLE_SIZE / 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: aborting due to 20 previous errors +error: aborting due to 25 previous errors -- cgit 1.4.1-3-g733a5 From 63a3c44060b9b06e10e7a854abcdbb853f6938c3 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 29 Nov 2020 14:32:11 -0300 Subject: Remove unnecessary unsafe_size_count_copies tests --- tests/ui/unsafe_sizeof_count_copies.rs | 22 +-------- tests/ui/unsafe_sizeof_count_copies.stderr | 76 +----------------------------- 2 files changed, 3 insertions(+), 95 deletions(-) (limited to 'tests') diff --git a/tests/ui/unsafe_sizeof_count_copies.rs b/tests/ui/unsafe_sizeof_count_copies.rs index 6aed8c31f7e..2a9adeb6bd9 100644 --- a/tests/ui/unsafe_sizeof_count_copies.rs +++ b/tests/ui/unsafe_sizeof_count_copies.rs @@ -34,36 +34,16 @@ fn main() { // Count expression involving multiplication of size_of (Should trigger the lint) unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; - unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * SIZE) }; - - unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; - unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * SIZE) }; // Count expression involving nested multiplications of size_of (Should trigger the lint) - unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * HALF_SIZE * 2) }; unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) }; - unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE * HALF_SIZE) }; - unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * HALF_SIZE * 2) }; - // Count expression involving divisions of size_of (Should trigger the lint) - unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * DOUBLE_SIZE / 2) }; - unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / 2 * size_of_val(&x[0])) }; - unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; - unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * DOUBLE_SIZE / 2) }; // No size_of calls (Should not trigger the lint) - unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), SIZE) }; - unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), SIZE) }; - - unsafe { copy(x.as_ptr(), y.as_mut_ptr(), SIZE) }; unsafe { copy(x.as_ptr(), y.as_mut_ptr(), SIZE) }; // Different types for pointee and size_of (Should not trigger the lint) - unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() / 2 * SIZE) }; - unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&0u16) / 2 * SIZE) }; - - unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() / 2 * SIZE) }; - unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&0u16) / 2 * SIZE) }; + unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() / 2 * SIZE) }; } diff --git a/tests/ui/unsafe_sizeof_count_copies.stderr b/tests/ui/unsafe_sizeof_count_copies.stderr index 6f491bc4e4a..7989e96dd21 100644 --- a/tests/ui/unsafe_sizeof_count_copies.stderr +++ b/tests/ui/unsafe_sizeof_count_copies.stderr @@ -111,93 +111,21 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::( | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:37:14 - | -LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T --> $DIR/unsafe_sizeof_count_copies.rs:39:14 | -LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:40:14 - | -LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:43:14 - | -LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * HALF_SIZE * 2) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:44:14 - | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:46:14 - | -LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE * HALF_SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:47:14 - | -LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * HALF_SIZE * 2) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:50:14 - | -LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * DOUBLE_SIZE / 2) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:51:14 - | -LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / 2 * size_of_val(&x[0])) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:53:14 + --> $DIR/unsafe_sizeof_count_copies.rs:42:14 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:54:14 - | -LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * DOUBLE_SIZE / 2) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: aborting due to 25 previous errors +error: aborting due to 16 previous errors -- cgit 1.4.1-3-g733a5 From af9685bb1e7e27a7b21d9939a42c1e9dce8c4df5 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 3 Dec 2020 20:55:38 -0300 Subject: Rename unsafe_sizeof_count_copies to size_of_in_element_count Also fix review comments: - Use const arrays and iterate them for the method/function names - merge 2 if_chain's into one using a rest pattern - remove unnecessary unsafe block in test And make the lint only point to the count expression instead of the entire function call --- CHANGELOG.md | 2 +- clippy_lints/src/lib.rs | 10 +- clippy_lints/src/size_of_in_element_count.rs | 138 +++++++++++++++++++++++ clippy_lints/src/unsafe_sizeof_count_copies.rs | 146 ------------------------- tests/ui/size_of_in_element_count.rs | 50 +++++++++ tests/ui/size_of_in_element_count.stderr | 131 ++++++++++++++++++++++ tests/ui/unsafe_sizeof_count_copies.rs | 49 --------- tests/ui/unsafe_sizeof_count_copies.stderr | 131 ---------------------- 8 files changed, 325 insertions(+), 332 deletions(-) create mode 100644 clippy_lints/src/size_of_in_element_count.rs delete mode 100644 clippy_lints/src/unsafe_sizeof_count_copies.rs create mode 100644 tests/ui/size_of_in_element_count.rs create mode 100644 tests/ui/size_of_in_element_count.stderr delete mode 100644 tests/ui/unsafe_sizeof_count_copies.rs delete mode 100644 tests/ui/unsafe_sizeof_count_copies.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index e0f3b82ad25..c7e02aaf4e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2057,6 +2057,7 @@ Released 2018-09-13 [`single_element_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_element_loop [`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match [`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else +[`size_of_in_element_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_in_element_count [`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next [`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization [`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive @@ -2124,7 +2125,6 @@ Released 2018-09-13 [`unreadable_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal [`unsafe_derive_deserialize`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_derive_deserialize [`unsafe_removed_from_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_removed_from_name -[`unsafe_sizeof_count_copies`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_sizeof_count_copies [`unsafe_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_vector_initialization [`unseparated_literal_suffix`]: https://rust-lang.github.io/rust-clippy/master/index.html#unseparated_literal_suffix [`unsound_collection_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsound_collection_transmute diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 1bce0130b40..06961064a4b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -306,6 +306,7 @@ mod self_assignment; mod serde_api; mod shadow; mod single_component_path_imports; +mod size_of_in_element_count; mod slow_vector_initialization; mod stable_sort_primitive; mod strings; @@ -329,7 +330,6 @@ mod unnecessary_sort_by; mod unnecessary_wraps; mod unnested_or_patterns; mod unsafe_removed_from_name; -mod unsafe_sizeof_count_copies; mod unused_io_amount; mod unused_self; mod unused_unit; @@ -917,7 +917,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &unnecessary_wraps::UNNECESSARY_WRAPS, &unnested_or_patterns::UNNESTED_OR_PATTERNS, &unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, - &unsafe_sizeof_count_copies::UNSAFE_SIZEOF_COUNT_COPIES, + &size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT, &unused_io_amount::UNUSED_IO_AMOUNT, &unused_self::UNUSED_SELF, &unused_unit::UNUSED_UNIT, @@ -1000,7 +1000,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box matches::Matches::new(msrv)); store.register_early_pass(move || box manual_non_exhaustive::ManualNonExhaustive::new(msrv)); store.register_late_pass(move || box manual_strip::ManualStrip::new(msrv)); - store.register_late_pass(|| box unsafe_sizeof_count_copies::UnsafeSizeofCountCopies); + store.register_late_pass(|| box size_of_in_element_count::SizeOfInElementCount); store.register_late_pass(|| box map_clone::MapClone); store.register_late_pass(|| box map_err_ignore::MapErrIgnore); store.register_late_pass(|| box shadow::Shadow); @@ -1608,7 +1608,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), LintId::of(&unnecessary_wraps::UNNECESSARY_WRAPS), LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), - LintId::of(&unsafe_sizeof_count_copies::UNSAFE_SIZEOF_COUNT_COPIES), + LintId::of(&size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), LintId::of(&unused_unit::UNUSED_UNIT), LintId::of(&unwrap::PANICKING_UNWRAP), @@ -1887,7 +1887,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), - LintId::of(&unsafe_sizeof_count_copies::UNSAFE_SIZEOF_COUNT_COPIES), + LintId::of(&size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), 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/size_of_in_element_count.rs b/clippy_lints/src/size_of_in_element_count.rs new file mode 100644 index 00000000000..9701e793700 --- /dev/null +++ b/clippy_lints/src/size_of_in_element_count.rs @@ -0,0 +1,138 @@ +//! Lint on use of `size_of` or `size_of_val` of T in an expression +//! expecting a count of T + +use crate::utils::{match_def_path, paths, span_lint_and_help}; +use if_chain::if_chain; +use rustc_hir::BinOpKind; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, Ty, TyS, TypeAndMut}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Detects expressions where + /// size_of:: or size_of_val:: is used as a + /// count of elements of type T + /// + /// **Why is this bad?** These functions expect a count + /// of T and not a number of bytes + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,no_run + /// # use std::ptr::copy_nonoverlapping; + /// # use std::mem::size_of; + /// + /// const SIZE: usize = 128; + /// let x = [2u8; SIZE]; + /// let mut y = [2u8; SIZE]; + /// unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; + /// ``` + pub SIZE_OF_IN_ELEMENT_COUNT, + correctness, + "using size_of:: or size_of_val:: where a count of elements of T is expected" +} + +declare_lint_pass!(SizeOfInElementCount => [SIZE_OF_IN_ELEMENT_COUNT]); + +fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option> { + match expr.kind { + ExprKind::Call(count_func, _func_args) => { + if_chain! { + if let ExprKind::Path(ref count_func_qpath) = count_func.kind; + if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id(); + if match_def_path(cx, def_id, &paths::MEM_SIZE_OF) + || match_def_path(cx, def_id, &paths::MEM_SIZE_OF_VAL); + then { + cx.typeck_results().node_substs(count_func.hir_id).types().next() + } else { + None + } + } + }, + ExprKind::Binary(op, left, right) if BinOpKind::Mul == op.node || BinOpKind::Div == op.node => { + get_size_of_ty(cx, left).or_else(|| get_size_of_ty(cx, right)) + }, + _ => None, + } +} + +const FUNCTIONS: [[&str; 3]; 6] = [ + paths::COPY_NONOVERLAPPING, + paths::COPY, + paths::WRITE_BYTES, + paths::PTR_SWAP_NONOVERLAPPING, + paths::PTR_SLICE_FROM_RAW_PARTS, + paths::PTR_SLICE_FROM_RAW_PARTS_MUT, + ]; +const METHODS: [&str; 5] = [ + "write_bytes", + "copy_to", + "copy_from", + "copy_to_nonoverlapping", + "copy_from_nonoverlapping", + ]; +fn get_pointee_ty_and_count_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> { + if_chain! { + // Find calls to ptr::{copy, copy_nonoverlapping} + // and ptr::{swap_nonoverlapping, write_bytes}, + if let ExprKind::Call(func, args) = expr.kind; + if let [.., count] = args; + 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 FUNCTIONS.iter().any(|func_path| match_def_path(cx, def_id, func_path)); + + // Get the pointee type + if let Some(pointee_ty) = cx.typeck_results().node_substs(func.hir_id).types().next(); + then { + return Some((pointee_ty, count)); + } + }; + if_chain! { + // Find calls to copy_{from,to}{,_nonoverlapping} and write_bytes methods + if let ExprKind::MethodCall(method_path, _, args, _) = expr.kind; + if let [ptr_self, _, count] = args; + let method_ident = method_path.ident.as_str(); + if METHODS.iter().any(|m| *m == &*method_ident); + + // Get the pointee type + if let ty::RawPtr(TypeAndMut { ty: pointee_ty, mutbl:_mutability }) = + cx.typeck_results().expr_ty(ptr_self).kind(); + then { + return Some((pointee_ty, count)); + } + }; + None +} + +impl<'tcx> LateLintPass<'tcx> for SizeOfInElementCount { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + const HELP_MSG: &str = "use a count of elements instead of a count of bytes\ + , it already gets multiplied by the size of the type"; + + const LINT_MSG: &str = "found a count of bytes \ + instead of a count of elements of T"; + + if_chain! { + // Find calls to unsafe copy functions and get + // the pointee type and count parameter expression + if let Some((pointee_ty, count_expr)) = get_pointee_ty_and_count_expr(cx, expr); + + // Find a size_of call in the count parameter expression and + // check that it's the same type + if let Some(ty_used_for_size_of) = get_size_of_ty(cx, count_expr); + if TyS::same_type(pointee_ty, ty_used_for_size_of); + then { + span_lint_and_help( + cx, + SIZE_OF_IN_ELEMENT_COUNT, + count_expr.span, + LINT_MSG, + None, + HELP_MSG + ); + } + }; + } +} diff --git a/clippy_lints/src/unsafe_sizeof_count_copies.rs b/clippy_lints/src/unsafe_sizeof_count_copies.rs deleted file mode 100644 index 8a4538091e7..00000000000 --- a/clippy_lints/src/unsafe_sizeof_count_copies.rs +++ /dev/null @@ -1,146 +0,0 @@ -//! Lint on unsafe memory copying that use the `size_of` of the pointee type instead of a pointee -//! count - -use crate::utils::{match_def_path, paths, span_lint_and_help}; -use if_chain::if_chain; -use rustc_hir::BinOpKind; -use rustc_hir::{Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, Ty, TyS, TypeAndMut}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; - -declare_clippy_lint! { - /// **What it does:** Detects expressions where - /// size_of:: is used as the count argument to unsafe - /// memory copying functions like ptr::copy and - /// ptr::copy_nonoverlapping where T is the pointee type - /// of the pointers used - /// - /// **Why is this bad?** These functions expect a count - /// of T and not a number of bytes, which can lead to - /// copying the incorrect amount of bytes, which can - /// result in Undefined Behaviour - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust,no_run - /// # use std::ptr::copy_nonoverlapping; - /// # use std::mem::size_of; - /// - /// const SIZE: usize = 128; - /// let x = [2u8; SIZE]; - /// let mut y = [2u8; SIZE]; - /// unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; - /// ``` - pub UNSAFE_SIZEOF_COUNT_COPIES, - correctness, - "unsafe memory copying using a byte count instead of a count of T" -} - -declare_lint_pass!(UnsafeSizeofCountCopies => [UNSAFE_SIZEOF_COUNT_COPIES]); - -fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option> { - match expr.kind { - ExprKind::Call(count_func, _func_args) => { - if_chain! { - if let ExprKind::Path(ref count_func_qpath) = count_func.kind; - if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id(); - if match_def_path(cx, def_id, &paths::MEM_SIZE_OF) - || match_def_path(cx, def_id, &paths::MEM_SIZE_OF_VAL); - then { - cx.typeck_results().node_substs(count_func.hir_id).types().next() - } else { - None - } - } - }, - ExprKind::Binary(op, left, right) if BinOpKind::Mul == op.node || BinOpKind::Div == op.node => { - get_size_of_ty(cx, left).or_else(|| get_size_of_ty(cx, right)) - }, - _ => None, - } -} - -fn get_pointee_ty_and_count_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> { - if_chain! { - // Find calls to ptr::{copy, copy_nonoverlapping} - // and ptr::{swap_nonoverlapping, write_bytes}, - if let ExprKind::Call(func, args) = expr.kind; - if let [_, _, count] = args; - 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::COPY_NONOVERLAPPING) - || match_def_path(cx, def_id, &paths::COPY) - || match_def_path(cx, def_id, &paths::WRITE_BYTES) - || match_def_path(cx, def_id, &paths::PTR_SWAP_NONOVERLAPPING); - - // Get the pointee type - if let Some(pointee_ty) = cx.typeck_results().node_substs(func.hir_id).types().next(); - then { - return Some((pointee_ty, count)); - } - }; - if_chain! { - // Find calls to copy_{from,to}{,_nonoverlapping} and write_bytes methods - if let ExprKind::MethodCall(method_path, _, args, _) = expr.kind; - if let [ptr_self, _, count] = args; - let method_ident = method_path.ident.as_str(); - if method_ident == "write_bytes" || method_ident == "copy_to" || method_ident == "copy_from" - || method_ident == "copy_to_nonoverlapping" || method_ident == "copy_from_nonoverlapping"; - - // Get the pointee type - if let ty::RawPtr(TypeAndMut { ty: pointee_ty, mutbl:_mutability }) = - cx.typeck_results().expr_ty(ptr_self).kind(); - then { - return Some((pointee_ty, count)); - } - }; - if_chain! { - // Find calls to ptr::copy and copy_nonoverlapping - if let ExprKind::Call(func, args) = expr.kind; - if let [_data, count] = args; - 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::PTR_SLICE_FROM_RAW_PARTS) - || match_def_path(cx, def_id, &paths::PTR_SLICE_FROM_RAW_PARTS_MUT); - - // Get the pointee type - if let Some(pointee_ty) = cx.typeck_results().node_substs(func.hir_id).types().next(); - then { - return Some((pointee_ty, count)); - } - }; - None -} - -impl<'tcx> LateLintPass<'tcx> for UnsafeSizeofCountCopies { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - const HELP_MSG: &str = "use a count of elements instead of a count of bytes \ - for the count parameter, it already gets multiplied by the size of the pointed to type"; - - const LINT_MSG: &str = "unsafe memory copying using a byte count \ - (multiplied by size_of/size_of_val::) instead of a count of T"; - - if_chain! { - // Find calls to unsafe copy functions and get - // the pointee type and count parameter expression - if let Some((pointee_ty, count_expr)) = get_pointee_ty_and_count_expr(cx, expr); - - // Find a size_of call in the count parameter expression and - // check that it's the same type - if let Some(ty_used_for_size_of) = get_size_of_ty(cx, count_expr); - if TyS::same_type(pointee_ty, ty_used_for_size_of); - then { - span_lint_and_help( - cx, - UNSAFE_SIZEOF_COUNT_COPIES, - expr.span, - LINT_MSG, - None, - HELP_MSG - ); - } - }; - } -} diff --git a/tests/ui/size_of_in_element_count.rs b/tests/ui/size_of_in_element_count.rs new file mode 100644 index 00000000000..d4658ebf72d --- /dev/null +++ b/tests/ui/size_of_in_element_count.rs @@ -0,0 +1,50 @@ +#![warn(clippy::size_of_in_element_count)] + +use std::mem::{size_of, size_of_val}; +use std::ptr::{ + copy, copy_nonoverlapping, slice_from_raw_parts, + slice_from_raw_parts_mut, swap_nonoverlapping, write_bytes, +}; + +fn main() { + const SIZE: usize = 128; + const HALF_SIZE: usize = SIZE / 2; + const DOUBLE_SIZE: usize = SIZE * 2; + let mut x = [2u8; SIZE]; + let mut y = [2u8; SIZE]; + + // Count is size_of (Should trigger the lint) + unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; + + unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; + unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::()) }; + unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; + unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::()) }; + + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; + + unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() * SIZE) }; + unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::() * SIZE) }; + + unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::() * SIZE) }; + + slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE); + slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE); + + // Count expression involving multiplication of size_of (Should trigger the lint) + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; + + // Count expression involving nested multiplications of size_of (Should trigger the lint) + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) }; + + // Count expression involving divisions of size_of (Should trigger the lint) + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; + + // No size_of calls (Should not trigger the lint) + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), SIZE) }; + + // Different types for pointee and size_of (Should not trigger the lint) + unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() / 2 * SIZE) }; +} diff --git a/tests/ui/size_of_in_element_count.stderr b/tests/ui/size_of_in_element_count.stderr new file mode 100644 index 00000000000..80c3fec1b05 --- /dev/null +++ b/tests/ui/size_of_in_element_count.stderr @@ -0,0 +1,131 @@ +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:17:68 + | +LL | unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^ + | + = note: `-D clippy::size-of-in-element-count` implied by `-D warnings` + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:18:62 + | +LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; + | ^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:20:49 + | +LL | unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:21:64 + | +LL | unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:22:51 + | +LL | unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:23:66 + | +LL | unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:25:47 + | +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:26:47 + | +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; + | ^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:28:46 + | +LL | unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:29:47 + | +LL | unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:31:66 + | +LL | unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:33:46 + | +LL | slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:34:38 + | +LL | slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:37:62 + | +LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:40:62 + | +LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:43:47 + | +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: aborting due to 16 previous errors + diff --git a/tests/ui/unsafe_sizeof_count_copies.rs b/tests/ui/unsafe_sizeof_count_copies.rs deleted file mode 100644 index 2a9adeb6bd9..00000000000 --- a/tests/ui/unsafe_sizeof_count_copies.rs +++ /dev/null @@ -1,49 +0,0 @@ -#![warn(clippy::unsafe_sizeof_count_copies)] - -use std::mem::{size_of, size_of_val}; -use std::ptr::{ - copy, copy_nonoverlapping, slice_from_raw_parts, slice_from_raw_parts_mut, swap_nonoverlapping, write_bytes, -}; - -fn main() { - const SIZE: usize = 128; - const HALF_SIZE: usize = SIZE / 2; - const DOUBLE_SIZE: usize = SIZE * 2; - let mut x = [2u8; SIZE]; - let mut y = [2u8; SIZE]; - - // Count is size_of (Should trigger the lint) - unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; - unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; - - unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; - unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::()) }; - unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; - unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::()) }; - - unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; - unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; - - unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() * SIZE) }; - unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::() * SIZE) }; - - unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::() * SIZE) }; - - unsafe { slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE) }; - unsafe { slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE) }; - - // Count expression involving multiplication of size_of (Should trigger the lint) - unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; - - // Count expression involving nested multiplications of size_of (Should trigger the lint) - unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) }; - - // Count expression involving divisions of size_of (Should trigger the lint) - unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; - - // No size_of calls (Should not trigger the lint) - unsafe { copy(x.as_ptr(), y.as_mut_ptr(), SIZE) }; - - // Different types for pointee and size_of (Should not trigger the lint) - unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() / 2 * SIZE) }; -} diff --git a/tests/ui/unsafe_sizeof_count_copies.stderr b/tests/ui/unsafe_sizeof_count_copies.stderr deleted file mode 100644 index 7989e96dd21..00000000000 --- a/tests/ui/unsafe_sizeof_count_copies.stderr +++ /dev/null @@ -1,131 +0,0 @@ -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:16:14 - | -LL | unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::unsafe-sizeof-count-copies` implied by `-D warnings` - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:17:14 - | -LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:19:14 - | -LL | unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:20:14 - | -LL | unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:21:14 - | -LL | unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:22:14 - | -LL | unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:24:14 - | -LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:25:14 - | -LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:27:14 - | -LL | unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:28:14 - | -LL | unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::() * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:30:14 - | -LL | unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::() * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:32:14 - | -LL | unsafe { slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:33:14 - | -LL | unsafe { slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:36:14 - | -LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:39:14 - | -LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: unsafe memory copying using a byte count (multiplied by size_of/size_of_val::) instead of a count of T - --> $DIR/unsafe_sizeof_count_copies.rs:42:14 - | -LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes for the count parameter, it already gets multiplied by the size of the pointed to type - -error: aborting due to 16 previous errors - -- cgit 1.4.1-3-g733a5 From c1a5329475d041dbeb077ecda6ae71f690b4bcc1 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 30 Nov 2020 21:54:50 -0300 Subject: Add more functions to size_of_in_element_count Specifically ptr::{sub, wrapping_sub, add, wrapping_add, offset, wrapping_offset} and slice::{from_raw_parts, from_raw_parts_mut} The lint now also looks for size_of calls through casts (Since offset takes an isize) --- clippy_lints/src/lib.rs | 6 +- clippy_lints/src/size_of_in_element_count.rs | 48 ++++++++------ clippy_lints/src/utils/paths.rs | 2 + tests/ui/size_of_in_element_count.rs | 15 ++++- tests/ui/size_of_in_element_count.stderr | 98 +++++++++++++++++++++++----- 5 files changed, 127 insertions(+), 42 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 06961064a4b..4ef595bcffd 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -848,6 +848,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &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, @@ -917,7 +918,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &unnecessary_wraps::UNNECESSARY_WRAPS, &unnested_or_patterns::UNNESTED_OR_PATTERNS, &unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, - &size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT, &unused_io_amount::UNUSED_IO_AMOUNT, &unused_self::UNUSED_SELF, &unused_unit::UNUSED_UNIT, @@ -1562,6 +1562,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&self_assignment::SELF_ASSIGNMENT), 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), @@ -1608,7 +1609,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY), LintId::of(&unnecessary_wraps::UNNECESSARY_WRAPS), LintId::of(&unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), - LintId::of(&size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT), LintId::of(&unused_unit::UNUSED_UNIT), LintId::of(&unwrap::PANICKING_UNWRAP), @@ -1872,6 +1872,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(®ex::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(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), LintId::of(&swap::ALMOST_SWAPPED), @@ -1887,7 +1888,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), - LintId::of(&size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), 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/size_of_in_element_count.rs b/clippy_lints/src/size_of_in_element_count.rs index 9701e793700..210cf5773e1 100644 --- a/clippy_lints/src/size_of_in_element_count.rs +++ b/clippy_lints/src/size_of_in_element_count.rs @@ -54,31 +54,40 @@ fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option { get_size_of_ty(cx, left).or_else(|| get_size_of_ty(cx, right)) }, + ExprKind::Cast(expr, _) => get_size_of_ty(cx, expr), _ => None, } } -const FUNCTIONS: [[&str; 3]; 6] = [ - paths::COPY_NONOVERLAPPING, - paths::COPY, - paths::WRITE_BYTES, - paths::PTR_SWAP_NONOVERLAPPING, - paths::PTR_SLICE_FROM_RAW_PARTS, - paths::PTR_SLICE_FROM_RAW_PARTS_MUT, +fn get_pointee_ty_and_count_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> { + const FUNCTIONS: [&[&str]; 8] = [ + &paths::COPY_NONOVERLAPPING, + &paths::COPY, + &paths::WRITE_BYTES, + &paths::PTR_SWAP_NONOVERLAPPING, + &paths::PTR_SLICE_FROM_RAW_PARTS, + &paths::PTR_SLICE_FROM_RAW_PARTS_MUT, + &paths::SLICE_FROM_RAW_PARTS, + &paths::SLICE_FROM_RAW_PARTS_MUT, ]; -const METHODS: [&str; 5] = [ - "write_bytes", - "copy_to", - "copy_from", - "copy_to_nonoverlapping", - "copy_from_nonoverlapping", + const METHODS: [&str; 11] = [ + "write_bytes", + "copy_to", + "copy_from", + "copy_to_nonoverlapping", + "copy_from_nonoverlapping", + "add", + "wrapping_add", + "sub", + "wrapping_sub", + "offset", + "wrapping_offset", ]; -fn get_pointee_ty_and_count_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> { + if_chain! { // Find calls to ptr::{copy, copy_nonoverlapping} // and ptr::{swap_nonoverlapping, write_bytes}, - if let ExprKind::Call(func, args) = expr.kind; - if let [.., count] = args; + if let ExprKind::Call(func, [.., count]) = expr.kind; 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 FUNCTIONS.iter().any(|func_path| match_def_path(cx, def_id, func_path)); @@ -91,13 +100,12 @@ fn get_pointee_ty_and_count_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) - }; if_chain! { // Find calls to copy_{from,to}{,_nonoverlapping} and write_bytes methods - if let ExprKind::MethodCall(method_path, _, args, _) = expr.kind; - if let [ptr_self, _, count] = args; + if let ExprKind::MethodCall(method_path, _, [ptr_self, .., count], _) = expr.kind; let method_ident = method_path.ident.as_str(); if METHODS.iter().any(|m| *m == &*method_ident); // Get the pointee type - if let ty::RawPtr(TypeAndMut { ty: pointee_ty, mutbl:_mutability }) = + if let ty::RawPtr(TypeAndMut { ty: pointee_ty, .. }) = cx.typeck_results().expr_ty(ptr_self).kind(); then { return Some((pointee_ty, count)); @@ -115,7 +123,7 @@ impl<'tcx> LateLintPass<'tcx> for SizeOfInElementCount { instead of a count of elements of T"; if_chain! { - // Find calls to unsafe copy functions and get + // Find calls to functions with an element count parameter and get // the pointee type and count parameter expression if let Some((pointee_ty, count_expr)) = get_pointee_ty_and_count_expr(cx, expr); diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 87c020a99db..6fdc7b4587f 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -128,6 +128,8 @@ pub const RWLOCK_READ_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockReadGu pub const RWLOCK_WRITE_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockWriteGuard"]; pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"]; pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"]; +pub const SLICE_FROM_RAW_PARTS: [&str; 4] = ["core", "slice", "raw", "from_raw_parts"]; +pub const SLICE_FROM_RAW_PARTS_MUT: [&str; 4] = ["core", "slice", "raw", "from_raw_parts_mut"]; pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "", "into_vec"]; pub const SLICE_ITER: [&str; 4] = ["core", "slice", "iter", "Iter"]; pub const STDERR: [&str; 4] = ["std", "io", "stdio", "stderr"]; diff --git a/tests/ui/size_of_in_element_count.rs b/tests/ui/size_of_in_element_count.rs index d4658ebf72d..b13e390705a 100644 --- a/tests/ui/size_of_in_element_count.rs +++ b/tests/ui/size_of_in_element_count.rs @@ -1,10 +1,11 @@ #![warn(clippy::size_of_in_element_count)] +#![allow(clippy::ptr_offset_with_cast)] use std::mem::{size_of, size_of_val}; use std::ptr::{ - copy, copy_nonoverlapping, slice_from_raw_parts, - slice_from_raw_parts_mut, swap_nonoverlapping, write_bytes, + copy, copy_nonoverlapping, slice_from_raw_parts, slice_from_raw_parts_mut, swap_nonoverlapping, write_bytes, }; +use std::slice::{from_raw_parts, from_raw_parts_mut}; fn main() { const SIZE: usize = 128; @@ -33,6 +34,16 @@ fn main() { slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE); slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE); + unsafe { from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE) }; + unsafe { from_raw_parts(y.as_ptr(), size_of::() * SIZE) }; + + unsafe { y.as_mut_ptr().sub(size_of::()) }; + y.as_ptr().wrapping_sub(size_of::()); + unsafe { y.as_ptr().add(size_of::()) }; + y.as_mut_ptr().wrapping_add(size_of::()); + unsafe { y.as_ptr().offset(size_of::() as isize) }; + y.as_mut_ptr().wrapping_offset(size_of::() as isize); + // Count expression involving multiplication of size_of (Should trigger the lint) unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; diff --git a/tests/ui/size_of_in_element_count.stderr b/tests/ui/size_of_in_element_count.stderr index 80c3fec1b05..b7f421ec997 100644 --- a/tests/ui/size_of_in_element_count.stderr +++ b/tests/ui/size_of_in_element_count.stderr @@ -1,5 +1,5 @@ error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:17:68 + --> $DIR/size_of_in_element_count.rs:18:68 | LL | unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; | ^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of: = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:18:62 + --> $DIR/size_of_in_element_count.rs:19:62 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; | ^^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:20:49 + --> $DIR/size_of_in_element_count.rs:21:49 | LL | unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; | ^^^^^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL | unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:21:64 + --> $DIR/size_of_in_element_count.rs:22:64 | LL | unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::()) }; | ^^^^^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of:: $DIR/size_of_in_element_count.rs:22:51 + --> $DIR/size_of_in_element_count.rs:23:51 | LL | unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; | ^^^^^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL | unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:23:66 + --> $DIR/size_of_in_element_count.rs:24:66 | LL | unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::()) }; | ^^^^^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL | unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::< = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:25:47 + --> $DIR/size_of_in_element_count.rs:26:47 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; | ^^^^^^^^^^^^^^^ @@ -56,7 +56,7 @@ LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:26:47 + --> $DIR/size_of_in_element_count.rs:27:47 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; | ^^^^^^^^^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:28:46 + --> $DIR/size_of_in_element_count.rs:29:46 | LL | unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() * SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() * SIZE) }; = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:29:47 + --> $DIR/size_of_in_element_count.rs:30:47 | LL | unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::() * SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -80,7 +80,7 @@ LL | unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::() * SIZE) }; = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:31:66 + --> $DIR/size_of_in_element_count.rs:32:66 | LL | unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::() * SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -88,7 +88,7 @@ LL | unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::< = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:33:46 + --> $DIR/size_of_in_element_count.rs:34:46 | LL | slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE); | ^^^^^^^^^^^^^^^^^^^^^^ @@ -96,7 +96,7 @@ LL | slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE); = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:34:38 + --> $DIR/size_of_in_element_count.rs:35:38 | LL | slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE); | ^^^^^^^^^^^^^^^^^^^^^^ @@ -104,7 +104,71 @@ LL | slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE); = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:37:62 + --> $DIR/size_of_in_element_count.rs:37:49 + | +LL | unsafe { from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:38:41 + | +LL | unsafe { from_raw_parts(y.as_ptr(), size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:40:33 + | +LL | unsafe { y.as_mut_ptr().sub(size_of::()) }; + | ^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:41:29 + | +LL | y.as_ptr().wrapping_sub(size_of::()); + | ^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:42:29 + | +LL | unsafe { y.as_ptr().add(size_of::()) }; + | ^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:43:33 + | +LL | y.as_mut_ptr().wrapping_add(size_of::()); + | ^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:44:32 + | +LL | unsafe { y.as_ptr().offset(size_of::() as isize) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:45:36 + | +LL | y.as_mut_ptr().wrapping_offset(size_of::() as isize); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of T + --> $DIR/size_of_in_element_count.rs:48:62 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -112,7 +176,7 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::( = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:40:62 + --> $DIR/size_of_in_element_count.rs:51:62 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -120,12 +184,12 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * si = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type error: found a count of bytes instead of a count of elements of T - --> $DIR/size_of_in_element_count.rs:43:47 + --> $DIR/size_of_in_element_count.rs:54:47 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: aborting due to 16 previous errors +error: aborting due to 24 previous errors -- cgit 1.4.1-3-g733a5 From 5f821fbcf1687c4476c117bcab5a3b2a4a977d4c Mon Sep 17 00:00:00 2001 From: Ricky Date: Thu, 3 Dec 2020 19:41:44 -0500 Subject: Added test to make sure ignoring the error with a named wildcard value works --- tests/ui/map_err.rs | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'tests') diff --git a/tests/ui/map_err.rs b/tests/ui/map_err.rs index 05b9949f102..00e037843f8 100644 --- a/tests/ui/map_err.rs +++ b/tests/ui/map_err.rs @@ -22,5 +22,9 @@ fn main() -> Result<(), Errors> { println!("{:?}", x.map_err(|_| Errors::Ignored)); + // Should not warn you because you explicitly ignore the parameter + // using a named wildcard value + println!("{:?}", x.map_err(|_foo| Errors::Ignored)); + Ok(()) } -- cgit 1.4.1-3-g733a5 From 75140e813f3701e76ab64e091653395ec397f68d Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Fri, 4 Dec 2020 23:36:07 +0900 Subject: Fix a style of texts in `size_of_in_element_count` --- clippy_lints/src/size_of_in_element_count.rs | 11 +++---- tests/ui/size_of_in_element_count.stderr | 48 ++++++++++++++-------------- 2 files changed, 29 insertions(+), 30 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/size_of_in_element_count.rs b/clippy_lints/src/size_of_in_element_count.rs index 210cf5773e1..ea7a76146f5 100644 --- a/clippy_lints/src/size_of_in_element_count.rs +++ b/clippy_lints/src/size_of_in_element_count.rs @@ -11,11 +11,11 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// **What it does:** Detects expressions where - /// size_of:: or size_of_val:: is used as a - /// count of elements of type T + /// `size_of::` or `size_of_val::` is used as a + /// count of elements of type `T` /// /// **Why is this bad?** These functions expect a count - /// of T and not a number of bytes + /// of `T` and not a number of bytes /// /// **Known problems:** None. /// @@ -23,7 +23,6 @@ declare_clippy_lint! { /// ```rust,no_run /// # use std::ptr::copy_nonoverlapping; /// # use std::mem::size_of; - /// /// const SIZE: usize = 128; /// let x = [2u8; SIZE]; /// let mut y = [2u8; SIZE]; @@ -31,7 +30,7 @@ declare_clippy_lint! { /// ``` pub SIZE_OF_IN_ELEMENT_COUNT, correctness, - "using size_of:: or size_of_val:: where a count of elements of T is expected" + "using `size_of::` or `size_of_val::` where a count of elements of `T` is expected" } declare_lint_pass!(SizeOfInElementCount => [SIZE_OF_IN_ELEMENT_COUNT]); @@ -120,7 +119,7 @@ impl<'tcx> LateLintPass<'tcx> for SizeOfInElementCount { , it already gets multiplied by the size of the type"; const LINT_MSG: &str = "found a count of bytes \ - instead of a count of elements of T"; + instead of a count of elements of `T`"; if_chain! { // Find calls to functions with an element count parameter and get diff --git a/tests/ui/size_of_in_element_count.stderr b/tests/ui/size_of_in_element_count.stderr index b7f421ec997..8cf3612abda 100644 --- a/tests/ui/size_of_in_element_count.stderr +++ b/tests/ui/size_of_in_element_count.stderr @@ -1,4 +1,4 @@ -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:18:68 | LL | unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; @@ -7,7 +7,7 @@ LL | unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of: = note: `-D clippy::size-of-in-element-count` implied by `-D warnings` = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:19:62 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; @@ -15,7 +15,7 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:21:49 | LL | unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; @@ -23,7 +23,7 @@ LL | unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:22:64 | LL | unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::()) }; @@ -31,7 +31,7 @@ LL | unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of:: $DIR/size_of_in_element_count.rs:23:51 | LL | unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; @@ -39,7 +39,7 @@ LL | unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:24:66 | LL | unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::()) }; @@ -47,7 +47,7 @@ LL | unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::< | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:26:47 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; @@ -55,7 +55,7 @@ LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:27:47 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; @@ -63,7 +63,7 @@ LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:29:46 | LL | unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() * SIZE) }; @@ -71,7 +71,7 @@ LL | unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() * SIZE) }; | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:30:47 | LL | unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::() * SIZE) }; @@ -79,7 +79,7 @@ LL | unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::() * SIZE) }; | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:32:66 | LL | unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::() * SIZE) }; @@ -87,7 +87,7 @@ LL | unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::< | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:34:46 | LL | slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE); @@ -95,7 +95,7 @@ LL | slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE); | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:35:38 | LL | slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE); @@ -103,7 +103,7 @@ LL | slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE); | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:37:49 | LL | unsafe { from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE) }; @@ -111,7 +111,7 @@ LL | unsafe { from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE) }; | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:38:41 | LL | unsafe { from_raw_parts(y.as_ptr(), size_of::() * SIZE) }; @@ -119,7 +119,7 @@ LL | unsafe { from_raw_parts(y.as_ptr(), size_of::() * SIZE) }; | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:40:33 | LL | unsafe { y.as_mut_ptr().sub(size_of::()) }; @@ -127,7 +127,7 @@ LL | unsafe { y.as_mut_ptr().sub(size_of::()) }; | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:41:29 | LL | y.as_ptr().wrapping_sub(size_of::()); @@ -135,7 +135,7 @@ LL | y.as_ptr().wrapping_sub(size_of::()); | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:42:29 | LL | unsafe { y.as_ptr().add(size_of::()) }; @@ -143,7 +143,7 @@ LL | unsafe { y.as_ptr().add(size_of::()) }; | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:43:33 | LL | y.as_mut_ptr().wrapping_add(size_of::()); @@ -151,7 +151,7 @@ LL | y.as_mut_ptr().wrapping_add(size_of::()); | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:44:32 | LL | unsafe { y.as_ptr().offset(size_of::() as isize) }; @@ -159,7 +159,7 @@ LL | unsafe { y.as_ptr().offset(size_of::() as isize) }; | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:45:36 | LL | y.as_mut_ptr().wrapping_offset(size_of::() as isize); @@ -167,7 +167,7 @@ LL | y.as_mut_ptr().wrapping_offset(size_of::() as isize); | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:48:62 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; @@ -175,7 +175,7 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::( | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:51:62 | LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) }; @@ -183,7 +183,7 @@ LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * si | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: found a count of bytes instead of a count of elements of T +error: found a count of bytes instead of a count of elements of `T` --> $DIR/size_of_in_element_count.rs:54:47 | LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; -- cgit 1.4.1-3-g733a5 From 6edd59885605d2cf0aa8727cf2cd30cd13098804 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Fri, 4 Dec 2020 21:26:47 +0000 Subject: Added a lint-fraction-readability flag to the configuration --- clippy_lints/src/lib.rs | 3 +- clippy_lints/src/literal_representation.rs | 37 ++++++++++++++----- clippy_lints/src/utils/conf.rs | 2 ++ tests/ui-toml/lint_decimal_readability/clippy.toml | 1 + tests/ui-toml/lint_decimal_readability/test.rs | 22 ++++++++++++ tests/ui-toml/lint_decimal_readability/test.stderr | 10 ++++++ .../toml_unknown_key/conf_unknown_key.stderr | 2 +- tests/ui/unreadable_literal.fixed | 18 +++++++--- tests/ui/unreadable_literal.rs | 18 +++++++--- tests/ui/unreadable_literal.stderr | 42 ++++++++++++---------- 10 files changed, 118 insertions(+), 37 deletions(-) create mode 100644 tests/ui-toml/lint_decimal_readability/clippy.toml create mode 100644 tests/ui-toml/lint_decimal_readability/test.rs create mode 100644 tests/ui-toml/lint_decimal_readability/test.stderr (limited to 'tests') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index a861a34aeb7..58e91dd32bd 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1138,7 +1138,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box cargo_common_metadata::CargoCommonMetadata); store.register_late_pass(|| box multiple_crate_versions::MultipleCrateVersions); store.register_late_pass(|| box wildcard_dependencies::WildcardDependencies); - store.register_early_pass(|| box literal_representation::LiteralDigitGrouping); + let literal_representation_lint_fraction_readability = conf.lint_fraction_readability; + store.register_early_pass(move || box literal_representation::LiteralDigitGrouping::new(literal_representation_lint_fraction_readability)); let literal_representation_threshold = conf.literal_representation_threshold; store.register_early_pass(move || box literal_representation::DecimalLiteralRepresentation::new(literal_representation_threshold)); let enum_variant_name_threshold = conf.enum_variant_name_threshold; diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index e8a741683da..3920e5b6e83 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -11,7 +11,7 @@ use rustc_ast::ast::{Expr, ExprKind, Lit, LitKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_clippy_lint! { /// **What it does:** Warns if a long integral or floating-point constant does @@ -32,7 +32,7 @@ declare_clippy_lint! { /// ``` pub UNREADABLE_LITERAL, pedantic, - "long integer literal without underscores" + "long literal without underscores" } declare_clippy_lint! { @@ -208,7 +208,13 @@ impl WarningType { } } -declare_lint_pass!(LiteralDigitGrouping => [ +#[allow(clippy::module_name_repetitions)] +#[derive(Copy, Clone)] +pub struct LiteralDigitGrouping { + lint_fraction_readability: bool, +} + +impl_lint_pass!(LiteralDigitGrouping => [ UNREADABLE_LITERAL, INCONSISTENT_DIGIT_GROUPING, LARGE_DIGIT_GROUPS, @@ -223,7 +229,7 @@ impl EarlyLintPass for LiteralDigitGrouping { } if let ExprKind::Lit(ref lit) = expr.kind { - Self::check_lit(cx, lit) + self.check_lit(cx, lit) } } } @@ -232,7 +238,13 @@ impl EarlyLintPass for LiteralDigitGrouping { const UUID_GROUP_LENS: [usize; 5] = [8, 4, 4, 4, 12]; impl LiteralDigitGrouping { - fn check_lit(cx: &EarlyContext<'_>, lit: &Lit) { + pub fn new(lint_fraction_readability: bool) -> Self { + Self { + lint_fraction_readability, + } + } + + fn check_lit(&self, cx: &EarlyContext<'_>, lit: &Lit) { if_chain! { if let Some(src) = snippet_opt(cx, lit.span); if let Some(mut num_lit) = NumericLiteral::from_lit(&src, &lit); @@ -247,9 +259,12 @@ impl LiteralDigitGrouping { let result = (|| { - let integral_group_size = Self::get_group_size(num_lit.integer.split('_'), num_lit.radix)?; + let integral_group_size = Self::get_group_size(num_lit.integer.split('_'), num_lit.radix, true)?; if let Some(fraction) = num_lit.fraction { - let fractional_group_size = Self::get_group_size(fraction.rsplit('_'), num_lit.radix)?; + let fractional_group_size = Self::get_group_size( + fraction.rsplit('_'), + num_lit.radix, + self.lint_fraction_readability)?; let consistent = Self::parts_consistent(integral_group_size, fractional_group_size, @@ -363,7 +378,11 @@ impl LiteralDigitGrouping { /// Returns the size of the digit groups (or None if ungrouped) if successful, /// otherwise returns a `WarningType` for linting. - fn get_group_size<'a>(groups: impl Iterator, radix: Radix) -> Result, WarningType> { + fn get_group_size<'a>( + groups: impl Iterator, + radix: Radix, + lint_unreadable: bool, + ) -> Result, WarningType> { let mut groups = groups.map(str::len); let first = groups.next().expect("At least one group"); @@ -380,7 +399,7 @@ impl LiteralDigitGrouping { } else { Ok(Some(second)) } - } else if first > 5 { + } else if first > 5 && lint_unreadable { Err(WarningType::UnreadableLiteral) } else { Ok(None) diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index fc6304118d9..c9d5f781f1b 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -170,6 +170,8 @@ define_Conf! { (warn_on_all_wildcard_imports, "warn_on_all_wildcard_imports": bool, false), /// Lint: DISALLOWED_METHOD. The list of blacklisted methods to lint about. NB: `bar` is not here since it has legitimate uses (disallowed_methods, "disallowed_methods": Vec, Vec::::new()), + /// Lint: UNREADABLE_LITERAL. Should the fraction of a decimal be linted to include separators. + (lint_fraction_readability, "lint_fraction_readability": bool, true), } impl Default for Conf { diff --git a/tests/ui-toml/lint_decimal_readability/clippy.toml b/tests/ui-toml/lint_decimal_readability/clippy.toml new file mode 100644 index 00000000000..635e282dc06 --- /dev/null +++ b/tests/ui-toml/lint_decimal_readability/clippy.toml @@ -0,0 +1 @@ +lint-fraction-readability = false \ No newline at end of file diff --git a/tests/ui-toml/lint_decimal_readability/test.rs b/tests/ui-toml/lint_decimal_readability/test.rs new file mode 100644 index 00000000000..9377eb69b23 --- /dev/null +++ b/tests/ui-toml/lint_decimal_readability/test.rs @@ -0,0 +1,22 @@ +#[deny(clippy::unreadable_literal)] + +fn allow_inconsistent_digit_grouping() { + #![allow(clippy::inconsistent_digit_grouping)] + let _pass1 = 100_200_300.123456789; +} + +fn main() { + allow_inconsistent_digit_grouping(); + + let _pass1 = 100_200_300.100_200_300; + let _pass2 = 1.123456789; + let _pass3 = 1.0; + let _pass4 = 10000.00001; + let _pass5 = 1.123456789e1; + + // due to clippy::inconsistent-digit-grouping + let _fail1 = 100_200_300.123456789; + + // fail due to the integer part + let _fail2 = 100200300.300200100; +} diff --git a/tests/ui-toml/lint_decimal_readability/test.stderr b/tests/ui-toml/lint_decimal_readability/test.stderr new file mode 100644 index 00000000000..9119ef19a7b --- /dev/null +++ b/tests/ui-toml/lint_decimal_readability/test.stderr @@ -0,0 +1,10 @@ +error: digits grouped inconsistently by underscores + --> $DIR/test.rs:18:18 + | +LL | let _fail1 = 100_200_300.123456789; + | ^^^^^^^^^^^^^^^^^^^^^ help: consider: `100_200_300.123_456_789` + | + = note: `-D clippy::inconsistent-digit-grouping` implied by `-D warnings` + +error: aborting due to previous error + 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 af3d9ecf6e8..eff4da7e658 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 `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`, `third-party` at line 5 column 1 +error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `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`, `lint-fraction-readability`, `third-party` at line 5 column 1 error: aborting due to previous error diff --git a/tests/ui/unreadable_literal.fixed b/tests/ui/unreadable_literal.fixed index 4043d53299f..c2e38037add 100644 --- a/tests/ui/unreadable_literal.fixed +++ b/tests/ui/unreadable_literal.fixed @@ -10,6 +10,14 @@ macro_rules! foo { }; } +struct Bar(f32); + +macro_rules! bar { + () => { + Bar(100200300400.100200300400500) + }; +} + fn main() { let _good = ( 0b1011_i64, @@ -26,10 +34,12 @@ fn main() { let _good_sci = 1.1234e1; let _bad_sci = 1.123_456e1; - let _fail9 = 0x00ab_cdef; - let _fail10: u32 = 0xBAFE_BAFE; - let _fail11 = 0x0abc_deff; - let _fail12: i128 = 0x00ab_cabc_abca_bcab_cabc; + let _fail1 = 0x00ab_cdef; + let _fail2: u32 = 0xBAFE_BAFE; + let _fail3 = 0x0abc_deff; + let _fail4: i128 = 0x00ab_cabc_abca_bcab_cabc; + let _fail5 = 1.100_300_400; let _ = foo!(); + let _ = bar!(); } diff --git a/tests/ui/unreadable_literal.rs b/tests/ui/unreadable_literal.rs index e658a5f28c9..8296945b25e 100644 --- a/tests/ui/unreadable_literal.rs +++ b/tests/ui/unreadable_literal.rs @@ -10,6 +10,14 @@ macro_rules! foo { }; } +struct Bar(f32); + +macro_rules! bar { + () => { + Bar(100200300400.100200300400500) + }; +} + fn main() { let _good = ( 0b1011_i64, @@ -26,10 +34,12 @@ fn main() { let _good_sci = 1.1234e1; let _bad_sci = 1.123456e1; - let _fail9 = 0xabcdef; - let _fail10: u32 = 0xBAFEBAFE; - let _fail11 = 0xabcdeff; - let _fail12: i128 = 0xabcabcabcabcabcabc; + let _fail1 = 0xabcdef; + let _fail2: u32 = 0xBAFEBAFE; + let _fail3 = 0xabcdeff; + let _fail4: i128 = 0xabcabcabcabcabcabc; + let _fail5 = 1.100300400; let _ = foo!(); + let _ = bar!(); } diff --git a/tests/ui/unreadable_literal.stderr b/tests/ui/unreadable_literal.stderr index 8645cabeabb..8436aac17ac 100644 --- a/tests/ui/unreadable_literal.stderr +++ b/tests/ui/unreadable_literal.stderr @@ -1,5 +1,5 @@ error: digits of hex or binary literal not grouped by four - --> $DIR/unreadable_literal.rs:17:9 + --> $DIR/unreadable_literal.rs:25:9 | LL | 0x1_234_567, | ^^^^^^^^^^^ help: consider: `0x0123_4567` @@ -7,7 +7,7 @@ LL | 0x1_234_567, = note: `-D clippy::unusual-byte-groupings` implied by `-D warnings` error: long literal lacking separators - --> $DIR/unreadable_literal.rs:25:17 + --> $DIR/unreadable_literal.rs:33:17 | LL | let _bad = (0b110110_i64, 0xcafebabe_usize, 123456_f32, 1.234567_f32); | ^^^^^^^^^^^^ help: consider: `0b11_0110_i64` @@ -15,52 +15,58 @@ LL | let _bad = (0b110110_i64, 0xcafebabe_usize, 123456_f32, 1.234567_f32); = note: `-D clippy::unreadable-literal` implied by `-D warnings` error: long literal lacking separators - --> $DIR/unreadable_literal.rs:25:31 + --> $DIR/unreadable_literal.rs:33:31 | LL | let _bad = (0b110110_i64, 0xcafebabe_usize, 123456_f32, 1.234567_f32); | ^^^^^^^^^^^^^^^^ help: consider: `0xcafe_babe_usize` error: long literal lacking separators - --> $DIR/unreadable_literal.rs:25:49 + --> $DIR/unreadable_literal.rs:33:49 | LL | let _bad = (0b110110_i64, 0xcafebabe_usize, 123456_f32, 1.234567_f32); | ^^^^^^^^^^ help: consider: `123_456_f32` error: long literal lacking separators - --> $DIR/unreadable_literal.rs:25:61 + --> $DIR/unreadable_literal.rs:33:61 | LL | let _bad = (0b110110_i64, 0xcafebabe_usize, 123456_f32, 1.234567_f32); | ^^^^^^^^^^^^ help: consider: `1.234_567_f32` error: long literal lacking separators - --> $DIR/unreadable_literal.rs:27:20 + --> $DIR/unreadable_literal.rs:35:20 | LL | let _bad_sci = 1.123456e1; | ^^^^^^^^^^ help: consider: `1.123_456e1` error: long literal lacking separators - --> $DIR/unreadable_literal.rs:29:18 + --> $DIR/unreadable_literal.rs:37:18 | -LL | let _fail9 = 0xabcdef; +LL | let _fail1 = 0xabcdef; | ^^^^^^^^ help: consider: `0x00ab_cdef` error: long literal lacking separators - --> $DIR/unreadable_literal.rs:30:24 + --> $DIR/unreadable_literal.rs:38:23 | -LL | let _fail10: u32 = 0xBAFEBAFE; - | ^^^^^^^^^^ help: consider: `0xBAFE_BAFE` +LL | let _fail2: u32 = 0xBAFEBAFE; + | ^^^^^^^^^^ help: consider: `0xBAFE_BAFE` error: long literal lacking separators - --> $DIR/unreadable_literal.rs:31:19 + --> $DIR/unreadable_literal.rs:39:18 | -LL | let _fail11 = 0xabcdeff; - | ^^^^^^^^^ help: consider: `0x0abc_deff` +LL | let _fail3 = 0xabcdeff; + | ^^^^^^^^^ help: consider: `0x0abc_deff` error: long literal lacking separators - --> $DIR/unreadable_literal.rs:32:25 + --> $DIR/unreadable_literal.rs:40:24 | -LL | let _fail12: i128 = 0xabcabcabcabcabcabc; - | ^^^^^^^^^^^^^^^^^^^^ help: consider: `0x00ab_cabc_abca_bcab_cabc` +LL | let _fail4: i128 = 0xabcabcabcabcabcabc; + | ^^^^^^^^^^^^^^^^^^^^ help: consider: `0x00ab_cabc_abca_bcab_cabc` -error: aborting due to 10 previous errors +error: long literal lacking separators + --> $DIR/unreadable_literal.rs:41:18 + | +LL | let _fail5 = 1.100300400; + | ^^^^^^^^^^^ help: consider: `1.100_300_400` + +error: aborting due to 11 previous errors -- cgit 1.4.1-3-g733a5 From 898b7c594cfdf1eb56d24a4c6fa02678cf5029a8 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sat, 5 Dec 2020 20:59:53 +0000 Subject: Renamed the configuraiton to unreadable-literal-lint-fractions --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/utils/conf.rs | 2 +- tests/ui-toml/lint_decimal_readability/clippy.toml | 2 +- tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 58e91dd32bd..2b99ed570b1 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1138,7 +1138,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box cargo_common_metadata::CargoCommonMetadata); store.register_late_pass(|| box multiple_crate_versions::MultipleCrateVersions); store.register_late_pass(|| box wildcard_dependencies::WildcardDependencies); - let literal_representation_lint_fraction_readability = conf.lint_fraction_readability; + let literal_representation_lint_fraction_readability = conf.unreadable_literal_lint_fractions; store.register_early_pass(move || box literal_representation::LiteralDigitGrouping::new(literal_representation_lint_fraction_readability)); let literal_representation_threshold = conf.literal_representation_threshold; store.register_early_pass(move || box literal_representation::DecimalLiteralRepresentation::new(literal_representation_threshold)); diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index c9d5f781f1b..6403ff6dad1 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -171,7 +171,7 @@ define_Conf! { /// Lint: DISALLOWED_METHOD. The list of blacklisted methods to lint about. NB: `bar` is not here since it has legitimate uses (disallowed_methods, "disallowed_methods": Vec, Vec::::new()), /// Lint: UNREADABLE_LITERAL. Should the fraction of a decimal be linted to include separators. - (lint_fraction_readability, "lint_fraction_readability": bool, true), + (unreadable_literal_lint_fractions, "unreadable_literal_lint_fractions": bool, true), } impl Default for Conf { diff --git a/tests/ui-toml/lint_decimal_readability/clippy.toml b/tests/ui-toml/lint_decimal_readability/clippy.toml index 635e282dc06..6feaf7d5c0c 100644 --- a/tests/ui-toml/lint_decimal_readability/clippy.toml +++ b/tests/ui-toml/lint_decimal_readability/clippy.toml @@ -1 +1 @@ -lint-fraction-readability = false \ No newline at end of file +unreadable-literal-lint-fractions = false \ No newline at end of file diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index eff4da7e658..7b3c476461d 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 `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`, `lint-fraction-readability`, `third-party` at line 5 column 1 +error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `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`, `unreadable-literal-lint-fractions`, `third-party` at line 5 column 1 error: aborting due to previous error -- cgit 1.4.1-3-g733a5 From e90b977a082eac4283a52b53d4da3a507a8ee9a0 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Sun, 22 Nov 2020 22:04:18 +0900 Subject: Fix FP in `unnecessary_lazy_evaluations` --- clippy_lints/src/utils/usage.rs | 21 ++++++++++++++------- tests/ui/unnecessary_lazy_eval_unfixable.rs | 4 ++++ 2 files changed, 18 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/utils/usage.rs b/clippy_lints/src/utils/usage.rs index a7d0ea6ccfb..fc0db7f64ec 100644 --- a/clippy_lints/src/utils/usage.rs +++ b/clippy_lints/src/utils/usage.rs @@ -116,20 +116,27 @@ pub struct ParamBindingIdCollector { } impl<'tcx> ParamBindingIdCollector { fn collect_binding_hir_ids(body: &'tcx hir::Body<'tcx>) -> Vec { - let mut finder = ParamBindingIdCollector { - binding_hir_ids: Vec::new(), - }; - finder.visit_body(body); - finder.binding_hir_ids + let mut hir_ids: Vec = Vec::new(); + for param in body.params.iter() { + let mut finder = ParamBindingIdCollector { + binding_hir_ids: Vec::new(), + }; + finder.visit_param(param); + for hir_id in &finder.binding_hir_ids { + hir_ids.push(*hir_id); + } + } + hir_ids } } impl<'tcx> intravisit::Visitor<'tcx> for ParamBindingIdCollector { type Map = Map<'tcx>; - fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) { - if let hir::PatKind::Binding(_, hir_id, ..) = param.pat.kind { + fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) { + if let hir::PatKind::Binding(_, hir_id, ..) = pat.kind { self.binding_hir_ids.push(hir_id); } + intravisit::walk_pat(self, pat); } fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { diff --git a/tests/ui/unnecessary_lazy_eval_unfixable.rs b/tests/ui/unnecessary_lazy_eval_unfixable.rs index 2e923bc97a2..b05dd143bfd 100644 --- a/tests/ui/unnecessary_lazy_eval_unfixable.rs +++ b/tests/ui/unnecessary_lazy_eval_unfixable.rs @@ -15,4 +15,8 @@ fn main() { } let _ = Ok(1).unwrap_or_else(|e::E| 2); let _ = Ok(1).unwrap_or_else(|SomeStruct { .. }| 2); + + // Fix #6343 + let arr = [(Some(1),)]; + Some(&0).and_then(|&i| arr[i].0); } -- cgit 1.4.1-3-g733a5 From b81141cfb91cbf39e87e32a27530537f18e85405 Mon Sep 17 00:00:00 2001 From: Josias Date: Sun, 22 Nov 2020 19:02:57 +0100 Subject: Add lint print_stderr Resolves #6348 Almost identical to print_stdout, this lint applies to the `eprintln!` and `eprint!` macros rather than `println!` and `print!`. --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 2 ++ clippy_lints/src/write.rs | 23 +++++++++++++++++++++++ tests/ui/print_stderr.rs | 6 ++++++ tests/ui/print_stderr.stderr | 16 ++++++++++++++++ 5 files changed, 48 insertions(+) create mode 100644 tests/ui/print_stderr.rs create mode 100644 tests/ui/print_stderr.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index c7e02aaf4e1..1f2c4f310de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2006,6 +2006,7 @@ Released 2018-09-13 [`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma [`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence [`print_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_literal +[`print_stderr`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stderr [`print_stdout`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stdout [`print_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_with_newline [`println_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#println_empty_string diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 2b99ed570b1..0e630a352fe 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -934,6 +934,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &wildcard_imports::WILDCARD_IMPORTS, &write::PRINTLN_EMPTY_STRING, &write::PRINT_LITERAL, + &write::PRINT_STDERR, &write::PRINT_STDOUT, &write::PRINT_WITH_NEWLINE, &write::USE_DEBUG, @@ -1247,6 +1248,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::RC_BUFFER), 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/write.rs b/clippy_lints/src/write.rs index ff414f748ef..2248bc1e32e 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -75,6 +75,24 @@ declare_clippy_lint! { "printing on stdout" } +declare_clippy_lint! { + /// **What it does:** Checks for printing on *stderr*. The purpose of this lint + /// is to catch debugging remnants. + /// + /// **Why is this bad?** People often print on *stderr* while debugging an + /// application and might forget to remove those prints afterward. + /// + /// **Known problems:** Only catches `eprint!` and `eprintln!` calls. + /// + /// **Example:** + /// ```rust + /// eprintln!("Hello world!"); + /// ``` + pub PRINT_STDERR, + restriction, + "printing on stderr" +} + declare_clippy_lint! { /// **What it does:** Checks for use of `Debug` formatting. The purpose of this /// lint is to catch debugging remnants. @@ -201,6 +219,7 @@ impl_lint_pass!(Write => [ PRINT_WITH_NEWLINE, PRINTLN_EMPTY_STRING, PRINT_STDOUT, + PRINT_STDERR, USE_DEBUG, PRINT_LITERAL, WRITE_WITH_NEWLINE, @@ -260,6 +279,10 @@ impl EarlyLintPass for Write { ); } } + } else if mac.path == sym!(eprintln) { + span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprintln!`"); + } else if mac.path == sym!(eprint) { + span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprint!`"); } else if mac.path == sym!(print) { if !is_build_script(cx) { span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`"); diff --git a/tests/ui/print_stderr.rs b/tests/ui/print_stderr.rs new file mode 100644 index 00000000000..e53f46b1c2f --- /dev/null +++ b/tests/ui/print_stderr.rs @@ -0,0 +1,6 @@ +#![warn(clippy::print_stderr)] + +fn main() { + eprintln!("Hello"); + eprint!("World"); +} diff --git a/tests/ui/print_stderr.stderr b/tests/ui/print_stderr.stderr new file mode 100644 index 00000000000..7252fce72c6 --- /dev/null +++ b/tests/ui/print_stderr.stderr @@ -0,0 +1,16 @@ +error: use of `eprintln!` + --> $DIR/print_stderr.rs:4:5 + | +LL | eprintln!("Hello"); + | ^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::print-stderr` implied by `-D warnings` + +error: use of `eprint!` + --> $DIR/print_stderr.rs:5:5 + | +LL | eprint!("World"); + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From 51cee15be0f67403eedfe88c0c4ca07f306bef42 Mon Sep 17 00:00:00 2001 From: Josias Date: Sun, 29 Nov 2020 09:55:47 +0100 Subject: Add negative tests --- tests/ui/print_stderr.rs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'tests') diff --git a/tests/ui/print_stderr.rs b/tests/ui/print_stderr.rs index e53f46b1c2f..fa07e74a7be 100644 --- a/tests/ui/print_stderr.rs +++ b/tests/ui/print_stderr.rs @@ -2,5 +2,7 @@ fn main() { eprintln!("Hello"); + println!("This should not do anything"); eprint!("World"); + print!("Nor should this"); } -- cgit 1.4.1-3-g733a5 From b04bfbd09b63d978f1bb7f4c9222cb2d37607309 Mon Sep 17 00:00:00 2001 From: Josias Date: Tue, 1 Dec 2020 11:29:49 +0100 Subject: Fix print_stderr.stderr test --- tests/ui/print_stderr.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/ui/print_stderr.stderr b/tests/ui/print_stderr.stderr index 7252fce72c6..5af735af657 100644 --- a/tests/ui/print_stderr.stderr +++ b/tests/ui/print_stderr.stderr @@ -7,7 +7,7 @@ LL | eprintln!("Hello"); = note: `-D clippy::print-stderr` implied by `-D warnings` error: use of `eprint!` - --> $DIR/print_stderr.rs:5:5 + --> $DIR/print_stderr.rs:6:5 | LL | eprint!("World"); | ^^^^^^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From 7063c36c912990bd67327a41445706a451fe5b48 Mon Sep 17 00:00:00 2001 From: Josias Date: Fri, 4 Dec 2020 15:39:09 +0100 Subject: Add eprint! to print_with_newline lint --- clippy_lints/src/write.rs | 20 ++++++ tests/ui/eprint_with_newline.rs | 49 +++++++++++++++ tests/ui/eprint_with_newline.stderr | 121 ++++++++++++++++++++++++++++++++++++ 3 files changed, 190 insertions(+) create mode 100644 tests/ui/eprint_with_newline.rs create mode 100644 tests/ui/eprint_with_newline.stderr (limited to 'tests') diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 2248bc1e32e..0fd56f78572 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -283,6 +283,26 @@ impl EarlyLintPass for Write { span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprintln!`"); } else if mac.path == sym!(eprint) { span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprint!`"); + if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { + if check_newlines(&fmt_str) { + span_lint_and_then( + cx, + PRINT_WITH_NEWLINE, + mac.span(), + "using `eprint!()` with a format string that ends in a single newline", + |err| { + err.multipart_suggestion( + "use `eprintln!` instead", + vec![ + (mac.path.span, String::from("eprintln")), + (newline_span(&fmt_str), String::new()), + ], + Applicability::MachineApplicable, + ); + }, + ); + } + } } else if mac.path == sym!(print) { if !is_build_script(cx) { span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`"); diff --git a/tests/ui/eprint_with_newline.rs b/tests/ui/eprint_with_newline.rs new file mode 100644 index 00000000000..8df32649ad9 --- /dev/null +++ b/tests/ui/eprint_with_newline.rs @@ -0,0 +1,49 @@ +#![allow(clippy::print_literal)] +#![warn(clippy::print_with_newline)] + +fn main() { + eprint!("Hello\n"); + eprint!("Hello {}\n", "world"); + eprint!("Hello {} {}\n", "world", "#2"); + eprint!("{}\n", 1265); + eprint!("\n"); + + // these are all fine + eprint!(""); + eprint!("Hello"); + eprintln!("Hello"); + eprintln!("Hello\n"); + eprintln!("Hello {}\n", "world"); + eprint!("Issue\n{}", 1265); + eprint!("{}", 1265); + eprint!("\n{}", 1275); + eprint!("\n\n"); + eprint!("like eof\n\n"); + eprint!("Hello {} {}\n\n", "world", "#2"); + eprintln!("\ndon't\nwarn\nfor\nmultiple\nnewlines\n"); // #3126 + eprintln!("\nbla\n\n"); // #3126 + + // Escaping + eprint!("\\n"); // #3514 + eprint!("\\\n"); // should fail + eprint!("\\\\n"); + + // Raw strings + eprint!(r"\n"); // #3778 + + // Literal newlines should also fail + eprint!( + " +" + ); + eprint!( + r" +" + ); + + // Don't warn on CRLF (#4208) + eprint!("\r\n"); + eprint!("foo\r\n"); + eprint!("\\r\n"); //~ ERROR + eprint!("foo\rbar\n") // ~ ERROR +} diff --git a/tests/ui/eprint_with_newline.stderr b/tests/ui/eprint_with_newline.stderr new file mode 100644 index 00000000000..31811d1d92a --- /dev/null +++ b/tests/ui/eprint_with_newline.stderr @@ -0,0 +1,121 @@ +error: using `eprint!()` with a format string that ends in a single newline + --> $DIR/eprint_with_newline.rs:5:5 + | +LL | eprint!("Hello/n"); + | ^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::print-with-newline` implied by `-D warnings` +help: use `eprintln!` instead + | +LL | eprintln!("Hello"); + | ^^^^^^^^ -- + +error: using `eprint!()` with a format string that ends in a single newline + --> $DIR/eprint_with_newline.rs:6:5 + | +LL | eprint!("Hello {}/n", "world"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `eprintln!` instead + | +LL | eprintln!("Hello {}", "world"); + | ^^^^^^^^ -- + +error: using `eprint!()` with a format string that ends in a single newline + --> $DIR/eprint_with_newline.rs:7:5 + | +LL | eprint!("Hello {} {}/n", "world", "#2"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `eprintln!` instead + | +LL | eprintln!("Hello {} {}", "world", "#2"); + | ^^^^^^^^ -- + +error: using `eprint!()` with a format string that ends in a single newline + --> $DIR/eprint_with_newline.rs:8:5 + | +LL | eprint!("{}/n", 1265); + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: use `eprintln!` instead + | +LL | eprintln!("{}", 1265); + | ^^^^^^^^ -- + +error: using `eprint!()` with a format string that ends in a single newline + --> $DIR/eprint_with_newline.rs:9:5 + | +LL | eprint!("/n"); + | ^^^^^^^^^^^^^ + | +help: use `eprintln!` instead + | +LL | eprintln!(); + | ^^^^^^^^ -- + +error: using `eprint!()` with a format string that ends in a single newline + --> $DIR/eprint_with_newline.rs:28:5 + | +LL | eprint!("//n"); // should fail + | ^^^^^^^^^^^^^^^ + | +help: use `eprintln!` instead + | +LL | eprintln!("/"); // should fail + | ^^^^^^^^ -- + +error: using `eprint!()` with a format string that ends in a single newline + --> $DIR/eprint_with_newline.rs:35:5 + | +LL | / eprint!( +LL | | " +LL | | " +LL | | ); + | |_____^ + | +help: use `eprintln!` instead + | +LL | eprintln!( +LL | "" + | + +error: using `eprint!()` with a format string that ends in a single newline + --> $DIR/eprint_with_newline.rs:39:5 + | +LL | / eprint!( +LL | | r" +LL | | " +LL | | ); + | |_____^ + | +help: use `eprintln!` instead + | +LL | eprintln!( +LL | r"" + | + +error: using `eprint!()` with a format string that ends in a single newline + --> $DIR/eprint_with_newline.rs:47:5 + | +LL | eprint!("/r/n"); //~ ERROR + | ^^^^^^^^^^^^^^^^ + | +help: use `eprintln!` instead + | +LL | eprintln!("/r"); //~ ERROR + | ^^^^^^^^ -- + +error: using `eprint!()` with a format string that ends in a single newline + --> $DIR/eprint_with_newline.rs:48:5 + | +LL | eprint!("foo/rbar/n") // ~ ERROR + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: use `eprintln!` instead + | +LL | eprintln!("foo/rbar") // ~ ERROR + | ^^^^^^^^ -- + +error: aborting due to 10 previous errors + -- cgit 1.4.1-3-g733a5 From e58c7dd1685cb19e2ec87568cbc2cbd0aba3e2c7 Mon Sep 17 00:00:00 2001 From: Dobe Peter Date: Sat, 31 Oct 2020 20:31:34 +0100 Subject: panic_in_result_fn: Extend to also check usages of [debug_]assert* macros Also, the macro-finding logic has been moved to the util module, for use by future lints. --- clippy_lints/src/panic_in_result_fn.rs | 68 ++++++++++------------ clippy_lints/src/utils/mod.rs | 33 ++++++++++- tests/ui/panic_in_result_fn.stderr | 24 ++++---- tests/ui/panic_in_result_fn_assertions.rs | 48 +++++++++++++++ tests/ui/panic_in_result_fn_assertions.stderr | 57 ++++++++++++++++++ tests/ui/panic_in_result_fn_debug_assertions.rs | 48 +++++++++++++++ .../ui/panic_in_result_fn_debug_assertions.stderr | 57 ++++++++++++++++++ 7 files changed, 285 insertions(+), 50 deletions(-) create mode 100644 tests/ui/panic_in_result_fn_assertions.rs create mode 100644 tests/ui/panic_in_result_fn_assertions.stderr create mode 100644 tests/ui/panic_in_result_fn_debug_assertions.rs create mode 100644 tests/ui/panic_in_result_fn_debug_assertions.stderr (limited to 'tests') diff --git a/clippy_lints/src/panic_in_result_fn.rs b/clippy_lints/src/panic_in_result_fn.rs index 72dfccc1089..cdabb0d0dd6 100644 --- a/clippy_lints/src/panic_in_result_fn.rs +++ b/clippy_lints/src/panic_in_result_fn.rs @@ -1,18 +1,16 @@ -use crate::utils::{is_expn_of, is_type_diagnostic_item, return_ty, span_lint_and_then}; +use crate::utils::{find_macro_calls, is_type_diagnostic_item, return_ty, span_lint_and_then}; use rustc_hir as hir; -use rustc_hir::intravisit::{self, FnKind, NestedVisitorMap, Visitor}; -use rustc_hir::Expr; +use rustc_hir::intravisit::FnKind; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{sym, Span}; declare_clippy_lint! { - /// **What it does:** Checks for usage of `panic!`, `unimplemented!`, `todo!` or `unreachable!` in a function of type result. + /// **What it does:** Checks for usage of `panic!`, `unimplemented!`, `todo!`, `unreachable!` or assertions in a function of type result. /// - /// **Why is this bad?** For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence unimplemented, panic and unreachable should be avoided. + /// **Why is this bad?** For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence panicking macros should be avoided. /// - /// **Known problems:** None. + /// **Known problems:** Functions called from a function returning a `Result` may invoke a panicking macro. This is not checked. /// /// **Example:** /// @@ -22,9 +20,15 @@ declare_clippy_lint! { /// panic!("error"); /// } /// ``` + /// Use instead: + /// ```rust + /// fn result_without_panic() -> Result { + /// Err(String::from("error")) + /// } + /// ``` pub PANIC_IN_RESULT_FN, restriction, - "functions of type `Result<..>` that contain `panic!()`, `todo!()` or `unreachable()` or `unimplemented()` " + "functions of type `Result<..>` that contain `panic!()`, `todo!()`, `unreachable()`, `unimplemented()` or assertion" } declare_lint_pass!(PanicInResultFn => [PANIC_IN_RESULT_FN]); @@ -47,43 +51,33 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResultFn { } } -struct FindPanicUnimplementedUnreachable { - result: Vec, -} - -impl<'tcx> Visitor<'tcx> for FindPanicUnimplementedUnreachable { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if ["unimplemented", "unreachable", "panic", "todo"] - .iter() - .any(|fun| is_expn_of(expr.span, fun).is_some()) - { - self.result.push(expr.span); - } - // and check sub-expressions - intravisit::walk_expr(self, expr); - } - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } -} - fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) { - let mut panics = FindPanicUnimplementedUnreachable { result: Vec::new() }; - panics.visit_expr(&body.value); - if !panics.result.is_empty() { + let panics = find_macro_calls( + vec![ + "unimplemented", + "unreachable", + "panic", + "todo", + "assert", + "assert_eq", + "assert_ne", + "debug_assert", + "debug_assert_eq", + "debug_assert_ne", + ], + body, + ); + if !panics.is_empty() { span_lint_and_then( cx, PANIC_IN_RESULT_FN, impl_span, - "used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result`", + "used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`", move |diag| { diag.help( - "`unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing", + "`unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing", ); - diag.span_note(panics.result, "return Err() instead of panicking"); + diag.span_note(panics, "return Err() instead of panicking"); }, ); } diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 007e72d129f..e47d71aac99 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -41,7 +41,7 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; -use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; +use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::Node; use rustc_hir::{ def, Arm, Block, Body, Constness, Crate, Expr, ExprKind, FnDecl, HirId, ImplItem, ImplItemKind, Item, ItemKind, @@ -603,6 +603,37 @@ pub fn contains_return(expr: &hir::Expr<'_>) -> bool { visitor.found } +struct FindMacroCalls<'a> { + names: Vec<&'a str>, + result: Vec, +} + +impl<'a, 'tcx> Visitor<'tcx> for FindMacroCalls<'a> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if self.names.iter().any(|fun| is_expn_of(expr.span, fun).is_some()) { + self.result.push(expr.span); + } + // and check sub-expressions + intravisit::walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +/// Finds calls of the specified macros in a function body. +pub fn find_macro_calls(names: Vec<&str>, body: &'tcx Body<'tcx>) -> Vec { + let mut fmc = FindMacroCalls { + names, + result: Vec::new(), + }; + fmc.visit_expr(&body.value); + fmc.result +} + /// Converts a span to a code snippet if available, otherwise use default. /// /// This is useful if you want to provide suggestions for your lint or more generally, if you want diff --git a/tests/ui/panic_in_result_fn.stderr b/tests/ui/panic_in_result_fn.stderr index ca73ac5a411..eb744b0c198 100644 --- a/tests/ui/panic_in_result_fn.stderr +++ b/tests/ui/panic_in_result_fn.stderr @@ -1,4 +1,4 @@ -error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn.rs:7:5 | LL | / fn result_with_panic() -> Result // should emit lint @@ -8,7 +8,7 @@ LL | | } | |_____^ | = note: `-D clippy::panic-in-result-fn` implied by `-D warnings` - = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking --> $DIR/panic_in_result_fn.rs:9:9 | @@ -16,7 +16,7 @@ LL | panic!("error"); | ^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn.rs:12:5 | LL | / fn result_with_unimplemented() -> Result // should emit lint @@ -25,7 +25,7 @@ LL | | unimplemented!(); LL | | } | |_____^ | - = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking --> $DIR/panic_in_result_fn.rs:14:9 | @@ -33,7 +33,7 @@ LL | unimplemented!(); | ^^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn.rs:17:5 | LL | / fn result_with_unreachable() -> Result // should emit lint @@ -42,7 +42,7 @@ LL | | unreachable!(); LL | | } | |_____^ | - = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking --> $DIR/panic_in_result_fn.rs:19:9 | @@ -50,7 +50,7 @@ LL | unreachable!(); | ^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn.rs:22:5 | LL | / fn result_with_todo() -> Result // should emit lint @@ -59,7 +59,7 @@ LL | | todo!("Finish this"); LL | | } | |_____^ | - = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking --> $DIR/panic_in_result_fn.rs:24:9 | @@ -67,7 +67,7 @@ LL | todo!("Finish this"); | ^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn.rs:53:1 | LL | / fn function_result_with_panic() -> Result // should emit lint @@ -76,7 +76,7 @@ LL | | panic!("error"); LL | | } | |_^ | - = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking --> $DIR/panic_in_result_fn.rs:55:5 | @@ -84,7 +84,7 @@ LL | panic!("error"); | ^^^^^^^^^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: used `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` in a function that returns `Result` +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` --> $DIR/panic_in_result_fn.rs:68:1 | LL | / fn main() -> Result<(), String> { @@ -93,7 +93,7 @@ LL | | Ok(()) LL | | } | |_^ | - = help: `unimplemented!()`, `unreachable!()`, `todo!()` or `panic!()` should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing note: return Err() instead of panicking --> $DIR/panic_in_result_fn.rs:69:5 | diff --git a/tests/ui/panic_in_result_fn_assertions.rs b/tests/ui/panic_in_result_fn_assertions.rs new file mode 100644 index 00000000000..ffdf8288adc --- /dev/null +++ b/tests/ui/panic_in_result_fn_assertions.rs @@ -0,0 +1,48 @@ +#![warn(clippy::panic_in_result_fn)] +#![allow(clippy::unnecessary_wraps)] + +struct A; + +impl A { + fn result_with_assert_with_message(x: i32) -> Result // should emit lint + { + assert!(x == 5, "wrong argument"); + Ok(true) + } + + fn result_with_assert_eq(x: i32) -> Result // should emit lint + { + assert_eq!(x, 5); + Ok(true) + } + + fn result_with_assert_ne(x: i32) -> Result // should emit lint + { + assert_ne!(x, 1); + Ok(true) + } + + fn other_with_assert_with_message(x: i32) // should not emit lint + { + assert!(x == 5, "wrong argument"); + } + + fn other_with_assert_eq(x: i32) // should not emit lint + { + assert_eq!(x, 5); + } + + fn other_with_assert_ne(x: i32) // should not emit lint + { + assert_ne!(x, 1); + } + + fn result_without_banned_functions() -> Result // should not emit lint + { + let assert = "assert!"; + println!("No {}", assert); + Ok(true) + } +} + +fn main() {} diff --git a/tests/ui/panic_in_result_fn_assertions.stderr b/tests/ui/panic_in_result_fn_assertions.stderr new file mode 100644 index 00000000000..a17f043737d --- /dev/null +++ b/tests/ui/panic_in_result_fn_assertions.stderr @@ -0,0 +1,57 @@ +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` + --> $DIR/panic_in_result_fn_assertions.rs:7:5 + | +LL | / fn result_with_assert_with_message(x: i32) -> Result // should emit lint +LL | | { +LL | | assert!(x == 5, "wrong argument"); +LL | | Ok(true) +LL | | } + | |_____^ + | + = note: `-D clippy::panic-in-result-fn` implied by `-D warnings` + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +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 a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` + --> $DIR/panic_in_result_fn_assertions.rs:13:5 + | +LL | / fn result_with_assert_eq(x: i32) -> Result // should emit lint +LL | | { +LL | | assert_eq!(x, 5); +LL | | Ok(true) +LL | | } + | |_____^ + | + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +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 a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` + --> $DIR/panic_in_result_fn_assertions.rs:19:5 + | +LL | / fn result_with_assert_ne(x: i32) -> Result // should emit lint +LL | | { +LL | | assert_ne!(x, 1); +LL | | Ok(true) +LL | | } + | |_____^ + | + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +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 a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 3 previous errors + diff --git a/tests/ui/panic_in_result_fn_debug_assertions.rs b/tests/ui/panic_in_result_fn_debug_assertions.rs new file mode 100644 index 00000000000..b60c79f97c8 --- /dev/null +++ b/tests/ui/panic_in_result_fn_debug_assertions.rs @@ -0,0 +1,48 @@ +#![warn(clippy::panic_in_result_fn)] +#![allow(clippy::unnecessary_wraps)] + +struct A; + +impl A { + fn result_with_debug_assert_with_message(x: i32) -> Result // should emit lint + { + debug_assert!(x == 5, "wrong argument"); + Ok(true) + } + + fn result_with_debug_assert_eq(x: i32) -> Result // should emit lint + { + debug_assert_eq!(x, 5); + Ok(true) + } + + fn result_with_debug_assert_ne(x: i32) -> Result // should emit lint + { + debug_assert_ne!(x, 1); + Ok(true) + } + + fn other_with_debug_assert_with_message(x: i32) // should not emit lint + { + debug_assert!(x == 5, "wrong argument"); + } + + fn other_with_debug_assert_eq(x: i32) // should not emit lint + { + debug_assert_eq!(x, 5); + } + + fn other_with_debug_assert_ne(x: i32) // should not emit lint + { + debug_assert_ne!(x, 1); + } + + fn result_without_banned_functions() -> Result // should not emit lint + { + let debug_assert = "debug_assert!"; + println!("No {}", debug_assert); + Ok(true) + } +} + +fn main() {} diff --git a/tests/ui/panic_in_result_fn_debug_assertions.stderr b/tests/ui/panic_in_result_fn_debug_assertions.stderr new file mode 100644 index 00000000000..ec18e89698c --- /dev/null +++ b/tests/ui/panic_in_result_fn_debug_assertions.stderr @@ -0,0 +1,57 @@ +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` + --> $DIR/panic_in_result_fn_debug_assertions.rs:7:5 + | +LL | / fn result_with_debug_assert_with_message(x: i32) -> Result // should emit lint +LL | | { +LL | | debug_assert!(x == 5, "wrong argument"); +LL | | Ok(true) +LL | | } + | |_____^ + | + = note: `-D clippy::panic-in-result-fn` implied by `-D warnings` + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking + --> $DIR/panic_in_result_fn_debug_assertions.rs:9:9 + | +LL | debug_assert!(x == 5, "wrong argument"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` + --> $DIR/panic_in_result_fn_debug_assertions.rs:13:5 + | +LL | / fn result_with_debug_assert_eq(x: i32) -> Result // should emit lint +LL | | { +LL | | debug_assert_eq!(x, 5); +LL | | Ok(true) +LL | | } + | |_____^ + | + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking + --> $DIR/panic_in_result_fn_debug_assertions.rs:15:9 + | +LL | debug_assert_eq!(x, 5); + | ^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` + --> $DIR/panic_in_result_fn_debug_assertions.rs:19:5 + | +LL | / fn result_with_debug_assert_ne(x: i32) -> Result // should emit lint +LL | | { +LL | | debug_assert_ne!(x, 1); +LL | | Ok(true) +LL | | } + | |_____^ + | + = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing +note: return Err() instead of panicking + --> $DIR/panic_in_result_fn_debug_assertions.rs:21:9 + | +LL | debug_assert_ne!(x, 1); + | ^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 3 previous errors + -- cgit 1.4.1-3-g733a5 From 16d0e569248675da54fd892b4f36fd0ebc88eb1b Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Tue, 8 Dec 2020 00:14:05 +0100 Subject: Update reference file --- tests/ui/panic_in_result_fn_assertions.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/ui/panic_in_result_fn_assertions.stderr b/tests/ui/panic_in_result_fn_assertions.stderr index a17f043737d..86f61ad718a 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 a macro (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` -- cgit 1.4.1-3-g733a5 From 3187cad8ec0e9c32272341028526d6f1e208a704 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Tue, 8 Dec 2020 23:17:12 +0100 Subject: Factor out some code in write.rs Get rid of the too-many-lines error. --- clippy_lints/src/write.rs | 110 ++++++++++++++++------------------- tests/ui/println_empty_string.fixed | 7 +++ tests/ui/println_empty_string.rs | 7 +++ tests/ui/println_empty_string.stderr | 14 ++++- 4 files changed, 77 insertions(+), 61 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 0fd56f78572..337f7a229b9 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -262,71 +262,22 @@ impl EarlyLintPass for Write { .map_or(false, |crate_name| crate_name == "build_script_build") } - if mac.path == sym!(println) { + if mac.path == sym!(print) { if !is_build_script(cx) { - span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`"); + span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`"); } - if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { - if fmt_str.symbol == Symbol::intern("") { - span_lint_and_sugg( - cx, - PRINTLN_EMPTY_STRING, - mac.span(), - "using `println!(\"\")`", - "replace it with", - "println!()".to_string(), - Applicability::MachineApplicable, - ); - } + self.lint_print_with_newline(cx, mac); + } else if mac.path == sym!(println) { + if !is_build_script(cx) { + span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`"); } - } else if mac.path == sym!(eprintln) { - span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprintln!`"); + self.lint_println_empty_string(cx, mac); } else if mac.path == sym!(eprint) { span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprint!`"); - if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { - if check_newlines(&fmt_str) { - span_lint_and_then( - cx, - PRINT_WITH_NEWLINE, - mac.span(), - "using `eprint!()` with a format string that ends in a single newline", - |err| { - err.multipart_suggestion( - "use `eprintln!` instead", - vec![ - (mac.path.span, String::from("eprintln")), - (newline_span(&fmt_str), String::new()), - ], - Applicability::MachineApplicable, - ); - }, - ); - } - } - } else if mac.path == sym!(print) { - if !is_build_script(cx) { - span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`"); - } - if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { - if check_newlines(&fmt_str) { - span_lint_and_then( - cx, - PRINT_WITH_NEWLINE, - mac.span(), - "using `print!()` with a format string that ends in a single newline", - |err| { - err.multipart_suggestion( - "use `println!` instead", - vec![ - (mac.path.span, String::from("println")), - (newline_span(&fmt_str), String::new()), - ], - Applicability::MachineApplicable, - ); - }, - ); - } - } + self.lint_print_with_newline(cx, mac); + } else if mac.path == sym!(eprintln) { + span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprintln!`"); + self.lint_println_empty_string(cx, mac); } else if mac.path == sym!(write) { if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), true) { if check_newlines(&fmt_str) { @@ -530,6 +481,45 @@ impl Write { } } } + + fn lint_println_empty_string(&self, cx: &EarlyContext<'_>, mac: &MacCall) { + if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { + if fmt_str.symbol == Symbol::intern("") { + let name = mac.path.segments[0].ident.name; + span_lint_and_sugg( + cx, + PRINTLN_EMPTY_STRING, + mac.span(), + &format!("using `{}!(\"\")`", name), + "replace it with", + format!("{}!()", name), + Applicability::MachineApplicable, + ); + } + } + } + + fn lint_print_with_newline(&self, cx: &EarlyContext<'_>, mac: &MacCall) { + if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { + if check_newlines(&fmt_str) { + let name = mac.path.segments[0].ident.name; + let suggested = format!("{}ln", name); + span_lint_and_then( + cx, + PRINT_WITH_NEWLINE, + mac.span(), + &format!("using `{}!()` with a format string that ends in a single newline", name), + |err| { + err.multipart_suggestion( + &format!("use `{}!` instead", suggested), + vec![(mac.path.span, suggested), (newline_span(&fmt_str), String::new())], + Applicability::MachineApplicable, + ); + }, + ); + } + } + } } /// Checks if the format string contains a single newline that terminates it. diff --git a/tests/ui/println_empty_string.fixed b/tests/ui/println_empty_string.fixed index 2b889b62ea9..9760680927a 100644 --- a/tests/ui/println_empty_string.fixed +++ b/tests/ui/println_empty_string.fixed @@ -8,4 +8,11 @@ fn main() { match "a" { _ => println!(), } + + eprintln!(); + eprintln!(); + + match "a" { + _ => eprintln!(), + } } diff --git a/tests/ui/println_empty_string.rs b/tests/ui/println_empty_string.rs index 890f5f68476..80fdb3e6e21 100644 --- a/tests/ui/println_empty_string.rs +++ b/tests/ui/println_empty_string.rs @@ -8,4 +8,11 @@ fn main() { match "a" { _ => println!(""), } + + eprintln!(); + eprintln!(""); + + match "a" { + _ => eprintln!(""), + } } diff --git a/tests/ui/println_empty_string.stderr b/tests/ui/println_empty_string.stderr index 23112b88168..17fe4ea7479 100644 --- a/tests/ui/println_empty_string.stderr +++ b/tests/ui/println_empty_string.stderr @@ -12,5 +12,17 @@ error: using `println!("")` LL | _ => println!(""), | ^^^^^^^^^^^^ help: replace it with: `println!()` -error: aborting due to 2 previous errors +error: using `eprintln!("")` + --> $DIR/println_empty_string.rs:13:5 + | +LL | eprintln!(""); + | ^^^^^^^^^^^^^ help: replace it with: `eprintln!()` + +error: using `eprintln!("")` + --> $DIR/println_empty_string.rs:16:14 + | +LL | _ => eprintln!(""), + | ^^^^^^^^^^^^^ help: replace it with: `eprintln!()` + +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From f77f1db35b1f263286962cfa5e5c08a55add83cb Mon Sep 17 00:00:00 2001 From: Korrat Date: Sat, 24 Oct 2020 16:48:10 +0200 Subject: Add a lint for maps with zero-sized values Co-authored-by: Eduardo Broto --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 4 ++ clippy_lints/src/zero_sized_map_values.rs | 82 ++++++++++++++++++++++ tests/ui/zero_sized_btreemap_values.rs | 68 ++++++++++++++++++ tests/ui/zero_sized_btreemap_values.stderr | 107 +++++++++++++++++++++++++++++ tests/ui/zero_sized_hashmap_values.rs | 68 ++++++++++++++++++ tests/ui/zero_sized_hashmap_values.stderr | 107 +++++++++++++++++++++++++++++ 7 files changed, 437 insertions(+) create mode 100644 clippy_lints/src/zero_sized_map_values.rs create mode 100644 tests/ui/zero_sized_btreemap_values.rs create mode 100644 tests/ui/zero_sized_btreemap_values.stderr create mode 100644 tests/ui/zero_sized_hashmap_values.rs create mode 100644 tests/ui/zero_sized_hashmap_values.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 82f2ad7ec4e..af3b1c1db2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2171,5 +2171,6 @@ Released 2018-09-13 [`zero_divided_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_divided_by_zero [`zero_prefixed_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_prefixed_literal [`zero_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_ptr +[`zero_sized_map_values`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_sized_map_values [`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index a92ae9ed8d9..5fca088c9b4 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -345,6 +345,7 @@ mod wildcard_dependencies; mod wildcard_imports; mod write; mod zero_div_zero; +mod zero_sized_map_values; // end lints modules, do not remove this comment, it’s used in `update_lints` pub use crate::utils::conf::Conf; @@ -944,6 +945,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &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` @@ -1204,6 +1206,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box undropped_manually_drops::UndroppedManuallyDrops); store.register_late_pass(|| box strings::StrToString); store.register_late_pass(|| box strings::StringToString); + store.register_late_pass(|| box zero_sized_map_values::ZeroSizedMapValues); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1336,6 +1339,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), ]); #[cfg(feature = "internal-lints")] diff --git a/clippy_lints/src/zero_sized_map_values.rs b/clippy_lints/src/zero_sized_map_values.rs new file mode 100644 index 00000000000..1d5fa8d06a9 --- /dev/null +++ b/clippy_lints/src/zero_sized_map_values.rs @@ -0,0 +1,82 @@ +use if_chain::if_chain; +use rustc_hir::{self as hir, HirId, ItemKind, Node}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{Adt, Ty}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_target::abi::LayoutOf as _; +use rustc_typeck::hir_ty_to_ty; + +use crate::utils::{is_type_diagnostic_item, match_type, paths, span_lint_and_help}; + +declare_clippy_lint! { + /// **What it does:** Checks for maps with zero-sized value types anywhere in the code. + /// + /// **Why is this bad?** Since there is only a single value for a zero-sized type, a map + /// containing zero sized values is effectively a set. Using a set in that case improves + /// readability and communicates intent more clearly. + /// + /// **Known problems:** + /// * A zero-sized type cannot be recovered later if it contains private fields. + /// * This lints the signature of public items + /// + /// **Example:** + /// + /// ```rust + /// # use std::collections::HashMap; + /// fn unique_words(text: &str) -> HashMap<&str, ()> { + /// todo!(); + /// } + /// ``` + /// Use instead: + /// ```rust + /// # use std::collections::HashSet; + /// fn unique_words(text: &str) -> HashSet<&str> { + /// todo!(); + /// } + /// ``` + pub ZERO_SIZED_MAP_VALUES, + pedantic, + "usage of map with zero-sized value type" +} + +declare_lint_pass!(ZeroSizedMapValues => [ZERO_SIZED_MAP_VALUES]); + +impl LateLintPass<'_> for ZeroSizedMapValues { + fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) { + if_chain! { + if !hir_ty.span.from_expansion(); + if !in_trait_impl(cx, hir_ty.hir_id); + let ty = ty_from_hir_ty(cx, hir_ty); + if is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) || match_type(cx, ty, &paths::BTREEMAP); + if let Adt(_, ref substs) = ty.kind(); + let ty = substs.type_at(1); + if let Ok(layout) = cx.layout_of(ty); + if layout.is_zst(); + then { + span_lint_and_help(cx, ZERO_SIZED_MAP_VALUES, hir_ty.span, "map with zero-sized value type", None, "consider using a set instead"); + } + } + } +} + +fn in_trait_impl(cx: &LateContext<'_>, hir_id: HirId) -> bool { + let parent_id = cx.tcx.hir().get_parent_item(hir_id); + if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_item(parent_id)) { + if let ItemKind::Impl { of_trait: Some(_), .. } = item.kind { + return true; + } + } + false +} + +fn ty_from_hir_ty<'tcx>(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'_>) -> Ty<'tcx> { + cx.maybe_typeck_results() + .and_then(|results| { + if results.hir_owner == hir_ty.hir_id.owner { + results.node_type_opt(hir_ty.hir_id) + } else { + None + } + }) + .unwrap_or_else(|| hir_ty_to_ty(cx.tcx, hir_ty)) +} diff --git a/tests/ui/zero_sized_btreemap_values.rs b/tests/ui/zero_sized_btreemap_values.rs new file mode 100644 index 00000000000..5cd254787d8 --- /dev/null +++ b/tests/ui/zero_sized_btreemap_values.rs @@ -0,0 +1,68 @@ +#![warn(clippy::zero_sized_map_values)] +use std::collections::BTreeMap; + +const CONST_OK: Option> = None; +const CONST_NOT_OK: Option> = None; + +static STATIC_OK: Option> = None; +static STATIC_NOT_OK: Option> = None; + +type OkMap = BTreeMap; +type NotOkMap = BTreeMap; + +enum TestEnum { + Ok(BTreeMap), + NotOk(BTreeMap), +} + +struct Test { + ok: BTreeMap, + not_ok: BTreeMap, + + also_not_ok: Vec>, +} + +trait TestTrait { + type Output; + + fn produce_output() -> Self::Output; + + fn weird_map(&self, map: BTreeMap); +} + +impl Test { + fn ok(&self) -> BTreeMap { + todo!() + } + + fn not_ok(&self) -> BTreeMap { + todo!() + } +} + +impl TestTrait for Test { + type Output = BTreeMap; + + fn produce_output() -> Self::Output { + todo!(); + } + + fn weird_map(&self, map: BTreeMap) { + todo!(); + } +} + +fn test(map: BTreeMap, key: &str) -> BTreeMap { + todo!(); +} + +fn test2(map: BTreeMap, key: &str) -> BTreeMap { + todo!(); +} + +fn main() { + let _: BTreeMap = BTreeMap::new(); + let _: BTreeMap = BTreeMap::new(); + + let _: BTreeMap<_, _> = std::iter::empty::<(String, ())>().collect(); +} diff --git a/tests/ui/zero_sized_btreemap_values.stderr b/tests/ui/zero_sized_btreemap_values.stderr new file mode 100644 index 00000000000..334d921a9af --- /dev/null +++ b/tests/ui/zero_sized_btreemap_values.stderr @@ -0,0 +1,107 @@ +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:5:28 + | +LL | const CONST_NOT_OK: Option> = None; + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::zero-sized-map-values` implied by `-D warnings` + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:8:30 + | +LL | static STATIC_NOT_OK: Option> = None; + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:11:17 + | +LL | type NotOkMap = BTreeMap; + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:15:11 + | +LL | NotOk(BTreeMap), + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:20:13 + | +LL | not_ok: BTreeMap, + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:22:22 + | +LL | also_not_ok: Vec>, + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:30:30 + | +LL | fn weird_map(&self, map: BTreeMap); + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:38:25 + | +LL | fn not_ok(&self) -> BTreeMap { + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:55:14 + | +LL | fn test(map: BTreeMap, key: &str) -> BTreeMap { + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:55:50 + | +LL | fn test(map: BTreeMap, key: &str) -> BTreeMap { + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:64:35 + | +LL | let _: BTreeMap = BTreeMap::new(); + | ^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:64:12 + | +LL | let _: BTreeMap = BTreeMap::new(); + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_btreemap_values.rs:67:12 + | +LL | let _: BTreeMap<_, _> = std::iter::empty::<(String, ())>().collect(); + | ^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: aborting due to 13 previous errors + diff --git a/tests/ui/zero_sized_hashmap_values.rs b/tests/ui/zero_sized_hashmap_values.rs new file mode 100644 index 00000000000..a1608d863fb --- /dev/null +++ b/tests/ui/zero_sized_hashmap_values.rs @@ -0,0 +1,68 @@ +#![warn(clippy::zero_sized_map_values)] +use std::collections::HashMap; + +const CONST_OK: Option> = None; +const CONST_NOT_OK: Option> = None; + +static STATIC_OK: Option> = None; +static STATIC_NOT_OK: Option> = None; + +type OkMap = HashMap; +type NotOkMap = HashMap; + +enum TestEnum { + Ok(HashMap), + NotOk(HashMap), +} + +struct Test { + ok: HashMap, + not_ok: HashMap, + + also_not_ok: Vec>, +} + +trait TestTrait { + type Output; + + fn produce_output() -> Self::Output; + + fn weird_map(&self, map: HashMap); +} + +impl Test { + fn ok(&self) -> HashMap { + todo!() + } + + fn not_ok(&self) -> HashMap { + todo!() + } +} + +impl TestTrait for Test { + type Output = HashMap; + + fn produce_output() -> Self::Output { + todo!(); + } + + fn weird_map(&self, map: HashMap) { + todo!(); + } +} + +fn test(map: HashMap, key: &str) -> HashMap { + todo!(); +} + +fn test2(map: HashMap, key: &str) -> HashMap { + todo!(); +} + +fn main() { + let _: HashMap = HashMap::new(); + let _: HashMap = HashMap::new(); + + let _: HashMap<_, _> = std::iter::empty::<(String, ())>().collect(); +} diff --git a/tests/ui/zero_sized_hashmap_values.stderr b/tests/ui/zero_sized_hashmap_values.stderr new file mode 100644 index 00000000000..43987b3d01d --- /dev/null +++ b/tests/ui/zero_sized_hashmap_values.stderr @@ -0,0 +1,107 @@ +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:5:28 + | +LL | const CONST_NOT_OK: Option> = None; + | ^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::zero-sized-map-values` implied by `-D warnings` + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:8:30 + | +LL | static STATIC_NOT_OK: Option> = None; + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:11:17 + | +LL | type NotOkMap = HashMap; + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:15:11 + | +LL | NotOk(HashMap), + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:20:13 + | +LL | not_ok: HashMap, + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:22:22 + | +LL | also_not_ok: Vec>, + | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:30:30 + | +LL | fn weird_map(&self, map: HashMap); + | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:38:25 + | +LL | fn not_ok(&self) -> HashMap { + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:55:14 + | +LL | fn test(map: HashMap, key: &str) -> HashMap { + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:55:49 + | +LL | fn test(map: HashMap, key: &str) -> HashMap { + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:64:34 + | +LL | let _: HashMap = HashMap::new(); + | ^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:64:12 + | +LL | let _: HashMap = HashMap::new(); + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: map with zero-sized value type + --> $DIR/zero_sized_hashmap_values.rs:67:12 + | +LL | let _: HashMap<_, _> = std::iter::empty::<(String, ())>().collect(); + | ^^^^^^^^^^^^^ + | + = help: consider using a set instead + +error: aborting due to 13 previous errors + -- cgit 1.4.1-3-g733a5 From 3f41fe2704e331399568890f41874c2fc086e838 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 10 Dec 2020 10:44:33 +0100 Subject: Error in integration test, if required toolchain is not installed --- tests/integration.rs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'tests') diff --git a/tests/integration.rs b/tests/integration.rs index a78273ce0da..1718089e8bd 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -72,6 +72,8 @@ fn integration_test() { panic!("incompatible crate versions"); } else if stderr.contains("failed to run `rustc` to learn about target-specific information") { panic!("couldn't find librustc_driver, consider setting `LD_LIBRARY_PATH`"); + } else if stderr.contains("toolchain") && stderr.contains("is not installed") { + panic!("missing required toolchain"); } match output.status.code() { -- cgit 1.4.1-3-g733a5 From a6bb9276f7964b96899d04d680bc04bf99c8bf47 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Mon, 9 Nov 2020 23:38:08 +0100 Subject: Lint wrong self convention in trait also --- clippy_lints/src/methods/mod.rs | 49 ++++++++++++++++++++++++++++++----- tests/ui/wrong_self_convention.rs | 26 +++++++++++++++++++ tests/ui/wrong_self_convention.stderr | 34 +++++++++++++++++++++++- 3 files changed, 101 insertions(+), 8 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 8002c27a5e9..3c89c1b6ed2 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -22,6 +22,7 @@ use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_span::symbol::{sym, SymbolStr}; +use rustc_typeck::hir_ty_to_ty; use crate::consts::{constant, Constant}; use crate::utils::eager_or_lazy::is_lazyness_candidate; @@ -1623,10 +1624,15 @@ impl<'tcx> LateLintPass<'tcx> for Methods { let item = cx.tcx.hir().expect_item(parent); let def_id = cx.tcx.hir().local_def_id(item.hir_id); let self_ty = cx.tcx.type_of(def_id); + + // if this impl block implements a trait, lint in trait definition instead + if let hir::ItemKind::Impl { of_trait: Some(_), .. } = item.kind { + return; + } + if_chain! { if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind; if let Some(first_arg) = iter_input_pats(&sig.decl, cx.tcx.hir().body(id)).next(); - if let hir::ItemKind::Impl{ of_trait: None, .. } = item.kind; let method_def_id = cx.tcx.hir().local_def_id(impl_item.hir_id); let method_sig = cx.tcx.fn_sig(method_def_id); @@ -1697,11 +1703,6 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } } - // if this impl block implements a trait, lint in trait definition instead - if let hir::ItemKind::Impl { of_trait: Some(_), .. } = item.kind { - return; - } - if let hir::ImplItemKind::Fn(_, _) = impl_item.kind { let ret_ty = return_ty(cx, impl_item.hir_id); @@ -1735,8 +1736,42 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { + if in_external_macro(cx.tcx.sess, item.span) { + return; + } + + if_chain! { + if let TraitItemKind::Fn(ref sig, _) = item.kind; + if let Some(first_arg_ty) = sig.decl.inputs.iter().next(); + let first_arg_span = first_arg_ty.span; + let first_arg_ty = hir_ty_to_ty(cx.tcx, first_arg_ty); + let self_ty = TraitRef::identity(cx.tcx, item.hir_id.owner.to_def_id()).self_ty(); + + then { + if let Some((ref conv, self_kinds)) = &CONVENTIONS + .iter() + .find(|(ref conv, _)| conv.check(&item.ident.name.as_str())) + { + if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) { + span_lint( + cx, + WRONG_PUB_SELF_CONVENTION, + first_arg_span, + &format!("methods called `{}` usually take {}; consider choosing a less ambiguous name", + conv, + &self_kinds + .iter() + .map(|k| k.description()) + .collect::>() + .join(" or ") + ), + ); + } + } + } + } + if_chain! { - if !in_external_macro(cx.tcx.sess, item.span); if item.ident.name == sym::new; if let TraitItemKind::Fn(_, _) = item.kind; let ret_ty = return_ty(cx, item.hir_id); diff --git a/tests/ui/wrong_self_convention.rs b/tests/ui/wrong_self_convention.rs index f44305d7e48..275866b8248 100644 --- a/tests/ui/wrong_self_convention.rs +++ b/tests/ui/wrong_self_convention.rs @@ -88,3 +88,29 @@ mod issue4037 { } } } + +// Lint also in trait definition (see #6307) +mod issue6307{ + trait T: Sized { + fn as_i32(self) {} + fn as_u32(&self) {} + fn into_i32(&self) {} + fn into_u32(self) {} + fn is_i32(self) {} + fn is_u32(&self) {} + fn to_i32(self) {} + fn to_u32(&self) {} + fn from_i32(self) {} + // check whether the lint can be allowed at the function level + #[allow(clippy::wrong_pub_self_convention)] + fn from_cake(self) {} + + // test for false positives + fn as_(self) {} + fn into_(&self) {} + fn is_(self) {} + fn to_(self) {} + fn from_(self) {} + fn to_mut(&mut self) {} + } +} \ No newline at end of file diff --git a/tests/ui/wrong_self_convention.stderr b/tests/ui/wrong_self_convention.stderr index ef3ad73ebc7..64aa957fed6 100644 --- a/tests/ui/wrong_self_convention.stderr +++ b/tests/ui/wrong_self_convention.stderr @@ -72,5 +72,37 @@ error: methods called `from_*` usually take no self; consider choosing a less am LL | pub fn from_i64(self) {} | ^^^^ -error: aborting due to 12 previous errors +error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:95:19 + | +LL | fn as_i32(self) {} + | ^^^^ + | + = note: `-D clippy::wrong-pub-self-convention` implied by `-D warnings` + +error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:97:21 + | +LL | fn into_i32(&self) {} + | ^^^^^ + +error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:99:19 + | +LL | fn is_i32(self) {} + | ^^^^ + +error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:101:19 + | +LL | fn to_i32(self) {} + | ^^^^ + +error: methods called `from_*` usually take no self; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:103:21 + | +LL | fn from_i32(self) {} + | ^^^^ + +error: aborting due to 17 previous errors -- cgit 1.4.1-3-g733a5 From 4af9382bec3f49d9c7e0f03e9cfb5e8f1c70fdbe Mon Sep 17 00:00:00 2001 From: ThibsG Date: Tue, 10 Nov 2020 08:56:17 +0100 Subject: Common function to lint wrong self convention from impl and trait def --- clippy_lints/src/methods/mod.rs | 88 +++++++++++++++++------------------ tests/ui/wrong_self_convention.rs | 6 +-- tests/ui/wrong_self_convention.stderr | 2 - 3 files changed, 45 insertions(+), 51 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 3c89c1b6ed2..9a082a89229 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1674,32 +1674,14 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } } - if let Some((ref conv, self_kinds)) = &CONVENTIONS - .iter() - .find(|(ref conv, _)| conv.check(&name)) - { - if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) { - let lint = if item.vis.node.is_pub() { - WRONG_PUB_SELF_CONVENTION - } else { - WRONG_SELF_CONVENTION - }; - - span_lint( - cx, - lint, - first_arg.pat.span, - &format!("methods called `{}` usually take {}; consider choosing a less ambiguous name", - conv, - &self_kinds - .iter() - .map(|k| k.description()) - .collect::>() - .join(" or ") - ), - ); - } - } + lint_wrong_self_convention( + cx, + &name, + item.vis.node.is_pub(), + self_ty, + first_arg_ty, + first_arg.pat.span + ); } } @@ -1748,26 +1730,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { let self_ty = TraitRef::identity(cx.tcx, item.hir_id.owner.to_def_id()).self_ty(); then { - if let Some((ref conv, self_kinds)) = &CONVENTIONS - .iter() - .find(|(ref conv, _)| conv.check(&item.ident.name.as_str())) - { - if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) { - span_lint( - cx, - WRONG_PUB_SELF_CONVENTION, - first_arg_span, - &format!("methods called `{}` usually take {}; consider choosing a less ambiguous name", - conv, - &self_kinds - .iter() - .map(|k| k.description()) - .collect::>() - .join(" or ") - ), - ); - } - } + lint_wrong_self_convention(cx, &item.ident.name.as_str(), false, self_ty, first_arg_ty, first_arg_span); } } @@ -1792,6 +1755,39 @@ impl<'tcx> LateLintPass<'tcx> for Methods { extract_msrv_attr!(LateContext); } +fn lint_wrong_self_convention<'tcx>( + cx: &LateContext<'tcx>, + item_name: &str, + is_pub: bool, + self_ty: &'tcx TyS<'tcx>, + first_arg_ty: &'tcx TyS<'tcx>, + first_arg_span: Span, +) { + let lint = if is_pub { + WRONG_PUB_SELF_CONVENTION + } else { + WRONG_SELF_CONVENTION + }; + if let Some((ref conv, self_kinds)) = &CONVENTIONS.iter().find(|(ref conv, _)| conv.check(item_name)) { + if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) { + span_lint( + cx, + lint, + first_arg_span, + &format!( + "methods called `{}` usually take {}; consider choosing a less ambiguous name", + conv, + &self_kinds + .iter() + .map(|k| k.description()) + .collect::>() + .join(" or ") + ), + ); + } + } +} + /// Checks for the `OR_FUN_CALL` lint. #[allow(clippy::too_many_lines)] fn lint_or_fun_call<'tcx>( diff --git a/tests/ui/wrong_self_convention.rs b/tests/ui/wrong_self_convention.rs index 275866b8248..795ba77274c 100644 --- a/tests/ui/wrong_self_convention.rs +++ b/tests/ui/wrong_self_convention.rs @@ -90,7 +90,7 @@ mod issue4037 { } // Lint also in trait definition (see #6307) -mod issue6307{ +mod issue6307 { trait T: Sized { fn as_i32(self) {} fn as_u32(&self) {} @@ -102,7 +102,7 @@ mod issue6307{ fn to_u32(&self) {} fn from_i32(self) {} // check whether the lint can be allowed at the function level - #[allow(clippy::wrong_pub_self_convention)] + #[allow(clippy::wrong_self_convention)] fn from_cake(self) {} // test for false positives @@ -113,4 +113,4 @@ mod issue6307{ fn from_(self) {} fn to_mut(&mut self) {} } -} \ No newline at end of file +} diff --git a/tests/ui/wrong_self_convention.stderr b/tests/ui/wrong_self_convention.stderr index 64aa957fed6..289da6f059e 100644 --- a/tests/ui/wrong_self_convention.stderr +++ b/tests/ui/wrong_self_convention.stderr @@ -77,8 +77,6 @@ error: methods called `as_*` usually take self by reference or self by mutable r | LL | fn as_i32(self) {} | ^^^^ - | - = note: `-D clippy::wrong-pub-self-convention` implied by `-D warnings` error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name --> $DIR/wrong_self_convention.rs:97:21 -- cgit 1.4.1-3-g733a5 From db98651e722ca1cc12f2ffe159c1bc128880f8a4 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Tue, 10 Nov 2020 12:26:09 +0100 Subject: Allow `wrong_self_convention` in `use_self` test for trait def --- tests/ui/use_self.fixed | 1 + tests/ui/use_self.rs | 1 + tests/ui/use_self.stderr | 38 +++++++++++++++++++------------------- 3 files changed, 21 insertions(+), 19 deletions(-) (limited to 'tests') diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed index ebb3aa28daf..ded3fbb56eb 100644 --- a/tests/ui/use_self.fixed +++ b/tests/ui/use_self.fixed @@ -71,6 +71,7 @@ mod lifetimes { mod issue2894 { trait IntoBytes { + #[allow(clippy::clippy::wrong_self_convention)] fn into_bytes(&self) -> Vec; } diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index 8a182192ab3..a4f7b0bfd24 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -71,6 +71,7 @@ mod lifetimes { mod issue2894 { trait IntoBytes { + #[allow(clippy::clippy::wrong_self_convention)] fn into_bytes(&self) -> Vec; } diff --git a/tests/ui/use_self.stderr b/tests/ui/use_self.stderr index b33928597c1..80e1bfc75e8 100644 --- a/tests/ui/use_self.stderr +++ b/tests/ui/use_self.stderr @@ -37,19 +37,19 @@ LL | Foo::new() | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:89:56 + --> $DIR/use_self.rs:90:56 | LL | fn bad(foos: &[Self]) -> impl Iterator { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:104:13 + --> $DIR/use_self.rs:105:13 | LL | TS(0) | ^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:112:25 + --> $DIR/use_self.rs:113:25 | LL | fn new() -> Foo { | ^^^ help: use the applicable keyword: `Self` @@ -60,7 +60,7 @@ LL | use_self_expand!(); // Should lint in local macros = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: unnecessary structure name repetition - --> $DIR/use_self.rs:113:17 + --> $DIR/use_self.rs:114:17 | LL | Foo {} | ^^^ help: use the applicable keyword: `Self` @@ -71,91 +71,91 @@ LL | use_self_expand!(); // Should lint in local macros = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: unnecessary structure name repetition - --> $DIR/use_self.rs:148:21 + --> $DIR/use_self.rs:149:21 | LL | fn baz() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:149:13 + --> $DIR/use_self.rs:150:13 | LL | Foo {} | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:136:29 + --> $DIR/use_self.rs:137:29 | LL | fn bar() -> Bar { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:137:21 + --> $DIR/use_self.rs:138:21 | LL | Bar { foo: Foo {} } | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:166:21 + --> $DIR/use_self.rs:167:21 | LL | let _ = Enum::B(42); | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:167:21 + --> $DIR/use_self.rs:168:21 | LL | let _ = Enum::C { field: true }; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:168:21 + --> $DIR/use_self.rs:169:21 | LL | let _ = Enum::A; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:199:13 + --> $DIR/use_self.rs:200:13 | LL | nested::A::fun_1(); | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:200:13 + --> $DIR/use_self.rs:201:13 | LL | nested::A::A; | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:202:13 + --> $DIR/use_self.rs:203:13 | LL | nested::A {}; | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:221:13 + --> $DIR/use_self.rs:222:13 | LL | TestStruct::from_something() | ^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:235:25 + --> $DIR/use_self.rs:236:25 | LL | async fn g() -> S { | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:236:13 + --> $DIR/use_self.rs:237:13 | LL | S {} | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:240:16 + --> $DIR/use_self.rs:241:16 | LL | &p[S::A..S::B] | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:240:22 + --> $DIR/use_self.rs:241:22 | LL | &p[S::A..S::B] | ^ help: use the applicable keyword: `Self` -- cgit 1.4.1-3-g733a5 From 1e0f85b2640e9c2a90c197780c534a96148138bc Mon Sep 17 00:00:00 2001 From: Thibaud Date: Tue, 24 Nov 2020 18:04:58 +0100 Subject: Update tests/ui/use_self.rs Co-authored-by: Eduardo Broto --- tests/ui/use_self.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index a4f7b0bfd24..b04d9ce75b2 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -71,7 +71,7 @@ mod lifetimes { mod issue2894 { trait IntoBytes { - #[allow(clippy::clippy::wrong_self_convention)] + #[allow(clippy::wrong_self_convention)] fn into_bytes(&self) -> Vec; } -- cgit 1.4.1-3-g733a5 From 90a16e43972f8039e1f045752f04b4011a38b92f Mon Sep 17 00:00:00 2001 From: ThibsG Date: Thu, 10 Dec 2020 17:00:55 +0100 Subject: Add tests for unsized trait in `wrong_self_convention` lint --- tests/ui/use_self.fixed | 2 +- tests/ui/wrong_self_convention.rs | 23 +++++++++++++++++++++++ tests/ui/wrong_self_convention.stderr | 32 +++++++++++++++++++++++++++++++- 3 files changed, 55 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed index ded3fbb56eb..d6a890014e6 100644 --- a/tests/ui/use_self.fixed +++ b/tests/ui/use_self.fixed @@ -71,7 +71,7 @@ mod lifetimes { mod issue2894 { trait IntoBytes { - #[allow(clippy::clippy::wrong_self_convention)] + #[allow(clippy::wrong_self_convention)] fn into_bytes(&self) -> Vec; } diff --git a/tests/ui/wrong_self_convention.rs b/tests/ui/wrong_self_convention.rs index 795ba77274c..5282eba74fd 100644 --- a/tests/ui/wrong_self_convention.rs +++ b/tests/ui/wrong_self_convention.rs @@ -113,4 +113,27 @@ mod issue6307 { fn from_(self) {} fn to_mut(&mut self) {} } + + trait U { + fn as_i32(self); + fn as_u32(&self); + fn into_i32(&self); + fn into_u32(self); + fn is_i32(self); + fn is_u32(&self); + fn to_i32(self); + fn to_u32(&self); + fn from_i32(self); + // check whether the lint can be allowed at the function level + #[allow(clippy::wrong_self_convention)] + fn from_cake(self); + + // test for false positives + fn as_(self); + fn into_(&self); + fn is_(self); + fn to_(self); + fn from_(self); + fn to_mut(&mut self); + } } diff --git a/tests/ui/wrong_self_convention.stderr b/tests/ui/wrong_self_convention.stderr index 289da6f059e..86467eb0fc7 100644 --- a/tests/ui/wrong_self_convention.stderr +++ b/tests/ui/wrong_self_convention.stderr @@ -102,5 +102,35 @@ error: methods called `from_*` usually take no self; consider choosing a less am LL | fn from_i32(self) {} | ^^^^ -error: aborting due to 17 previous errors +error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:118:19 + | +LL | fn as_i32(self); + | ^^^^ + +error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:120:21 + | +LL | fn into_i32(&self); + | ^^^^^ + +error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:122:19 + | +LL | fn is_i32(self); + | ^^^^ + +error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:124:19 + | +LL | fn to_i32(self); + | ^^^^ + +error: methods called `from_*` usually take no self; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:126:21 + | +LL | fn from_i32(self); + | ^^^^ + +error: aborting due to 22 previous errors -- cgit 1.4.1-3-g733a5 From a7cfffef263a8b5a5dadfad0b7f56acd83d02b9b Mon Sep 17 00:00:00 2001 From: suyash458 Date: Sat, 5 Dec 2020 04:59:22 -0800 Subject: add MSRV to more lints specified in #6097 update tests --- clippy_lints/src/checked_conversions.rs | 26 +++++++-- clippy_lints/src/lib.rs | 15 +++--- clippy_lints/src/mem_replace.rs | 27 ++++++++-- clippy_lints/src/methods/mod.rs | 23 ++++++-- clippy_lints/src/missing_const_for_fn.rs | 29 ++++++++-- clippy_lints/src/ranges.rs | 29 +++++++--- clippy_lints/src/redundant_field_names.rs | 25 +++++++-- clippy_lints/src/redundant_static_lifetimes.rs | 26 +++++++-- clippy_lints/src/use_self.rs | 25 +++++++-- tests/ui/min_rust_version_attr.rs | 74 +++++++++++++++++++++++++- tests/ui/min_rust_version_attr.stderr | 51 ++++++++---------- 11 files changed, 282 insertions(+), 68 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index 28c1a54d2c5..54bc69e058b 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -6,9 +6,12 @@ use rustc_errors::Applicability; use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; -use crate::utils::{snippet_with_applicability, span_lint_and_sugg, SpanlessEq}; +use crate::utils::{meets_msrv, snippet_with_applicability, span_lint_and_sugg, SpanlessEq}; + +const CHECKED_CONVERSIONS_MSRV: RustcVersion = RustcVersion::new(1, 34, 0); declare_clippy_lint! { /// **What it does:** Checks for explicit bounds checking when casting. @@ -39,10 +42,25 @@ declare_clippy_lint! { "`try_from` could replace manual bounds checking when casting" } -declare_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]); +pub struct CheckedConversions { + msrv: Option, +} + +impl CheckedConversions { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]); impl<'tcx> LateLintPass<'tcx> for CheckedConversions { fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) { + if !meets_msrv(self.msrv.as_ref(), &CHECKED_CONVERSIONS_MSRV) { + return; + } + let result = if_chain! { if !in_external_macro(cx.sess(), item.span); if let ExprKind::Binary(op, ref left, ref right) = &item.kind; @@ -74,6 +92,8 @@ impl<'tcx> LateLintPass<'tcx> for CheckedConversions { } } } + + extract_msrv_attr!(LateContext); } /// Searches for a single check from unsigned to _ is done diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index a92ae9ed8d9..38a25d22aa2 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1003,6 +1003,14 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box matches::Matches::new(msrv)); store.register_early_pass(move || box manual_non_exhaustive::ManualNonExhaustive::new(msrv)); store.register_late_pass(move || box manual_strip::ManualStrip::new(msrv)); + store.register_early_pass(move || box redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv)); + store.register_early_pass(move || box redundant_field_names::RedundantFieldNames::new(msrv)); + store.register_late_pass(move || box checked_conversions::CheckedConversions::new(msrv)); + store.register_late_pass(move || box mem_replace::MemReplace::new(msrv)); + store.register_late_pass(move || box ranges::Ranges::new(msrv)); + store.register_late_pass(move || box use_self::UseSelf::new(msrv)); + store.register_late_pass(move || box missing_const_for_fn::MissingConstForFn::new(msrv)); + store.register_late_pass(|| box size_of_in_element_count::SizeOfInElementCount); store.register_late_pass(|| box map_clone::MapClone); store.register_late_pass(|| box map_err_ignore::MapErrIgnore); @@ -1013,7 +1021,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box main_recursion::MainRecursion::default()); store.register_late_pass(|| box lifetimes::Lifetimes); store.register_late_pass(|| box entry::HashMapPass); - store.register_late_pass(|| box ranges::Ranges); store.register_late_pass(|| box types::Casts); let type_complexity_threshold = conf.type_complexity_threshold; store.register_late_pass(move || box types::TypeComplexity::new(type_complexity_threshold)); @@ -1058,7 +1065,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box neg_multiply::NegMultiply); store.register_late_pass(|| box mem_discriminant::MemDiscriminant); store.register_late_pass(|| box mem_forget::MemForget); - store.register_late_pass(|| box mem_replace::MemReplace); store.register_late_pass(|| box arithmetic::Arithmetic::default()); store.register_late_pass(|| box assign_ops::AssignOps); store.register_late_pass(|| box let_if_seq::LetIfSeq); @@ -1080,7 +1086,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box pass_by_ref_or_value); store.register_late_pass(|| box ref_option_ref::RefOptionRef); store.register_late_pass(|| box try_err::TryErr); - store.register_late_pass(|| box use_self::UseSelf); store.register_late_pass(|| box bytecount::ByteCount); store.register_late_pass(|| box infinite_iter::InfiniteIter); store.register_late_pass(|| box inline_fn_without_body::InlineFnWithoutBody); @@ -1106,10 +1111,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box unnecessary_wraps::UnnecessaryWraps); store.register_late_pass(|| box types::RefToMut); store.register_late_pass(|| box assertions_on_constants::AssertionsOnConstants); - store.register_late_pass(|| box missing_const_for_fn::MissingConstForFn); store.register_late_pass(|| box transmuting_null::TransmutingNull); store.register_late_pass(|| box path_buf_push_overwrite::PathBufPushOverwrite); - store.register_late_pass(|| box checked_conversions::CheckedConversions); store.register_late_pass(|| box integer_division::IntegerDivision); store.register_late_pass(|| box inherent_to_string::InherentToString); let max_trait_bounds = conf.max_trait_bounds; @@ -1138,7 +1141,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box redundant_else::RedundantElse); store.register_late_pass(|| box create_dir::CreateDir); store.register_early_pass(|| box needless_arbitrary_self_type::NeedlessArbitrarySelfType); - store.register_early_pass(|| box redundant_static_lifetimes::RedundantStaticLifetimes); store.register_late_pass(|| box cargo_common_metadata::CargoCommonMetadata); store.register_late_pass(|| box multiple_crate_versions::MultipleCrateVersions); store.register_late_pass(|| box wildcard_dependencies::WildcardDependencies); @@ -1178,7 +1180,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box mut_mutex_lock::MutMutexLock); store.register_late_pass(|| box match_on_vec_items::MatchOnVecItems); store.register_late_pass(|| box manual_async_fn::ManualAsyncFn); - store.register_early_pass(|| box redundant_field_names::RedundantFieldNames); store.register_late_pass(|| box vec_resize_to_zero::VecResizeToZero); store.register_late_pass(|| box panic_in_result_fn::PanicInResultFn); let single_char_binding_names_threshold = conf.single_char_binding_names_threshold; diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index bb0acecc5a9..19087b02077 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -1,13 +1,14 @@ use crate::utils::{ - in_macro, match_def_path, match_qpath, paths, snippet, snippet_with_applicability, span_lint_and_help, + in_macro, match_def_path, match_qpath, meets_msrv, paths, snippet, snippet_with_applicability, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, }; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_span::symbol::sym; @@ -94,7 +95,7 @@ declare_clippy_lint! { "replacing a value of type `T` with `T::default()` instead of using `std::mem::take`" } -declare_lint_pass!(MemReplace => +impl_lint_pass!(MemReplace => [MEM_REPLACE_OPTION_WITH_NONE, MEM_REPLACE_WITH_UNINIT, MEM_REPLACE_WITH_DEFAULT]); fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) { @@ -224,6 +225,19 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr< } } +const MEM_REPLACE_WITH_DEFAULT_MSRV: RustcVersion = RustcVersion::new(1, 40, 0); + +pub struct MemReplace { + msrv: Option, +} + +impl MemReplace { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + impl<'tcx> LateLintPass<'tcx> for MemReplace { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { @@ -236,8 +250,11 @@ impl<'tcx> LateLintPass<'tcx> for MemReplace { then { check_replace_option_with_none(cx, src, dest, expr.span); check_replace_with_uninit(cx, src, dest, expr.span); - check_replace_with_default(cx, src, dest, expr.span); + if meets_msrv(self.msrv.as_ref(), &MEM_REPLACE_WITH_DEFAULT_MSRV) { + check_replace_with_default(cx, src, dest, expr.span); + } } } } + extract_msrv_attr!(LateContext); } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 8002c27a5e9..5133f31e0e7 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1487,7 +1487,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["expect", ..] => lint_expect(cx, expr, arg_lists[0]), ["unwrap_or", "map"] => option_map_unwrap_or::lint(cx, expr, arg_lists[1], arg_lists[0], method_spans[1]), ["unwrap_or_else", "map"] => { - if !lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0]) { + if !lint_map_unwrap_or_else(cx, expr, arg_lists[1], arg_lists[0], self.msrv.as_ref()) { unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "unwrap_or"); } }, @@ -1509,7 +1509,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["next", "iter"] => lint_iter_next(cx, expr, arg_lists[1]), ["map", "filter"] => lint_filter_map(cx, expr, arg_lists[1], arg_lists[0]), ["map", "filter_map"] => lint_filter_map_map(cx, expr, arg_lists[1], arg_lists[0]), - ["next", "filter_map"] => lint_filter_map_next(cx, expr, arg_lists[1]), + ["next", "filter_map"] => lint_filter_map_next(cx, expr, arg_lists[1], self.msrv.as_ref()), ["map", "find"] => lint_find_map(cx, expr, arg_lists[1], arg_lists[0]), ["flat_map", "filter"] => lint_filter_flat_map(cx, expr, arg_lists[1], arg_lists[0]), ["flat_map", "filter_map"] => lint_filter_map_flat_map(cx, expr, arg_lists[1], arg_lists[0]), @@ -2733,6 +2733,8 @@ fn lint_map_flatten<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map } } +const MAP_UNWRAP_OR_MSRV: RustcVersion = RustcVersion::new(1, 41, 0); + /// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s /// Return true if lint triggered fn lint_map_unwrap_or_else<'tcx>( @@ -2740,7 +2742,11 @@ fn lint_map_unwrap_or_else<'tcx>( expr: &'tcx hir::Expr<'_>, map_args: &'tcx [hir::Expr<'_>], unwrap_args: &'tcx [hir::Expr<'_>], + msrv: Option<&RustcVersion>, ) -> bool { + if !meets_msrv(msrv, &MAP_UNWRAP_OR_MSRV) { + return false; + } // lint if the caller of `map()` is an `Option` let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::option_type); let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::result_type); @@ -2923,9 +2929,20 @@ fn lint_filter_map<'tcx>( } } +const FILTER_MAP_NEXT_MSRV: RustcVersion = RustcVersion::new(1, 30, 0); + /// lint use of `filter_map().next()` for `Iterators` -fn lint_filter_map_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, filter_args: &'tcx [hir::Expr<'_>]) { +fn lint_filter_map_next<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'_>, + filter_args: &'tcx [hir::Expr<'_>], + msrv: Option<&RustcVersion>, +) { if match_trait_method(cx, expr, &paths::ITERATOR) { + if !meets_msrv(msrv, &FILTER_MAP_NEXT_MSRV) { + return; + } + let msg = "called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling \ `.find_map(..)` instead."; let filter_snippet = snippet(cx, filter_args[1].span, ".."); diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index 38e2ce563ee..6ebeaced62a 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -1,14 +1,19 @@ use crate::utils::qualify_min_const_fn::is_min_const_fn; -use crate::utils::{fn_has_unsatisfiable_preds, has_drop, is_entrypoint_fn, span_lint, trait_ref_of_method}; +use crate::utils::{ + fn_has_unsatisfiable_preds, has_drop, is_entrypoint_fn, meets_msrv, span_lint, trait_ref_of_method, +}; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; use rustc_typeck::hir_ty_to_ty; +const MISSING_CONST_FOR_FN_MSRV: RustcVersion = RustcVersion::new(1, 37, 0); + declare_clippy_lint! { /// **What it does:** /// @@ -69,7 +74,18 @@ declare_clippy_lint! { "Lint functions definitions that could be made `const fn`" } -declare_lint_pass!(MissingConstForFn => [MISSING_CONST_FOR_FN]); +impl_lint_pass!(MissingConstForFn => [MISSING_CONST_FOR_FN]); + +pub struct MissingConstForFn { + msrv: Option, +} + +impl MissingConstForFn { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { fn check_fn( @@ -81,6 +97,10 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { span: Span, hir_id: HirId, ) { + if !meets_msrv(self.msrv.as_ref(), &MISSING_CONST_FOR_FN_MSRV) { + return; + } + let def_id = cx.tcx.hir().local_def_id(hir_id); if in_external_macro(cx.tcx.sess, span) || is_entrypoint_fn(cx, def_id.to_def_id()) { @@ -126,6 +146,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { span_lint(cx, MISSING_CONST_FOR_FN, span, "this could be a `const fn`"); } } + extract_msrv_attr!(LateContext); } /// Returns true if any of the method parameters is a type that implements `Drop`. The method diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 4b514bbd42c..f9173808089 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -3,9 +3,10 @@ use if_chain::if_chain; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, QPath}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::{Span, Spanned}; use rustc_span::sym; use rustc_span::symbol::Ident; @@ -13,8 +14,8 @@ use std::cmp::Ordering; use crate::utils::sugg::Sugg; use crate::utils::{ - get_parent_expr, is_integer_const, single_segment_path, snippet, snippet_opt, snippet_with_applicability, - span_lint, span_lint_and_sugg, span_lint_and_then, + get_parent_expr, is_integer_const, meets_msrv, single_segment_path, snippet, snippet_opt, + snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then, }; use crate::utils::{higher, SpanlessEq}; @@ -160,7 +161,20 @@ declare_clippy_lint! { "manually reimplementing {`Range`, `RangeInclusive`}`::contains`" } -declare_lint_pass!(Ranges => [ +const MANUAL_RANGE_CONTAINS_MSRV: RustcVersion = RustcVersion::new(1, 35, 0); + +pub struct Ranges { + msrv: Option, +} + +impl Ranges { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(Ranges => [ RANGE_ZIP_WITH_LEN, RANGE_PLUS_ONE, RANGE_MINUS_ONE, @@ -175,7 +189,9 @@ impl<'tcx> LateLintPass<'tcx> for Ranges { check_range_zip_with_len(cx, path, args, expr.span); }, ExprKind::Binary(ref op, ref l, ref r) => { - check_possible_range_contains(cx, op.node, l, r, expr.span); + if meets_msrv(self.msrv.as_ref(), &MANUAL_RANGE_CONTAINS_MSRV) { + check_possible_range_contains(cx, op.node, l, r, expr.span); + } }, _ => {}, } @@ -184,6 +200,7 @@ impl<'tcx> LateLintPass<'tcx> for Ranges { check_inclusive_range_minus_one(cx, expr); check_reversed_empty_range(cx, expr); } + extract_msrv_attr!(LateContext); } fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'_>, r: &Expr<'_>, span: Span) { diff --git a/clippy_lints/src/redundant_field_names.rs b/clippy_lints/src/redundant_field_names.rs index 2a81170e49e..38dcf7a192c 100644 --- a/clippy_lints/src/redundant_field_names.rs +++ b/clippy_lints/src/redundant_field_names.rs @@ -1,9 +1,12 @@ -use crate::utils::span_lint_and_sugg; +use crate::utils::{meets_msrv, span_lint_and_sugg}; use rustc_ast::ast::{Expr, ExprKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_middle::lint::in_external_macro; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +const REDUNDANT_FIELD_NAMES_MSRV: RustcVersion = RustcVersion::new(1, 17, 0); declare_clippy_lint! { /// **What it does:** Checks for fields in struct literals where shorthands @@ -33,10 +36,25 @@ declare_clippy_lint! { "checks for fields in struct literals where shorthands could be used" } -declare_lint_pass!(RedundantFieldNames => [REDUNDANT_FIELD_NAMES]); +pub struct RedundantFieldNames { + msrv: Option, +} + +impl RedundantFieldNames { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(RedundantFieldNames => [REDUNDANT_FIELD_NAMES]); impl EarlyLintPass for RedundantFieldNames { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + if !meets_msrv(self.msrv.as_ref(), &REDUNDANT_FIELD_NAMES_MSRV) { + return; + } + if in_external_macro(cx.sess, expr.span) { return; } @@ -64,4 +82,5 @@ impl EarlyLintPass for RedundantFieldNames { } } } + extract_msrv_attr!(EarlyContext); } diff --git a/clippy_lints/src/redundant_static_lifetimes.rs b/clippy_lints/src/redundant_static_lifetimes.rs index 7bbcc67aa2d..fcfa3c12755 100644 --- a/clippy_lints/src/redundant_static_lifetimes.rs +++ b/clippy_lints/src/redundant_static_lifetimes.rs @@ -1,8 +1,11 @@ -use crate::utils::{snippet, span_lint_and_then}; +use crate::utils::{meets_msrv, snippet, span_lint_and_then}; use rustc_ast::ast::{Item, ItemKind, Ty, TyKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +const REDUNDANT_STATIC_LIFETIMES_MSRV: RustcVersion = RustcVersion::new(1, 17, 0); declare_clippy_lint! { /// **What it does:** Checks for constants and statics with an explicit `'static` lifetime. @@ -29,7 +32,18 @@ declare_clippy_lint! { "Using explicit `'static` lifetime for constants or statics when elision rules would allow omitting them." } -declare_lint_pass!(RedundantStaticLifetimes => [REDUNDANT_STATIC_LIFETIMES]); +pub struct RedundantStaticLifetimes { + msrv: Option, +} + +impl RedundantStaticLifetimes { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(RedundantStaticLifetimes => [REDUNDANT_STATIC_LIFETIMES]); impl RedundantStaticLifetimes { // Recursively visit types @@ -84,6 +98,10 @@ impl RedundantStaticLifetimes { impl EarlyLintPass for RedundantStaticLifetimes { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + if !meets_msrv(self.msrv.as_ref(), &REDUNDANT_STATIC_LIFETIMES_MSRV) { + return; + } + if !item.span.from_expansion() { if let ItemKind::Const(_, ref var_type, _) = item.kind { self.visit_type(var_type, cx, "constants have by default a `'static` lifetime"); @@ -96,4 +114,6 @@ impl EarlyLintPass for RedundantStaticLifetimes { } } } + + extract_msrv_attr!(EarlyContext); } diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 5ac4797680b..3b23f885e08 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -12,11 +12,12 @@ use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_middle::ty::{DefIdTree, Ty}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::kw; use rustc_typeck::hir_ty_to_ty; -use crate::utils::{differing_macro_contexts, span_lint_and_sugg}; +use crate::utils::{differing_macro_contexts, meets_msrv, span_lint_and_sugg}; declare_clippy_lint! { /// **What it does:** Checks for unnecessary repetition of structure name when a @@ -53,7 +54,7 @@ declare_clippy_lint! { "unnecessary structure name repetition whereas `Self` is applicable" } -declare_lint_pass!(UseSelf => [USE_SELF]); +impl_lint_pass!(UseSelf => [USE_SELF]); const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element"; @@ -157,8 +158,25 @@ fn check_trait_method_impl_decl<'tcx>( } } +const USE_SELF_MSRV: RustcVersion = RustcVersion::new(1, 37, 0); + +pub struct UseSelf { + msrv: Option, +} + +impl UseSelf { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + impl<'tcx> LateLintPass<'tcx> for UseSelf { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + if !meets_msrv(self.msrv.as_ref(), &USE_SELF_MSRV) { + return; + } + if in_external_macro(cx.sess(), item.span) { return; } @@ -204,6 +222,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { } } } + extract_msrv_attr!(LateContext); } struct UseSelfVisitor<'a, 'tcx> { diff --git a/tests/ui/min_rust_version_attr.rs b/tests/ui/min_rust_version_attr.rs index 1026cc40d3b..ac75f5e46c3 100644 --- a/tests/ui/min_rust_version_attr.rs +++ b/tests/ui/min_rust_version_attr.rs @@ -2,7 +2,7 @@ #![feature(custom_inner_attributes)] #![clippy::msrv = "1.0.0"] -use std::ops::Deref; +use std::ops::{Deref, RangeFrom}; fn option_as_ref_deref() { let mut opt = Some(String::from("123")); @@ -42,12 +42,84 @@ pub fn manual_strip_msrv() { } } +pub fn redundant_fieldnames() { + let start = 0; + let _ = RangeFrom { start: start }; +} + +pub fn redundant_static_lifetime() { + const VAR_ONE: &'static str = "Test constant #1"; +} + +pub fn checked_conversion() { + let value: i64 = 42; + let _ = value <= (u32::max_value() as i64) && value >= 0; + let _ = value <= (u32::MAX as i64) && value >= 0; +} + +pub fn filter_map_next() { + let a = ["1", "lol", "3", "NaN", "5"]; + + #[rustfmt::skip] + let _: Option = vec![1, 2, 3, 4, 5, 6] + .into_iter() + .filter_map(|x| { + if x == 2 { + Some(x * 2) + } else { + None + } + }) + .next(); +} + +pub fn manual_range_contains() { + x >= 8 && x < 12; +} + +pub fn use_self() { + struct Foo {} + + impl Foo { + fn new() -> Foo { + Foo {} + } + fn test() -> Foo { + Foo::new() + } + } +} + +fn replace_with_default() { + let mut s = String::from("foo"); + let _ = std::mem::replace(s, String::default()); +} + +fn map_unwrap_or() { + let opt = Some(1); + + // Check for `option.map(_).unwrap_or(_)` use. + // Single line case. + let _ = opt + .map(|x| x + 1) + // Should lint even though this call is on a separate line. + .unwrap_or(0); +} + fn main() { + filter_map_next(); + checked_conversion(); + redundant_fieldnames(); + redundant_static_lifetime(); option_as_ref_deref(); match_like_matches(); match_same_arms(); match_same_arms2(); manual_strip_msrv(); + manual_range_contains(); + use_self(); + replace_with_default(); + map_unwrap_or(); } mod meets_msrv { diff --git a/tests/ui/min_rust_version_attr.stderr b/tests/ui/min_rust_version_attr.stderr index 3e1af046e7a..d3eafe7312f 100644 --- a/tests/ui/min_rust_version_attr.stderr +++ b/tests/ui/min_rust_version_attr.stderr @@ -1,37 +1,28 @@ -error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:60:24 - | -LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::manual-strip` implied by `-D warnings` -note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:59:9 - | -LL | if s.starts_with("hello, ") { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: try using the `strip_prefix` method - | -LL | if let Some() = s.strip_prefix("hello, ") { -LL | assert_eq!(.to_uppercase(), "WORLD!"); +error[E0425]: cannot find value `x` in this scope + --> $DIR/min_rust_version_attr.rs:77:5 | +LL | x >= 8 && x < 12; + | ^ not found in this scope -error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:72:24 +error[E0425]: cannot find value `x` in this scope + --> $DIR/min_rust_version_attr.rs:77:15 | -LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); - | ^^^^^^^^^^^^^^^^^^^^ - | -note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:71:9 - | -LL | if s.starts_with("hello, ") { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: try using the `strip_prefix` method +LL | x >= 8 && x < 12; + | ^ not found in this scope + +error[E0308]: mismatched types + --> $DIR/min_rust_version_attr.rs:95:31 | -LL | if let Some() = s.strip_prefix("hello, ") { -LL | assert_eq!(.to_uppercase(), "WORLD!"); +LL | let _ = std::mem::replace(s, String::default()); + | ^ + | | + | expected `&mut _`, found struct `std::string::String` + | help: consider mutably borrowing here: `&mut s` | + = note: expected mutable reference `&mut _` + found struct `std::string::String` -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors +Some errors have detailed explanations: E0308, E0425. +For more information about an error, try `rustc --explain E0308`. -- cgit 1.4.1-3-g733a5 From 9f27b7428307ecc6995a06f3bd666eccdbed6c99 Mon Sep 17 00:00:00 2001 From: Suyash458 Date: Wed, 9 Dec 2020 16:39:33 +0530 Subject: add test for missing_const_for_fn. fix test stderr --- tests/ui/min_rust_version_attr.rs | 12 ++++++++- tests/ui/min_rust_version_attr.stderr | 51 ++++++++++++++++++++--------------- 2 files changed, 41 insertions(+), 22 deletions(-) (limited to 'tests') diff --git a/tests/ui/min_rust_version_attr.rs b/tests/ui/min_rust_version_attr.rs index ac75f5e46c3..3848bca3207 100644 --- a/tests/ui/min_rust_version_attr.rs +++ b/tests/ui/min_rust_version_attr.rs @@ -73,7 +73,11 @@ pub fn filter_map_next() { .next(); } +#[allow(clippy::no_effect)] +#[allow(clippy::short_circuit_statement)] +#[allow(clippy::unnecessary_operation)] pub fn manual_range_contains() { + let x = 5; x >= 8 && x < 12; } @@ -92,7 +96,7 @@ pub fn use_self() { fn replace_with_default() { let mut s = String::from("foo"); - let _ = std::mem::replace(s, String::default()); + let _ = std::mem::replace(&mut s, String::default()); } fn map_unwrap_or() { @@ -106,6 +110,11 @@ fn map_unwrap_or() { .unwrap_or(0); } +// Could be const +fn missing_const_for_fn() -> i32 { + 1 +} + fn main() { filter_map_next(); checked_conversion(); @@ -120,6 +129,7 @@ fn main() { use_self(); replace_with_default(); map_unwrap_or(); + missing_const_for_fn(); } mod meets_msrv { diff --git a/tests/ui/min_rust_version_attr.stderr b/tests/ui/min_rust_version_attr.stderr index d3eafe7312f..34805263104 100644 --- a/tests/ui/min_rust_version_attr.stderr +++ b/tests/ui/min_rust_version_attr.stderr @@ -1,28 +1,37 @@ -error[E0425]: cannot find value `x` in this scope - --> $DIR/min_rust_version_attr.rs:77:5 +error: stripping a prefix manually + --> $DIR/min_rust_version_attr.rs:142:24 | -LL | x >= 8 && x < 12; - | ^ not found in this scope - -error[E0425]: cannot find value `x` in this scope - --> $DIR/min_rust_version_attr.rs:77:15 +LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::manual-strip` implied by `-D warnings` +note: the prefix was tested here + --> $DIR/min_rust_version_attr.rs:141:9 + | +LL | if s.starts_with("hello, ") { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: try using the `strip_prefix` method + | +LL | if let Some() = s.strip_prefix("hello, ") { +LL | assert_eq!(.to_uppercase(), "WORLD!"); | -LL | x >= 8 && x < 12; - | ^ not found in this scope -error[E0308]: mismatched types - --> $DIR/min_rust_version_attr.rs:95:31 +error: stripping a prefix manually + --> $DIR/min_rust_version_attr.rs:154:24 + | +LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); + | ^^^^^^^^^^^^^^^^^^^^ + | +note: the prefix was tested here + --> $DIR/min_rust_version_attr.rs:153:9 + | +LL | if s.starts_with("hello, ") { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: try using the `strip_prefix` method | -LL | let _ = std::mem::replace(s, String::default()); - | ^ - | | - | expected `&mut _`, found struct `std::string::String` - | help: consider mutably borrowing here: `&mut s` +LL | if let Some() = s.strip_prefix("hello, ") { +LL | assert_eq!(.to_uppercase(), "WORLD!"); | - = note: expected mutable reference `&mut _` - found struct `std::string::String` -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors -Some errors have detailed explanations: E0308, E0425. -For more information about an error, try `rustc --explain E0308`. -- cgit 1.4.1-3-g733a5 From 26c61c7e49b173c8ae7c54c3a4c90b60cd9f71b8 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Wed, 25 Nov 2020 17:07:50 +0900 Subject: Fix FP of `manual_range_contains` in `const fn` --- clippy_lints/src/ranges.rs | 11 ++++++++--- tests/ui/range_contains.fixed | 5 +++++ tests/ui/range_contains.rs | 5 +++++ 3 files changed, 18 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index f9173808089..3e454eecd97 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -14,7 +14,7 @@ use std::cmp::Ordering; use crate::utils::sugg::Sugg; use crate::utils::{ - get_parent_expr, is_integer_const, meets_msrv, single_segment_path, snippet, snippet_opt, + get_parent_expr, in_constant, is_integer_const, meets_msrv, single_segment_path, snippet, snippet_opt, snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then, }; use crate::utils::{higher, SpanlessEq}; @@ -190,7 +190,7 @@ impl<'tcx> LateLintPass<'tcx> for Ranges { }, ExprKind::Binary(ref op, ref l, ref r) => { if meets_msrv(self.msrv.as_ref(), &MANUAL_RANGE_CONTAINS_MSRV) { - check_possible_range_contains(cx, op.node, l, r, expr.span); + check_possible_range_contains(cx, op.node, l, r, expr); } }, _ => {}, @@ -203,7 +203,12 @@ impl<'tcx> LateLintPass<'tcx> for Ranges { extract_msrv_attr!(LateContext); } -fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'_>, r: &Expr<'_>, span: Span) { +fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'_>, r: &Expr<'_>, expr: &Expr<'_>) { + if in_constant(cx, expr.hir_id) { + return; + } + + let span = expr.span; let combine_and = match op { BinOpKind::And | BinOpKind::BitAnd => true, BinOpKind::Or | BinOpKind::BitOr => false, diff --git a/tests/ui/range_contains.fixed b/tests/ui/range_contains.fixed index 048874a7f82..47c974e614b 100644 --- a/tests/ui/range_contains.fixed +++ b/tests/ui/range_contains.fixed @@ -44,3 +44,8 @@ fn main() { (0. ..1.).contains(&y); !(0. ..=1.).contains(&y); } + +// Fix #6373 +pub const fn in_range(a: i32) -> bool { + 3 <= a && a <= 20 +} diff --git a/tests/ui/range_contains.rs b/tests/ui/range_contains.rs index 60ad259f404..835deced5e4 100644 --- a/tests/ui/range_contains.rs +++ b/tests/ui/range_contains.rs @@ -44,3 +44,8 @@ fn main() { y >= 0. && y < 1.; y < 0. || y > 1.; } + +// Fix #6373 +pub const fn in_range(a: i32) -> bool { + 3 <= a && a <= 20 +} -- cgit 1.4.1-3-g733a5 From 0b145d688b293a92cd855000f249d83acae53f9d Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Sat, 12 Dec 2020 01:09:30 +0100 Subject: clone_double_ref: print reference type in lint message changelog: clone_double_ref: print the type of the reference in lint message --- clippy_lints/src/methods/mod.rs | 7 +++++-- tests/ui/unnecessary_clone.stderr | 6 +++--- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 5133f31e0e7..ce234e01a1b 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2100,8 +2100,11 @@ fn lint_clone_on_copy(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Exp cx, CLONE_DOUBLE_REF, expr.span, - "using `clone` on a double-reference; \ - this will copy the reference instead of cloning the inner type", + &format!( + "using `clone` on a double-reference; \ + this will copy the reference of type `{}` instead of cloning the inner type", + ty + ), |diag| { if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) { let mut ty = innermost; diff --git a/tests/ui/unnecessary_clone.stderr b/tests/ui/unnecessary_clone.stderr index 5ffa6c4fd06..b908d0ce9c1 100644 --- a/tests/ui/unnecessary_clone.stderr +++ b/tests/ui/unnecessary_clone.stderr @@ -44,7 +44,7 @@ error: using `clone` on a `Copy` type LL | Some(t).clone(); | ^^^^^^^^^^^^^^^ help: try removing the `clone` call: `Some(t)` -error: using `clone` on a double-reference; this will copy the reference instead of cloning the inner type +error: using `clone` on a double-reference; this will copy the reference of type `&std::vec::Vec` instead of cloning the inner type --> $DIR/unnecessary_clone.rs:48:22 | LL | let z: &Vec<_> = y.clone(); @@ -66,7 +66,7 @@ error: using `clone` on a `Copy` type LL | let _: E = a.clone(); | ^^^^^^^^^ help: try dereferencing it: `*****a` -error: using `clone` on a double-reference; this will copy the reference instead of cloning the inner type +error: using `clone` on a double-reference; this will copy the reference of type `&[u8]` instead of cloning the inner type --> $DIR/unnecessary_clone.rs:89:22 | LL | let _ = &mut encoded.clone(); @@ -81,7 +81,7 @@ help: or try being explicit if you are sure, that you want to clone a reference LL | let _ = &mut <&[u8]>::clone(encoded); | ^^^^^^^^^^^^^^^^^^^^^^^ -error: using `clone` on a double-reference; this will copy the reference instead of cloning the inner type +error: using `clone` on a double-reference; this will copy the reference of type `&[u8]` instead of cloning the inner type --> $DIR/unnecessary_clone.rs:90:18 | LL | let _ = &encoded.clone(); -- cgit 1.4.1-3-g733a5 From b2cb6ffbe3735ef8f137c9a6c1290c4a078793ef Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Sat, 12 Dec 2020 01:23:28 +0100 Subject: clone_on_copy: show the type in the lint message changelog: clone_on_copy: show the type in the lint message --- clippy_lints/src/methods/mod.rs | 16 +++++++++++----- tests/ui/clone_on_copy.stderr | 10 +++++----- tests/ui/unnecessary_clone.stderr | 6 +++--- 3 files changed, 19 insertions(+), 13 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 5133f31e0e7..03eaee35fc4 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2174,11 +2174,17 @@ fn lint_clone_on_copy(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Exp } else { snip = None; } - span_lint_and_then(cx, CLONE_ON_COPY, expr.span, "using `clone` on a `Copy` type", |diag| { - if let Some((text, snip)) = snip { - diag.span_suggestion(expr.span, text, snip, Applicability::MachineApplicable); - } - }); + span_lint_and_then( + cx, + CLONE_ON_COPY, + expr.span, + &format!("using `clone` on type `{}` which implements the `Copy` trait", ty), + |diag| { + if let Some((text, snip)) = snip { + diag.span_suggestion(expr.span, text, snip, Applicability::MachineApplicable); + } + }, + ); } } diff --git a/tests/ui/clone_on_copy.stderr b/tests/ui/clone_on_copy.stderr index ec2faf4ab40..14a700886a7 100644 --- a/tests/ui/clone_on_copy.stderr +++ b/tests/ui/clone_on_copy.stderr @@ -1,4 +1,4 @@ -error: using `clone` on a `Copy` type +error: using `clone` on type `i32` which implements the `Copy` trait --> $DIR/clone_on_copy.rs:22:5 | LL | 42.clone(); @@ -6,25 +6,25 @@ LL | 42.clone(); | = note: `-D clippy::clone-on-copy` implied by `-D warnings` -error: using `clone` on a `Copy` type +error: using `clone` on type `i32` which implements the `Copy` trait --> $DIR/clone_on_copy.rs:26:5 | LL | (&42).clone(); | ^^^^^^^^^^^^^ help: try dereferencing it: `*(&42)` -error: using `clone` on a `Copy` type +error: using `clone` on type `i32` which implements the `Copy` trait --> $DIR/clone_on_copy.rs:29:5 | LL | rc.borrow().clone(); | ^^^^^^^^^^^^^^^^^^^ help: try dereferencing it: `*rc.borrow()` -error: using `clone` on a `Copy` type +error: using `clone` on type `char` which implements the `Copy` trait --> $DIR/clone_on_copy.rs:35:14 | LL | is_ascii('z'.clone()); | ^^^^^^^^^^^ help: try removing the `clone` call: `'z'` -error: using `clone` on a `Copy` type +error: using `clone` on type `i32` which implements the `Copy` trait --> $DIR/clone_on_copy.rs:39:14 | LL | vec.push(42.clone()); diff --git a/tests/ui/unnecessary_clone.stderr b/tests/ui/unnecessary_clone.stderr index 5ffa6c4fd06..bb2dd998f27 100644 --- a/tests/ui/unnecessary_clone.stderr +++ b/tests/ui/unnecessary_clone.stderr @@ -30,7 +30,7 @@ error: using `.clone()` on a ref-counted pointer LL | let _: Arc = x.clone(); | ^^^^^^^^^ help: try this: `Arc::::clone(&x)` -error: using `clone` on a `Copy` type +error: using `clone` on type `T` which implements the `Copy` trait --> $DIR/unnecessary_clone.rs:40:5 | LL | t.clone(); @@ -38,7 +38,7 @@ LL | t.clone(); | = note: `-D clippy::clone-on-copy` implied by `-D warnings` -error: using `clone` on a `Copy` type +error: using `clone` on type `std::option::Option` which implements the `Copy` trait --> $DIR/unnecessary_clone.rs:42:5 | LL | Some(t).clone(); @@ -60,7 +60,7 @@ help: or try being explicit if you are sure, that you want to clone a reference LL | let z: &Vec<_> = <&std::vec::Vec>::clone(y); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: using `clone` on a `Copy` type +error: using `clone` on type `many_derefs::E` which implements the `Copy` trait --> $DIR/unnecessary_clone.rs:84:20 | LL | let _: E = a.clone(); -- cgit 1.4.1-3-g733a5 From 4bd9ed9b88d47bba3dc91fde6c0a27b63f63fe4b Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Wed, 2 Dec 2020 18:20:02 +0100 Subject: Rewrite update-all-references bash scripts in Rust This replaces the `update-all-references` scripts with a single cargo dev bless command. cc #5394 --- clippy_dev/src/bless.rs | 71 +++++++++++++++++++++++++++++++++ clippy_dev/src/lib.rs | 1 + clippy_dev/src/main.rs | 6 ++- doc/adding_lints.md | 13 +++--- doc/basics.md | 2 +- tests/ui-cargo/update-all-references.sh | 17 +------- tests/ui-cargo/update-references.sh | 46 --------------------- tests/ui-toml/update-all-references.sh | 17 +------- tests/ui-toml/update-references.sh | 46 --------------------- tests/ui/update-all-references.sh | 20 +--------- tests/ui/update-references.sh | 56 -------------------------- 11 files changed, 87 insertions(+), 208 deletions(-) create mode 100644 clippy_dev/src/bless.rs delete mode 100755 tests/ui-cargo/update-references.sh delete mode 100755 tests/ui-toml/update-references.sh delete mode 100755 tests/ui/update-references.sh (limited to 'tests') diff --git a/clippy_dev/src/bless.rs b/clippy_dev/src/bless.rs new file mode 100644 index 00000000000..45e403fa74d --- /dev/null +++ b/clippy_dev/src/bless.rs @@ -0,0 +1,71 @@ +//! `bless` updates the 'expected output' files in the repo with changed output files +//! from the last test run. + +use std::env; +use std::ffi::OsStr; +use std::fs; +use std::lazy::SyncLazy; +use std::path::PathBuf; +use walkdir::WalkDir; + +use crate::clippy_project_root; + +// NOTE: this is duplicated with tests/cargo/mod.rs What to do? +pub static CARGO_TARGET_DIR: SyncLazy = SyncLazy::new(|| match env::var_os("CARGO_TARGET_DIR") { + Some(v) => v.into(), + None => env::current_dir().unwrap().join("target"), +}); + +pub fn bless() { + let test_dirs = [ + clippy_project_root().join("tests").join("ui"), + clippy_project_root().join("tests").join("ui-toml"), + clippy_project_root().join("tests").join("ui-cargo"), + ]; + for test_dir in &test_dirs { + WalkDir::new(test_dir) + .into_iter() + .filter_map(Result::ok) + .filter(|f| f.path().extension() == Some(OsStr::new("rs"))) + .for_each(|f| { + update_test_file(f.path().with_extension("stdout")); + update_test_file(f.path().with_extension("stderr")); + update_test_file(f.path().with_extension("fixed")); + }); + } +} + +fn update_test_file(test_file_path: PathBuf) { + let build_output_path = build_dir().join(PathBuf::from(test_file_path.file_name().unwrap())); + let relative_test_file_path = test_file_path.strip_prefix(clippy_project_root()).unwrap(); + + // If compiletest did not write any changes during the test run, + // we don't have to update anything + if !build_output_path.exists() { + return; + } + + let build_output = fs::read(&build_output_path).expect("Unable to read build output file"); + let test_file = fs::read(&test_file_path).expect("Unable to read test file"); + + if build_output != test_file { + // If a test run caused an output file to change, update the test file + println!("updating {}", &relative_test_file_path.display()); + fs::copy(build_output_path, &test_file_path).expect("Could not update test file"); + + if test_file.is_empty() { + // If we copied over an empty output file, we remove it + println!("removing {}", &relative_test_file_path.display()); + fs::remove_file(test_file_path).expect("Could not remove test file"); + } + } +} + +fn build_dir() -> PathBuf { + let profile = format!("{}", env::var("PROFILE").unwrap_or("debug".to_string())); + let mut path = PathBuf::new(); + path.push(CARGO_TARGET_DIR.clone()); + path.push(profile); + path.push("test_build_base"); + path +} diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index f51c45e9eb5..17cc08ee10f 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -10,6 +10,7 @@ use std::lazy::SyncLazy; use std::path::{Path, PathBuf}; use walkdir::WalkDir; +pub mod bless; pub mod fmt; pub mod new_lint; pub mod ra_setup; diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index 7a8cbd5251d..f66855620e7 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -1,10 +1,11 @@ #![cfg_attr(feature = "deny-warnings", deny(warnings))] use clap::{App, Arg, SubCommand}; -use clippy_dev::{fmt, new_lint, ra_setup, serve, stderr_length_check, update_lints}; +use clippy_dev::{bless, fmt, new_lint, ra_setup, serve, stderr_length_check, update_lints}; fn main() { let matches = App::new("Clippy developer tooling") + .subcommand(SubCommand::with_name("bless").about("bless the test output changes")) .subcommand( SubCommand::with_name("fmt") .about("Run rustfmt on all projects and tests") @@ -116,6 +117,9 @@ fn main() { .get_matches(); match matches.subcommand() { + ("bless", Some(_)) => { + bless::bless(); + }, ("fmt", Some(matches)) => { fmt::run(matches.is_present("check"), matches.is_present("verbose")); }, diff --git a/doc/adding_lints.md b/doc/adding_lints.md index a723b0a4c20..60dfdb76650 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -98,12 +98,12 @@ While we are working on implementing our lint, we can keep running the UI test. That allows us to check if the output is turning into what we want. Once we are satisfied with the output, we need to run -`tests/ui/update-all-references.sh` to update the `.stderr` file for our lint. +`cargo dev bless` to update the `.stderr` file for our lint. Please note that, we should run `TESTNAME=foo_functions cargo uitest` -every time before running `tests/ui/update-all-references.sh`. +every time before running `cargo dev bless`. Running `TESTNAME=foo_functions cargo uitest` should pass then. When we commit our lint, we need to commit the generated `.stderr` files, too. In general, you -should only commit files changed by `tests/ui/update-all-references.sh` for the +should only commit files changed by `cargo dev bless` for the specific lint you are creating/editing. Note that if the generated files are empty, they should be removed. @@ -122,8 +122,7 @@ we will find by default two new crates, each with its manifest file: If you need more cases, you can copy one of those crates (under `foo_categories`) and rename it. The process of generating the `.stderr` file is the same, and prepending the `TESTNAME` -variable to `cargo uitest` works too, but the script to update the references -is in another path: `tests/ui-cargo/update-all-references.sh`. +variable to `cargo uitest` works too. ## Rustfix tests @@ -133,7 +132,7 @@ additionally run [rustfix] for that test. Rustfix will apply the suggestions from the lint to the code of the test file and compare that to the contents of a `.fixed` file. -Use `tests/ui/update-all-references.sh` to automatically generate the +Use `cargo dev bless` to automatically generate the `.fixed` file after running the tests. [rustfix]: https://github.com/rust-lang/rustfix @@ -368,7 +367,7 @@ fn is_foo_fn(fn_kind: FnKind<'_>) -> bool { Now we should also run the full test suite with `cargo test`. At this point running `cargo test` should produce the expected output. Remember to run -`tests/ui/update-all-references.sh` to update the `.stderr` file. +`cargo dev bless` to update the `.stderr` file. `cargo test` (as opposed to `cargo uitest`) will also ensure that our lint implementation is not violating any Clippy lints itself. diff --git a/doc/basics.md b/doc/basics.md index 8b2a8a23890..dc71f022773 100644 --- a/doc/basics.md +++ b/doc/basics.md @@ -61,7 +61,7 @@ If the output of a [UI test] differs from the expected output, you can update th reference file with: ```bash -sh tests/ui/update-all-references.sh +cargo dev bless ``` For example, this is necessary, if you fix a typo in an error message of a lint diff --git a/tests/ui-cargo/update-all-references.sh b/tests/ui-cargo/update-all-references.sh index 7028b251ea0..4391499a1e1 100755 --- a/tests/ui-cargo/update-all-references.sh +++ b/tests/ui-cargo/update-all-references.sh @@ -1,18 +1,3 @@ #!/bin/bash -# -# A script to update the references for all tests. The idea is that -# you do a run, which will generate files in the build directory -# containing the (normalized) actual output of the compiler. You then -# run this script, which will copy those files over. If you find -# yourself manually editing a foo.stderr file, you're doing it wrong. -# -# See all `update-references.sh`, if you just want to update a single test. -if [[ "$1" == "--help" || "$1" == "-h" ]]; then - echo "usage: $0" -fi - -BUILD_DIR=$PWD/target/debug/test_build_base -MY_DIR=$(dirname "$0") -cd "$MY_DIR" || exit -find . -name '*.rs' -exec ./update-references.sh "$BUILD_DIR" {} + +echo "Please use 'cargo dev bless' instead." diff --git a/tests/ui-cargo/update-references.sh b/tests/ui-cargo/update-references.sh deleted file mode 100755 index 2ab51168bca..00000000000 --- a/tests/ui-cargo/update-references.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/bash - -# A script to update the references for particular tests. The idea is -# that you do a run, which will generate files in the build directory -# containing the (normalized) actual output of the compiler. This -# script will then copy that output and replace the "expected output" -# files. You can then commit the changes. -# -# If you find yourself manually editing a foo.stderr file, you're -# doing it wrong. - -if [[ "$1" == "--help" || "$1" == "-h" || "$1" == "" || "$2" == "" ]]; then - echo "usage: $0 " - echo "" - echo "For example:" - echo " $0 ../../../build/x86_64-apple-darwin/test/ui *.rs */*.rs" -fi - -MYDIR=$(dirname "$0") - -BUILD_DIR="$1" -shift - -while [[ "$1" != "" ]]; do - STDERR_NAME="${1/%.rs/.stderr}" - STDOUT_NAME="${1/%.rs/.stdout}" - shift - if [[ -f "$BUILD_DIR"/"$STDOUT_NAME" ]] && \ - ! (cmp -s -- "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"); then - echo updating "$MYDIR"/"$STDOUT_NAME" - cp "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME" - if [[ ! -s "$MYDIR"/"$STDOUT_NAME" ]]; then - echo removing "$MYDIR"/"$STDOUT_NAME" - rm "$MYDIR"/"$STDOUT_NAME" - fi - fi - if [[ -f "$BUILD_DIR"/"$STDERR_NAME" ]] && \ - ! (cmp -s -- "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"); then - echo updating "$MYDIR"/"$STDERR_NAME" - cp "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME" - if [[ ! -s "$MYDIR"/"$STDERR_NAME" ]]; then - echo removing "$MYDIR"/"$STDERR_NAME" - rm "$MYDIR"/"$STDERR_NAME" - fi - fi -done diff --git a/tests/ui-toml/update-all-references.sh b/tests/ui-toml/update-all-references.sh index 7028b251ea0..4391499a1e1 100755 --- a/tests/ui-toml/update-all-references.sh +++ b/tests/ui-toml/update-all-references.sh @@ -1,18 +1,3 @@ #!/bin/bash -# -# A script to update the references for all tests. The idea is that -# you do a run, which will generate files in the build directory -# containing the (normalized) actual output of the compiler. You then -# run this script, which will copy those files over. If you find -# yourself manually editing a foo.stderr file, you're doing it wrong. -# -# See all `update-references.sh`, if you just want to update a single test. -if [[ "$1" == "--help" || "$1" == "-h" ]]; then - echo "usage: $0" -fi - -BUILD_DIR=$PWD/target/debug/test_build_base -MY_DIR=$(dirname "$0") -cd "$MY_DIR" || exit -find . -name '*.rs' -exec ./update-references.sh "$BUILD_DIR" {} + +echo "Please use 'cargo dev bless' instead." diff --git a/tests/ui-toml/update-references.sh b/tests/ui-toml/update-references.sh deleted file mode 100755 index 2ab51168bca..00000000000 --- a/tests/ui-toml/update-references.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/bash - -# A script to update the references for particular tests. The idea is -# that you do a run, which will generate files in the build directory -# containing the (normalized) actual output of the compiler. This -# script will then copy that output and replace the "expected output" -# files. You can then commit the changes. -# -# If you find yourself manually editing a foo.stderr file, you're -# doing it wrong. - -if [[ "$1" == "--help" || "$1" == "-h" || "$1" == "" || "$2" == "" ]]; then - echo "usage: $0 " - echo "" - echo "For example:" - echo " $0 ../../../build/x86_64-apple-darwin/test/ui *.rs */*.rs" -fi - -MYDIR=$(dirname "$0") - -BUILD_DIR="$1" -shift - -while [[ "$1" != "" ]]; do - STDERR_NAME="${1/%.rs/.stderr}" - STDOUT_NAME="${1/%.rs/.stdout}" - shift - if [[ -f "$BUILD_DIR"/"$STDOUT_NAME" ]] && \ - ! (cmp -s -- "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"); then - echo updating "$MYDIR"/"$STDOUT_NAME" - cp "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME" - if [[ ! -s "$MYDIR"/"$STDOUT_NAME" ]]; then - echo removing "$MYDIR"/"$STDOUT_NAME" - rm "$MYDIR"/"$STDOUT_NAME" - fi - fi - if [[ -f "$BUILD_DIR"/"$STDERR_NAME" ]] && \ - ! (cmp -s -- "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"); then - echo updating "$MYDIR"/"$STDERR_NAME" - cp "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME" - if [[ ! -s "$MYDIR"/"$STDERR_NAME" ]]; then - echo removing "$MYDIR"/"$STDERR_NAME" - rm "$MYDIR"/"$STDERR_NAME" - fi - fi -done diff --git a/tests/ui/update-all-references.sh b/tests/ui/update-all-references.sh index 30ba9188db4..4391499a1e1 100755 --- a/tests/ui/update-all-references.sh +++ b/tests/ui/update-all-references.sh @@ -1,21 +1,3 @@ #!/bin/bash -# A script to update the references for all tests. The idea is that -# you do a run, which will generate files in the build directory -# containing the (normalized) actual output of the compiler. You then -# run this script, which will copy those files over. If you find -# yourself manually editing a foo.stderr file, you're doing it wrong. -# -# See all `update-references.sh`, if you just want to update a single test. - -if [[ "$1" == "--help" || "$1" == "-h" ]]; then - echo "usage: $0" -fi - -CARGO_TARGET_DIR=${CARGO_TARGET_DIR:-$PWD/target} -PROFILE=${PROFILE:-debug} -BUILD_DIR=${CARGO_TARGET_DIR}/${PROFILE}/test_build_base - -MY_DIR=$(dirname "$0") -cd "$MY_DIR" || exit -find . -name '*.rs' -exec ./update-references.sh "$BUILD_DIR" {} + +echo "Please use 'cargo dev bless' instead." diff --git a/tests/ui/update-references.sh b/tests/ui/update-references.sh deleted file mode 100755 index e16ed600ef8..00000000000 --- a/tests/ui/update-references.sh +++ /dev/null @@ -1,56 +0,0 @@ -#!/bin/bash - -# A script to update the references for particular tests. The idea is -# that you do a run, which will generate files in the build directory -# containing the (normalized) actual output of the compiler. This -# script will then copy that output and replace the "expected output" -# files. You can then commit the changes. -# -# If you find yourself manually editing a `foo.stderr` file, you're -# doing it wrong. - -if [[ "$1" == "--help" || "$1" == "-h" || "$1" == "" || "$2" == "" ]]; then - echo "usage: $0 " - echo "" - echo "For example:" - echo " $0 ../../../build/x86_64-apple-darwin/test/ui *.rs */*.rs" -fi - -MYDIR=$(dirname "$0") - -BUILD_DIR="$1" -shift - -while [[ "$1" != "" ]]; do - STDERR_NAME="${1/%.rs/.stderr}" - STDOUT_NAME="${1/%.rs/.stdout}" - FIXED_NAME="${1/%.rs/.fixed}" - shift - if [[ -f "$BUILD_DIR"/"$STDOUT_NAME" ]] && \ - ! (cmp -s -- "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"); then - echo updating "$MYDIR"/"$STDOUT_NAME" - cp "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME" - if [[ ! -s "$MYDIR"/"$STDOUT_NAME" ]]; then - echo removing "$MYDIR"/"$STDOUT_NAME" - rm "$MYDIR"/"$STDOUT_NAME" - fi - fi - if [[ -f "$BUILD_DIR"/"$STDERR_NAME" ]] && \ - ! (cmp -s -- "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"); then - echo updating "$MYDIR"/"$STDERR_NAME" - cp "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME" - if [[ ! -s "$MYDIR"/"$STDERR_NAME" ]]; then - echo removing "$MYDIR"/"$STDERR_NAME" - rm "$MYDIR"/"$STDERR_NAME" - fi - fi - if [[ -f "$BUILD_DIR"/"$FIXED_NAME" ]] && \ - ! (cmp -s -- "$BUILD_DIR"/"$FIXED_NAME" "$MYDIR"/"$FIXED_NAME"); then - echo updating "$MYDIR"/"$FIXED_NAME" - cp "$BUILD_DIR"/"$FIXED_NAME" "$MYDIR"/"$FIXED_NAME" - if [[ ! -s "$MYDIR"/"$FIXED_NAME" ]]; then - echo removing "$MYDIR"/"$FIXED_NAME" - rm "$MYDIR"/"$FIXED_NAME" - fi - fi -done -- cgit 1.4.1-3-g733a5 From 3af09b8cf1229fb05a549a13b144aca6b60784c7 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Sun, 13 Dec 2020 06:32:41 +0200 Subject: New internal lint: interning_defined_symbol --- clippy_lints/src/lib.rs | 4 ++ clippy_lints/src/utils/internal_lints.rs | 80 +++++++++++++++++++++++ clippy_lints/src/utils/paths.rs | 6 ++ tests/ui-internal/interning_defined_symbol.fixed | 33 ++++++++++ tests/ui-internal/interning_defined_symbol.rs | 33 ++++++++++ tests/ui-internal/interning_defined_symbol.stderr | 27 ++++++++ 6 files changed, 183 insertions(+) create mode 100644 tests/ui-internal/interning_defined_symbol.fixed create mode 100644 tests/ui-internal/interning_defined_symbol.rs create mode 100644 tests/ui-internal/interning_defined_symbol.stderr (limited to 'tests') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f06926fa91d..97018599b05 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -513,6 +513,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: #[cfg(feature = "internal-lints")] &utils::internal_lints::INVALID_PATHS, #[cfg(feature = "internal-lints")] + &utils::internal_lints::INTERNING_DEFINED_SYMBOL, + #[cfg(feature = "internal-lints")] &utils::internal_lints::LINT_WITHOUT_LINT_PASS, #[cfg(feature = "internal-lints")] &utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM, @@ -958,6 +960,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls); store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); store.register_late_pass(|| box utils::internal_lints::InvalidPaths); + store.register_late_pass(|| box utils::internal_lints::InterningDefinedSymbol::default()); store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default()); store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem); store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass); @@ -1350,6 +1353,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&utils::internal_lints::COMPILER_LINT_FUNCTIONS), LintId::of(&utils::internal_lints::DEFAULT_LINT), LintId::of(&utils::internal_lints::INVALID_PATHS), + LintId::of(&utils::internal_lints::INTERNING_DEFINED_SYMBOL), 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), diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 8b59a9541a7..0de87fab528 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -15,6 +15,7 @@ use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; use rustc_hir::{Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Node, Path, StmtKind, Ty, TyKind}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_middle::hir::map::Map; +use rustc_middle::mir::interpret::ConstValue; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::{Span, Spanned}; @@ -247,6 +248,30 @@ declare_clippy_lint! { "invalid path" } +declare_clippy_lint! { + /// **What it does:** + /// Checks for interning symbols that have already been pre-interned and defined as constants. + /// + /// **Why is this bad?** + /// It's faster and easier to use the symbol constant. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// Bad: + /// ```rust,ignore + /// let _ = sym!(f32); + /// ``` + /// + /// Good: + /// ```rust,ignore + /// let _ = sym::f32; + /// ``` + pub INTERNING_DEFINED_SYMBOL, + internal, + "interning a symbol that is pre-interned and defined as a constant" +} + declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]); impl EarlyLintPass for ClippyLintsInternal { @@ -840,3 +865,58 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths { } } } + +#[derive(Default)] +pub struct InterningDefinedSymbol { + // Maps the symbol to the constant name. + symbol_map: FxHashMap, +} + +impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL]); + +impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { + fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) { + if !self.symbol_map.is_empty() { + return; + } + + if let Some(Res::Def(_, def_id)) = path_to_res(cx, &paths::SYM_MODULE) { + for item in cx.tcx.item_children(def_id).iter() { + if_chain! { + if let Res::Def(DefKind::Const, item_def_id) = item.res; + let ty = cx.tcx.type_of(item_def_id); + if match_type(cx, ty, &paths::SYMBOL); + if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id); + if let Ok(value) = value.to_u32(); + then { + // SAFETY: We're converting the raw bytes of the symbol value back + // into a Symbol instance. + let symbol = unsafe { std::mem::transmute::(value) }; + self.symbol_map.insert(symbol.to_string(), item.ident.to_string()); + } + } + } + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + if let ExprKind::Call(func, [arg]) = &expr.kind; + if let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind(); + if match_def_path(cx, *def_id, &paths::SYMBOL_INTERN); + if let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg); + if let Some(symbol_const) = self.symbol_map.get(&arg); + then { + span_lint_and_sugg( + cx, + INTERNING_DEFINED_SYMBOL, + is_expn_of(expr.span, "sym").unwrap_or(expr.span), + "interning a defined symbol", + "try", + format!("rustc_span::symbol::sym::{}", symbol_const), + Applicability::MachineApplicable, + ); + } + } + } +} diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 6fdc7b4587f..2080a49a11c 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -146,6 +146,12 @@ pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"]; pub const STR_LEN: [&str; 4] = ["core", "str", "", "len"]; pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "", "starts_with"]; #[cfg(feature = "internal-lints")] +pub const SYMBOL: [&str; 3] = ["rustc_span", "symbol", "Symbol"]; +#[cfg(feature = "internal-lints")] +pub const SYMBOL_INTERN: [&str; 4] = ["rustc_span", "symbol", "Symbol", "intern"]; +#[cfg(feature = "internal-lints")] +pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"]; +#[cfg(feature = "internal-lints")] pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]; pub const TO_OWNED: [&str; 3] = ["alloc", "borrow", "ToOwned"]; pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"]; diff --git a/tests/ui-internal/interning_defined_symbol.fixed b/tests/ui-internal/interning_defined_symbol.fixed new file mode 100644 index 00000000000..c6b84d2ef65 --- /dev/null +++ b/tests/ui-internal/interning_defined_symbol.fixed @@ -0,0 +1,33 @@ +// run-rustfix +#![deny(clippy::internal)] +#![feature(rustc_private)] + +extern crate rustc_span; + +use rustc_span::symbol::Symbol; + +macro_rules! sym { + ($tt:tt) => { + rustc_span::symbol::Symbol::intern(stringify!($tt)) + }; +} + +fn main() { + // Direct use of Symbol::intern + let _ = rustc_span::symbol::sym::f32; + + // Using a sym macro + let _ = rustc_span::symbol::sym::f32; + + // Correct suggestion when symbol isn't stringified constant name + let _ = rustc_span::symbol::sym::proc_dash_macro; + + // Interning a symbol that is not defined + let _ = Symbol::intern("xyz123"); + let _ = sym!(xyz123); + + // Using a different `intern` function + let _ = intern("f32"); +} + +fn intern(_: &str) {} diff --git a/tests/ui-internal/interning_defined_symbol.rs b/tests/ui-internal/interning_defined_symbol.rs new file mode 100644 index 00000000000..9ec82d4ad0b --- /dev/null +++ b/tests/ui-internal/interning_defined_symbol.rs @@ -0,0 +1,33 @@ +// run-rustfix +#![deny(clippy::internal)] +#![feature(rustc_private)] + +extern crate rustc_span; + +use rustc_span::symbol::Symbol; + +macro_rules! sym { + ($tt:tt) => { + rustc_span::symbol::Symbol::intern(stringify!($tt)) + }; +} + +fn main() { + // Direct use of Symbol::intern + let _ = Symbol::intern("f32"); + + // Using a sym macro + let _ = sym!(f32); + + // Correct suggestion when symbol isn't stringified constant name + let _ = Symbol::intern("proc-macro"); + + // Interning a symbol that is not defined + let _ = Symbol::intern("xyz123"); + let _ = sym!(xyz123); + + // Using a different `intern` function + let _ = intern("f32"); +} + +fn intern(_: &str) {} diff --git a/tests/ui-internal/interning_defined_symbol.stderr b/tests/ui-internal/interning_defined_symbol.stderr new file mode 100644 index 00000000000..74b906c8a57 --- /dev/null +++ b/tests/ui-internal/interning_defined_symbol.stderr @@ -0,0 +1,27 @@ +error: interning a defined symbol + --> $DIR/interning_defined_symbol.rs:17:13 + | +LL | let _ = Symbol::intern("f32"); + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::symbol::sym::f32` + | +note: the lint level is defined here + --> $DIR/interning_defined_symbol.rs:2:9 + | +LL | #![deny(clippy::internal)] + | ^^^^^^^^^^^^^^^^ + = note: `#[deny(clippy::interning_defined_symbol)]` implied by `#[deny(clippy::internal)]` + +error: interning a defined symbol + --> $DIR/interning_defined_symbol.rs:20:13 + | +LL | let _ = sym!(f32); + | ^^^^^^^^^ help: try: `rustc_span::symbol::sym::f32` + +error: interning a defined symbol + --> $DIR/interning_defined_symbol.rs:23:13 + | +LL | let _ = Symbol::intern("proc-macro"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::symbol::sym::proc_dash_macro` + +error: aborting due to 3 previous errors + -- cgit 1.4.1-3-g733a5 From cd2a62cb0cf89f5b4105c1c40651cf0eeaa85b14 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Sun, 13 Dec 2020 15:17:47 +0100 Subject: needless_borrow: print the type in the lint message changelog: needless_borrow: print type in lint message --- clippy_lints/src/needless_borrow.rs | 7 +++++-- tests/ui/eta.stderr | 2 +- tests/ui/needless_borrow.stderr | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/needless_borrow.rs b/clippy_lints/src/needless_borrow.rs index 405c21d608d..bff53eb8cca 100644 --- a/clippy_lints/src/needless_borrow.rs +++ b/clippy_lints/src/needless_borrow.rs @@ -47,7 +47,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow { return; } if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, ref inner) = e.kind { - if let ty::Ref(..) = cx.typeck_results().expr_ty(inner).kind() { + if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(inner).kind() { for adj3 in cx.typeck_results().expr_adjustments(e).windows(3) { if let [Adjustment { kind: Adjust::Deref(_), .. @@ -62,8 +62,11 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow { cx, NEEDLESS_BORROW, e.span, - "this expression borrows a reference that is immediately dereferenced \ + &format!( + "this expression borrows a reference (`&{}`) that is immediately dereferenced \ by the compiler", + ty + ), |diag| { if let Some(snippet) = snippet_opt(cx, inner.span) { diag.span_suggestion( diff --git a/tests/ui/eta.stderr b/tests/ui/eta.stderr index c4713ca8083..16aa1b07733 100644 --- a/tests/ui/eta.stderr +++ b/tests/ui/eta.stderr @@ -12,7 +12,7 @@ error: redundant closure found LL | meta(|a| foo(a)); | ^^^^^^^^^^ help: remove closure as shown: `foo` -error: this expression borrows a reference that is immediately dereferenced by the compiler +error: this expression borrows a reference (`&u8`) that is immediately dereferenced by the compiler --> $DIR/eta.rs:24:21 | LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted diff --git a/tests/ui/needless_borrow.stderr b/tests/ui/needless_borrow.stderr index 0bfeda7914d..bea4b41b803 100644 --- a/tests/ui/needless_borrow.stderr +++ b/tests/ui/needless_borrow.stderr @@ -1,4 +1,4 @@ -error: this expression borrows a reference that is immediately dereferenced by the compiler +error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler --> $DIR/needless_borrow.rs:14:15 | LL | let c = x(&&a); @@ -12,7 +12,7 @@ error: this pattern creates a reference to a reference LL | if let Some(ref cake) = Some(&5) {} | ^^^^^^^^ help: change this to: `cake` -error: this expression borrows a reference that is immediately dereferenced by the compiler +error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler --> $DIR/needless_borrow.rs:28:15 | LL | 46 => &&a, -- cgit 1.4.1-3-g733a5 From cc9695543ea8f3973a2be2936df0efc724de1c16 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 11 Dec 2020 23:54:47 +0100 Subject: Pass Clippy args also trough RUSTFLAGS --- README.md | 1 - src/driver.rs | 116 +++++++++++++++++++++++++++++++++++++++++-------------- src/main.rs | 98 ++++++++++++++++++++++++++++++++++++---------- tests/dogfood.rs | 2 +- 4 files changed, 165 insertions(+), 52 deletions(-) (limited to 'tests') diff --git a/README.md b/README.md index aaa55e11c7d..dc931963726 100644 --- a/README.md +++ b/README.md @@ -208,7 +208,6 @@ the lint(s) you are interested in: ```terminal cargo clippy -- -A clippy::all -W clippy::useless_format -W clippy::... ``` -Note that if you've run clippy before, this may only take effect after you've modified a file or ran `cargo clean`. ### Specifying the minimum supported Rust version diff --git a/src/driver.rs b/src/driver.rs index e490ee54c0b..40f1b802e60 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -1,5 +1,6 @@ #![feature(rustc_private)] #![feature(once_cell)] +#![feature(bool_to_option)] #![cfg_attr(feature = "deny-warnings", deny(warnings))] // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] @@ -19,6 +20,7 @@ use rustc_tools_util::VersionInfo; use std::borrow::Cow; use std::env; +use std::iter; use std::lazy::SyncLazy; use std::ops::Deref; use std::panic; @@ -47,20 +49,6 @@ fn arg_value<'a, T: Deref>( None } -#[test] -fn test_arg_value() { - let args = &["--bar=bar", "--foobar", "123", "--foo"]; - - assert_eq!(arg_value(&[] as &[&str], "--foobar", |_| true), None); - assert_eq!(arg_value(args, "--bar", |_| false), None); - assert_eq!(arg_value(args, "--bar", |_| true), Some("bar")); - assert_eq!(arg_value(args, "--bar", |p| p == "bar"), Some("bar")); - assert_eq!(arg_value(args, "--bar", |p| p == "foo"), None); - assert_eq!(arg_value(args, "--foobar", |p| p == "foo"), None); - assert_eq!(arg_value(args, "--foobar", |p| p == "123"), Some("123")); - assert_eq!(arg_value(args, "--foo", |_| true), None); -} - struct DefaultCallbacks; impl rustc_driver::Callbacks for DefaultCallbacks {} @@ -182,6 +170,28 @@ fn toolchain_path(home: Option, toolchain: Option) -> Option(args: &mut Vec, clippy_args: I) +where + T: AsRef, + U: AsRef + ?Sized + 'a, + I: Iterator + Clone, +{ + let args_iter = clippy_args.map(AsRef::as_ref); + let args_count = args_iter.clone().count(); + + if args_count > 0 { + if let Some(start) = args.windows(args_count).enumerate().find_map(|(current, window)| { + window + .iter() + .map(AsRef::as_ref) + .eq(args_iter.clone()) + .then_some(current) + }) { + args.drain(start..start + args_count); + } + } +} + #[allow(clippy::too_many_lines)] pub fn main() { rustc_driver::init_rustc_env_logger(); @@ -278,20 +288,9 @@ pub fn main() { args.extend(vec!["--sysroot".into(), sys_root]); }; - let mut no_deps = false; - let clippy_args = env::var("CLIPPY_ARGS") - .unwrap_or_default() - .split("__CLIPPY_HACKERY__") - .filter_map(|s| match s { - "" => None, - "--no-deps" => { - no_deps = true; - None - }, - _ => Some(s.to_string()), - }) - .chain(vec!["--cfg".into(), r#"feature="cargo-clippy""#.into()]) - .collect::>(); + let clippy_args = env::var("CLIPPY_ARGS").unwrap_or_default(); + let clippy_args = clippy_args.split_whitespace(); + let no_deps = clippy_args.clone().any(|flag| flag == "--no-deps"); // We enable Clippy if one of the following conditions is met // - IF Clippy is run on its test suite OR @@ -304,7 +303,11 @@ pub fn main() { let clippy_enabled = clippy_tests_set || (!cap_lints_allow && (!no_deps || in_primary_package)); if clippy_enabled { - args.extend(clippy_args); + remove_clippy_args(&mut args, iter::once("--no-deps")); + args.extend(vec!["--cfg".into(), r#"feature="cargo-clippy""#.into()]); + } else { + // Remove all flags passed through RUSTFLAGS if Clippy is not enabled. + remove_clippy_args(&mut args, clippy_args); } let mut clippy = ClippyCallbacks; @@ -315,3 +318,58 @@ pub fn main() { rustc_driver::RunCompiler::new(&args, callbacks).run() })) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_arg_value() { + let args = &["--bar=bar", "--foobar", "123", "--foo"]; + + assert_eq!(arg_value(&[] as &[&str], "--foobar", |_| true), None); + assert_eq!(arg_value(args, "--bar", |_| false), None); + assert_eq!(arg_value(args, "--bar", |_| true), Some("bar")); + assert_eq!(arg_value(args, "--bar", |p| p == "bar"), Some("bar")); + assert_eq!(arg_value(args, "--bar", |p| p == "foo"), None); + assert_eq!(arg_value(args, "--foobar", |p| p == "foo"), None); + assert_eq!(arg_value(args, "--foobar", |p| p == "123"), Some("123")); + assert_eq!(arg_value(args, "--foo", |_| true), None); + } + + #[test] + fn removes_clippy_args_from_start() { + let mut args = vec!["-D", "clippy::await_holding_lock", "--cfg", r#"feature="some_feat""#]; + let clippy_args = ["-D", "clippy::await_holding_lock"].iter(); + + remove_clippy_args(&mut args, clippy_args); + assert_eq!(args, &["--cfg", r#"feature="some_feat""#]); + } + + #[test] + fn removes_clippy_args_from_end() { + let mut args = vec!["-Zui-testing", "-A", "clippy::empty_loop", "--no-deps"]; + let clippy_args = ["-A", "clippy::empty_loop", "--no-deps"].iter(); + + remove_clippy_args(&mut args, clippy_args); + assert_eq!(args, &["-Zui-testing"]); + } + + #[test] + fn removes_clippy_args_from_middle() { + let mut args = vec!["-Zui-testing", "-W", "clippy::filter_map", "-L", "serde"]; + let clippy_args = ["-W", "clippy::filter_map"].iter(); + + remove_clippy_args(&mut args, clippy_args); + assert_eq!(args, &["-Zui-testing", "-L", "serde"]); + } + + #[test] + fn no_clippy_args_to_remove() { + let mut args = vec!["-Zui-testing", "-L", "serde"]; + let clippy_args: [&str; 0] = []; + + remove_clippy_args(&mut args, clippy_args.iter()); + assert_eq!(args, &["-Zui-testing", "-L", "serde"]); + } +} diff --git a/src/main.rs b/src/main.rs index ea06743394d..7594ea2c7b1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +#![feature(bool_to_option)] #![cfg_attr(feature = "deny-warnings", deny(warnings))] // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] @@ -62,11 +63,12 @@ struct ClippyCmd { unstable_options: bool, cargo_subcommand: &'static str, args: Vec, - clippy_args: Vec, + rustflags: Option, + clippy_args: Option, } impl ClippyCmd { - fn new(mut old_args: I) -> Self + fn new(mut old_args: I, rustflags: Option) -> Self where I: Iterator, { @@ -99,16 +101,19 @@ impl ClippyCmd { args.insert(0, "+nightly".to_string()); } - let mut clippy_args: Vec = old_args.collect(); - if cargo_subcommand == "fix" && !clippy_args.iter().any(|arg| arg == "--no-deps") { - clippy_args.push("--no-deps".into()); + let mut clippy_args = old_args.collect::>().join(" "); + if cargo_subcommand == "fix" && !clippy_args.contains("--no-deps") { + clippy_args = format!("{} --no-deps", clippy_args); } + let has_args = !clippy_args.is_empty(); ClippyCmd { unstable_options, cargo_subcommand, args, - clippy_args, + rustflags: has_args + .then(|| rustflags.map_or_else(|| clippy_args.clone(), |flags| format!("{} {}", clippy_args, flags))), + clippy_args: has_args.then_some(clippy_args), } } @@ -150,18 +155,19 @@ impl ClippyCmd { fn into_std_cmd(self) -> Command { let mut cmd = Command::new("cargo"); - let clippy_args: String = self - .clippy_args - .iter() - .map(|arg| format!("{}__CLIPPY_HACKERY__", arg)) - .collect(); cmd.env(self.path_env(), Self::path()) .envs(ClippyCmd::target_dir()) - .env("CLIPPY_ARGS", clippy_args) .arg(self.cargo_subcommand) .args(&self.args); + // HACK: pass Clippy args to the driver *also* through RUSTFLAGS. + // This guarantees that new builds will be triggered when Clippy flags change. + if let (Some(clippy_args), Some(rustflags)) = (self.clippy_args, self.rustflags) { + cmd.env("CLIPPY_ARGS", clippy_args); + cmd.env("RUSTFLAGS", rustflags); + } + cmd } } @@ -170,7 +176,7 @@ fn process(old_args: I) -> Result<(), i32> where I: Iterator, { - let cmd = ClippyCmd::new(old_args); + let cmd = ClippyCmd::new(old_args, env::var("RUSTFLAGS").ok()); let mut cmd = cmd.into_std_cmd(); @@ -195,7 +201,7 @@ mod tests { #[should_panic] fn fix_without_unstable() { let args = "cargo clippy --fix".split_whitespace().map(ToString::to_string); - let _ = ClippyCmd::new(args); + let _ = ClippyCmd::new(args, None); } #[test] @@ -203,7 +209,8 @@ mod tests { let args = "cargo clippy --fix -Zunstable-options" .split_whitespace() .map(ToString::to_string); - let cmd = ClippyCmd::new(args); + let cmd = ClippyCmd::new(args, None); + assert_eq!("fix", cmd.cargo_subcommand); assert_eq!("RUSTC_WORKSPACE_WRAPPER", cmd.path_env()); assert!(cmd.args.iter().any(|arg| arg.ends_with("unstable-options"))); @@ -214,8 +221,9 @@ mod tests { let args = "cargo clippy --fix -Zunstable-options" .split_whitespace() .map(ToString::to_string); - let cmd = ClippyCmd::new(args); - assert!(cmd.clippy_args.iter().any(|arg| arg == "--no-deps")); + let cmd = ClippyCmd::new(args, None); + + assert!(cmd.clippy_args.unwrap().contains("--no-deps")); } #[test] @@ -223,14 +231,16 @@ mod tests { let args = "cargo clippy --fix -Zunstable-options -- --no-deps" .split_whitespace() .map(ToString::to_string); - let cmd = ClippyCmd::new(args); - assert_eq!(cmd.clippy_args.iter().filter(|arg| *arg == "--no-deps").count(), 1); + let cmd = ClippyCmd::new(args, None); + + assert_eq!(1, cmd.clippy_args.unwrap().matches("--no-deps").count()); } #[test] fn check() { let args = "cargo clippy".split_whitespace().map(ToString::to_string); - let cmd = ClippyCmd::new(args); + let cmd = ClippyCmd::new(args, None); + assert_eq!("check", cmd.cargo_subcommand); assert_eq!("RUSTC_WRAPPER", cmd.path_env()); } @@ -240,8 +250,54 @@ mod tests { let args = "cargo clippy -Zunstable-options" .split_whitespace() .map(ToString::to_string); - let cmd = ClippyCmd::new(args); + let cmd = ClippyCmd::new(args, None); + assert_eq!("check", cmd.cargo_subcommand); assert_eq!("RUSTC_WORKSPACE_WRAPPER", cmd.path_env()); } + + #[test] + fn clippy_args_into_rustflags() { + let args = "cargo clippy -- -W clippy::as_conversions" + .split_whitespace() + .map(ToString::to_string); + let rustflags = None; + let cmd = ClippyCmd::new(args, rustflags); + + assert_eq!("-W clippy::as_conversions", cmd.rustflags.unwrap()); + } + + #[test] + fn clippy_args_respect_existing_rustflags() { + let args = "cargo clippy -- -D clippy::await_holding_lock" + .split_whitespace() + .map(ToString::to_string); + let rustflags = Some(r#"--cfg feature="some_feat""#.into()); + let cmd = ClippyCmd::new(args, rustflags); + + assert_eq!( + r#"-D clippy::await_holding_lock --cfg feature="some_feat""#, + cmd.rustflags.unwrap() + ); + } + + #[test] + fn no_env_change_if_no_clippy_args() { + let args = "cargo clippy".split_whitespace().map(ToString::to_string); + let rustflags = Some(r#"--cfg feature="some_feat""#.into()); + let cmd = ClippyCmd::new(args, rustflags); + + assert!(cmd.clippy_args.is_none()); + assert!(cmd.rustflags.is_none()); + } + + #[test] + fn no_env_change_if_no_clippy_args_nor_rustflags() { + let args = "cargo clippy".split_whitespace().map(ToString::to_string); + let rustflags = None; + let cmd = ClippyCmd::new(args, rustflags); + + assert!(cmd.clippy_args.is_none()); + assert!(cmd.rustflags.is_none()); + } } diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 052223d6d6f..fda1413868e 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -23,7 +23,7 @@ fn dogfood_clippy() { .current_dir(root_dir) .env("CLIPPY_DOGFOOD", "1") .env("CARGO_INCREMENTAL", "0") - .arg("clippy-preview") + .arg("clippy") .arg("--all-targets") .arg("--all-features") .arg("--") -- cgit 1.4.1-3-g733a5 From a37af06fea574f3d9f4d3cca58a70cd545523486 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Wed, 9 Dec 2020 12:25:45 +0000 Subject: Removing false positive for the match_single_binding lint * Applying suggested changes from the PR --- clippy_lints/src/matches.rs | 22 ++++++++++++++++++++-- tests/ui/match_single_binding.fixed | 28 ++++++++++++++++++++++++++++ tests/ui/match_single_binding.rs | 33 +++++++++++++++++++++++++++++++++ tests/ui/match_single_binding.stderr | 13 ++++++++++++- 4 files changed, 93 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 2a1a73f98ee..04b35835c6b 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -4,8 +4,8 @@ use crate::utils::usage::is_unused; use crate::utils::{ expr_block, get_arg_name, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of, is_refutable, is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, meets_msrv, multispan_sugg, remove_blocks, - snippet, snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, - span_lint_and_then, + snippet, snippet_block, snippet_opt, snippet_with_applicability, span_lint_and_help, span_lint_and_note, + span_lint_and_sugg, span_lint_and_then, }; use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash}; use if_chain::if_chain; @@ -1237,6 +1237,24 @@ fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[A if in_macro(expr.span) || arms.len() != 1 || is_refutable(cx, arms[0].pat) { return; } + + // HACK: + // This is a hack to deal with arms that are excluded by macros like `#[cfg]`. It is only used here + // to prevent false positives as there is currently no better way to detect if code was excluded by + // a macro. See PR #6435 + if_chain! { + if let Some(match_snippet) = snippet_opt(cx, expr.span); + if let Some(arm_snippet) = snippet_opt(cx, arms[0].span); + if let Some(ex_snippet) = snippet_opt(cx, ex.span); + let rest_snippet = match_snippet.replace(&arm_snippet, "").replace(&ex_snippet, ""); + if rest_snippet.contains("=>"); + then { + // The code it self contains another thick arrow "=>" + // -> Either another arm or a comment + return; + } + } + let matched_vars = ex.span; let bind_names = arms[0].pat.span; let match_body = remove_blocks(&arms[0].body); diff --git a/tests/ui/match_single_binding.fixed b/tests/ui/match_single_binding.fixed index f3627902eec..526e94b10bd 100644 --- a/tests/ui/match_single_binding.fixed +++ b/tests/ui/match_single_binding.fixed @@ -87,4 +87,32 @@ fn main() { unwrapped }) .collect::>(); + // Ok + let x = 1; + match x { + #[cfg(disabled_feature)] + 0 => println!("Disabled branch"), + _ => println!("Enabled branch"), + } + // Lint + let x = 1; + let y = 1; + println!("Single branch"); + // Ok + let x = 1; + let y = 1; + match match y { + 0 => 1, + _ => 2, + } { + #[cfg(disabled_feature)] + 0 => println!("Array index start"), + _ => println!("Not an array index start"), + } + // False negative + let x = 1; + match x { + // => + _ => println!("Not an array index start"), + } } diff --git a/tests/ui/match_single_binding.rs b/tests/ui/match_single_binding.rs index 8c182148ae1..6a2ca7c5e93 100644 --- a/tests/ui/match_single_binding.rs +++ b/tests/ui/match_single_binding.rs @@ -99,4 +99,37 @@ fn main() { unwrapped => unwrapped, }) .collect::>(); + // Ok + let x = 1; + match x { + #[cfg(disabled_feature)] + 0 => println!("Disabled branch"), + _ => println!("Enabled branch"), + } + // Lint + let x = 1; + let y = 1; + match match y { + 0 => 1, + _ => 2, + } { + _ => println!("Single branch"), + } + // Ok + let x = 1; + let y = 1; + match match y { + 0 => 1, + _ => 2, + } { + #[cfg(disabled_feature)] + 0 => println!("Array index start"), + _ => println!("Not an array index start"), + } + // False negative + let x = 1; + match x { + // => + _ => println!("Not an array index start"), + } } diff --git a/tests/ui/match_single_binding.stderr b/tests/ui/match_single_binding.stderr index 795c8c3e24d..cbbf5d29c02 100644 --- a/tests/ui/match_single_binding.stderr +++ b/tests/ui/match_single_binding.stderr @@ -167,5 +167,16 @@ LL | unwrapped LL | }) | -error: aborting due to 11 previous errors +error: this match could be replaced by its body itself + --> $DIR/match_single_binding.rs:112:5 + | +LL | / match match y { +LL | | 0 => 1, +LL | | _ => 2, +LL | | } { +LL | | _ => println!("Single branch"), +LL | | } + | |_____^ help: consider using the match body instead: `println!("Single branch");` + +error: aborting due to 12 previous errors -- cgit 1.4.1-3-g733a5 From 39bcf8e55438016fb86427f9b8a78b6a32a84126 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Tue, 15 Dec 2020 23:18:03 +0100 Subject: Handle fatal errors when parsing doctests --- clippy_lints/src/doc.rs | 116 ++++++++++++++++++++++-------------------- clippy_lints/src/lib.rs | 1 + tests/ui/needless_doc_main.rs | 6 +++ 3 files changed, 68 insertions(+), 55 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 55e4755c278..77203a286dd 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -451,66 +451,72 @@ fn check_doc<'a, Events: Iterator, Range, text: &str, span: Span) { - fn has_needless_main(code: &str) -> bool { - let filename = FileName::anon_source_code(code); - - let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let emitter = EmitterWriter::new(box io::sink(), None, false, false, false, None, false); - let handler = Handler::with_emitter(false, None, box emitter); - let sess = ParseSess::with_span_handler(handler, sm); - - let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code.into()) { - Ok(p) => p, - Err(errs) => { - for mut err in errs { - err.cancel(); - } - return false; - }, - }; - - let mut relevant_main_found = false; - loop { - match parser.parse_item() { - Ok(Some(item)) => match &item.kind { - // Tests with one of these items are ignored - ItemKind::Static(..) - | ItemKind::Const(..) - | ItemKind::ExternCrate(..) - | ItemKind::ForeignMod(..) => return false, - // We found a main function ... - ItemKind::Fn(_, sig, _, Some(block)) if item.ident.name == sym::main => { - let is_async = matches!(sig.header.asyncness, Async::Yes { .. }); - let returns_nothing = match &sig.decl.output { - FnRetTy::Default(..) => true, - FnRetTy::Ty(ty) if ty.kind.is_unit() => true, - _ => false, - }; - - if returns_nothing && !is_async && !block.stmts.is_empty() { - // This main function should be linted, but only if there are no other functions - relevant_main_found = true; - } else { - // This main function should not be linted, we're done - return false; + fn has_needless_main(cx: &LateContext<'_>, code: &str) -> bool { + rustc_driver::catch_fatal_errors(|| { + rustc_span::with_session_globals(cx.tcx.sess.edition(), || { + let filename = FileName::anon_source_code(code); + + let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); + let emitter = EmitterWriter::new(box io::sink(), None, false, false, false, None, false); + let handler = Handler::with_emitter(false, None, box emitter); + let sess = ParseSess::with_span_handler(handler, sm); + + let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code.into()) { + Ok(p) => p, + Err(errs) => { + for mut err in errs { + err.cancel(); } + return false; }, - // Another function was found; this case is ignored too - ItemKind::Fn(..) => return false, - _ => {}, - }, - Ok(None) => break, - Err(mut e) => { - e.cancel(); - return false; - }, - } - } + }; + + let mut relevant_main_found = false; + loop { + match parser.parse_item() { + Ok(Some(item)) => match &item.kind { + // Tests with one of these items are ignored + ItemKind::Static(..) + | ItemKind::Const(..) + | ItemKind::ExternCrate(..) + | ItemKind::ForeignMod(..) => return false, + // We found a main function ... + ItemKind::Fn(_, sig, _, Some(block)) if item.ident.name == sym::main => { + let is_async = matches!(sig.header.asyncness, Async::Yes { .. }); + let returns_nothing = match &sig.decl.output { + FnRetTy::Default(..) => true, + FnRetTy::Ty(ty) if ty.kind.is_unit() => true, + _ => false, + }; + + if returns_nothing && !is_async && !block.stmts.is_empty() { + // This main function should be linted, but only if there are no other functions + relevant_main_found = true; + } else { + // This main function should not be linted, we're done + return false; + } + }, + // Another function was found; this case is ignored too + ItemKind::Fn(..) => return false, + _ => {}, + }, + Ok(None) => break, + Err(mut e) => { + e.cancel(); + return false; + }, + } + } - relevant_main_found + relevant_main_found + }) + }) + .ok() + .unwrap_or_default() } - if has_needless_main(text) { + if has_needless_main(cx, text) { span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest"); } } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f06926fa91d..651b9e71131 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -27,6 +27,7 @@ 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; extern crate rustc_hir; extern crate rustc_hir_pretty; diff --git a/tests/ui/needless_doc_main.rs b/tests/ui/needless_doc_main.rs index 883683e08a2..52c84089fa8 100644 --- a/tests/ui/needless_doc_main.rs +++ b/tests/ui/needless_doc_main.rs @@ -128,6 +128,12 @@ fn bad_doctests() {} /// ``` fn no_false_positives() {} +/// Yields a parse error when interpreted as rust code: +/// ``` +/// r#"hi" +/// ``` +fn issue_6022() {} + fn main() { bad_doctests(); no_false_positives(); -- cgit 1.4.1-3-g733a5 From 41b5ebebfdc583d9e3702dd61c8d897999d1e7f5 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 16 Dec 2020 00:14:47 +0100 Subject: needless_doctest_main: add edition support --- clippy_lints/src/doc.rs | 26 ++++++++++++++++++-------- tests/ui/needless_doc_main.rs | 4 ++-- 2 files changed, 20 insertions(+), 10 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 77203a286dd..08134cc16c0 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -14,6 +14,7 @@ use rustc_middle::ty; use rustc_parse::maybe_new_parser_from_source_str; use rustc_session::parse::ParseSess; use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::edition::Edition; use rustc_span::source_map::{BytePos, FilePathMapping, MultiSpan, SourceMap, Span}; use rustc_span::{sym, FileName, Pos}; use std::io; @@ -377,7 +378,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs check_doc(cx, valid_idents, events, &spans) } -const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail", "edition2018"]; +const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"]; fn check_doc<'a, Events: Iterator, Range)>>( cx: &LateContext<'_>, @@ -400,13 +401,21 @@ fn check_doc<'a, Events: Iterator, Range { in_code = true; if let CodeBlockKind::Fenced(lang) = kind { - is_rust = - lang.is_empty() || !lang.contains("ignore") && lang.split(',').any(|i| RUST_CODE.contains(&i)); + let infos = lang.split(',').collect::>(); + is_rust = !infos.iter().any(|&i| i == "ignore") + && infos + .iter() + .any(|i| i.is_empty() || i.starts_with("edition") || RUST_CODE.contains(&i)); + edition = infos + .iter() + .find_map(|i| i.starts_with("edition").then(|| i[7..].parse::().ok())) + .flatten(); } }, End(CodeBlock(_)) => { @@ -436,7 +445,8 @@ fn check_doc<'a, Events: Iterator, Range, Range, text: &str, span: Span) { - fn has_needless_main(cx: &LateContext<'_>, code: &str) -> bool { +fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { + fn has_needless_main(code: &str, edition: Edition) -> bool { rustc_driver::catch_fatal_errors(|| { - rustc_span::with_session_globals(cx.tcx.sess.edition(), || { + rustc_span::with_session_globals(edition, || { let filename = FileName::anon_source_code(code); let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); @@ -516,7 +526,7 @@ fn check_code(cx: &LateContext<'_>, text: &str, span: Span) { .unwrap_or_default() } - if has_needless_main(cx, text) { + if has_needless_main(text, edition) { span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest"); } } diff --git a/tests/ui/needless_doc_main.rs b/tests/ui/needless_doc_main.rs index 52c84089fa8..83e9bbaa3af 100644 --- a/tests/ui/needless_doc_main.rs +++ b/tests/ui/needless_doc_main.rs @@ -10,7 +10,7 @@ /// ``` /// /// With an explicit return type it should lint too -/// ``` +/// ```edition2015 /// fn main() -> () { /// unimplemented!(); /// } @@ -39,7 +39,7 @@ fn bad_doctests() {} /// ``` /// /// This shouldn't lint either, because main is async: -/// ``` +/// ```edition2018 /// async fn main() { /// assert_eq!(42, ANSWER); /// } -- cgit 1.4.1-3-g733a5 From 1eb7608a2e7284eff2d0ba80705f1cf28a1117e5 Mon Sep 17 00:00:00 2001 From: Andrew Houts Date: Thu, 17 Dec 2020 21:09:55 -0600 Subject: make needless_update ignore non_exhaustive structs --- clippy_lints/src/needless_update.rs | 21 +++++++++++++++++++-- tests/ui/needless_update.rs | 11 +++++++++++ tests/ui/needless_update.stderr | 2 +- 3 files changed, 31 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/needless_update.rs b/clippy_lints/src/needless_update.rs index 98e9078094a..1e387b518c3 100644 --- a/clippy_lints/src/needless_update.rs +++ b/clippy_lints/src/needless_update.rs @@ -21,7 +21,14 @@ declare_clippy_lint! { /// # z: i32, /// # } /// # let zero_point = Point { x: 0, y: 0, z: 0 }; - /// + /// # + /// # #[non_exhaustive] + /// # struct Options { + /// # a: bool, + /// # b: i32, + /// # } + /// # let default_options = Options { a: false, b: 0 }; + /// # /// // Bad /// Point { /// x: 1, @@ -36,6 +43,14 @@ declare_clippy_lint! { /// y: 1, /// ..zero_point /// }; + /// + /// // this lint is not applied to structs marked with [non_exhaustive](https://doc.rust-lang.org/reference/attributes/type_system.html) + /// // Ok + /// Options { + /// a: true, + /// b: 321, + /// ..default_options + /// }; /// ``` pub NEEDLESS_UPDATE, complexity, @@ -49,7 +64,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessUpdate { if let ExprKind::Struct(_, ref fields, Some(ref base)) = expr.kind { let ty = cx.typeck_results().expr_ty(expr); if let ty::Adt(def, _) = ty.kind() { - if fields.len() == def.non_enum_variant().fields.len() { + if fields.len() == def.non_enum_variant().fields.len() + && !def.variants[0_usize.into()].is_field_list_non_exhaustive() + { span_lint( cx, NEEDLESS_UPDATE, diff --git a/tests/ui/needless_update.rs b/tests/ui/needless_update.rs index bfa005a19f9..b93ff048a62 100644 --- a/tests/ui/needless_update.rs +++ b/tests/ui/needless_update.rs @@ -6,9 +6,20 @@ struct S { pub b: i32, } +#[non_exhaustive] +struct T { + pub x: i32, + pub y: i32, +} + fn main() { let base = S { a: 0, b: 0 }; S { ..base }; // no error S { a: 1, ..base }; // no error S { a: 1, b: 1, ..base }; + + let base = T { x: 0, y: 0 }; + T { ..base }; // no error + T { x: 1, ..base }; // no error + T { x: 1, y: 1, ..base }; // no error } diff --git a/tests/ui/needless_update.stderr b/tests/ui/needless_update.stderr index 133c834880d..b154b3b306d 100644 --- a/tests/ui/needless_update.stderr +++ b/tests/ui/needless_update.stderr @@ -1,5 +1,5 @@ error: struct update has no effect, all the fields in the struct have already been specified - --> $DIR/needless_update.rs:13:23 + --> $DIR/needless_update.rs:19:23 | LL | S { a: 1, b: 1, ..base }; | ^^^^ -- cgit 1.4.1-3-g733a5 From 8ddf4ce87a1a0a50d015e40bf9cebede68f64d0a Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Sat, 19 Dec 2020 13:54:38 +0100 Subject: UI Tests: Separate suspicious_else_formatting tests --- tests/ui/formatting.rs | 87 +---------------------------- tests/ui/formatting.stderr | 89 +++--------------------------- tests/ui/suspicious_else_formatting.rs | 79 ++++++++++++++++++++++++++ tests/ui/suspicious_else_formatting.stderr | 77 ++++++++++++++++++++++++++ 4 files changed, 164 insertions(+), 168 deletions(-) create mode 100644 tests/ui/suspicious_else_formatting.rs create mode 100644 tests/ui/suspicious_else_formatting.stderr (limited to 'tests') diff --git a/tests/ui/formatting.rs b/tests/ui/formatting.rs index f54b3f2bfe2..0d14807ff1c 100644 --- a/tests/ui/formatting.rs +++ b/tests/ui/formatting.rs @@ -10,91 +10,6 @@ fn foo() -> bool { #[rustfmt::skip] fn main() { - // weird `else` formatting: - if foo() { - } { - } - - if foo() { - } if foo() { - } - - let _ = { // if as the last expression - let _ = 0; - - if foo() { - } if foo() { - } - else { - } - }; - - let _ = { // if in the middle of a block - if foo() { - } if foo() { - } - else { - } - - let _ = 0; - }; - - if foo() { - } else - { - } - - if foo() { - } - else - { - } - - if foo() { - } else - if foo() { // the span of the above error should continue here - } - - if foo() { - } - else - if foo() { // the span of the above error should continue here - } - - // those are ok: - if foo() { - } - { - } - - if foo() { - } else { - } - - if foo() { - } - else { - } - - if foo() { - } - if foo() { - } - - if foo() { - } else if foo() { - } - - if foo() { - } - else if foo() { - } - - if foo() { - } - else if - foo() {} - // weird op_eq formatting: let mut a = 42; a =- 35; @@ -146,7 +61,7 @@ fn main() { // don't lint if the indentation suggests not to let _ = &[ - 1 + 2, 3 + 1 + 2, 3 - 4, 5 ]; // lint if it doesn't diff --git a/tests/ui/formatting.stderr b/tests/ui/formatting.stderr index e2095cc125b..bde434c7e2e 100644 --- a/tests/ui/formatting.stderr +++ b/tests/ui/formatting.stderr @@ -1,80 +1,5 @@ -error: this looks like an `else {..}` but the `else` is missing - --> $DIR/formatting.rs:15:6 - | -LL | } { - | ^ - | - = note: `-D clippy::suspicious-else-formatting` implied by `-D warnings` - = note: to remove this lint, add the missing `else` or add a new line before the next block - -error: this looks like an `else if` but the `else` is missing - --> $DIR/formatting.rs:19:6 - | -LL | } if foo() { - | ^ - | - = note: to remove this lint, add the missing `else` or add a new line before the second `if` - -error: this looks like an `else if` but the `else` is missing - --> $DIR/formatting.rs:26:10 - | -LL | } if foo() { - | ^ - | - = note: to remove this lint, add the missing `else` or add a new line before the second `if` - -error: this looks like an `else if` but the `else` is missing - --> $DIR/formatting.rs:34:10 - | -LL | } if foo() { - | ^ - | - = note: to remove this lint, add the missing `else` or add a new line before the second `if` - -error: this is an `else {..}` but the formatting might hide it - --> $DIR/formatting.rs:43:6 - | -LL | } else - | ______^ -LL | | { - | |____^ - | - = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}` - -error: this is an `else {..}` but the formatting might hide it - --> $DIR/formatting.rs:48:6 - | -LL | } - | ______^ -LL | | else -LL | | { - | |____^ - | - = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}` - -error: this is an `else if` but the formatting might hide it - --> $DIR/formatting.rs:54:6 - | -LL | } else - | ______^ -LL | | if foo() { // the span of the above error should continue here - | |____^ - | - = note: to remove this lint, remove the `else` or remove the new line between `else` and `if` - -error: this is an `else if` but the formatting might hide it - --> $DIR/formatting.rs:59:6 - | -LL | } - | ______^ -LL | | else -LL | | if foo() { // the span of the above error should continue here - | |____^ - | - = note: to remove this lint, remove the `else` or remove the new line between `else` and `if` - error: this looks like you are trying to use `.. -= ..`, but you really are doing `.. = (- ..)` - --> $DIR/formatting.rs:100:6 + --> $DIR/formatting.rs:15:6 | LL | a =- 35; | ^^^^ @@ -83,7 +8,7 @@ LL | a =- 35; = note: to remove this lint, use either `-=` or `= -` error: this looks like you are trying to use `.. *= ..`, but you really are doing `.. = (* ..)` - --> $DIR/formatting.rs:101:6 + --> $DIR/formatting.rs:16:6 | LL | a =* &191; | ^^^^ @@ -91,7 +16,7 @@ LL | a =* &191; = note: to remove this lint, use either `*=` or `= *` error: this looks like you are trying to use `.. != ..`, but you really are doing `.. = (! ..)` - --> $DIR/formatting.rs:104:6 + --> $DIR/formatting.rs:19:6 | LL | b =! false; | ^^^^ @@ -99,7 +24,7 @@ LL | b =! false; = note: to remove this lint, use either `!=` or `= !` error: possibly missing a comma here - --> $DIR/formatting.rs:113:19 + --> $DIR/formatting.rs:28:19 | LL | -1, -2, -3 // <= no comma here | ^ @@ -108,7 +33,7 @@ LL | -1, -2, -3 // <= no comma here = note: to remove this lint, add a comma or write the expr in a single line error: possibly missing a comma here - --> $DIR/formatting.rs:117:19 + --> $DIR/formatting.rs:32:19 | LL | -1, -2, -3 // <= no comma here | ^ @@ -116,12 +41,12 @@ LL | -1, -2, -3 // <= no comma here = note: to remove this lint, add a comma or write the expr in a single line error: possibly missing a comma here - --> $DIR/formatting.rs:154:11 + --> $DIR/formatting.rs:69:11 | LL | -1 | ^ | = note: to remove this lint, add a comma or write the expr in a single line -error: aborting due to 14 previous errors +error: aborting due to 6 previous errors diff --git a/tests/ui/suspicious_else_formatting.rs b/tests/ui/suspicious_else_formatting.rs new file mode 100644 index 00000000000..226010ec6df --- /dev/null +++ b/tests/ui/suspicious_else_formatting.rs @@ -0,0 +1,79 @@ +#![warn(clippy::suspicious_else_formatting)] + +fn foo() -> bool { + true +} + +#[rustfmt::skip] +fn main() { + // weird `else` formatting: + if foo() { + } { + } + + if foo() { + } if foo() { + } + + let _ = { // if as the last expression + let _ = 0; + + if foo() { + } if foo() { + } + else { + } + }; + + let _ = { // if in the middle of a block + if foo() { + } if foo() { + } + else { + } + + let _ = 0; + }; + + if foo() { + } else + { + } + + if foo() { + } + else + { + } + + if foo() { + } else + if foo() { // the span of the above error should continue here + } + + if foo() { + } + else + if foo() { // the span of the above error should continue here + } + + // those are ok: + if foo() { + } + { + } + + if foo() { + } else { + } + + if foo() { + } + else { + } + + if foo() { + } + if foo() { + } +} diff --git a/tests/ui/suspicious_else_formatting.stderr b/tests/ui/suspicious_else_formatting.stderr new file mode 100644 index 00000000000..bbc036d376f --- /dev/null +++ b/tests/ui/suspicious_else_formatting.stderr @@ -0,0 +1,77 @@ +error: this looks like an `else {..}` but the `else` is missing + --> $DIR/suspicious_else_formatting.rs:11:6 + | +LL | } { + | ^ + | + = note: `-D clippy::suspicious-else-formatting` implied by `-D warnings` + = note: to remove this lint, add the missing `else` or add a new line before the next block + +error: this looks like an `else if` but the `else` is missing + --> $DIR/suspicious_else_formatting.rs:15:6 + | +LL | } if foo() { + | ^ + | + = note: to remove this lint, add the missing `else` or add a new line before the second `if` + +error: this looks like an `else if` but the `else` is missing + --> $DIR/suspicious_else_formatting.rs:22:10 + | +LL | } if foo() { + | ^ + | + = note: to remove this lint, add the missing `else` or add a new line before the second `if` + +error: this looks like an `else if` but the `else` is missing + --> $DIR/suspicious_else_formatting.rs:30:10 + | +LL | } if foo() { + | ^ + | + = note: to remove this lint, add the missing `else` or add a new line before the second `if` + +error: this is an `else {..}` but the formatting might hide it + --> $DIR/suspicious_else_formatting.rs:39:6 + | +LL | } else + | ______^ +LL | | { + | |____^ + | + = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}` + +error: this is an `else {..}` but the formatting might hide it + --> $DIR/suspicious_else_formatting.rs:44:6 + | +LL | } + | ______^ +LL | | else +LL | | { + | |____^ + | + = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}` + +error: this is an `else if` but the formatting might hide it + --> $DIR/suspicious_else_formatting.rs:50:6 + | +LL | } else + | ______^ +LL | | if foo() { // the span of the above error should continue here + | |____^ + | + = note: to remove this lint, remove the `else` or remove the new line between `else` and `if` + +error: this is an `else if` but the formatting might hide it + --> $DIR/suspicious_else_formatting.rs:55:6 + | +LL | } + | ______^ +LL | | else +LL | | if foo() { // the span of the above error should continue here + | |____^ + | + = note: to remove this lint, remove the `else` or remove the new line between `else` and `if` + +error: aborting due to 8 previous errors + -- cgit 1.4.1-3-g733a5 From a451b2af30c173b081f5d6150e558a65062a1dc8 Mon Sep 17 00:00:00 2001 From: Bastian Kersting Date: Sat, 19 Dec 2020 15:49:42 +0100 Subject: Added from_over_into lint --- CHANGELOG.md | 1 + clippy_lints/src/from_over_into.rs | 63 ++++++++++++++++++++++++++++++++++++++ clippy_lints/src/lib.rs | 5 +++ tests/ui/from_over_into.rs | 21 +++++++++++++ tests/ui/from_over_into.stderr | 15 +++++++++ tests/ui/unused_unit.fixed | 1 + tests/ui/unused_unit.rs | 1 + tests/ui/unused_unit.stderr | 38 +++++++++++------------ 8 files changed, 126 insertions(+), 19 deletions(-) create mode 100644 clippy_lints/src/from_over_into.rs create mode 100644 tests/ui/from_over_into.rs create mode 100644 tests/ui/from_over_into.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index af3b1c1db2a..de8da99cdee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1841,6 +1841,7 @@ Released 2018-09-13 [`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 [`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 [`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send [`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len [`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs new file mode 100644 index 00000000000..fe7120b0f9a --- /dev/null +++ b/clippy_lints/src/from_over_into.rs @@ -0,0 +1,63 @@ +use crate::utils::paths::INTO; +use crate::utils::{match_def_path, span_lint_and_help}; +use if_chain::if_chain; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Searches for implementations of the `Into<..>` trait and suggests to implement `From<..>` instead. + /// + /// **Why is this bad?** According the std docs implementing `From<..>` is preferred since it gives you `Into<..>` for free where the reverse isn't true. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// struct StringWrapper(String); + /// + /// impl Into for String { + /// fn into(self) -> StringWrapper { + /// StringWrapper(self) + /// } + /// } + /// ``` + /// Use instead: + /// ```rust + /// struct StringWrapper(String); + /// + /// impl From for StringWrapper { + /// fn from(s: String) -> StringWrapper { + /// StringWrapper(s) + /// } + /// } + /// ``` + pub FROM_OVER_INTO, + style, + "Warns on implementations of `Into<..>` to use `From<..>`" +} + +declare_lint_pass!(FromOverInto => [FROM_OVER_INTO]); + +impl LateLintPass<'_> for FromOverInto { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { + let impl_def_id = cx.tcx.hir().local_def_id(item.hir_id); + if_chain! { + if let hir::ItemKind::Impl{ .. } = &item.kind; + if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(impl_def_id); + if match_def_path(cx, impl_trait_ref.def_id, &INTO); + + then { + span_lint_and_help( + cx, + FROM_OVER_INTO, + item.span, + "An implementation of From is preferred since it gives you Into<..> for free where the reverse isn't true.", + None, + "consider implement From instead", + ); + } + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 02ba422a2f5..58587481922 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -207,6 +207,7 @@ mod float_literal; mod floating_point_arithmetic; mod format; mod formatting; +mod from_over_into; mod functions; mod future_not_send; mod get_last_with_len; @@ -614,6 +615,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING, &formatting::SUSPICIOUS_ELSE_FORMATTING, &formatting::SUSPICIOUS_UNARY_OP_FORMATTING, + &from_over_into::FROM_OVER_INTO, &functions::DOUBLE_MUST_USE, &functions::MUST_USE_CANDIDATE, &functions::MUST_USE_UNIT, @@ -1203,6 +1205,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box manual_unwrap_or::ManualUnwrapOr); store.register_late_pass(|| box manual_ok_or::ManualOkOr); store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); + store.register_late_pass(|| box from_over_into::FromOverInto); store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::>(); store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods)); @@ -1417,6 +1420,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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(&functions::DOUBLE_MUST_USE), LintId::of(&functions::MUST_USE_UNIT), LintId::of(&functions::NOT_UNSAFE_PTR_ARG_DEREF), @@ -1663,6 +1667,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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(&functions::DOUBLE_MUST_USE), LintId::of(&functions::MUST_USE_UNIT), LintId::of(&functions::RESULT_UNIT_ERR), diff --git a/tests/ui/from_over_into.rs b/tests/ui/from_over_into.rs new file mode 100644 index 00000000000..292d0924fb1 --- /dev/null +++ b/tests/ui/from_over_into.rs @@ -0,0 +1,21 @@ +#![warn(clippy::from_over_into)] + +// this should throw an error +struct StringWrapper(String); + +impl Into for String { + fn into(self) -> StringWrapper { + StringWrapper(self) + } +} + +// this is fine +struct A(String); + +impl From for A { + fn from(s: String) -> A { + A(s) + } +} + +fn main() {} diff --git a/tests/ui/from_over_into.stderr b/tests/ui/from_over_into.stderr new file mode 100644 index 00000000000..17f30fa837e --- /dev/null +++ b/tests/ui/from_over_into.stderr @@ -0,0 +1,15 @@ +error: An implementation of From is preferred since it gives you Into<..> for free where the reverse isn't true. + --> $DIR/from_over_into.rs:6:1 + | +LL | / impl Into for String { +LL | | fn into(self) -> StringWrapper { +LL | | StringWrapper(self) +LL | | } +LL | | } + | |_^ + | + = note: `-D clippy::from-over-into` implied by `-D warnings` + = help: consider implement From instead + +error: aborting due to previous error + diff --git a/tests/ui/unused_unit.fixed b/tests/ui/unused_unit.fixed index 7afc5361356..a192ebde3eb 100644 --- a/tests/ui/unused_unit.fixed +++ b/tests/ui/unused_unit.fixed @@ -11,6 +11,7 @@ #![deny(clippy::unused_unit)] #![allow(dead_code)] +#![allow(clippy::from_over_into)] struct Unitter; impl Unitter { diff --git a/tests/ui/unused_unit.rs b/tests/ui/unused_unit.rs index 96cef1ed5a5..96041a7dd85 100644 --- a/tests/ui/unused_unit.rs +++ b/tests/ui/unused_unit.rs @@ -11,6 +11,7 @@ #![deny(clippy::unused_unit)] #![allow(dead_code)] +#![allow(clippy::from_over_into)] struct Unitter; impl Unitter { diff --git a/tests/ui/unused_unit.stderr b/tests/ui/unused_unit.stderr index c45634c2b6d..02038b5fb6b 100644 --- a/tests/ui/unused_unit.stderr +++ b/tests/ui/unused_unit.stderr @@ -1,5 +1,5 @@ error: unneeded unit return type - --> $DIR/unused_unit.rs:18:28 + --> $DIR/unused_unit.rs:19:28 | LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () | ^^^^^^ help: remove the `-> ()` @@ -11,109 +11,109 @@ LL | #![deny(clippy::unused_unit)] | ^^^^^^^^^^^^^^^^^^^ error: unneeded unit return type - --> $DIR/unused_unit.rs:19:18 + --> $DIR/unused_unit.rs:20:18 | LL | where G: Fn() -> () { | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:18:58 + --> $DIR/unused_unit.rs:19:58 | LL | pub fn get_unit (), G>(&self, f: F, _g: G) -> () | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:20:26 + --> $DIR/unused_unit.rs:21:26 | LL | let _y: &dyn Fn() -> () = &f; | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:27:18 + --> $DIR/unused_unit.rs:28:18 | LL | fn into(self) -> () { | ^^^^^^ help: remove the `-> ()` error: unneeded unit expression - --> $DIR/unused_unit.rs:28:9 + --> $DIR/unused_unit.rs:29:9 | LL | () | ^^ help: remove the final `()` error: unneeded unit return type - --> $DIR/unused_unit.rs:33:29 + --> $DIR/unused_unit.rs:34:29 | LL | fn redundant (), G, H>(&self, _f: F, _g: G, _h: H) | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:35:19 + --> $DIR/unused_unit.rs:36:19 | LL | G: FnMut() -> (), | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:36:16 + --> $DIR/unused_unit.rs:37:16 | LL | H: Fn() -> (); | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:40:29 + --> $DIR/unused_unit.rs:41:29 | LL | fn redundant (), G, H>(&self, _f: F, _g: G, _h: H) | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:42:19 + --> $DIR/unused_unit.rs:43:19 | LL | G: FnMut() -> (), | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:43:16 + --> $DIR/unused_unit.rs:44:16 | LL | H: Fn() -> () {} | ^^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:46:17 + --> $DIR/unused_unit.rs:47:17 | LL | fn return_unit() -> () { () } | ^^^^^^ help: remove the `-> ()` error: unneeded unit expression - --> $DIR/unused_unit.rs:46:26 + --> $DIR/unused_unit.rs:47:26 | LL | fn return_unit() -> () { () } | ^^ help: remove the final `()` error: unneeded `()` - --> $DIR/unused_unit.rs:56:14 + --> $DIR/unused_unit.rs:57:14 | LL | break(); | ^^ help: remove the `()` error: unneeded `()` - --> $DIR/unused_unit.rs:58:11 + --> $DIR/unused_unit.rs:59:11 | LL | return(); | ^^ help: remove the `()` error: unneeded unit return type - --> $DIR/unused_unit.rs:75:10 + --> $DIR/unused_unit.rs:76:10 | LL | fn test()->(){} | ^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:78:11 + --> $DIR/unused_unit.rs:79:11 | LL | fn test2() ->(){} | ^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> $DIR/unused_unit.rs:81:11 + --> $DIR/unused_unit.rs:82:11 | LL | fn test3()-> (){} | ^^^^^ help: remove the `-> ()` -- cgit 1.4.1-3-g733a5 From dd005c17e72a48f8579cd26a7165c960b04f6aa3 Mon Sep 17 00:00:00 2001 From: Bastian Kersting Date: Sun, 20 Dec 2020 13:00:17 +0100 Subject: Added MSRV and fixed typo --- clippy_lints/src/from_over_into.rs | 30 +++++++++++++++++++++++++----- clippy_lints/src/lib.rs | 2 +- tests/ui/from_over_into.stderr | 2 +- tests/ui/min_rust_version_attr.rs | 8 ++++++++ tests/ui/min_rust_version_attr.stderr | 8 ++++---- 5 files changed, 39 insertions(+), 11 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index fe7120b0f9a..c7988d6f01f 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -1,9 +1,12 @@ use crate::utils::paths::INTO; -use crate::utils::{match_def_path, span_lint_and_help}; +use crate::utils::{match_def_path, meets_msrv, span_lint_and_help}; use if_chain::if_chain; use rustc_hir as hir; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +const FROM_OVER_INTO_MSRV: RustcVersion = RustcVersion::new(1, 41, 0); declare_clippy_lint! { /// **What it does:** Searches for implementations of the `Into<..>` trait and suggests to implement `From<..>` instead. @@ -38,10 +41,25 @@ declare_clippy_lint! { "Warns on implementations of `Into<..>` to use `From<..>`" } -declare_lint_pass!(FromOverInto => [FROM_OVER_INTO]); +pub struct FromOverInto { + msrv: Option, +} + +impl FromOverInto { + #[must_use] + pub fn new(msrv: Option) -> Self { + FromOverInto { msrv } + } +} + +impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]); impl LateLintPass<'_> for FromOverInto { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { + if !meets_msrv(self.msrv.as_ref(), &FROM_OVER_INTO_MSRV) { + return; + } + let impl_def_id = cx.tcx.hir().local_def_id(item.hir_id); if_chain! { if let hir::ItemKind::Impl{ .. } = &item.kind; @@ -55,9 +73,11 @@ impl LateLintPass<'_> for FromOverInto { item.span, "An implementation of From is preferred since it gives you Into<..> for free where the reverse isn't true.", None, - "consider implement From instead", + "consider to implement From instead", ); } } } + + extract_msrv_attr!(LateContext); } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 58587481922..35b057d7b6a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1016,6 +1016,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box checked_conversions::CheckedConversions::new(msrv)); store.register_late_pass(move || box mem_replace::MemReplace::new(msrv)); store.register_late_pass(move || box ranges::Ranges::new(msrv)); + store.register_late_pass(move || box from_over_into::FromOverInto::new(msrv)); store.register_late_pass(move || box use_self::UseSelf::new(msrv)); store.register_late_pass(move || box missing_const_for_fn::MissingConstForFn::new(msrv)); @@ -1205,7 +1206,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box manual_unwrap_or::ManualUnwrapOr); store.register_late_pass(|| box manual_ok_or::ManualOkOr); store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); - store.register_late_pass(|| box from_over_into::FromOverInto); store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::>(); store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods)); diff --git a/tests/ui/from_over_into.stderr b/tests/ui/from_over_into.stderr index 17f30fa837e..c9c8e7c4b53 100644 --- a/tests/ui/from_over_into.stderr +++ b/tests/ui/from_over_into.stderr @@ -9,7 +9,7 @@ LL | | } | |_^ | = note: `-D clippy::from-over-into` implied by `-D warnings` - = help: consider implement From instead + = help: consider to implement From instead error: aborting due to previous error diff --git a/tests/ui/min_rust_version_attr.rs b/tests/ui/min_rust_version_attr.rs index 3848bca3207..0f47f1cbc40 100644 --- a/tests/ui/min_rust_version_attr.rs +++ b/tests/ui/min_rust_version_attr.rs @@ -57,6 +57,14 @@ pub fn checked_conversion() { let _ = value <= (u32::MAX as i64) && value >= 0; } +pub struct FromOverInto(String); + +impl Into for String { + fn into(self) -> FromOverInto { + FromOverInto(self) + } +} + pub fn filter_map_next() { let a = ["1", "lol", "3", "NaN", "5"]; diff --git a/tests/ui/min_rust_version_attr.stderr b/tests/ui/min_rust_version_attr.stderr index 34805263104..e3e3b335cbe 100644 --- a/tests/ui/min_rust_version_attr.stderr +++ b/tests/ui/min_rust_version_attr.stderr @@ -1,12 +1,12 @@ error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:142:24 + --> $DIR/min_rust_version_attr.rs:150:24 | LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); | ^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::manual-strip` implied by `-D warnings` note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:141:9 + --> $DIR/min_rust_version_attr.rs:149:9 | LL | if s.starts_with("hello, ") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,13 +17,13 @@ LL | assert_eq!(.to_uppercase(), "WORLD!"); | error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:154:24 + --> $DIR/min_rust_version_attr.rs:162:24 | LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); | ^^^^^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:153:9 + --> $DIR/min_rust_version_attr.rs:161:9 | LL | if s.starts_with("hello, ") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From 7e641c8be774bba047d7d9ee57455957fd4e2a3b Mon Sep 17 00:00:00 2001 From: Bastian Kersting Date: Sun, 20 Dec 2020 20:10:00 +0100 Subject: Fixed error messages --- clippy_lints/src/from_over_into.rs | 4 ++-- tests/ui/from_over_into.stderr | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index c7988d6f01f..083101ab1c6 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -71,9 +71,9 @@ impl LateLintPass<'_> for FromOverInto { cx, FROM_OVER_INTO, item.span, - "An implementation of From is preferred since it gives you Into<..> for free where the reverse isn't true.", + "An implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true.", None, - "consider to implement From instead", + "consider to implement `From` instead", ); } } diff --git a/tests/ui/from_over_into.stderr b/tests/ui/from_over_into.stderr index c9c8e7c4b53..0825d47add2 100644 --- a/tests/ui/from_over_into.stderr +++ b/tests/ui/from_over_into.stderr @@ -1,4 +1,4 @@ -error: An implementation of From is preferred since it gives you Into<..> for free where the reverse isn't true. +error: An implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true. --> $DIR/from_over_into.rs:6:1 | LL | / impl Into for String { @@ -9,7 +9,7 @@ LL | | } | |_^ | = note: `-D clippy::from-over-into` implied by `-D warnings` - = help: consider to implement From instead + = help: consider to implement `From` instead error: aborting due to previous error -- cgit 1.4.1-3-g733a5 From 12bd244caa4547b4b84108d892e85bf953b1a408 Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Mon, 21 Dec 2020 11:09:49 +0100 Subject: Don't trigger large_enum_variant in external macros --- clippy_lints/src/large_enum_variant.rs | 4 ++++ tests/ui/auxiliary/macro_rules.rs | 10 ++++++++++ tests/ui/large_enum_variant.rs | 9 ++++++++- tests/ui/large_enum_variant.stderr | 18 +++++++++--------- 4 files changed, 31 insertions(+), 10 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/large_enum_variant.rs b/clippy_lints/src/large_enum_variant.rs index 3c7880d74ee..ad9b4f357a7 100644 --- a/clippy_lints/src/large_enum_variant.rs +++ b/clippy_lints/src/large_enum_variant.rs @@ -4,6 +4,7 @@ use crate::utils::{snippet_opt, span_lint_and_then}; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind, VariantData}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::lint::in_external_macro; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_target::abi::LayoutOf; @@ -58,6 +59,9 @@ impl_lint_pass!(LargeEnumVariant => [LARGE_ENUM_VARIANT]); impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + if in_external_macro(cx.tcx.sess, item.span) { + return; + } let did = cx.tcx.hir().local_def_id(item.hir_id); if let ItemKind::Enum(ref def, _) = item.kind { let ty = cx.tcx.type_of(did); diff --git a/tests/ui/auxiliary/macro_rules.rs b/tests/ui/auxiliary/macro_rules.rs index f985a15eda2..18324823468 100644 --- a/tests/ui/auxiliary/macro_rules.rs +++ b/tests/ui/auxiliary/macro_rules.rs @@ -84,3 +84,13 @@ macro_rules! as_conv { 0u32 as u64 }; } + +#[macro_export] +macro_rules! large_enum_variant { + () => { + enum LargeEnumInMacro { + A(i32), + B([i32; 8000]), + } + }; +} diff --git a/tests/ui/large_enum_variant.rs b/tests/ui/large_enum_variant.rs index 852ef5fec0e..d22fee3f27b 100644 --- a/tests/ui/large_enum_variant.rs +++ b/tests/ui/large_enum_variant.rs @@ -1,7 +1,12 @@ +// aux-build:macro_rules.rs + #![allow(dead_code)] #![allow(unused_variables)] #![warn(clippy::large_enum_variant)] +#[macro_use] +extern crate macro_rules; + enum LargeEnum { A(i32), B([i32; 8000]), @@ -51,4 +56,6 @@ enum LargeEnumOk { LargeB([i32; 8001]), } -fn main() {} +fn main() { + large_enum_variant!(); +} diff --git a/tests/ui/large_enum_variant.stderr b/tests/ui/large_enum_variant.stderr index 8ce641a81f2..d39a4d462aa 100644 --- a/tests/ui/large_enum_variant.stderr +++ b/tests/ui/large_enum_variant.stderr @@ -1,12 +1,12 @@ error: large size difference between variants - --> $DIR/large_enum_variant.rs:7:5 + --> $DIR/large_enum_variant.rs:12:5 | LL | B([i32; 8000]), | ^^^^^^^^^^^^^^ this variant is 32000 bytes | = note: `-D clippy::large-enum-variant` implied by `-D warnings` note: and the second-largest variant is 4 bytes: - --> $DIR/large_enum_variant.rs:6:5 + --> $DIR/large_enum_variant.rs:11:5 | LL | A(i32), | ^^^^^^ @@ -16,13 +16,13 @@ LL | B(Box<[i32; 8000]>), | ^^^^^^^^^^^^^^^^ error: large size difference between variants - --> $DIR/large_enum_variant.rs:31:5 + --> $DIR/large_enum_variant.rs:36:5 | LL | ContainingLargeEnum(LargeEnum), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 32004 bytes | note: and the second-largest variant is 8 bytes: - --> $DIR/large_enum_variant.rs:30:5 + --> $DIR/large_enum_variant.rs:35:5 | LL | VariantOk(i32, u32), | ^^^^^^^^^^^^^^^^^^^ @@ -32,30 +32,30 @@ LL | ContainingLargeEnum(Box), | ^^^^^^^^^^^^^^ error: large size difference between variants - --> $DIR/large_enum_variant.rs:41:5 + --> $DIR/large_enum_variant.rs:46: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:40:5 + --> $DIR/large_enum_variant.rs:45:5 | LL | VariantOk(i32, u32), | ^^^^^^^^^^^^^^^^^^^ help: consider boxing the large fields to reduce the total size of the enum - --> $DIR/large_enum_variant.rs:41:5 + --> $DIR/large_enum_variant.rs:46:5 | LL | StructLikeLarge { x: [i32; 8000], y: i32 }, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: large size difference between variants - --> $DIR/large_enum_variant.rs:46:5 + --> $DIR/large_enum_variant.rs:51: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:45:5 + --> $DIR/large_enum_variant.rs:50:5 | LL | VariantOk(i32, u32), | ^^^^^^^^^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From 53f4d437f4c448c4382a99c2b7ff03f104cf7368 Mon Sep 17 00:00:00 2001 From: Bastian Kersting <45260993+1c3t3a@users.noreply.github.com> Date: Mon, 21 Dec 2020 17:15:05 +0100 Subject: Update from_over_into.stderr --- tests/ui/from_over_into.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/ui/from_over_into.stderr b/tests/ui/from_over_into.stderr index 0825d47add2..18f56f85432 100644 --- a/tests/ui/from_over_into.stderr +++ b/tests/ui/from_over_into.stderr @@ -1,4 +1,4 @@ -error: An implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true. +error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true --> $DIR/from_over_into.rs:6:1 | LL | / impl Into for String { -- cgit 1.4.1-3-g733a5 From 8bdf34e10cfa47b6e0cfccd5d0f8e6a5c079bc30 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 23 Nov 2020 12:26:35 -0600 Subject: Fix default initialized fields in suggestion The default value for a field type does not necessarily match the default value for that field in the struct Default. --- clippy_lints/src/default.rs | 6 ------ tests/ui/field_reassign_with_default.stderr | 4 ++-- 2 files changed, 2 insertions(+), 8 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index f69f6f1412a..adcc17266d2 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -165,12 +165,6 @@ impl LateLintPass<'_> for Default { let stmt = &block.stmts[stmt_idx]; if let StmtKind::Local(preceding_local) = &stmt.kind { - // filter out fields like `= Default::default()`, because the FRU already covers them - let assigned_fields = assigned_fields - .into_iter() - .filter(|(_, rhs)| !is_expr_default(rhs, cx)) - .collect::)>>(); - // if all fields of the struct are not assigned, add `.. Default::default()` to the suggestion. let ext_with_default = !fields_of_type(binding_type) .iter() diff --git a/tests/ui/field_reassign_with_default.stderr b/tests/ui/field_reassign_with_default.stderr index c788ebae552..9a2bc778c3f 100644 --- a/tests/ui/field_reassign_with_default.stderr +++ b/tests/ui/field_reassign_with_default.stderr @@ -53,7 +53,7 @@ error: field assignment outside of initializer for an instance created with Defa LL | a.i = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: consider initializing the variable with `A::default()` and removing relevant reassignments +note: consider initializing the variable with `A { i: Default::default(), ..Default::default() }` and removing relevant reassignments --> $DIR/field_reassign_with_default.rs:90:5 | LL | let mut a: A = Default::default(); @@ -65,7 +65,7 @@ error: field assignment outside of initializer for an instance created with Defa LL | a.i = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: consider initializing the variable with `A { j: 45, ..Default::default() }` and removing relevant reassignments +note: consider initializing the variable with `A { i: Default::default(), j: 45 }` and removing relevant reassignments --> $DIR/field_reassign_with_default.rs:94:5 | LL | let mut a: A = Default::default(); -- cgit 1.4.1-3-g733a5 From c6450c70ddb5354bc2218bff20a325ded9682613 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 23 Nov 2020 09:23:24 -0600 Subject: Fix field_reassign_with_default for private fields --- clippy_lints/src/default.rs | 162 ++++++++++++++------------------ tests/ui/field_reassign_with_default.rs | 12 +++ 2 files changed, 81 insertions(+), 93 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index c3fe77f6250..b0d7c7b3baa 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -6,7 +6,7 @@ use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, Adt, Ty}; +use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::{Ident, Symbol}; use rustc_span::Span; @@ -103,18 +103,41 @@ impl LateLintPass<'_> for Default { } fn check_block<'tcx>(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) { - // find all binding statements like `let mut _ = T::default()` where `T::default()` is the - // `default` method of the `Default` trait, and store statement index in current block being - // checked and the name of the bound variable - let binding_statements_using_default = enumerate_bindings_using_default(cx, block); - // start from the `let mut _ = _::default();` and look at all the following // statements, see if they re-assign the fields of the binding - for (stmt_idx, binding_name, binding_type, span) in binding_statements_using_default { - // the last statement of a block cannot trigger the lint - if stmt_idx == block.stmts.len() - 1 { - break; - } + let stmts_head = match block.stmts { + // Skip the last statement since there cannot possibly be any following statements that re-assign fields. + [head @ .., _] if !head.is_empty() => head, + _ => return, + }; + for (stmt_idx, stmt) in stmts_head.iter().enumerate() { + // find all binding statements like `let mut _ = T::default()` where `T::default()` is the + // `default` method of the `Default` trait, and store statement index in current block being + // checked and the name of the bound variable + let (local, variant, binding_name, binding_type, span) = if_chain! { + // only take `let ...` statements + if let StmtKind::Local(local) = stmt.kind; + if let Some(expr) = local.init; + // only take bindings to identifiers + if let PatKind::Binding(_, binding_id, ident, _) = local.pat.kind; + // only when assigning `... = Default::default()` + if is_expr_default(expr, cx); + let binding_type = cx.typeck_results().node_type(binding_id); + if let Some(adt) = binding_type.ty_adt_def(); + if adt.is_struct(); + let variant = adt.non_enum_variant(); + if adt.did.is_local() || !variant.is_field_list_non_exhaustive(); + let module_did = cx.tcx.parent_module(stmt.hir_id).to_def_id(); + if variant + .fields + .iter() + .all(|field| field.vis.is_accessible_from(module_did, cx.tcx)); + then { + (local, variant, ident.name, binding_type, expr.span) + } else { + continue; + } + }; // find all "later statement"'s where the fields of the binding set as // Default::default() get reassigned, unless the reassignment refers to the original binding @@ -154,49 +177,45 @@ impl LateLintPass<'_> for Default { // if there are incorrectly assigned fields, do a span_lint_and_note to suggest // construction using `Ty { fields, ..Default::default() }` if !assigned_fields.is_empty() && !cancel_lint { - // take the original assignment as span - let stmt = &block.stmts[stmt_idx]; - - if let StmtKind::Local(preceding_local) = &stmt.kind { - // if all fields of the struct are not assigned, add `.. Default::default()` to the suggestion. - let ext_with_default = !fields_of_type(binding_type) - .iter() - .all(|field| assigned_fields.iter().any(|(a, _)| a == &field.name)); + // if all fields of the struct are not assigned, add `.. Default::default()` to the suggestion. + let ext_with_default = !variant + .fields + .iter() + .all(|field| assigned_fields.iter().any(|(a, _)| a == &field.ident.name)); - let field_list = assigned_fields - .into_iter() - .map(|(field, rhs)| { - // extract and store the assigned value for help message - let value_snippet = snippet(cx, rhs.span, ".."); - format!("{}: {}", field, value_snippet) - }) - .collect::>() - .join(", "); + let field_list = assigned_fields + .into_iter() + .map(|(field, rhs)| { + // extract and store the assigned value for help message + let value_snippet = snippet(cx, rhs.span, ".."); + format!("{}: {}", field, value_snippet) + }) + .collect::>() + .join(", "); - let sugg = if ext_with_default { - if field_list.is_empty() { - format!("{}::default()", binding_type) - } else { - format!("{} {{ {}, ..Default::default() }}", binding_type, field_list) - } + let sugg = if ext_with_default { + if field_list.is_empty() { + format!("{}::default()", binding_type) } else { - format!("{} {{ {} }}", binding_type, field_list) - }; + format!("{} {{ {}, ..Default::default() }}", binding_type, field_list) + } + } else { + format!("{} {{ {} }}", binding_type, field_list) + }; - // span lint once per statement that binds default - span_lint_and_note( - cx, - FIELD_REASSIGN_WITH_DEFAULT, - first_assign.unwrap().span, - "field assignment outside of initializer for an instance created with Default::default()", - Some(preceding_local.span), - &format!( - "consider initializing the variable with `{}` and removing relevant reassignments", - sugg - ), - ); - self.reassigned_linted.insert(span); - } + // span lint once per statement that binds default + span_lint_and_note( + cx, + FIELD_REASSIGN_WITH_DEFAULT, + first_assign.unwrap().span, + "field assignment outside of initializer for an instance created with Default::default()", + Some(local.span), + &format!( + "consider initializing the variable with `{}` and removing relevant reassignments", + sugg + ), + ); + self.reassigned_linted.insert(span); } } } @@ -217,38 +236,6 @@ fn is_expr_default<'tcx>(expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> bool } } -/// Returns the block indices, identifiers and types of bindings set as `Default::default()`, except -/// for when the pattern type is a tuple. -fn enumerate_bindings_using_default<'tcx>( - cx: &LateContext<'tcx>, - block: &Block<'tcx>, -) -> Vec<(usize, Symbol, Ty<'tcx>, Span)> { - block - .stmts - .iter() - .enumerate() - .filter_map(|(idx, stmt)| { - if_chain! { - // only take `let ...` statements - if let StmtKind::Local(ref local) = stmt.kind; - // only take bindings to identifiers - if let PatKind::Binding(_, _, ident, _) = local.pat.kind; - // that are not tuples - let ty = cx.typeck_results().pat_ty(local.pat); - if !matches!(ty.kind(), ty::Tuple(_)); - // only when assigning `... = Default::default()` - if let Some(ref expr) = local.init; - if is_expr_default(expr, cx); - then { - Some((idx, ident.name, ty, expr.span)) - } else { - None - } - } - }) - .collect() -} - /// Returns the reassigned field and the assigning expression (right-hand side of assign). fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Option<(Ident, &'tcx Expr<'tcx>)> { if_chain! { @@ -268,14 +255,3 @@ fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Op } } } - -/// Returns the vec of fields for a struct and an empty vec for non-struct ADTs. -fn fields_of_type(ty: Ty<'_>) -> Vec { - if let Adt(adt, _) = ty.kind() { - if adt.is_struct() { - let variant = &adt.non_enum_variant(); - return variant.fields.iter().map(|f| f.ident).collect(); - } - } - vec![] -} diff --git a/tests/ui/field_reassign_with_default.rs b/tests/ui/field_reassign_with_default.rs index 79a30c22f95..3e0921022b4 100644 --- a/tests/ui/field_reassign_with_default.rs +++ b/tests/ui/field_reassign_with_default.rs @@ -107,4 +107,16 @@ fn main() { x.i = side_effect.next(); x.j = 2; x.i = side_effect.next(); + + // don't lint - some private fields + let mut x = m::F::default(); + x.a = 1; +} + +mod m { + #[derive(Default)] + pub struct F { + pub a: u64, + b: u64, + } } -- cgit 1.4.1-3-g733a5 From 7fa1d78c89f74c73d645901d6ee4728bcd6a72bf Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Tue, 22 Dec 2020 19:17:59 +0100 Subject: Revert "Pass Clippy args also trough RUSTFLAGS" --- Cargo.toml | 1 + README.md | 1 + src/driver.rs | 116 ++++++++++++++----------------------------------------- src/main.rs | 98 +++++++--------------------------------------- tests/dogfood.rs | 2 +- 5 files changed, 47 insertions(+), 171 deletions(-) (limited to 'tests') diff --git a/Cargo.toml b/Cargo.toml index 7f9d22e594b..a765390c603 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ publish = false [[bin]] name = "cargo-clippy" +test = false path = "src/main.rs" [[bin]] diff --git a/README.md b/README.md index 74719f02fe0..a4928e17e6a 100644 --- a/README.md +++ b/README.md @@ -185,6 +185,7 @@ the lint(s) you are interested in: ```terminal cargo clippy -- -A clippy::all -W clippy::useless_format -W clippy::... ``` +Note that if you've run clippy before, this may only take effect after you've modified a file or ran `cargo clean`. ### Specifying the minimum supported Rust version diff --git a/src/driver.rs b/src/driver.rs index 40f1b802e60..e490ee54c0b 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -1,6 +1,5 @@ #![feature(rustc_private)] #![feature(once_cell)] -#![feature(bool_to_option)] #![cfg_attr(feature = "deny-warnings", deny(warnings))] // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] @@ -20,7 +19,6 @@ use rustc_tools_util::VersionInfo; use std::borrow::Cow; use std::env; -use std::iter; use std::lazy::SyncLazy; use std::ops::Deref; use std::panic; @@ -49,6 +47,20 @@ fn arg_value<'a, T: Deref>( None } +#[test] +fn test_arg_value() { + let args = &["--bar=bar", "--foobar", "123", "--foo"]; + + assert_eq!(arg_value(&[] as &[&str], "--foobar", |_| true), None); + assert_eq!(arg_value(args, "--bar", |_| false), None); + assert_eq!(arg_value(args, "--bar", |_| true), Some("bar")); + assert_eq!(arg_value(args, "--bar", |p| p == "bar"), Some("bar")); + assert_eq!(arg_value(args, "--bar", |p| p == "foo"), None); + assert_eq!(arg_value(args, "--foobar", |p| p == "foo"), None); + assert_eq!(arg_value(args, "--foobar", |p| p == "123"), Some("123")); + assert_eq!(arg_value(args, "--foo", |_| true), None); +} + struct DefaultCallbacks; impl rustc_driver::Callbacks for DefaultCallbacks {} @@ -170,28 +182,6 @@ fn toolchain_path(home: Option, toolchain: Option) -> Option(args: &mut Vec, clippy_args: I) -where - T: AsRef, - U: AsRef + ?Sized + 'a, - I: Iterator + Clone, -{ - let args_iter = clippy_args.map(AsRef::as_ref); - let args_count = args_iter.clone().count(); - - if args_count > 0 { - if let Some(start) = args.windows(args_count).enumerate().find_map(|(current, window)| { - window - .iter() - .map(AsRef::as_ref) - .eq(args_iter.clone()) - .then_some(current) - }) { - args.drain(start..start + args_count); - } - } -} - #[allow(clippy::too_many_lines)] pub fn main() { rustc_driver::init_rustc_env_logger(); @@ -288,9 +278,20 @@ pub fn main() { args.extend(vec!["--sysroot".into(), sys_root]); }; - let clippy_args = env::var("CLIPPY_ARGS").unwrap_or_default(); - let clippy_args = clippy_args.split_whitespace(); - let no_deps = clippy_args.clone().any(|flag| flag == "--no-deps"); + let mut no_deps = false; + let clippy_args = env::var("CLIPPY_ARGS") + .unwrap_or_default() + .split("__CLIPPY_HACKERY__") + .filter_map(|s| match s { + "" => None, + "--no-deps" => { + no_deps = true; + None + }, + _ => Some(s.to_string()), + }) + .chain(vec!["--cfg".into(), r#"feature="cargo-clippy""#.into()]) + .collect::>(); // We enable Clippy if one of the following conditions is met // - IF Clippy is run on its test suite OR @@ -303,11 +304,7 @@ pub fn main() { let clippy_enabled = clippy_tests_set || (!cap_lints_allow && (!no_deps || in_primary_package)); if clippy_enabled { - remove_clippy_args(&mut args, iter::once("--no-deps")); - args.extend(vec!["--cfg".into(), r#"feature="cargo-clippy""#.into()]); - } else { - // Remove all flags passed through RUSTFLAGS if Clippy is not enabled. - remove_clippy_args(&mut args, clippy_args); + args.extend(clippy_args); } let mut clippy = ClippyCallbacks; @@ -318,58 +315,3 @@ pub fn main() { rustc_driver::RunCompiler::new(&args, callbacks).run() })) } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_arg_value() { - let args = &["--bar=bar", "--foobar", "123", "--foo"]; - - assert_eq!(arg_value(&[] as &[&str], "--foobar", |_| true), None); - assert_eq!(arg_value(args, "--bar", |_| false), None); - assert_eq!(arg_value(args, "--bar", |_| true), Some("bar")); - assert_eq!(arg_value(args, "--bar", |p| p == "bar"), Some("bar")); - assert_eq!(arg_value(args, "--bar", |p| p == "foo"), None); - assert_eq!(arg_value(args, "--foobar", |p| p == "foo"), None); - assert_eq!(arg_value(args, "--foobar", |p| p == "123"), Some("123")); - assert_eq!(arg_value(args, "--foo", |_| true), None); - } - - #[test] - fn removes_clippy_args_from_start() { - let mut args = vec!["-D", "clippy::await_holding_lock", "--cfg", r#"feature="some_feat""#]; - let clippy_args = ["-D", "clippy::await_holding_lock"].iter(); - - remove_clippy_args(&mut args, clippy_args); - assert_eq!(args, &["--cfg", r#"feature="some_feat""#]); - } - - #[test] - fn removes_clippy_args_from_end() { - let mut args = vec!["-Zui-testing", "-A", "clippy::empty_loop", "--no-deps"]; - let clippy_args = ["-A", "clippy::empty_loop", "--no-deps"].iter(); - - remove_clippy_args(&mut args, clippy_args); - assert_eq!(args, &["-Zui-testing"]); - } - - #[test] - fn removes_clippy_args_from_middle() { - let mut args = vec!["-Zui-testing", "-W", "clippy::filter_map", "-L", "serde"]; - let clippy_args = ["-W", "clippy::filter_map"].iter(); - - remove_clippy_args(&mut args, clippy_args); - assert_eq!(args, &["-Zui-testing", "-L", "serde"]); - } - - #[test] - fn no_clippy_args_to_remove() { - let mut args = vec!["-Zui-testing", "-L", "serde"]; - let clippy_args: [&str; 0] = []; - - remove_clippy_args(&mut args, clippy_args.iter()); - assert_eq!(args, &["-Zui-testing", "-L", "serde"]); - } -} diff --git a/src/main.rs b/src/main.rs index 1c0e04689a9..ea06743394d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,3 @@ -#![feature(bool_to_option)] -#![feature(command_access)] #![cfg_attr(feature = "deny-warnings", deny(warnings))] // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] @@ -64,7 +62,7 @@ struct ClippyCmd { unstable_options: bool, cargo_subcommand: &'static str, args: Vec, - clippy_args: Option, + clippy_args: Vec, } impl ClippyCmd { @@ -101,17 +99,16 @@ impl ClippyCmd { args.insert(0, "+nightly".to_string()); } - let mut clippy_args = old_args.collect::>().join(" "); - if cargo_subcommand == "fix" && !clippy_args.contains("--no-deps") { - clippy_args = format!("{} --no-deps", clippy_args); + let mut clippy_args: Vec = old_args.collect(); + if cargo_subcommand == "fix" && !clippy_args.iter().any(|arg| arg == "--no-deps") { + clippy_args.push("--no-deps".into()); } - let has_args = !clippy_args.is_empty(); ClippyCmd { unstable_options, cargo_subcommand, args, - clippy_args: has_args.then_some(clippy_args), + clippy_args, } } @@ -151,24 +148,20 @@ impl ClippyCmd { .map(|p| ("CARGO_TARGET_DIR", p)) } - fn into_std_cmd(self, rustflags: Option) -> Command { + fn into_std_cmd(self) -> Command { let mut cmd = Command::new("cargo"); + let clippy_args: String = self + .clippy_args + .iter() + .map(|arg| format!("{}__CLIPPY_HACKERY__", arg)) + .collect(); cmd.env(self.path_env(), Self::path()) .envs(ClippyCmd::target_dir()) + .env("CLIPPY_ARGS", clippy_args) .arg(self.cargo_subcommand) .args(&self.args); - // HACK: pass Clippy args to the driver *also* through RUSTFLAGS. - // This guarantees that new builds will be triggered when Clippy flags change. - if let Some(clippy_args) = self.clippy_args { - cmd.env( - "RUSTFLAGS", - rustflags.map_or(clippy_args.clone(), |flags| format!("{} {}", clippy_args, flags)), - ); - cmd.env("CLIPPY_ARGS", clippy_args); - } - cmd } } @@ -179,7 +172,7 @@ where { let cmd = ClippyCmd::new(old_args); - let mut cmd = cmd.into_std_cmd(env::var("RUSTFLAGS").ok()); + let mut cmd = cmd.into_std_cmd(); let exit_status = cmd .spawn() @@ -197,7 +190,6 @@ where #[cfg(test)] mod tests { use super::ClippyCmd; - use std::ffi::OsStr; #[test] #[should_panic] @@ -212,7 +204,6 @@ mod tests { .split_whitespace() .map(ToString::to_string); let cmd = ClippyCmd::new(args); - assert_eq!("fix", cmd.cargo_subcommand); assert_eq!("RUSTC_WORKSPACE_WRAPPER", cmd.path_env()); assert!(cmd.args.iter().any(|arg| arg.ends_with("unstable-options"))); @@ -224,8 +215,7 @@ mod tests { .split_whitespace() .map(ToString::to_string); let cmd = ClippyCmd::new(args); - - assert!(cmd.clippy_args.unwrap().contains("--no-deps")); + assert!(cmd.clippy_args.iter().any(|arg| arg == "--no-deps")); } #[test] @@ -234,15 +224,13 @@ mod tests { .split_whitespace() .map(ToString::to_string); let cmd = ClippyCmd::new(args); - - assert_eq!(1, cmd.clippy_args.unwrap().matches("--no-deps").count()); + assert_eq!(cmd.clippy_args.iter().filter(|arg| *arg == "--no-deps").count(), 1); } #[test] fn check() { let args = "cargo clippy".split_whitespace().map(ToString::to_string); let cmd = ClippyCmd::new(args); - assert_eq!("check", cmd.cargo_subcommand); assert_eq!("RUSTC_WRAPPER", cmd.path_env()); } @@ -253,63 +241,7 @@ mod tests { .split_whitespace() .map(ToString::to_string); let cmd = ClippyCmd::new(args); - assert_eq!("check", cmd.cargo_subcommand); assert_eq!("RUSTC_WORKSPACE_WRAPPER", cmd.path_env()); } - - #[test] - fn clippy_args_into_rustflags() { - let args = "cargo clippy -- -W clippy::as_conversions" - .split_whitespace() - .map(ToString::to_string); - let cmd = ClippyCmd::new(args); - - let rustflags = None; - let cmd = cmd.into_std_cmd(rustflags); - - assert!(cmd - .get_envs() - .any(|(key, val)| key == "RUSTFLAGS" && val == Some(OsStr::new("-W clippy::as_conversions")))); - } - - #[test] - fn clippy_args_respect_existing_rustflags() { - let args = "cargo clippy -- -D clippy::await_holding_lock" - .split_whitespace() - .map(ToString::to_string); - let cmd = ClippyCmd::new(args); - - let rustflags = Some(r#"--cfg feature="some_feat""#.into()); - let cmd = cmd.into_std_cmd(rustflags); - - assert!(cmd.get_envs().any(|(key, val)| key == "RUSTFLAGS" - && val == Some(OsStr::new(r#"-D clippy::await_holding_lock --cfg feature="some_feat""#)))); - } - - #[test] - fn no_env_change_if_no_clippy_args() { - let args = "cargo clippy".split_whitespace().map(ToString::to_string); - let cmd = ClippyCmd::new(args); - - let rustflags = Some(r#"--cfg feature="some_feat""#.into()); - let cmd = cmd.into_std_cmd(rustflags); - - assert!(!cmd - .get_envs() - .any(|(key, _)| key == "RUSTFLAGS" || key == "CLIPPY_ARGS")); - } - - #[test] - fn no_env_change_if_no_clippy_args_nor_rustflags() { - let args = "cargo clippy".split_whitespace().map(ToString::to_string); - let cmd = ClippyCmd::new(args); - - let rustflags = None; - let cmd = cmd.into_std_cmd(rustflags); - - assert!(!cmd - .get_envs() - .any(|(key, _)| key == "RUSTFLAGS" || key == "CLIPPY_ARGS")) - } } diff --git a/tests/dogfood.rs b/tests/dogfood.rs index fda1413868e..052223d6d6f 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -23,7 +23,7 @@ fn dogfood_clippy() { .current_dir(root_dir) .env("CLIPPY_DOGFOOD", "1") .env("CARGO_INCREMENTAL", "0") - .arg("clippy") + .arg("clippy-preview") .arg("--all-targets") .arg("--all-features") .arg("--") -- cgit 1.4.1-3-g733a5 From e1743ef5250d67260a2a1b0126708d969f80909f Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Fri, 25 Dec 2020 08:59:34 +0900 Subject: Fix a style of texts in `map_err_ignore` --- clippy_lints/src/map_err_ignore.rs | 4 ++-- tests/ui/map_err.stderr | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/map_err_ignore.rs b/clippy_lints/src/map_err_ignore.rs index f3c0515b9bc..76fe8e776ea 100644 --- a/clippy_lints/src/map_err_ignore.rs +++ b/clippy_lints/src/map_err_ignore.rs @@ -7,7 +7,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// **What it does:** Checks for instances of `map_err(|_| Some::Enum)` /// - /// **Why is this bad?** This map_err throws away the original error rather than allowing the enum to contain and report the cause of the error + /// **Why is this bad?** This `map_err` throws away the original error rather than allowing the enum to contain and report the cause of the error /// /// **Known problems:** None. /// @@ -135,7 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for MapErrIgnore { body_span, "`map_err(|_|...` wildcard pattern discards the original error", None, - "Consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)", + "consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)", ); } } diff --git a/tests/ui/map_err.stderr b/tests/ui/map_err.stderr index 8ee2941790d..37e87e64de2 100644 --- a/tests/ui/map_err.stderr +++ b/tests/ui/map_err.stderr @@ -5,7 +5,7 @@ LL | println!("{:?}", x.map_err(|_| Errors::Ignored)); | ^^^ | = note: `-D clippy::map-err-ignore` implied by `-D warnings` - = help: Consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`) + = help: consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`) error: aborting due to previous error -- cgit 1.4.1-3-g733a5 From dfaea9c9677f97656ad75f6bacd78f8f87e1d339 Mon Sep 17 00:00:00 2001 From: Aleksei Latyshev Date: Fri, 25 Dec 2020 14:45:04 +0300 Subject: lint &PathBuf instead of &Path in PTR_ARG - extract get_only_generic_arg_snippet to improve readability --- clippy_dev/src/ra_setup.rs | 4 ++-- clippy_lints/src/ptr.rs | 60 ++++++++++++++++++++++++++++++++++------------ tests/ui/ptr_arg.rs | 22 +++++++++++++++++ tests/ui/ptr_arg.stderr | 45 +++++++++++++++++++++++++++------- 4 files changed, 106 insertions(+), 25 deletions(-) (limited to 'tests') diff --git a/clippy_dev/src/ra_setup.rs b/clippy_dev/src/ra_setup.rs index 40bf4a9505a..5f5048e79e7 100644 --- a/clippy_dev/src/ra_setup.rs +++ b/clippy_dev/src/ra_setup.rs @@ -3,7 +3,7 @@ use std::fs; use std::fs::File; use std::io::prelude::*; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; // This module takes an absolute path to a rustc repo and alters the dependencies to point towards // the respective rustc subcrates instead of using extern crate xyz. @@ -44,7 +44,7 @@ pub fn run(rustc_path: Option<&str>) { } fn inject_deps_into_manifest( - rustc_source_dir: &PathBuf, + rustc_source_dir: &Path, manifest_path: &str, cargo_toml: &str, lib_rs: &str, diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index dcb643a28ae..c494a713631 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -182,20 +182,6 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: if let ty::Ref(_, ty, Mutability::Not) = ty.kind() { if is_type_diagnostic_item(cx, ty, sym::vec_type) { - let mut ty_snippet = None; - if_chain! { - if let TyKind::Path(QPath::Resolved(_, ref path)) = walk_ptrs_hir_ty(arg).kind; - if let Some(&PathSegment{args: Some(ref parameters), ..}) = path.segments.last(); - then { - let types: Vec<_> = parameters.args.iter().filter_map(|arg| match arg { - GenericArg::Type(ty) => Some(ty), - _ => None, - }).collect(); - if types.len() == 1 { - ty_snippet = snippet_opt(cx, types[0].span); - } - } - }; if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_owned()")]) { span_lint_and_then( cx, @@ -204,7 +190,7 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: "writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used \ with non-Vec-based slices.", |diag| { - if let Some(ref snippet) = ty_snippet { + if let Some(ref snippet) = get_only_generic_arg_snippet(cx, arg) { diag.span_suggestion( arg.span, "change this to", @@ -247,6 +233,33 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: }, ); } + } else if match_type(cx, ty, &paths::PATH_BUF) { + if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_path_buf()"), ("as_path", "")]) { + span_lint_and_then( + cx, + PTR_ARG, + arg.span, + "writing `&PathBuf` instead of `&Path` involves a new object where a slice will do.", + |diag| { + diag.span_suggestion( + arg.span, + "change this to", + "&Path".into(), + Applicability::Unspecified, + ); + for (clonespan, suggestion) in spans { + diag.span_suggestion_short( + clonespan, + &snippet_opt(cx, clonespan).map_or("change the call to".into(), |x| { + Cow::Owned(format!("change `{}` to", x)) + }), + suggestion.into(), + Applicability::Unspecified, + ); + } + }, + ); + } } else if match_type(cx, ty, &paths::COW) { if_chain! { if let TyKind::Rptr(_, MutTy { ref ty, ..} ) = arg.kind; @@ -309,6 +322,23 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: } } +fn get_only_generic_arg_snippet(cx: &LateContext<'_>, arg: &Ty<'_>) -> Option { + if_chain! { + if let TyKind::Path(QPath::Resolved(_, ref path)) = walk_ptrs_hir_ty(arg).kind; + if let Some(&PathSegment{args: Some(ref parameters), ..}) = path.segments.last(); + let types: Vec<_> = parameters.args.iter().filter_map(|arg| match arg { + GenericArg::Type(ty) => Some(ty), + _ => None, + }).collect(); + if types.len() == 1; + then { + snippet_opt(cx, types[0].span) + } else { + None + } + } +} + fn get_rptr_lm<'tcx>(ty: &'tcx Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, Span)> { if let TyKind::Rptr(ref lt, ref m) = ty.kind { Some((lt, m.mutbl, ty.span)) diff --git a/tests/ui/ptr_arg.rs b/tests/ui/ptr_arg.rs index 541225e6351..e8854fb73d9 100644 --- a/tests/ui/ptr_arg.rs +++ b/tests/ui/ptr_arg.rs @@ -2,6 +2,7 @@ #![warn(clippy::ptr_arg)] use std::borrow::Cow; +use std::path::PathBuf; fn do_vec(x: &Vec) { //Nothing here @@ -21,6 +22,15 @@ fn do_str_mut(x: &mut String) { //Nothing here either } +fn do_path(x: &PathBuf) { + //Nothing here either +} + +fn do_path_mut(x: &mut PathBuf) { + // no error here + //Nothing here either +} + fn main() {} trait Foo { @@ -55,6 +65,14 @@ fn str_cloned(x: &String) -> String { x.clone() } +fn path_cloned(x: &PathBuf) -> PathBuf { + let a = x.clone(); + let b = x.clone(); + let c = b.clone(); + let d = a.clone().clone().clone(); + x.clone() +} + fn false_positive_capacity(x: &Vec, y: &String) { let a = x.capacity(); let b = y.clone(); @@ -87,10 +105,12 @@ impl Foo2 for String { // Check that the allow attribute on parameters is honored mod issue_5644 { use std::borrow::Cow; + use std::path::PathBuf; fn allowed( #[allow(clippy::ptr_arg)] _v: &Vec, #[allow(clippy::ptr_arg)] _s: &String, + #[allow(clippy::ptr_arg)] _p: &PathBuf, #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>, ) { } @@ -100,6 +120,7 @@ mod issue_5644 { fn allowed( #[allow(clippy::ptr_arg)] _v: &Vec, #[allow(clippy::ptr_arg)] _s: &String, + #[allow(clippy::ptr_arg)] _p: &PathBuf, #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>, ) { } @@ -109,6 +130,7 @@ mod issue_5644 { fn allowed( #[allow(clippy::ptr_arg)] _v: &Vec, #[allow(clippy::ptr_arg)] _s: &String, + #[allow(clippy::ptr_arg)] _p: &PathBuf, #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>, ) { } diff --git a/tests/ui/ptr_arg.stderr b/tests/ui/ptr_arg.stderr index 314f23497f9..70d1b2f5258 100644 --- a/tests/ui/ptr_arg.stderr +++ b/tests/ui/ptr_arg.stderr @@ -1,5 +1,5 @@ error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices. - --> $DIR/ptr_arg.rs:6:14 + --> $DIR/ptr_arg.rs:7:14 | LL | fn do_vec(x: &Vec) { | ^^^^^^^^^ help: change this to: `&[i64]` @@ -7,19 +7,25 @@ LL | fn do_vec(x: &Vec) { = note: `-D clippy::ptr-arg` implied by `-D warnings` error: writing `&String` instead of `&str` involves a new object where a slice will do. - --> $DIR/ptr_arg.rs:15:14 + --> $DIR/ptr_arg.rs:16:14 | LL | fn do_str(x: &String) { | ^^^^^^^ help: change this to: `&str` +error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do. + --> $DIR/ptr_arg.rs:25:15 + | +LL | fn do_path(x: &PathBuf) { + | ^^^^^^^^ help: change this to: `&Path` + error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices. - --> $DIR/ptr_arg.rs:28:18 + --> $DIR/ptr_arg.rs:38:18 | LL | fn do_vec(x: &Vec); | ^^^^^^^^^ help: change this to: `&[i64]` error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices. - --> $DIR/ptr_arg.rs:41:14 + --> $DIR/ptr_arg.rs:51:14 | LL | fn cloned(x: &Vec) -> Vec { | ^^^^^^^^ @@ -38,7 +44,7 @@ LL | x.to_owned() | error: writing `&String` instead of `&str` involves a new object where a slice will do. - --> $DIR/ptr_arg.rs:50:18 + --> $DIR/ptr_arg.rs:60:18 | LL | fn str_cloned(x: &String) -> String { | ^^^^^^^ @@ -60,8 +66,31 @@ help: change `x.clone()` to LL | x.to_string() | +error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do. + --> $DIR/ptr_arg.rs:68:19 + | +LL | fn path_cloned(x: &PathBuf) -> PathBuf { + | ^^^^^^^^ + | +help: change this to + | +LL | fn path_cloned(x: &Path) -> PathBuf { + | ^^^^^ +help: change `x.clone()` to + | +LL | let a = x.to_path_buf(); + | ^^^^^^^^^^^^^^^ +help: change `x.clone()` to + | +LL | let b = x.to_path_buf(); + | ^^^^^^^^^^^^^^^ +help: change `x.clone()` to + | +LL | x.to_path_buf() + | + error: writing `&String` instead of `&str` involves a new object where a slice will do. - --> $DIR/ptr_arg.rs:58:44 + --> $DIR/ptr_arg.rs:76:44 | LL | fn false_positive_capacity(x: &Vec, y: &String) { | ^^^^^^^ @@ -80,10 +109,10 @@ LL | let c = y; | ^ error: using a reference to `Cow` is not recommended. - --> $DIR/ptr_arg.rs:72:25 + --> $DIR/ptr_arg.rs:90:25 | LL | fn test_cow_with_ref(c: &Cow<[i32]>) {} | ^^^^^^^^^^^ help: change this to: `&[i32]` -error: aborting due to 7 previous errors +error: aborting due to 9 previous errors -- cgit 1.4.1-3-g733a5 From 203715aa4ecebb167868100fe14b0eaa05133718 Mon Sep 17 00:00:00 2001 From: Aleksei Latyshev Date: Mon, 28 Dec 2020 01:09:04 +0300 Subject: don't ignore expression after first not matched method call in PtrCloneVisitor --- clippy_lints/src/utils/ptr.rs | 1 - tests/ui/ptr_arg.rs | 19 ++++++++++++++ tests/ui/ptr_arg.stderr | 59 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 77 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/utils/ptr.rs b/clippy_lints/src/utils/ptr.rs index bd2c619f000..b330f3d890e 100644 --- a/clippy_lints/src/utils/ptr.rs +++ b/clippy_lints/src/utils/ptr.rs @@ -72,7 +72,6 @@ impl<'a, 'tcx> Visitor<'tcx> for PtrCloneVisitor<'a, 'tcx> { } } } - return; } walk_expr(self, expr); } diff --git a/tests/ui/ptr_arg.rs b/tests/ui/ptr_arg.rs index e8854fb73d9..06370dfce65 100644 --- a/tests/ui/ptr_arg.rs +++ b/tests/ui/ptr_arg.rs @@ -136,3 +136,22 @@ mod issue_5644 { } } } + +mod issue6509 { + use std::path::PathBuf; + + fn foo_vec(vec: &Vec) { + let _ = vec.clone().pop(); + let _ = vec.clone().clone(); + } + + fn foo_path(path: &PathBuf) { + let _ = path.clone().pop(); + let _ = path.clone().clone(); + } + + fn foo_str(str: &PathBuf) { + let _ = str.clone().pop(); + let _ = str.clone().clone(); + } +} diff --git a/tests/ui/ptr_arg.stderr b/tests/ui/ptr_arg.stderr index 70d1b2f5258..708318bbe29 100644 --- a/tests/ui/ptr_arg.stderr +++ b/tests/ui/ptr_arg.stderr @@ -114,5 +114,62 @@ error: using a reference to `Cow` is not recommended. LL | fn test_cow_with_ref(c: &Cow<[i32]>) {} | ^^^^^^^^^^^ help: change this to: `&[i32]` -error: aborting due to 9 previous errors +error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices. + --> $DIR/ptr_arg.rs:143:21 + | +LL | fn foo_vec(vec: &Vec) { + | ^^^^^^^^ + | +help: change this to + | +LL | fn foo_vec(vec: &[u8]) { + | ^^^^^ +help: change `vec.clone()` to + | +LL | let _ = vec.to_owned().pop(); + | ^^^^^^^^^^^^^^ +help: change `vec.clone()` to + | +LL | let _ = vec.to_owned().clone(); + | ^^^^^^^^^^^^^^ + +error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do. + --> $DIR/ptr_arg.rs:148:23 + | +LL | fn foo_path(path: &PathBuf) { + | ^^^^^^^^ + | +help: change this to + | +LL | fn foo_path(path: &Path) { + | ^^^^^ +help: change `path.clone()` to + | +LL | let _ = path.to_path_buf().pop(); + | ^^^^^^^^^^^^^^^^^^ +help: change `path.clone()` to + | +LL | let _ = path.to_path_buf().clone(); + | ^^^^^^^^^^^^^^^^^^ + +error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do. + --> $DIR/ptr_arg.rs:153:21 + | +LL | fn foo_str(str: &PathBuf) { + | ^^^^^^^^ + | +help: change this to + | +LL | fn foo_str(str: &Path) { + | ^^^^^ +help: change `str.clone()` to + | +LL | let _ = str.to_path_buf().pop(); + | ^^^^^^^^^^^^^^^^^ +help: change `str.clone()` to + | +LL | let _ = str.to_path_buf().clone(); + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 12 previous errors -- cgit 1.4.1-3-g733a5 From 83a458acf113a35ad9b50d5a4c7943ea5f5415ea Mon Sep 17 00:00:00 2001 From: nahuakang Date: Mon, 28 Dec 2020 20:18:27 +0100 Subject: Enable never type in empty enum ui test; run cargo dev bless --- tests/ui/empty_enum.rs | 3 ++- tests/ui/empty_enum.stderr | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/ui/empty_enum.rs b/tests/ui/empty_enum.rs index 12428f29625..a2e5c13c452 100644 --- a/tests/ui/empty_enum.rs +++ b/tests/ui/empty_enum.rs @@ -1,6 +1,7 @@ #![allow(dead_code)] #![warn(clippy::empty_enum)] - +// Enable never type to test empty enum lint +#![feature(never_type)] enum Empty {} fn main() {} diff --git a/tests/ui/empty_enum.stderr b/tests/ui/empty_enum.stderr index 466dfbe7cee..7125e5f602b 100644 --- a/tests/ui/empty_enum.stderr +++ b/tests/ui/empty_enum.stderr @@ -1,5 +1,5 @@ error: enum with no variants - --> $DIR/empty_enum.rs:4:1 + --> $DIR/empty_enum.rs:5:1 | LL | enum Empty {} | ^^^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From af480a67be108a936073c44942b5f7c5d2a69621 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Wed, 30 Dec 2020 12:02:26 +0100 Subject: Ensure `Copy` exception in trait definition for `wrong_self_convention` lint --- tests/ui/wrong_self_convention.rs | 30 ++++++++++++++++++++++++-- tests/ui/wrong_self_convention.stderr | 40 +++++++++++++++++++++++------------ 2 files changed, 54 insertions(+), 16 deletions(-) (limited to 'tests') diff --git a/tests/ui/wrong_self_convention.rs b/tests/ui/wrong_self_convention.rs index 5282eba74fd..6cfc0fcb4ca 100644 --- a/tests/ui/wrong_self_convention.rs +++ b/tests/ui/wrong_self_convention.rs @@ -94,7 +94,8 @@ mod issue6307 { trait T: Sized { fn as_i32(self) {} fn as_u32(&self) {} - fn into_i32(&self) {} + fn into_i32(self) {} + fn into_i32_ref(&self) {} fn into_u32(self) {} fn is_i32(self) {} fn is_u32(&self) {} @@ -117,7 +118,32 @@ mod issue6307 { trait U { fn as_i32(self); fn as_u32(&self); - fn into_i32(&self); + fn into_i32(self); + fn into_i32_ref(&self); + fn into_u32(self); + fn is_i32(self); + fn is_u32(&self); + fn to_i32(self); + fn to_u32(&self); + fn from_i32(self); + // check whether the lint can be allowed at the function level + #[allow(clippy::wrong_self_convention)] + fn from_cake(self); + + // test for false positives + fn as_(self); + fn into_(&self); + fn is_(self); + fn to_(self); + fn from_(self); + fn to_mut(&mut self); + } + + trait C: Copy { + fn as_i32(self); + fn as_u32(&self); + fn into_i32(self); + fn into_i32_ref(&self); fn into_u32(self); fn is_i32(self); fn is_u32(&self); diff --git a/tests/ui/wrong_self_convention.stderr b/tests/ui/wrong_self_convention.stderr index 86467eb0fc7..32bd9075bd5 100644 --- a/tests/ui/wrong_self_convention.stderr +++ b/tests/ui/wrong_self_convention.stderr @@ -79,58 +79,70 @@ LL | fn as_i32(self) {} | ^^^^ error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:97:21 + --> $DIR/wrong_self_convention.rs:98:25 | -LL | fn into_i32(&self) {} - | ^^^^^ +LL | fn into_i32_ref(&self) {} + | ^^^^^ error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:99:19 + --> $DIR/wrong_self_convention.rs:100:19 | LL | fn is_i32(self) {} | ^^^^ error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:101:19 + --> $DIR/wrong_self_convention.rs:102:19 | LL | fn to_i32(self) {} | ^^^^ error: methods called `from_*` usually take no self; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:103:21 + --> $DIR/wrong_self_convention.rs:104:21 | LL | fn from_i32(self) {} | ^^^^ error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:118:19 + --> $DIR/wrong_self_convention.rs:119:19 | LL | fn as_i32(self); | ^^^^ error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:120:21 + --> $DIR/wrong_self_convention.rs:122:25 | -LL | fn into_i32(&self); - | ^^^^^ +LL | fn into_i32_ref(&self); + | ^^^^^ error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:122:19 + --> $DIR/wrong_self_convention.rs:124:19 | LL | fn is_i32(self); | ^^^^ error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:124:19 + --> $DIR/wrong_self_convention.rs:126:19 | LL | fn to_i32(self); | ^^^^ error: methods called `from_*` usually take no self; consider choosing a less ambiguous name - --> $DIR/wrong_self_convention.rs:126:21 + --> $DIR/wrong_self_convention.rs:128:21 + | +LL | fn from_i32(self); + | ^^^^ + +error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:146:25 + | +LL | fn into_i32_ref(&self); + | ^^^^^ + +error: methods called `from_*` usually take no self; consider choosing a less ambiguous name + --> $DIR/wrong_self_convention.rs:152:21 | LL | fn from_i32(self); | ^^^^ -error: aborting due to 22 previous errors +error: aborting due to 24 previous errors -- cgit 1.4.1-3-g733a5 From 59397d6abb55116e778e0b9667b7ac74f0356704 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 31 Dec 2020 16:07:20 +0100 Subject: make clippy version number correspond to rustc version number. clippy 0.1.50 corresponds to rustc 1.50.x This bumps the clippy version number from 0.0.212 to 0.1.50 Fixes #6499 --- Cargo.toml | 4 ++-- clippy_lints/Cargo.toml | 2 +- tests/versioncheck.rs | 56 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/Cargo.toml b/Cargo.toml index a765390c603..93a1e71ecab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.0.212" +version = "0.1.50" authors = [ "Manish Goregaokar ", "Andre Bogus ", @@ -29,7 +29,7 @@ path = "src/driver.rs" [dependencies] # begin automatic update -clippy_lints = { version = "0.0.212", path = "clippy_lints" } +clippy_lints = { version = "0.1.50", path = "clippy_lints" } # end automatic update semver = "0.11" rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util" } diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 7697eba650a..7e3eaf3ae74 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_lints" # begin automatic update -version = "0.0.212" +version = "0.1.50" # end automatic update authors = [ "Manish Goregaokar ", diff --git a/tests/versioncheck.rs b/tests/versioncheck.rs index f5d03c645df..ec54e11dc06 100644 --- a/tests/versioncheck.rs +++ b/tests/versioncheck.rs @@ -1,3 +1,6 @@ +#![allow(clippy::single_match_else)] +use rustc_tools_util::VersionInfo; + #[test] fn check_that_clippy_lints_has_the_same_version_as_clippy() { let clippy_meta = cargo_metadata::MetadataCommand::new() @@ -17,3 +20,56 @@ fn check_that_clippy_lints_has_the_same_version_as_clippy() { } } } + +#[test] +fn check_that_clippy_has_the_same_major_version_as_rustc() { + let clippy_version = rustc_tools_util::get_version_info!(); + let clippy_major = clippy_version.major; + let clippy_minor = clippy_version.minor; + let clippy_patch = clippy_version.patch; + + // get the rustc version from cargo + // this way the rust-toolchain file version is honored + let rustc_version = String::from_utf8( + std::process::Command::new("cargo") + .arg("--version") + .output() + .expect("failed to run 'cargo --version'") + .stdout, + ) + .unwrap(); + // extract "1 50 0" from "cargo 1.50.0-nightly (a3c2627fb 2020-12-14)" + let vsplit: Vec<&str> = rustc_version + .split(' ') + .nth(1) + .unwrap() + .split('-') + .next() + .unwrap() + .split('.') + .collect(); + match vsplit.as_slice() { + [rustc_major, rustc_minor, _rustc_patch] => { + // clippy 0.1.50 should correspond to rustc 1.50.0 + dbg!(&rustc_version); + dbg!(&clippy_version); + assert_eq!(clippy_major, 0); // this will probably stay the same for a long time + assert_eq!( + clippy_minor.to_string(), + *rustc_major, + "clippy minor version does not equal rustc major version" + ); + assert_eq!( + clippy_patch.to_string(), + *rustc_minor, + "clippy patch version does not equal rustc minor version" + ); + // do not check rustc_patch because when a stable-patch-release is made (like 1.50.2), + // we don't want our tests failing suddenly + }, + _ => { + dbg!(vsplit); + panic!("Failed to parse rustc version"); + }, + }; +} -- cgit 1.4.1-3-g733a5 From 5d48b91b40b3da6c29e8b6eea617bf8ed4033dec Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Fri, 1 Jan 2021 16:59:59 +0100 Subject: field_reassign_with_default: don't expand macros in suggestion fixes #6522 changelog: field_reassign_with_default: don't expand macros in lint suggestion (#6522) --- clippy_lints/src/default.rs | 6 +++-- tests/ui/field_reassign_with_default.rs | 9 +++++++ tests/ui/field_reassign_with_default.stderr | 38 +++++++++++++++++++---------- 3 files changed, 38 insertions(+), 15 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index b0d7c7b3baa..9fa06d7cde9 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -1,4 +1,6 @@ -use crate::utils::{any_parent_is_automatically_derived, contains_name, match_def_path, paths, qpath_res, snippet}; +use crate::utils::{ + any_parent_is_automatically_derived, contains_name, match_def_path, paths, qpath_res, snippet_with_macro_callsite, +}; use crate::utils::{span_lint_and_note, span_lint_and_sugg}; use if_chain::if_chain; use rustc_data_structures::fx::FxHashSet; @@ -187,7 +189,7 @@ impl LateLintPass<'_> for Default { .into_iter() .map(|(field, rhs)| { // extract and store the assigned value for help message - let value_snippet = snippet(cx, rhs.span, ".."); + let value_snippet = snippet_with_macro_callsite(cx, rhs.span, ".."); format!("{}: {}", field, value_snippet) }) .collect::>() diff --git a/tests/ui/field_reassign_with_default.rs b/tests/ui/field_reassign_with_default.rs index 3e0921022b4..2990397c03e 100644 --- a/tests/ui/field_reassign_with_default.rs +++ b/tests/ui/field_reassign_with_default.rs @@ -11,6 +11,11 @@ struct B { j: i64, } +#[derive(Default)] +struct C { + i: Vec, + j: i64, +} /// Implements .next() that returns a different number each time. struct SideEffect(i32); @@ -111,6 +116,10 @@ fn main() { // don't lint - some private fields let mut x = m::F::default(); x.a = 1; + + // don't expand macros in the suggestion (#6522) + let mut a: C = C::default(); + a.i = vec![1]; } mod m { diff --git a/tests/ui/field_reassign_with_default.stderr b/tests/ui/field_reassign_with_default.stderr index 9a2bc778c3f..59d2ac8ed69 100644 --- a/tests/ui/field_reassign_with_default.stderr +++ b/tests/ui/field_reassign_with_default.stderr @@ -1,75 +1,87 @@ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:30:5 + --> $DIR/field_reassign_with_default.rs:35:5 | LL | a.i = 42; | ^^^^^^^^^ | = note: `-D clippy::field-reassign-with-default` implied by `-D warnings` note: consider initializing the variable with `A { i: 42, ..Default::default() }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:29:5 + --> $DIR/field_reassign_with_default.rs:34:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:70:5 + --> $DIR/field_reassign_with_default.rs:75:5 | LL | a.j = 43; | ^^^^^^^^^ | note: consider initializing the variable with `A { j: 43, i: 42 }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:69:5 + --> $DIR/field_reassign_with_default.rs:74:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:75:5 + --> $DIR/field_reassign_with_default.rs:80:5 | LL | a.i = 42; | ^^^^^^^^^ | note: consider initializing the variable with `A { i: 42, j: 44 }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:74:5 + --> $DIR/field_reassign_with_default.rs:79:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:81:5 + --> $DIR/field_reassign_with_default.rs:86:5 | LL | a.i = 42; | ^^^^^^^^^ | note: consider initializing the variable with `A { i: 42, ..Default::default() }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:80:5 + --> $DIR/field_reassign_with_default.rs:85:5 | LL | let mut a = A::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:91:5 + --> $DIR/field_reassign_with_default.rs:96:5 | LL | a.i = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: consider initializing the variable with `A { i: Default::default(), ..Default::default() }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:90:5 + --> $DIR/field_reassign_with_default.rs:95:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:95:5 + --> $DIR/field_reassign_with_default.rs:100:5 | LL | a.i = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: consider initializing the variable with `A { i: Default::default(), j: 45 }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:94:5 + --> $DIR/field_reassign_with_default.rs:99:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 6 previous errors +error: field assignment outside of initializer for an instance created with Default::default() + --> $DIR/field_reassign_with_default.rs:122:5 + | +LL | a.i = vec![1]; + | ^^^^^^^^^^^^^^ + | +note: consider initializing the variable with `C { i: vec![1], ..Default::default() }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:121:5 + | +LL | let mut a: C = C::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 7 previous errors -- cgit 1.4.1-3-g733a5 From 106ca9665371b5738091533aca481ab0b230400b Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sat, 2 Jan 2021 16:20:43 +0100 Subject: Use rustc --version in versioncheck --- tests/versioncheck.rs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'tests') diff --git a/tests/versioncheck.rs b/tests/versioncheck.rs index ec54e11dc06..589b19f68f7 100644 --- a/tests/versioncheck.rs +++ b/tests/versioncheck.rs @@ -28,17 +28,17 @@ fn check_that_clippy_has_the_same_major_version_as_rustc() { let clippy_minor = clippy_version.minor; let clippy_patch = clippy_version.patch; - // get the rustc version from cargo + // get the rustc version // this way the rust-toolchain file version is honored let rustc_version = String::from_utf8( - std::process::Command::new("cargo") + std::process::Command::new("rustc") .arg("--version") .output() - .expect("failed to run 'cargo --version'") + .expect("failed to run `rustc --version`") .stdout, ) .unwrap(); - // extract "1 50 0" from "cargo 1.50.0-nightly (a3c2627fb 2020-12-14)" + // extract "1 XX 0" from "rustc 1.XX.0-nightly ( )" let vsplit: Vec<&str> = rustc_version .split(' ') .nth(1) @@ -50,9 +50,7 @@ fn check_that_clippy_has_the_same_major_version_as_rustc() { .collect(); match vsplit.as_slice() { [rustc_major, rustc_minor, _rustc_patch] => { - // clippy 0.1.50 should correspond to rustc 1.50.0 - dbg!(&rustc_version); - dbg!(&clippy_version); + // clippy 0.1.XX should correspond to rustc 1.XX.0 assert_eq!(clippy_major, 0); // this will probably stay the same for a long time assert_eq!( clippy_minor.to_string(), @@ -68,8 +66,7 @@ fn check_that_clippy_has_the_same_major_version_as_rustc() { // we don't want our tests failing suddenly }, _ => { - dbg!(vsplit); - panic!("Failed to parse rustc version"); + panic!("Failed to parse rustc version: {:?}", vsplit); }, }; } -- cgit 1.4.1-3-g733a5 From 1853f8b228298d5b91e3d03ee677da91574554b4 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 2 Jan 2021 11:08:56 -0500 Subject: Add lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 + clippy_lints/src/vec_init_then_push.rs | 186 +++++++++++++++++++++++++++++++++ tests/ui/vec_init_then_push.rs | 21 ++++ tests/ui/vec_init_then_push.stderr | 34 ++++++ 5 files changed, 247 insertions(+) create mode 100644 clippy_lints/src/vec_init_then_push.rs create mode 100644 tests/ui/vec_init_then_push.rs create mode 100644 tests/ui/vec_init_then_push.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index de8da99cdee..572dd8c0c6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2152,6 +2152,7 @@ Released 2018-09-13 [`useless_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_transmute [`useless_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec [`vec_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_box +[`vec_init_then_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_init_then_push [`vec_resize_to_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_resize_to_zero [`verbose_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_bit_mask [`verbose_file_reads`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_file_reads diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 35b057d7b6a..6d84026d093 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -341,6 +341,7 @@ mod unwrap_in_result; mod use_self; mod useless_conversion; mod vec; +mod vec_init_then_push; mod vec_resize_to_zero; mod verbose_file_reads; mod wildcard_dependencies; @@ -935,6 +936,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &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, @@ -1215,6 +1217,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box strings::StrToString); store.register_late_pass(|| box strings::StringToString); store.register_late_pass(|| box zero_sized_map_values::ZeroSizedMapValues); + store.register_late_pass(|| box vec_init_then_push::VecInitThenPush::default()); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1636,6 +1639,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&unwrap::UNNECESSARY_UNWRAP), 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), @@ -1935,6 +1939,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::BOX_VEC), 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![ diff --git a/clippy_lints/src/vec_init_then_push.rs b/clippy_lints/src/vec_init_then_push.rs new file mode 100644 index 00000000000..0aadb453444 --- /dev/null +++ b/clippy_lints/src/vec_init_then_push.rs @@ -0,0 +1,186 @@ +use crate::utils::{is_type_diagnostic_item, match_def_path, paths, snippet, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, Local, PatKind, QPath, 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, Symbol}; + +declare_clippy_lint! { + /// **What it does:** Checks for calls to `push` immediately after creating a new `Vec`. + /// + /// **Why is this bad?** The `vec![]` macro is both more performant and easier to read than + /// multiple `push` calls. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let mut v: Vec = Vec::new(); + /// v.push(0); + /// ``` + /// Use instead: + /// ```rust + /// let v: Vec = vec![0]; + /// ``` + pub VEC_INIT_THEN_PUSH, + perf, + "`push` immediately after `Vec` creation" +} + +impl_lint_pass!(VecInitThenPush => [VEC_INIT_THEN_PUSH]); + +#[derive(Default)] +pub struct VecInitThenPush { + searcher: Option, +} + +#[derive(Clone, Copy)] +enum VecInitKind { + New, + WithCapacity(u64), +} +struct VecPushSearcher { + init: VecInitKind, + name: Symbol, + lhs_is_local: bool, + lhs_span: Span, + err_span: Span, + found: u64, +} +impl VecPushSearcher { + fn display_err(&self, cx: &LateContext<'_>) { + match self.init { + _ if self.found == 0 => return, + VecInitKind::WithCapacity(x) if x > self.found => return, + _ => (), + }; + + let mut s = if self.lhs_is_local { + String::from("let ") + } else { + String::new() + }; + s.push_str(&snippet(cx, self.lhs_span, "..")); + s.push_str(" = vec![..];"); + + span_lint_and_sugg( + cx, + VEC_INIT_THEN_PUSH, + self.err_span, + "calls to `push` immediately after creation", + "consider using the `vec![]` macro", + s, + Applicability::HasPlaceholders, + ); + } +} + +impl LateLintPass<'_> for VecInitThenPush { + fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { + self.searcher = None; + if_chain! { + if !in_external_macro(cx.sess(), local.span); + if let Some(init) = local.init; + if let PatKind::Binding(BindingAnnotation::Mutable, _, ident, None) = local.pat.kind; + if let Some(init_kind) = get_vec_init_kind(cx, init); + then { + self.searcher = Some(VecPushSearcher { + init: init_kind, + name: ident.name, + lhs_is_local: true, + lhs_span: local.ty.map(|t| local.pat.span.to(t.span)).unwrap_or(local.pat.span), + err_span: local.span, + found: 0, + }); + } + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if self.searcher.is_none() { + if_chain! { + if !in_external_macro(cx.sess(), expr.span); + if let ExprKind::Assign(left, right, _) = expr.kind; + if let ExprKind::Path(QPath::Resolved(_, path)) = left.kind; + if let Some(name) = path.segments.get(0); + if let Some(init_kind) = get_vec_init_kind(cx, right); + then { + self.searcher = Some(VecPushSearcher { + init: init_kind, + name: name.ident.name, + lhs_is_local: false, + lhs_span: left.span, + err_span: expr.span, + found: 0, + }); + } + } + } + } + + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { + if let Some(searcher) = self.searcher.take() { + if_chain! { + if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind; + if let ExprKind::MethodCall(path, _, [self_arg, _], _) = expr.kind; + if path.ident.name.as_str() == "push"; + if let ExprKind::Path(QPath::Resolved(_, self_path)) = self_arg.kind; + if let [self_name] = self_path.segments; + if self_name.ident.name == searcher.name; + then { + self.searcher = Some(VecPushSearcher { + found: searcher.found + 1, + err_span: searcher.err_span.to(stmt.span), + .. searcher + }); + } else { + searcher.display_err(cx); + } + } + } + } + + fn check_block_post(&mut self, cx: &LateContext<'tcx>, _: &'tcx Block<'tcx>) { + if let Some(searcher) = self.searcher.take() { + searcher.display_err(cx); + } + } +} + +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_type) => + { + if name.ident.name.as_str() == "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 as u64)) + } 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_type) => + { + return Some(VecInitKind::New); + } + _ => (), + } + } + None +} diff --git a/tests/ui/vec_init_then_push.rs b/tests/ui/vec_init_then_push.rs new file mode 100644 index 00000000000..642ce504009 --- /dev/null +++ b/tests/ui/vec_init_then_push.rs @@ -0,0 +1,21 @@ +#![allow(unused_variables)] +#![warn(clippy::vec_init_then_push)] + +fn main() { + let mut def_err: Vec = Default::default(); + def_err.push(0); + + let mut new_err = Vec::::new(); + new_err.push(1); + + let mut cap_err = Vec::with_capacity(2); + cap_err.push(0); + cap_err.push(1); + cap_err.push(2); + + let mut cap_ok = Vec::with_capacity(10); + cap_ok.push(0); + + new_err = Vec::new(); + new_err.push(0); +} diff --git a/tests/ui/vec_init_then_push.stderr b/tests/ui/vec_init_then_push.stderr new file mode 100644 index 00000000000..819ed47d099 --- /dev/null +++ b/tests/ui/vec_init_then_push.stderr @@ -0,0 +1,34 @@ +error: calls to `push` immediately after creation + --> $DIR/vec_init_then_push.rs:5:5 + | +LL | / let mut def_err: Vec = Default::default(); +LL | | def_err.push(0); + | |____________________^ help: consider using the `vec![]` macro: `let mut def_err: Vec = vec![..];` + | + = note: `-D clippy::vec-init-then-push` implied by `-D warnings` + +error: calls to `push` immediately after creation + --> $DIR/vec_init_then_push.rs:8:5 + | +LL | / let mut new_err = Vec::::new(); +LL | | new_err.push(1); + | |____________________^ help: consider using the `vec![]` macro: `let mut new_err = vec![..];` + +error: calls to `push` immediately after creation + --> $DIR/vec_init_then_push.rs:11:5 + | +LL | / let mut cap_err = Vec::with_capacity(2); +LL | | cap_err.push(0); +LL | | cap_err.push(1); +LL | | cap_err.push(2); + | |____________________^ help: consider using the `vec![]` macro: `let mut cap_err = vec![..];` + +error: calls to `push` immediately after creation + --> $DIR/vec_init_then_push.rs:19:5 + | +LL | / new_err = Vec::new(); +LL | | new_err.push(0); + | |____________________^ help: consider using the `vec![]` macro: `new_err = vec![..];` + +error: aborting due to 4 previous errors + -- cgit 1.4.1-3-g733a5 From 9427e0356beab35222ba3cbdf394b4c9198880ba Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 2 Jan 2021 11:18:04 -0500 Subject: Fix clone_on_copy test --- tests/ui/clone_on_copy.fixed | 3 ++- tests/ui/clone_on_copy.rs | 3 ++- tests/ui/clone_on_copy.stderr | 10 +++++----- 3 files changed, 9 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/tests/ui/clone_on_copy.fixed b/tests/ui/clone_on_copy.fixed index 1f0ca101757..d924625132e 100644 --- a/tests/ui/clone_on_copy.fixed +++ b/tests/ui/clone_on_copy.fixed @@ -5,7 +5,8 @@ clippy::redundant_clone, clippy::deref_addrof, clippy::no_effect, - clippy::unnecessary_operation + clippy::unnecessary_operation, + clippy::vec_init_then_push )] use std::cell::RefCell; diff --git a/tests/ui/clone_on_copy.rs b/tests/ui/clone_on_copy.rs index ca39a654b4f..97f49467244 100644 --- a/tests/ui/clone_on_copy.rs +++ b/tests/ui/clone_on_copy.rs @@ -5,7 +5,8 @@ clippy::redundant_clone, clippy::deref_addrof, clippy::no_effect, - clippy::unnecessary_operation + clippy::unnecessary_operation, + clippy::vec_init_then_push )] use std::cell::RefCell; diff --git a/tests/ui/clone_on_copy.stderr b/tests/ui/clone_on_copy.stderr index 14a700886a7..7a706884fb0 100644 --- a/tests/ui/clone_on_copy.stderr +++ b/tests/ui/clone_on_copy.stderr @@ -1,5 +1,5 @@ error: using `clone` on type `i32` which implements the `Copy` trait - --> $DIR/clone_on_copy.rs:22:5 + --> $DIR/clone_on_copy.rs:23:5 | LL | 42.clone(); | ^^^^^^^^^^ help: try removing the `clone` call: `42` @@ -7,25 +7,25 @@ LL | 42.clone(); = note: `-D clippy::clone-on-copy` implied by `-D warnings` error: using `clone` on type `i32` which implements the `Copy` trait - --> $DIR/clone_on_copy.rs:26:5 + --> $DIR/clone_on_copy.rs:27:5 | LL | (&42).clone(); | ^^^^^^^^^^^^^ help: try dereferencing it: `*(&42)` error: using `clone` on type `i32` which implements the `Copy` trait - --> $DIR/clone_on_copy.rs:29:5 + --> $DIR/clone_on_copy.rs:30:5 | LL | rc.borrow().clone(); | ^^^^^^^^^^^^^^^^^^^ help: try dereferencing it: `*rc.borrow()` error: using `clone` on type `char` which implements the `Copy` trait - --> $DIR/clone_on_copy.rs:35:14 + --> $DIR/clone_on_copy.rs:36:14 | LL | is_ascii('z'.clone()); | ^^^^^^^^^^^ help: try removing the `clone` call: `'z'` error: using `clone` on type `i32` which implements the `Copy` trait - --> $DIR/clone_on_copy.rs:39:14 + --> $DIR/clone_on_copy.rs:40:14 | LL | vec.push(42.clone()); | ^^^^^^^^^^ help: try removing the `clone` call: `42` -- cgit 1.4.1-3-g733a5 From 39f39d5405b7e55eb08cb927e78686a5ce7377d4 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Fri, 1 Jan 2021 17:43:24 +0100 Subject: match_like_matches_macro: strip refs in suggestion fixes #6503 changelog: match_like_matches_macro: strip refs in suggestion (#6503) --- clippy_lints/src/matches.rs | 10 +++- tests/ui/match_expr_like_matches_macro.fixed | 47 +++++++++++++++ tests/ui/match_expr_like_matches_macro.rs | 62 +++++++++++++++++++ tests/ui/match_expr_like_matches_macro.stderr | 85 ++++++++++++++++++++++++++- 4 files changed, 202 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 04b35835c6b..6372cb86616 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -1185,6 +1185,14 @@ fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr } else { pat }; + + // strip potential borrows (#6503), but only if the type is a reference + let mut ex_new = ex; + if let ExprKind::AddrOf(BorrowKind::Ref, .., ex_inner) = ex.kind { + if let ty::Ref(..) = cx.typeck_results().expr_ty(&ex_inner).kind() { + ex_new = ex_inner; + } + }; span_lint_and_sugg( cx, MATCH_LIKE_MATCHES_MACRO, @@ -1194,7 +1202,7 @@ fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr format!( "{}matches!({}, {})", if b0 { "" } else { "!" }, - snippet_with_applicability(cx, ex.span, "..", &mut applicability), + snippet_with_applicability(cx, ex_new.span, "..", &mut applicability), pat_and_guard, ), applicability, diff --git a/tests/ui/match_expr_like_matches_macro.fixed b/tests/ui/match_expr_like_matches_macro.fixed index 84981a52597..319299862a7 100644 --- a/tests/ui/match_expr_like_matches_macro.fixed +++ b/tests/ui/match_expr_like_matches_macro.fixed @@ -99,4 +99,51 @@ fn main() { _ => false, }; } + + { + // should print "z" in suggestion (#6503) + let z = &Some(3); + let _z = matches!(z, Some(3)); + } + + { + // this could also print "z" in suggestion..? + let z = Some(3); + let _z = matches!(&z, Some(3)); + } + + { + enum AnEnum { + X, + Y, + } + + fn foo(_x: AnEnum) {} + + fn main() { + let z = AnEnum::X; + // we can't remove the reference here! + let _ = matches!(&z, AnEnum::X); + foo(z); + } + } + + { + struct S(i32); + + fn fun(_val: Option) {} + let val = Some(S(42)); + // we need the reference here because later val is consumed by fun() + let _res = matches!(&val, &Some(ref _a)); + fun(val); + } + + { + struct S(i32); + + fn fun(_val: Option) {} + let val = Some(S(42)); + let _res = matches!(&val, &Some(ref _a)); + fun(val); + } } diff --git a/tests/ui/match_expr_like_matches_macro.rs b/tests/ui/match_expr_like_matches_macro.rs index 94c7c3cadac..2ef6cf42387 100644 --- a/tests/ui/match_expr_like_matches_macro.rs +++ b/tests/ui/match_expr_like_matches_macro.rs @@ -119,4 +119,66 @@ fn main() { _ => false, }; } + + { + // should print "z" in suggestion (#6503) + let z = &Some(3); + let _z = match &z { + Some(3) => true, + _ => false, + }; + } + + { + // this could also print "z" in suggestion..? + let z = Some(3); + let _z = match &z { + Some(3) => true, + _ => false, + }; + } + + { + enum AnEnum { + X, + Y, + } + + fn foo(_x: AnEnum) {} + + fn main() { + let z = AnEnum::X; + // we can't remove the reference here! + let _ = match &z { + AnEnum::X => true, + _ => false, + }; + foo(z); + } + } + + { + struct S(i32); + + fn fun(_val: Option) {} + let val = Some(S(42)); + // we need the reference here because later val is consumed by fun() + let _res = match &val { + &Some(ref _a) => true, + _ => false, + }; + fun(val); + } + + { + struct S(i32); + + fn fun(_val: Option) {} + let val = Some(S(42)); + let _res = match &val { + &Some(ref _a) => true, + _ => false, + }; + fun(val); + } } diff --git a/tests/ui/match_expr_like_matches_macro.stderr b/tests/ui/match_expr_like_matches_macro.stderr index c52e41c7889..f27b4e9cb20 100644 --- a/tests/ui/match_expr_like_matches_macro.stderr +++ b/tests/ui/match_expr_like_matches_macro.stderr @@ -70,5 +70,88 @@ LL | | _ => true, LL | | }; | |_________^ help: try this: `!matches!(x, E::B(_) | E::C)` -error: aborting due to 7 previous errors +error: match expression looks like `matches!` macro + --> $DIR/match_expr_like_matches_macro.rs:126:18 + | +LL | let _z = match &z { + | __________________^ +LL | | Some(3) => true, +LL | | _ => false, +LL | | }; + | |_________^ help: try this: `matches!(z, Some(3))` + +error: match expression looks like `matches!` macro + --> $DIR/match_expr_like_matches_macro.rs:135:18 + | +LL | let _z = match &z { + | __________________^ +LL | | Some(3) => true, +LL | | _ => false, +LL | | }; + | |_________^ help: try this: `matches!(&z, Some(3))` + +error: match expression looks like `matches!` macro + --> $DIR/match_expr_like_matches_macro.rs:152:21 + | +LL | let _ = match &z { + | _____________________^ +LL | | AnEnum::X => true, +LL | | _ => false, +LL | | }; + | |_____________^ help: try this: `matches!(&z, AnEnum::X)` + +error: match expression looks like `matches!` macro + --> $DIR/match_expr_like_matches_macro.rs:166:20 + | +LL | let _res = match &val { + | ____________________^ +LL | | &Some(ref _a) => true, +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 + | +LL | let _res = match &val { + | ____________________^ +LL | | &Some(ref _a) => true, +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 -- cgit 1.4.1-3-g733a5 From 6dcec6ae863930b7056a68744759d2bfb3481f92 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Mon, 4 Jan 2021 08:25:38 +0100 Subject: collapsible_if: split collapsible_else_if into its own lint so we can enable/disable it particularly This splits up clippy::collapsible_if into collapsible_if for if x { if y { } } => if x && y { } and collapsible_else_if for if x { } else { if y { } } => if x { } else if y { } so that we can lint for only the latter but not the first if we desire. changelog: collapsible_if: split up linting for if x {} else { if y {} } into collapsible_else_if lint --- CHANGELOG.md | 1 + clippy_lints/src/collapsible_if.rs | 44 +++++++++++++++++++++++++------------ clippy_lints/src/lib.rs | 3 +++ tests/ui/collapsible_else_if.fixed | 2 ++ tests/ui/collapsible_else_if.rs | 2 ++ tests/ui/collapsible_else_if.stderr | 16 +++++++------- tests/ui/if_same_then_else2.rs | 1 + tests/ui/if_same_then_else2.stderr | 24 ++++++++++---------- 8 files changed, 59 insertions(+), 34 deletions(-) (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index ec424068a54..b643627c2e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1896,6 +1896,7 @@ Released 2018-09-13 [`cmp_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_null [`cmp_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_owned [`cognitive_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity +[`collapsible_else_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_else_if [`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if [`collapsible_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match [`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index 42bff564de0..93ccc76d0c9 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -23,9 +23,7 @@ use rustc_errors::Applicability; declare_clippy_lint! { /// **What it does:** Checks for nested `if` statements which can be collapsed - /// by `&&`-combining their conditions and for `else { if ... }` expressions - /// that - /// can be collapsed to `else if ...`. + /// by `&&`-combining their conditions. /// /// **Why is this bad?** Each `if`-statement adds one level of nesting, which /// makes code look more complex than it really is. @@ -40,7 +38,31 @@ declare_clippy_lint! { /// } /// } /// - /// // or + /// ``` + /// + /// Should be written: + /// + /// ```rust.ignore + /// if x && y { + /// … + /// } + /// ``` + pub COLLAPSIBLE_IF, + style, + "nested `if`s that can be collapsed (e.g., `if x { if y { ... } }`" +} + +declare_clippy_lint! { + /// **What it does:** Checks for collapsible `else { if ... }` expressions + /// that can be collapsed to `else if ...`. + /// + /// **Why is this bad?** Each `if`-statement adds one level of nesting, which + /// makes code look more complex than it really is. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust,ignore /// /// if x { /// … @@ -54,24 +76,18 @@ declare_clippy_lint! { /// Should be written: /// /// ```rust.ignore - /// if x && y { - /// … - /// } - /// - /// // or - /// /// if x { /// … /// } else if y { /// … /// } /// ``` - pub COLLAPSIBLE_IF, + pub COLLAPSIBLE_ELSE_IF, style, - "`if`s that can be collapsed (e.g., `if x { if y { ... } }` and `else { if x { ... } }`)" + "nested `else`-`if` expressions that can be collapsed (e.g., `else { if x { ... } }`)" } -declare_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF]); +declare_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF, COLLAPSIBLE_ELSE_IF]); impl EarlyLintPass for CollapsibleIf { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { @@ -112,7 +128,7 @@ fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, else_: &ast::Expr) { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, - COLLAPSIBLE_IF, + COLLAPSIBLE_ELSE_IF, block.span, "this `else { if .. }` block can be collapsed", "collapse nested if block", diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 35b057d7b6a..4f41b6c77e2 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -556,6 +556,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &cargo_common_metadata::CARGO_COMMON_METADATA, &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, @@ -1384,6 +1385,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&booleans::LOGIC_BUG), LintId::of(&booleans::NONMINIMAL_BOOL), LintId::of(&bytecount::NAIVE_BYTECOUNT), + 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), @@ -1653,6 +1655,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&attrs::UNKNOWN_CLIPPY_LINTS), LintId::of(&blacklisted_name::BLACKLISTED_NAME), LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), + 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), diff --git a/tests/ui/collapsible_else_if.fixed b/tests/ui/collapsible_else_if.fixed index ce2a1c28c8a..fa4bc30e933 100644 --- a/tests/ui/collapsible_else_if.fixed +++ b/tests/ui/collapsible_else_if.fixed @@ -3,6 +3,8 @@ #[rustfmt::skip] #[warn(clippy::collapsible_if)] +#[warn(clippy::collapsible_else_if)] + fn main() { let x = "hello"; let y = "world"; diff --git a/tests/ui/collapsible_else_if.rs b/tests/ui/collapsible_else_if.rs index 99c40b8d38e..bf6c1d1f894 100644 --- a/tests/ui/collapsible_else_if.rs +++ b/tests/ui/collapsible_else_if.rs @@ -3,6 +3,8 @@ #[rustfmt::skip] #[warn(clippy::collapsible_if)] +#[warn(clippy::collapsible_else_if)] + fn main() { let x = "hello"; let y = "world"; diff --git a/tests/ui/collapsible_else_if.stderr b/tests/ui/collapsible_else_if.stderr index 3d1c458879e..ee3e11ae565 100644 --- a/tests/ui/collapsible_else_if.stderr +++ b/tests/ui/collapsible_else_if.stderr @@ -1,5 +1,5 @@ error: this `else { if .. }` block can be collapsed - --> $DIR/collapsible_else_if.rs:12:12 + --> $DIR/collapsible_else_if.rs:14:12 | LL | } else { | ____________^ @@ -9,7 +9,7 @@ LL | | } LL | | } | |_____^ | - = note: `-D clippy::collapsible-if` implied by `-D warnings` + = note: `-D clippy::collapsible-else-if` implied by `-D warnings` help: collapse nested if block | LL | } else if y == "world" { @@ -18,7 +18,7 @@ LL | } | error: this `else { if .. }` block can be collapsed - --> $DIR/collapsible_else_if.rs:20:12 + --> $DIR/collapsible_else_if.rs:22:12 | LL | } else { | ____________^ @@ -36,7 +36,7 @@ LL | } | error: this `else { if .. }` block can be collapsed - --> $DIR/collapsible_else_if.rs:28:12 + --> $DIR/collapsible_else_if.rs:30:12 | LL | } else { | ____________^ @@ -59,7 +59,7 @@ LL | } | error: this `else { if .. }` block can be collapsed - --> $DIR/collapsible_else_if.rs:39:12 + --> $DIR/collapsible_else_if.rs:41:12 | LL | } else { | ____________^ @@ -82,7 +82,7 @@ LL | } | error: this `else { if .. }` block can be collapsed - --> $DIR/collapsible_else_if.rs:50:12 + --> $DIR/collapsible_else_if.rs:52:12 | LL | } else { | ____________^ @@ -105,7 +105,7 @@ LL | } | error: this `else { if .. }` block can be collapsed - --> $DIR/collapsible_else_if.rs:61:12 + --> $DIR/collapsible_else_if.rs:63:12 | LL | } else { | ____________^ @@ -128,7 +128,7 @@ LL | } | error: this `else { if .. }` block can be collapsed - --> $DIR/collapsible_else_if.rs:72:12 + --> $DIR/collapsible_else_if.rs:74:12 | LL | } else { | ____________^ diff --git a/tests/ui/if_same_then_else2.rs b/tests/ui/if_same_then_else2.rs index 8d54f75b5d1..e83ce47e563 100644 --- a/tests/ui/if_same_then_else2.rs +++ b/tests/ui/if_same_then_else2.rs @@ -1,6 +1,7 @@ #![warn(clippy::if_same_then_else)] #![allow( clippy::blacklisted_name, + clippy::collapsible_else_if, 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 da2be6c8aa5..f98e30fa376 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:20:12 + --> $DIR/if_same_then_else2.rs:21:12 | LL | } else { | ____________^ @@ -13,7 +13,7 @@ LL | | } | = note: `-D clippy::if-same-then-else` implied by `-D warnings` note: same as this - --> $DIR/if_same_then_else2.rs:11:13 + --> $DIR/if_same_then_else2.rs:12:13 | LL | if true { | _____________^ @@ -26,7 +26,7 @@ LL | | } else { | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:34:12 + --> $DIR/if_same_then_else2.rs:35:12 | LL | } else { | ____________^ @@ -36,7 +36,7 @@ LL | | } | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:32:13 + --> $DIR/if_same_then_else2.rs:33:13 | LL | if true { | _____________^ @@ -45,7 +45,7 @@ LL | | } else { | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:41:12 + --> $DIR/if_same_then_else2.rs:42:12 | LL | } else { | ____________^ @@ -55,7 +55,7 @@ LL | | } | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:39:13 + --> $DIR/if_same_then_else2.rs:40:13 | LL | if true { | _____________^ @@ -64,7 +64,7 @@ LL | | } else { | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:91:12 + --> $DIR/if_same_then_else2.rs:92:12 | LL | } else { | ____________^ @@ -74,7 +74,7 @@ LL | | }; | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:89:21 + --> $DIR/if_same_then_else2.rs:90:21 | LL | let _ = if true { | _____________________^ @@ -83,7 +83,7 @@ LL | | } else { | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:98:12 + --> $DIR/if_same_then_else2.rs:99:12 | LL | } else { | ____________^ @@ -93,7 +93,7 @@ LL | | } | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:96:13 + --> $DIR/if_same_then_else2.rs:97:13 | LL | if true { | _____________^ @@ -102,7 +102,7 @@ LL | | } else { | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:123:12 + --> $DIR/if_same_then_else2.rs:124:12 | LL | } else { | ____________^ @@ -112,7 +112,7 @@ LL | | } | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:120:20 + --> $DIR/if_same_then_else2.rs:121:20 | LL | } else if true { | ____________________^ -- cgit 1.4.1-3-g733a5 From ba87acb44090412f5ace0a5ca655e8298d82b874 Mon Sep 17 00:00:00 2001 From: Benjamin Sparks Date: Sat, 26 Dec 2020 18:02:46 +0100 Subject: Implemented needless question mark lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 + clippy_lints/src/needless_question_mark.rs | 232 +++++++++++++++++++++++++++++ tests/ui/needless_question_mark.fixed | 163 ++++++++++++++++++++ tests/ui/needless_question_mark.rs | 163 ++++++++++++++++++++ tests/ui/needless_question_mark.stderr | 88 +++++++++++ tests/ui/try_err.fixed | 2 +- tests/ui/try_err.rs | 2 +- tests/ui/unit_arg.rs | 3 +- tests/ui/unit_arg.stderr | 20 +-- 10 files changed, 666 insertions(+), 13 deletions(-) create mode 100644 clippy_lints/src/needless_question_mark.rs create mode 100644 tests/ui/needless_question_mark.fixed create mode 100644 tests/ui/needless_question_mark.rs create mode 100644 tests/ui/needless_question_mark.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index de8da99cdee..41a92dd4c41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1973,6 +1973,7 @@ Released 2018-09-13 [`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main [`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes [`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value +[`needless_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark [`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop [`needless_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_return [`needless_update`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_update diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 35b057d7b6a..0299f8e12fa 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -271,6 +271,7 @@ mod needless_borrow; mod needless_borrowed_ref; mod needless_continue; mod needless_pass_by_value; +mod needless_question_mark; mod needless_update; mod neg_cmp_op_on_partial_ord; mod neg_multiply; @@ -799,6 +800,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE, &needless_continue::NEEDLESS_CONTINUE, &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, @@ -1019,6 +1021,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box from_over_into::FromOverInto::new(msrv)); store.register_late_pass(move || box use_self::UseSelf::new(msrv)); store.register_late_pass(move || box missing_const_for_fn::MissingConstForFn::new(msrv)); + store.register_late_pass(move || box needless_question_mark::NeedlessQuestionMark::new(msrv)); store.register_late_pass(|| box size_of_in_element_count::SizeOfInElementCount); store.register_late_pass(|| box map_clone::MapClone); @@ -1545,6 +1548,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&needless_bool::BOOL_COMPARISON), LintId::of(&needless_bool::NEEDLESS_BOOL), LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), + 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), @@ -1803,6 +1807,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&needless_bool::BOOL_COMPARISON), LintId::of(&needless_bool::NEEDLESS_BOOL), LintId::of(&needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), + 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), diff --git a/clippy_lints/src/needless_question_mark.rs b/clippy_lints/src/needless_question_mark.rs new file mode 100644 index 00000000000..783e6b716d4 --- /dev/null +++ b/clippy_lints/src/needless_question_mark.rs @@ -0,0 +1,232 @@ +use rustc_errors::Applicability; +use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; +use rustc_hir::{Body, Expr, ExprKind, LangItem, MatchSource, QPath}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::ty::DefIdTree; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::sym; + +use crate::utils; +use if_chain::if_chain; + +declare_clippy_lint! { + /// **What it does:** + /// Suggests alternatives for useless applications of `?` in terminating expressions + /// + /// **Why is this bad?** There's no reason to use ? to short-circuit when execution of the body will end there anyway. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// struct TO { + /// magic: Option, + /// } + /// + /// fn f(to: TO) -> Option { + /// Some(to.magic?) + /// } + /// + /// struct TR { + /// magic: Result, + /// } + /// + /// fn g(tr: Result) -> Result { + /// tr.and_then(|t| Ok(t.magic?)) + /// } + /// + /// ``` + /// Use instead: + /// ```rust + /// struct TO { + /// magic: Option, + /// } + /// + /// fn f(to: TO) -> Option { + /// to.magic + /// } + /// + /// struct TR { + /// magic: Result, + /// } + /// + /// fn g(tr: Result) -> Result { + /// tr.and_then(|t| t.magic) + /// } + /// ``` + pub NEEDLESS_QUESTION_MARK, + complexity, + "Suggest value.inner_option instead of Some(value.inner_option?). The same goes for Result." +} + +const NEEDLESS_QUESTION_MARK_RESULT_MSRV: RustcVersion = RustcVersion::new(1, 13, 0); +const NEEDLESS_QUESTION_MARK_OPTION_MSRV: RustcVersion = RustcVersion::new(1, 22, 0); + +pub struct NeedlessQuestionMark { + msrv: Option, +} + +impl NeedlessQuestionMark { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(NeedlessQuestionMark => [NEEDLESS_QUESTION_MARK]); + +#[derive(Debug)] +enum SomeOkCall<'a> { + SomeCall(&'a Expr<'a>, &'a Expr<'a>), + OkCall(&'a Expr<'a>, &'a Expr<'a>), +} + +impl LateLintPass<'_> for NeedlessQuestionMark { + /* + * The question mark operator is compatible with both Result and Option, + * from Rust 1.13 and 1.22 respectively. + */ + + /* + * What do we match: + * Expressions that look like this: + * Some(option?), Ok(result?) + * + * Where do we match: + * Last expression of a body + * Return statement + * A body's value (single line closure) + * + * What do we not match: + * Implicit calls to `from(..)` on the error value + */ + + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { + let e = match &expr.kind { + ExprKind::Ret(Some(e)) => e, + _ => return, + }; + + if let Some(ok_some_call) = is_some_or_ok_call(self, cx, e) { + emit_lint(cx, &ok_some_call); + } + } + + fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) { + // Function / Closure block + let expr_opt = if let ExprKind::Block(block, _) = &body.value.kind { + block.expr + } else { + // Single line closure + Some(&body.value) + }; + + if_chain! { + if let Some(expr) = expr_opt; + if let Some(ok_some_call) = is_some_or_ok_call(self, cx, expr); + then { + emit_lint(cx, &ok_some_call); + } + }; + } + + extract_msrv_attr!(LateContext); +} + +fn emit_lint(cx: &LateContext<'_>, expr: &SomeOkCall<'_>) { + let (entire_expr, inner_expr) = match expr { + SomeOkCall::OkCall(outer, inner) | SomeOkCall::SomeCall(outer, inner) => (outer, inner), + }; + + utils::span_lint_and_sugg( + cx, + NEEDLESS_QUESTION_MARK, + entire_expr.span, + "Question mark operator is useless here", + "try", + format!("{}", utils::snippet(cx, inner_expr.span, r#""...""#)), + Applicability::MachineApplicable, + ); +} + +fn is_some_or_ok_call<'a>( + nqml: &NeedlessQuestionMark, + cx: &'a LateContext<'_>, + expr: &'a Expr<'_>, +) -> Option> { + if_chain! { + // Check outer expression matches CALL_IDENT(ARGUMENT) format + if let ExprKind::Call(path, args) = &expr.kind; + if let ExprKind::Path(QPath::Resolved(None, path)) = &path.kind; + if is_some_ctor(cx, path.res) || is_ok_ctor(cx, path.res); + + // Extract inner expression from ARGUMENT + if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar) = &args[0].kind; + if let ExprKind::Call(called, args) = &inner_expr_with_q.kind; + if args.len() == 1; + + if let ExprKind::Path(QPath::LangItem(LangItem::TryIntoResult, _)) = &called.kind; + then { + // Extract inner expr type from match argument generated by + // question mark operator + let inner_expr = &args[0]; + + let inner_ty = cx.typeck_results().expr_ty(inner_expr); + let outer_ty = cx.typeck_results().expr_ty(expr); + + // Check if outer and inner type are Option + let outer_is_some = utils::is_type_diagnostic_item(cx, outer_ty, sym::option_type); + let inner_is_some = utils::is_type_diagnostic_item(cx, inner_ty, sym::option_type); + + // Check for Option MSRV + let meets_option_msrv = utils::meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_OPTION_MSRV); + if outer_is_some && inner_is_some && meets_option_msrv { + return Some(SomeOkCall::SomeCall(expr, inner_expr)); + } + + // Check if outer and inner type are Result + let outer_is_result = utils::is_type_diagnostic_item(cx, outer_ty, sym::result_type); + let inner_is_result = utils::is_type_diagnostic_item(cx, inner_ty, sym::result_type); + + // Additional check: if the error type of the Result can be converted + // via the From trait, then don't match + let does_not_call_from = !has_implicit_error_from(cx, expr, inner_expr); + + // Must meet Result MSRV + let meets_result_msrv = utils::meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_RESULT_MSRV); + if outer_is_result && inner_is_result && does_not_call_from && meets_result_msrv { + return Some(SomeOkCall::OkCall(expr, inner_expr)); + } + } + } + + None +} + +fn has_implicit_error_from(cx: &LateContext<'_>, entire_expr: &Expr<'_>, inner_result_expr: &Expr<'_>) -> bool { + return cx.typeck_results().expr_ty(entire_expr) != cx.typeck_results().expr_ty(inner_result_expr); +} + +fn is_ok_ctor(cx: &LateContext<'_>, res: Res) -> bool { + if let Some(ok_id) = cx.tcx.lang_items().result_ok_variant() { + if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res { + if let Some(variant_id) = cx.tcx.parent(id) { + return variant_id == ok_id; + } + } + } + false +} + +fn is_some_ctor(cx: &LateContext<'_>, res: Res) -> bool { + if let Some(some_id) = cx.tcx.lang_items().option_some_variant() { + if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res { + if let Some(variant_id) = cx.tcx.parent(id) { + return variant_id == some_id; + } + } + } + false +} diff --git a/tests/ui/needless_question_mark.fixed b/tests/ui/needless_question_mark.fixed new file mode 100644 index 00000000000..70218f3f041 --- /dev/null +++ b/tests/ui/needless_question_mark.fixed @@ -0,0 +1,163 @@ +// run-rustfix + +#![warn(clippy::needless_question_mark)] +#![allow(clippy::needless_return, clippy::unnecessary_unwrap, dead_code, unused_must_use)] +#![feature(custom_inner_attributes)] + +struct TO { + magic: Option, +} + +struct TR { + magic: Result, +} + +fn simple_option_bad1(to: TO) -> Option { + // return as a statement + return to.magic; +} + +// formatting will add a semi-colon, which would make +// this identical to the test case above +#[rustfmt::skip] +fn simple_option_bad2(to: TO) -> Option { + // return as an expression + return to.magic +} + +fn simple_option_bad3(to: TO) -> Option { + // block value "return" + to.magic +} + +fn simple_option_bad4(to: Option) -> Option { + // single line closure + to.and_then(|t| t.magic) +} + +// formatting this will remove the block brackets, making +// this test identical to the one above +#[rustfmt::skip] +fn simple_option_bad5(to: Option) -> Option { + // closure with body + to.and_then(|t| { + t.magic + }) +} + +fn simple_result_bad1(tr: TR) -> Result { + return tr.magic; +} + +// formatting will add a semi-colon, which would make +// this identical to the test case above +#[rustfmt::skip] +fn simple_result_bad2(tr: TR) -> Result { + return tr.magic +} + +fn simple_result_bad3(tr: TR) -> Result { + tr.magic +} + +fn simple_result_bad4(tr: Result) -> Result { + tr.and_then(|t| t.magic) +} + +// formatting this will remove the block brackets, making +// this test identical to the one above +#[rustfmt::skip] +fn simple_result_bad5(tr: Result) -> Result { + tr.and_then(|t| { + t.magic + }) +} + +fn also_bad(tr: Result) -> Result { + if tr.is_ok() { + let t = tr.unwrap(); + return t.magic; + } + Err(false) +} + +fn false_positive_test(x: Result<(), U>) -> Result<(), T> +where + T: From, +{ + Ok(x?) +} + +fn main() {} + +mod question_mark_none { + #![clippy::msrv = "1.12.0"] + fn needless_question_mark_option() -> Option { + struct TO { + magic: Option, + } + let to = TO { magic: None }; + Some(to.magic?) // should not be triggered + } + + fn needless_question_mark_result() -> Result { + struct TO { + magic: Result, + } + let to = TO { magic: Ok(1_usize) }; + Ok(to.magic?) // should not be triggered + } + + fn main() { + needless_question_mark_option(); + needless_question_mark_result(); + } +} + +mod question_mark_result { + #![clippy::msrv = "1.21.0"] + fn needless_question_mark_option() -> Option { + struct TO { + magic: Option, + } + let to = TO { magic: None }; + Some(to.magic?) // should not be triggered + } + + fn needless_question_mark_result() -> Result { + struct TO { + magic: Result, + } + let to = TO { magic: Ok(1_usize) }; + to.magic // should be triggered + } + + fn main() { + needless_question_mark_option(); + needless_question_mark_result(); + } +} + +mod question_mark_both { + #![clippy::msrv = "1.22.0"] + fn needless_question_mark_option() -> Option { + struct TO { + magic: Option, + } + let to = TO { magic: None }; + to.magic // should be triggered + } + + fn needless_question_mark_result() -> Result { + struct TO { + magic: Result, + } + let to = TO { magic: Ok(1_usize) }; + to.magic // should be triggered + } + + fn main() { + needless_question_mark_option(); + needless_question_mark_result(); + } +} diff --git a/tests/ui/needless_question_mark.rs b/tests/ui/needless_question_mark.rs new file mode 100644 index 00000000000..60ac2c8d72e --- /dev/null +++ b/tests/ui/needless_question_mark.rs @@ -0,0 +1,163 @@ +// run-rustfix + +#![warn(clippy::needless_question_mark)] +#![allow(clippy::needless_return, clippy::unnecessary_unwrap, dead_code, unused_must_use)] +#![feature(custom_inner_attributes)] + +struct TO { + magic: Option, +} + +struct TR { + magic: Result, +} + +fn simple_option_bad1(to: TO) -> Option { + // return as a statement + return Some(to.magic?); +} + +// formatting will add a semi-colon, which would make +// this identical to the test case above +#[rustfmt::skip] +fn simple_option_bad2(to: TO) -> Option { + // return as an expression + return Some(to.magic?) +} + +fn simple_option_bad3(to: TO) -> Option { + // block value "return" + Some(to.magic?) +} + +fn simple_option_bad4(to: Option) -> Option { + // single line closure + to.and_then(|t| Some(t.magic?)) +} + +// formatting this will remove the block brackets, making +// this test identical to the one above +#[rustfmt::skip] +fn simple_option_bad5(to: Option) -> Option { + // closure with body + to.and_then(|t| { + Some(t.magic?) + }) +} + +fn simple_result_bad1(tr: TR) -> Result { + return Ok(tr.magic?); +} + +// formatting will add a semi-colon, which would make +// this identical to the test case above +#[rustfmt::skip] +fn simple_result_bad2(tr: TR) -> Result { + return Ok(tr.magic?) +} + +fn simple_result_bad3(tr: TR) -> Result { + Ok(tr.magic?) +} + +fn simple_result_bad4(tr: Result) -> Result { + tr.and_then(|t| Ok(t.magic?)) +} + +// formatting this will remove the block brackets, making +// this test identical to the one above +#[rustfmt::skip] +fn simple_result_bad5(tr: Result) -> Result { + tr.and_then(|t| { + Ok(t.magic?) + }) +} + +fn also_bad(tr: Result) -> Result { + if tr.is_ok() { + let t = tr.unwrap(); + return Ok(t.magic?); + } + Err(false) +} + +fn false_positive_test(x: Result<(), U>) -> Result<(), T> +where + T: From, +{ + Ok(x?) +} + +fn main() {} + +mod question_mark_none { + #![clippy::msrv = "1.12.0"] + fn needless_question_mark_option() -> Option { + struct TO { + magic: Option, + } + let to = TO { magic: None }; + Some(to.magic?) // should not be triggered + } + + fn needless_question_mark_result() -> Result { + struct TO { + magic: Result, + } + let to = TO { magic: Ok(1_usize) }; + Ok(to.magic?) // should not be triggered + } + + fn main() { + needless_question_mark_option(); + needless_question_mark_result(); + } +} + +mod question_mark_result { + #![clippy::msrv = "1.21.0"] + fn needless_question_mark_option() -> Option { + struct TO { + magic: Option, + } + let to = TO { magic: None }; + Some(to.magic?) // should not be triggered + } + + fn needless_question_mark_result() -> Result { + struct TO { + magic: Result, + } + let to = TO { magic: Ok(1_usize) }; + Ok(to.magic?) // should be triggered + } + + fn main() { + needless_question_mark_option(); + needless_question_mark_result(); + } +} + +mod question_mark_both { + #![clippy::msrv = "1.22.0"] + fn needless_question_mark_option() -> Option { + struct TO { + magic: Option, + } + let to = TO { magic: None }; + Some(to.magic?) // should be triggered + } + + fn needless_question_mark_result() -> Result { + struct TO { + magic: Result, + } + let to = TO { magic: Ok(1_usize) }; + Ok(to.magic?) // should be triggered + } + + fn main() { + needless_question_mark_option(); + needless_question_mark_result(); + } +} diff --git a/tests/ui/needless_question_mark.stderr b/tests/ui/needless_question_mark.stderr new file mode 100644 index 00000000000..b4eb21882ec --- /dev/null +++ b/tests/ui/needless_question_mark.stderr @@ -0,0 +1,88 @@ +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:17:12 + | +LL | return Some(to.magic?); + | ^^^^^^^^^^^^^^^ help: try: `to.magic` + | + = note: `-D clippy::needless-question-mark` implied by `-D warnings` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:25:12 + | +LL | return Some(to.magic?) + | ^^^^^^^^^^^^^^^ help: try: `to.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:30:5 + | +LL | Some(to.magic?) + | ^^^^^^^^^^^^^^^ help: try: `to.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:35:21 + | +LL | to.and_then(|t| Some(t.magic?)) + | ^^^^^^^^^^^^^^ help: try: `t.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:44:9 + | +LL | Some(t.magic?) + | ^^^^^^^^^^^^^^ help: try: `t.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:49:12 + | +LL | return Ok(tr.magic?); + | ^^^^^^^^^^^^^ help: try: `tr.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:56:12 + | +LL | return Ok(tr.magic?) + | ^^^^^^^^^^^^^ help: try: `tr.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:60:5 + | +LL | Ok(tr.magic?) + | ^^^^^^^^^^^^^ help: try: `tr.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:64:21 + | +LL | tr.and_then(|t| Ok(t.magic?)) + | ^^^^^^^^^^^^ help: try: `t.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:72:9 + | +LL | Ok(t.magic?) + | ^^^^^^^^^^^^ help: try: `t.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:79:16 + | +LL | return Ok(t.magic?); + | ^^^^^^^^^^^^ help: try: `t.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:132:9 + | +LL | Ok(to.magic?) // should be triggered + | ^^^^^^^^^^^^^ help: try: `to.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:148:9 + | +LL | Some(to.magic?) // should be triggered + | ^^^^^^^^^^^^^^^ help: try: `to.magic` + +error: Question mark operator is useless here + --> $DIR/needless_question_mark.rs:156:9 + | +LL | Ok(to.magic?) // should be triggered + | ^^^^^^^^^^^^^ help: try: `to.magic` + +error: aborting due to 14 previous errors + diff --git a/tests/ui/try_err.fixed b/tests/ui/try_err.fixed index 652b611208b..5b96bb59c5f 100644 --- a/tests/ui/try_err.fixed +++ b/tests/ui/try_err.fixed @@ -2,7 +2,7 @@ // aux-build:macro_rules.rs #![deny(clippy::try_err)] -#![allow(clippy::unnecessary_wraps)] +#![allow(clippy::unnecessary_wraps, clippy::needless_question_mark)] #[macro_use] extern crate macro_rules; diff --git a/tests/ui/try_err.rs b/tests/ui/try_err.rs index 6bd479657b7..f220d697d2c 100644 --- a/tests/ui/try_err.rs +++ b/tests/ui/try_err.rs @@ -2,7 +2,7 @@ // aux-build:macro_rules.rs #![deny(clippy::try_err)] -#![allow(clippy::unnecessary_wraps)] +#![allow(clippy::unnecessary_wraps, clippy::needless_question_mark)] #[macro_use] extern crate macro_rules; diff --git a/tests/ui/unit_arg.rs b/tests/ui/unit_arg.rs index 9ad16d36509..b6a7bc5a1cc 100644 --- a/tests/ui/unit_arg.rs +++ b/tests/ui/unit_arg.rs @@ -5,7 +5,8 @@ unused_variables, clippy::unused_unit, clippy::unnecessary_wraps, - clippy::or_fun_call + clippy::or_fun_call, + clippy::needless_question_mark )] use std::fmt::Debug; diff --git a/tests/ui/unit_arg.stderr b/tests/ui/unit_arg.stderr index c3a839a9bf8..094cff8c985 100644 --- a/tests/ui/unit_arg.stderr +++ b/tests/ui/unit_arg.stderr @@ -1,5 +1,5 @@ error: passing a unit value to a function - --> $DIR/unit_arg.rs:30:5 + --> $DIR/unit_arg.rs:31:5 | LL | / foo({ LL | | 1; @@ -20,7 +20,7 @@ LL | foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:33:5 + --> $DIR/unit_arg.rs:34:5 | LL | foo(foo(1)); | ^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:34:5 + --> $DIR/unit_arg.rs:35:5 | LL | / foo({ LL | | foo(1); @@ -54,7 +54,7 @@ LL | foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:39:5 + --> $DIR/unit_arg.rs:40:5 | LL | / b.bar({ LL | | 1; @@ -74,7 +74,7 @@ LL | b.bar(()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:42:5 + --> $DIR/unit_arg.rs:43:5 | LL | taking_multiple_units(foo(0), foo(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -87,7 +87,7 @@ LL | taking_multiple_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:43:5 + --> $DIR/unit_arg.rs:44:5 | LL | / taking_multiple_units(foo(0), { LL | | foo(1); @@ -110,7 +110,7 @@ LL | taking_multiple_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:47:5 + --> $DIR/unit_arg.rs:48:5 | LL | / taking_multiple_units( LL | | { @@ -140,7 +140,7 @@ LL | foo(2); ... error: passing a unit value to a function - --> $DIR/unit_arg.rs:58:13 + --> $DIR/unit_arg.rs:59:13 | LL | None.or(Some(foo(2))); | ^^^^^^^^^^^^ @@ -154,7 +154,7 @@ LL | }); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:61:5 + --> $DIR/unit_arg.rs:62:5 | LL | foo(foo(())) | ^^^^^^^^^^^^ @@ -166,7 +166,7 @@ LL | foo(()) | error: passing a unit value to a function - --> $DIR/unit_arg.rs:94:5 + --> $DIR/unit_arg.rs:95:5 | LL | Some(foo(1)) | ^^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From bc97f5d2156e2fc42ff1a69ccbad9adb2b4568fb Mon Sep 17 00:00:00 2001 From: nahuakang Date: Mon, 4 Jan 2021 17:47:59 +0100 Subject: Address flip1995's review comments --- clippy_lints/src/empty_enum.rs | 15 +++++++++------ tests/ui/empty_enum_without_never_type.rs | 7 +++++++ 2 files changed, 16 insertions(+), 6 deletions(-) create mode 100644 tests/ui/empty_enum_without_never_type.rs (limited to 'tests') diff --git a/clippy_lints/src/empty_enum.rs b/clippy_lints/src/empty_enum.rs index 557a7c6ba98..4533d6447a7 100644 --- a/clippy_lints/src/empty_enum.rs +++ b/clippy_lints/src/empty_enum.rs @@ -8,12 +8,12 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// **What it does:** Checks for `enum`s with no variants. /// - /// As of this writing, the never type is still a + /// As of this writing, the `never_type` is still a /// nightly-only experimental API. Therefore, this lint is only triggered - /// if the never type is enabled + /// if the `never_type` is enabled. /// /// **Why is this bad?** If you want to introduce a type which - /// can't be instantiated, you should use `!` (the never type), + /// can't be instantiated, you should use `!` (the primitive type never), /// or a wrapper around it, because `!` has more extensive /// compiler support (type inference, etc...) and wrappers /// around it are the conventional way to define an uninhabited type. @@ -44,13 +44,16 @@ declare_lint_pass!(EmptyEnum => [EMPTY_ENUM]); impl<'tcx> LateLintPass<'tcx> for EmptyEnum { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + // Only suggest the `never_type` if the feature is enabled + if !cx.tcx.features().never_type { + return; + } + let did = cx.tcx.hir().local_def_id(item.hir_id); if let ItemKind::Enum(..) = item.kind { let ty = cx.tcx.type_of(did); let adt = ty.ty_adt_def().expect("already checked whether this is an enum"); - - // Only suggest the never type if the feature is enabled - if adt.variants.is_empty() && cx.tcx.features().never_type { + if adt.variants.is_empty() { span_lint_and_help( cx, EMPTY_ENUM, diff --git a/tests/ui/empty_enum_without_never_type.rs b/tests/ui/empty_enum_without_never_type.rs new file mode 100644 index 00000000000..4cbdfc47910 --- /dev/null +++ b/tests/ui/empty_enum_without_never_type.rs @@ -0,0 +1,7 @@ +#![allow(dead_code)] +#![warn(clippy::empty_enum)] + +// `never_type` is not enabled; this test has no stderr file +enum Empty {} + +fn main() {} \ No newline at end of file -- cgit 1.4.1-3-g733a5 From a8d47b4b78a6e6a23dc3320ad3c9e6308f6d1934 Mon Sep 17 00:00:00 2001 From: nahuakang Date: Mon, 4 Jan 2021 18:41:42 +0100 Subject: Run cargo dev fmt --- clippy_lints/src/empty_enum.rs | 2 +- tests/ui/empty_enum_without_never_type.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/empty_enum.rs b/clippy_lints/src/empty_enum.rs index 4533d6447a7..853b3afdc3a 100644 --- a/clippy_lints/src/empty_enum.rs +++ b/clippy_lints/src/empty_enum.rs @@ -13,7 +13,7 @@ declare_clippy_lint! { /// if the `never_type` is enabled. /// /// **Why is this bad?** If you want to introduce a type which - /// can't be instantiated, you should use `!` (the primitive type never), + /// can't be instantiated, you should use `!` (the primitive type "never"), /// or a wrapper around it, because `!` has more extensive /// compiler support (type inference, etc...) and wrappers /// around it are the conventional way to define an uninhabited type. diff --git a/tests/ui/empty_enum_without_never_type.rs b/tests/ui/empty_enum_without_never_type.rs index 4cbdfc47910..386677352e2 100644 --- a/tests/ui/empty_enum_without_never_type.rs +++ b/tests/ui/empty_enum_without_never_type.rs @@ -4,4 +4,4 @@ // `never_type` is not enabled; this test has no stderr file enum Empty {} -fn main() {} \ No newline at end of file +fn main() {} -- cgit 1.4.1-3-g733a5 From 4b478a5731956de7d19db9ac76fed81b2ae9db1c Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Mon, 4 Jan 2021 10:34:11 +1300 Subject: Add a new lint `ptr_as_ptr`, which checks for `as` casts between raw pointers without changing its mutability and suggest replacing it with `pointer::cast`. --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 3 ++ clippy_lints/src/types.rs | 99 ++++++++++++++++++++++++++++++++++++++++++++-- tests/ui/ptr_as_ptr.fixed | 30 ++++++++++++++ tests/ui/ptr_as_ptr.rs | 30 ++++++++++++++ tests/ui/ptr_as_ptr.stderr | 34 ++++++++++++++++ 6 files changed, 193 insertions(+), 4 deletions(-) create mode 100644 tests/ui/ptr_as_ptr.fixed create mode 100644 tests/ui/ptr_as_ptr.rs create mode 100644 tests/ui/ptr_as_ptr.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 38c943fec00..64864c2e278 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2141,6 +2141,7 @@ Released 2018-09-13 [`print_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_with_newline [`println_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#println_empty_string [`ptr_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_arg +[`ptr_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr [`ptr_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_eq [`ptr_offset_with_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast [`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f57c6bd6324..37a56bc20c8 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -912,6 +912,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &types::LET_UNIT_VALUE, &types::LINKEDLIST, &types::OPTION_OPTION, + &types::PTR_AS_PTR, &types::RC_BUFFER, &types::REDUNDANT_ALLOCATION, &types::TYPE_COMPLEXITY, @@ -1222,6 +1223,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box strings::StringToString); store.register_late_pass(|| box zero_sized_map_values::ZeroSizedMapValues); store.register_late_pass(|| box vec_init_then_push::VecInitThenPush::default()); + store.register_late_pass(move || box types::PtrAsPtr::new(msrv)); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1348,6 +1350,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::LET_UNIT_VALUE), LintId::of(&types::LINKEDLIST), LintId::of(&types::OPTION_OPTION), + LintId::of(&types::PTR_AS_PTR), LintId::of(&unicode::NON_ASCII_LITERAL), LintId::of(&unicode::UNICODE_NOT_NFC), LintId::of(&unnested_or_patterns::UNNESTED_OR_PATTERNS), diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index fd74783335d..d9cf26ad4b3 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -19,7 +19,8 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::TypeFoldable; -use rustc_middle::ty::{self, InferTy, Ty, TyCtxt, TyS, TypeckResults}; +use rustc_middle::ty::{self, InferTy, Ty, TyCtxt, TyS, TypeAndMut, TypeckResults}; +use rustc_semver::RustcVersion; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::Span; @@ -30,11 +31,13 @@ use rustc_typeck::hir_ty_to_ty; use crate::consts::{constant, Constant}; use crate::utils::paths; +use crate::utils::sugg::Sugg; use crate::utils::{ clip, comparisons, differing_macro_contexts, higher, in_constant, indent_of, int_bits, is_type_diagnostic_item, - last_path_segment, match_def_path, match_path, method_chain_args, multispan_sugg, numeric_literal::NumericLiteral, - qpath_res, reindent_multiline, sext, snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite, - span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext, + last_path_segment, match_def_path, match_path, meets_msrv, method_chain_args, multispan_sugg, + numeric_literal::NumericLiteral, qpath_res, reindent_multiline, sext, snippet, snippet_opt, + snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, + span_lint_and_then, unsext, }; declare_clippy_lint! { @@ -2878,3 +2881,91 @@ impl<'tcx> LateLintPass<'tcx> for RefToMut { } } } + +const PTR_AS_PTR_MSRV: RustcVersion = RustcVersion::new(1, 38, 0); + +declare_clippy_lint! { + /// **What it does:** + /// Checks for `as` casts between raw pointers without changing its mutability, + /// namely `*const T` to `*const U` and `*mut T` to `*mut U`. + /// + /// **Why is this bad?** + /// Though `as` casts between raw pointers is not terrible, `pointer::cast` is safer because + /// it cannot accidentally change the pointer's mutability nor cast the pointer to other types like `usize`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let ptr: *const u32 = &42_u32; + /// let mut_ptr: *mut u32 = &mut 42_u32; + /// let _ = ptr as *const i32; + /// let _ = mut_ptr as *mut i32; + /// ``` + /// Use instead: + /// ```rust + /// let ptr: *const u32 = &42_u32; + /// let mut_ptr: *mut u32 = &mut 42_u32; + /// let _ = ptr.cast::(); + /// let _ = mut_ptr.cast::(); + /// ``` + pub PTR_AS_PTR, + pedantic, + "casting using `as` from and to raw pointers that doesn't change its mutability, where `pointer::cast` could take the place of `as`" +} + +pub struct PtrAsPtr { + msrv: Option, +} + +impl PtrAsPtr { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(PtrAsPtr => [PTR_AS_PTR]); + +impl<'tcx> LateLintPass<'tcx> for PtrAsPtr { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if !meets_msrv(self.msrv.as_ref(), &PTR_AS_PTR_MSRV) { + return; + } + + if expr.span.from_expansion() { + return; + } + + if_chain! { + if let ExprKind::Cast(cast_expr, cast_to_hir_ty) = expr.kind; + let (cast_from, cast_to) = (cx.typeck_results().expr_ty(cast_expr), cx.typeck_results().expr_ty(expr)); + if let ty::RawPtr(TypeAndMut { mutbl: from_mutbl, .. }) = cast_from.kind(); + if let ty::RawPtr(TypeAndMut { ty: to_pointee_ty, mutbl: to_mutbl }) = cast_to.kind(); + if matches!((from_mutbl, to_mutbl), + (Mutability::Not, Mutability::Not) | (Mutability::Mut, Mutability::Mut)); + // The `U` in `pointer::cast` have to be `Sized` + // as explained here: https://github.com/rust-lang/rust/issues/60602. + if to_pointee_ty.is_sized(cx.tcx.at(expr.span), cx.param_env); + then { + let mut applicability = Applicability::MachineApplicable; + let cast_expr_sugg = Sugg::hir_with_applicability(cx, cast_expr, "_", &mut applicability); + let turbofish = match &cast_to_hir_ty.kind { + TyKind::Infer => Cow::Borrowed(""), + TyKind::Ptr(mut_ty) if matches!(mut_ty.ty.kind, TyKind::Infer) => Cow::Borrowed(""), + _ => Cow::Owned(format!("::<{}>", to_pointee_ty)), + }; + span_lint_and_sugg( + cx, + PTR_AS_PTR, + expr.span, + "`as` casting between raw pointers without changing its mutability", + "try `pointer::cast`, a safer alternative", + format!("{}.cast{}()", cast_expr_sugg.maybe_par(), turbofish), + applicability, + ); + } + } + } +} diff --git a/tests/ui/ptr_as_ptr.fixed b/tests/ui/ptr_as_ptr.fixed new file mode 100644 index 00000000000..e0b79004cbd --- /dev/null +++ b/tests/ui/ptr_as_ptr.fixed @@ -0,0 +1,30 @@ +// run-rustfix + +#![warn(clippy::ptr_as_ptr)] + +fn main() { + let ptr: *const u32 = &42_u32; + let mut_ptr: *mut u32 = &mut 42_u32; + + let _ = ptr.cast::(); + let _ = mut_ptr.cast::(); + + // Make sure the lint can handle the difference in their operator precedences. + unsafe { + let ptr_ptr: *const *const u32 = &ptr; + let _ = (*ptr_ptr).cast::(); + } + + // Changes in mutability. Do not lint this. + let _ = ptr as *mut i32; + let _ = mut_ptr as *const i32; + + // `pointer::cast` cannot perform unsized coercions unlike `as`. Do not lint this. + let ptr_of_array: *const [u32; 4] = &[1, 2, 3, 4]; + let _ = ptr_of_array as *const [u32]; + let _ = ptr_of_array as *const dyn std::fmt::Debug; + + // Ensure the lint doesn't produce unnecessary turbofish for inferred types. + let _: *const i32 = ptr.cast(); + let _: *mut i32 = mut_ptr.cast(); +} diff --git a/tests/ui/ptr_as_ptr.rs b/tests/ui/ptr_as_ptr.rs new file mode 100644 index 00000000000..f31940dbd1f --- /dev/null +++ b/tests/ui/ptr_as_ptr.rs @@ -0,0 +1,30 @@ +// run-rustfix + +#![warn(clippy::ptr_as_ptr)] + +fn main() { + let ptr: *const u32 = &42_u32; + let mut_ptr: *mut u32 = &mut 42_u32; + + let _ = ptr as *const i32; + let _ = mut_ptr as *mut i32; + + // Make sure the lint can handle the difference in their operator precedences. + unsafe { + let ptr_ptr: *const *const u32 = &ptr; + let _ = *ptr_ptr as *const i32; + } + + // Changes in mutability. Do not lint this. + let _ = ptr as *mut i32; + let _ = mut_ptr as *const i32; + + // `pointer::cast` cannot perform unsized coercions unlike `as`. Do not lint this. + let ptr_of_array: *const [u32; 4] = &[1, 2, 3, 4]; + let _ = ptr_of_array as *const [u32]; + let _ = ptr_of_array as *const dyn std::fmt::Debug; + + // Ensure the lint doesn't produce unnecessary turbofish for inferred types. + let _: *const i32 = ptr as *const _; + let _: *mut i32 = mut_ptr as _; +} diff --git a/tests/ui/ptr_as_ptr.stderr b/tests/ui/ptr_as_ptr.stderr new file mode 100644 index 00000000000..22217e18c54 --- /dev/null +++ b/tests/ui/ptr_as_ptr.stderr @@ -0,0 +1,34 @@ +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:9:13 + | +LL | let _ = ptr as *const i32; + | ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::()` + | + = note: `-D clippy::ptr-as-ptr` implied by `-D warnings` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:10:13 + | +LL | let _ = mut_ptr as *mut i32; + | ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:15:17 + | +LL | let _ = *ptr_ptr as *const i32; + | ^^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `(*ptr_ptr).cast::()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:28:25 + | +LL | let _: *const i32 = ptr as *const _; + | ^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:29:23 + | +LL | let _: *mut i32 = mut_ptr as _; + | ^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast()` + +error: aborting due to 5 previous errors + -- cgit 1.4.1-3-g733a5 From dfa5d7e818c32ea48bc141799276f700ab9d8fb7 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Tue, 5 Jan 2021 10:17:31 +1300 Subject: Fix the MSRV and add the tests for MSRV --- clippy_lints/src/types.rs | 2 ++ tests/ui/ptr_as_ptr.fixed | 20 ++++++++++++++++++++ tests/ui/ptr_as_ptr.rs | 20 ++++++++++++++++++++ tests/ui/ptr_as_ptr.stderr | 24 ++++++++++++++++++------ 4 files changed, 60 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index d9cf26ad4b3..b21f81bd517 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -2968,4 +2968,6 @@ impl<'tcx> LateLintPass<'tcx> for PtrAsPtr { } } } + + extract_msrv_attr!(LateContext); } diff --git a/tests/ui/ptr_as_ptr.fixed b/tests/ui/ptr_as_ptr.fixed index e0b79004cbd..8346a9454f4 100644 --- a/tests/ui/ptr_as_ptr.fixed +++ b/tests/ui/ptr_as_ptr.fixed @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::ptr_as_ptr)] +#![feature(custom_inner_attributes)] fn main() { let ptr: *const u32 = &42_u32; @@ -28,3 +29,22 @@ fn main() { let _: *const i32 = ptr.cast(); let _: *mut i32 = mut_ptr.cast(); } + +fn _msrv_1_37() { + #![clippy::msrv = "1.37"] + let ptr: *const u32 = &42_u32; + let mut_ptr: *mut u32 = &mut 42_u32; + + // `pointer::cast` was stabilized in 1.38. Do not lint this + let _ = ptr as *const i32; + let _ = mut_ptr as *mut i32; +} + +fn _msrv_1_38() { + #![clippy::msrv = "1.38"] + let ptr: *const u32 = &42_u32; + let mut_ptr: *mut u32 = &mut 42_u32; + + let _ = ptr.cast::(); + let _ = mut_ptr.cast::(); +} diff --git a/tests/ui/ptr_as_ptr.rs b/tests/ui/ptr_as_ptr.rs index f31940dbd1f..b68d4bc0aac 100644 --- a/tests/ui/ptr_as_ptr.rs +++ b/tests/ui/ptr_as_ptr.rs @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::ptr_as_ptr)] +#![feature(custom_inner_attributes)] fn main() { let ptr: *const u32 = &42_u32; @@ -28,3 +29,22 @@ fn main() { let _: *const i32 = ptr as *const _; let _: *mut i32 = mut_ptr as _; } + +fn _msrv_1_37() { + #![clippy::msrv = "1.37"] + let ptr: *const u32 = &42_u32; + let mut_ptr: *mut u32 = &mut 42_u32; + + // `pointer::cast` was stabilized in 1.38. Do not lint this + let _ = ptr as *const i32; + let _ = mut_ptr as *mut i32; +} + +fn _msrv_1_38() { + #![clippy::msrv = "1.38"] + let ptr: *const u32 = &42_u32; + let mut_ptr: *mut u32 = &mut 42_u32; + + let _ = ptr as *const i32; + let _ = mut_ptr as *mut i32; +} diff --git a/tests/ui/ptr_as_ptr.stderr b/tests/ui/ptr_as_ptr.stderr index 22217e18c54..854906dc111 100644 --- a/tests/ui/ptr_as_ptr.stderr +++ b/tests/ui/ptr_as_ptr.stderr @@ -1,5 +1,5 @@ error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:9:13 + --> $DIR/ptr_as_ptr.rs:10:13 | LL | let _ = ptr as *const i32; | ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::()` @@ -7,28 +7,40 @@ LL | let _ = ptr as *const i32; = note: `-D clippy::ptr-as-ptr` implied by `-D warnings` error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:10:13 + --> $DIR/ptr_as_ptr.rs:11:13 | LL | let _ = mut_ptr as *mut i32; | ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::()` error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:15:17 + --> $DIR/ptr_as_ptr.rs:16:17 | LL | let _ = *ptr_ptr as *const i32; | ^^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `(*ptr_ptr).cast::()` error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:28:25 + --> $DIR/ptr_as_ptr.rs:29:25 | LL | let _: *const i32 = ptr as *const _; | ^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast()` error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:29:23 + --> $DIR/ptr_as_ptr.rs:30:23 | LL | let _: *mut i32 = mut_ptr as _; | ^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast()` -error: aborting due to 5 previous errors +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:48:13 + | +LL | let _ = ptr as *const i32; + | ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:49:13 + | +LL | let _ = mut_ptr as *mut i32; + | ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::()` + +error: aborting due to 7 previous errors -- cgit 1.4.1-3-g733a5 From 61f3d9d46b5cbfdb56069a95e5839abe8fda9acf Mon Sep 17 00:00:00 2001 From: Javier Alvarez Date: Wed, 23 Dec 2020 12:37:37 +0100 Subject: Add case_sensitive_file_extensions lint Closes #6425 Looks for ends_with methods calls with case sensitive extensions. --- CHANGELOG.md | 1 + clippy_lints/Cargo.toml | 2 + .../case_sensitive_file_extension_comparisons.rs | 88 ++++++++++++++++++++++ clippy_lints/src/lib.rs | 4 + .../case_sensitive_file_extension_comparisons.rs | 44 +++++++++++ ...ase_sensitive_file_extension_comparisons.stderr | 43 +++++++++++ 6 files changed, 182 insertions(+) create mode 100644 clippy_lints/src/case_sensitive_file_extension_comparisons.rs create mode 100644 tests/ui/case_sensitive_file_extension_comparisons.rs create mode 100644 tests/ui/case_sensitive_file_extension_comparisons.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 64864c2e278..b0e9ad55b4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1878,6 +1878,7 @@ Released 2018-09-13 [`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local [`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow [`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata +[`case_sensitive_file_extension_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#case_sensitive_file_extension_comparisons [`cast_lossless`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless [`cast_possible_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_truncation [`cast_possible_wrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_wrap diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index a9516560a61..38098f8a14c 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -34,6 +34,8 @@ rustc-semver="1.1.0" url = { version = "2.1.0", features = ["serde"] } quote = "1" syn = { version = "1", features = ["full"] } +regex = "1.4" +lazy_static = "1.4" [features] deny-warnings = [] diff --git a/clippy_lints/src/case_sensitive_file_extension_comparisons.rs b/clippy_lints/src/case_sensitive_file_extension_comparisons.rs new file mode 100644 index 00000000000..b227e9a981a --- /dev/null +++ b/clippy_lints/src/case_sensitive_file_extension_comparisons.rs @@ -0,0 +1,88 @@ +use crate::utils::paths::STRING; +use crate::utils::{match_def_path, span_lint_and_help}; +use if_chain::if_chain; +use lazy_static::lazy_static; +use regex::Regex; +use rustc_ast::ast::LitKind; +use rustc_hir::{Expr, ExprKind, PathSegment}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{source_map::Spanned, Span}; + +declare_clippy_lint! { + /// **What it does:** + /// Checks for calls to `ends_with` with possible file extensions + /// and suggests to use a case-insensitive approach instead. + /// + /// **Why is this bad?** + /// `ends_with` is case-sensitive and may not detect files with a valid extension. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// fn is_rust_file(filename: &str) -> bool { + /// filename.ends_with(".rs") + /// } + /// ``` + /// Use instead: + /// ```rust + /// fn is_rust_file(filename: &str) -> bool { + /// filename.rsplit('.').next().map(|ext| ext.eq_ignore_ascii_case("rs")) == Some(true) + /// } + /// ``` + pub CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, + pedantic, + "default lint description" +} + +declare_lint_pass!(CaseSensitiveFileExtensionComparisons => [CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS]); + +fn check_case_sensitive_file_extension_comparison(ctx: &LateContext<'_>, expr: &Expr<'_>) -> Option { + lazy_static! { + static ref RE: Regex = Regex::new(r"^\.([a-z0-9]{1,5}|[A-Z0-9]{1,5})$").unwrap(); + } + if_chain! { + if let ExprKind::MethodCall(PathSegment { ident, .. }, _, [obj, extension, ..], span) = expr.kind; + if ident.as_str() == "ends_with"; + if let ExprKind::Lit(Spanned { node: LitKind::Str(ext_literal, ..), ..}) = extension.kind; + if RE.is_match(&ext_literal.as_str()); + then { + let mut ty = ctx.typeck_results().expr_ty(obj); + ty = match ty.kind() { + ty::Ref(_, ty, ..) => ty, + _ => ty + }; + + match ty.kind() { + ty::Str => { + return Some(span); + }, + ty::Adt(&ty::AdtDef { did, .. }, _) => { + if match_def_path(ctx, did, &STRING) { + return Some(span); + } + }, + _ => { return None; } + } + } + } + None +} + +impl LateLintPass<'tcx> for CaseSensitiveFileExtensionComparisons { + fn check_expr(&mut self, ctx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if let Some(span) = check_case_sensitive_file_extension_comparison(ctx, expr) { + span_lint_and_help( + ctx, + CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, + span, + "case-sensitive file extension comparison", + None, + "consider using a case-insensitive comparison instead", + ); + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 37a56bc20c8..ec433acf038 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -170,6 +170,7 @@ mod blocks_in_if_conditions; mod booleans; mod bytecount; mod cargo_common_metadata; +mod case_sensitive_file_extension_comparisons; mod checked_conversions; mod cognitive_complexity; mod collapsible_if; @@ -556,6 +557,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &booleans::NONMINIMAL_BOOL, &bytecount::NAIVE_BYTECOUNT, &cargo_common_metadata::CARGO_COMMON_METADATA, + &case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, &checked_conversions::CHECKED_CONVERSIONS, &cognitive_complexity::COGNITIVE_COMPLEXITY, &collapsible_if::COLLAPSIBLE_ELSE_IF, @@ -1224,6 +1226,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box zero_sized_map_values::ZeroSizedMapValues); store.register_late_pass(|| box vec_init_then_push::VecInitThenPush::default()); store.register_late_pass(move || box types::PtrAsPtr::new(msrv)); + store.register_late_pass(|| box case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1281,6 +1284,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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(&case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS), LintId::of(&checked_conversions::CHECKED_CONVERSIONS), LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION), LintId::of(©_iterator::COPY_ITERATOR), diff --git a/tests/ui/case_sensitive_file_extension_comparisons.rs b/tests/ui/case_sensitive_file_extension_comparisons.rs new file mode 100644 index 00000000000..68719c2bc6d --- /dev/null +++ b/tests/ui/case_sensitive_file_extension_comparisons.rs @@ -0,0 +1,44 @@ +#![warn(clippy::case_sensitive_file_extension_comparisons)] + +use std::string::String; + +struct TestStruct {} + +impl TestStruct { + fn ends_with(self, arg: &str) {} +} + +fn is_rust_file(filename: &str) -> bool { + filename.ends_with(".rs") +} + +fn main() { + // std::string::String and &str should trigger the lint failure with .ext12 + let _ = String::from("").ends_with(".ext12"); + let _ = "str".ends_with(".ext12"); + + // The test struct should not trigger the lint failure with .ext12 + TestStruct {}.ends_with(".ext12"); + + // std::string::String and &str should trigger the lint failure with .EXT12 + let _ = String::from("").ends_with(".EXT12"); + let _ = "str".ends_with(".EXT12"); + + // The test struct should not trigger the lint failure with .EXT12 + TestStruct {}.ends_with(".EXT12"); + + // Should not trigger the lint failure with .eXT12 + let _ = String::from("").ends_with(".eXT12"); + let _ = "str".ends_with(".eXT12"); + TestStruct {}.ends_with(".eXT12"); + + // Should not trigger the lint failure with .EXT123 (too long) + let _ = String::from("").ends_with(".EXT123"); + let _ = "str".ends_with(".EXT123"); + TestStruct {}.ends_with(".EXT123"); + + // Shouldn't fail if it doesn't start with a dot + let _ = String::from("").ends_with("a.ext"); + let _ = "str".ends_with("a.extA"); + TestStruct {}.ends_with("a.ext"); +} diff --git a/tests/ui/case_sensitive_file_extension_comparisons.stderr b/tests/ui/case_sensitive_file_extension_comparisons.stderr new file mode 100644 index 00000000000..05b98169f2d --- /dev/null +++ b/tests/ui/case_sensitive_file_extension_comparisons.stderr @@ -0,0 +1,43 @@ +error: case-sensitive file extension comparison + --> $DIR/case_sensitive_file_extension_comparisons.rs:12:14 + | +LL | filename.ends_with(".rs") + | ^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::case-sensitive-file-extension-comparisons` implied by `-D warnings` + = help: consider using a case-insensitive comparison instead + +error: case-sensitive file extension comparison + --> $DIR/case_sensitive_file_extension_comparisons.rs:17:30 + | +LL | let _ = String::from("").ends_with(".ext12"); + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a case-insensitive comparison instead + +error: case-sensitive file extension comparison + --> $DIR/case_sensitive_file_extension_comparisons.rs:18:19 + | +LL | let _ = "str".ends_with(".ext12"); + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a case-insensitive comparison instead + +error: case-sensitive file extension comparison + --> $DIR/case_sensitive_file_extension_comparisons.rs:24:30 + | +LL | let _ = String::from("").ends_with(".EXT12"); + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a case-insensitive comparison instead + +error: case-sensitive file extension comparison + --> $DIR/case_sensitive_file_extension_comparisons.rs:25:19 + | +LL | let _ = "str".ends_with(".EXT12"); + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider using a case-insensitive comparison instead + +error: aborting due to 5 previous errors + -- cgit 1.4.1-3-g733a5 From 1527fb61b9352c15d87ebcbc6f786baa2d390bcd Mon Sep 17 00:00:00 2001 From: Javier Alvarez Date: Wed, 23 Dec 2020 16:31:04 +0100 Subject: Fix case-sensitive extension check --- tests/compile-test.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/compile-test.rs b/tests/compile-test.rs index ec3af94b9ca..2b4ecc73150 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -44,7 +44,9 @@ fn third_party_crates() -> String { }; if let Some(name) = path.file_name().and_then(OsStr::to_str) { for dep in CRATES { - if name.starts_with(&format!("lib{}-", dep)) && name.ends_with(".rlib") { + if name.starts_with(&format!("lib{}-", dep)) + && name.rsplit('.').next().map(|ext| ext.eq_ignore_ascii_case("rlib")) == Some(true) + { if let Some(old) = crates.insert(dep, path.clone()) { panic!("Found multiple rlibs for crate `{}`: `{:?}` and `{:?}", dep, old, path); } -- cgit 1.4.1-3-g733a5 From 92f2bbbe06b92ad9fc984804307958d8566cd4ed Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Tue, 5 Jan 2021 20:11:37 +0100 Subject: Fix macro issues with field_reassign_with_default --- clippy_lints/src/default.rs | 3 +++ tests/ui/auxiliary/macro_rules.rs | 16 ++++++++++++ tests/ui/auxiliary/proc_macro_derive.rs | 18 +++++++++++++ tests/ui/field_reassign_with_default.rs | 16 ++++++++++++ tests/ui/field_reassign_with_default.stderr | 40 ++++++++++++++--------------- 5 files changed, 73 insertions(+), 20 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index 9fa06d7cde9..f7224811e6e 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -8,6 +8,7 @@ use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::{Ident, Symbol}; @@ -120,6 +121,8 @@ impl LateLintPass<'_> for Default { // only take `let ...` statements if let StmtKind::Local(local) = stmt.kind; if let Some(expr) = local.init; + if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id); + if !in_external_macro(cx.tcx.sess, expr.span); // only take bindings to identifiers if let PatKind::Binding(_, binding_id, ident, _) = local.pat.kind; // only when assigning `... = Default::default()` diff --git a/tests/ui/auxiliary/macro_rules.rs b/tests/ui/auxiliary/macro_rules.rs index 18324823468..d6ecd8568ce 100644 --- a/tests/ui/auxiliary/macro_rules.rs +++ b/tests/ui/auxiliary/macro_rules.rs @@ -94,3 +94,19 @@ macro_rules! large_enum_variant { } }; } + +#[macro_export] +macro_rules! field_reassign_with_default { + () => { + #[derive(Default)] + struct A { + pub i: i32, + pub j: i64, + } + fn lint() { + let mut a: A = Default::default(); + a.i = 42; + a; + } + }; +} diff --git a/tests/ui/auxiliary/proc_macro_derive.rs b/tests/ui/auxiliary/proc_macro_derive.rs index 7c4e4a14551..24891682d36 100644 --- a/tests/ui/auxiliary/proc_macro_derive.rs +++ b/tests/ui/auxiliary/proc_macro_derive.rs @@ -4,6 +4,7 @@ #![crate_type = "proc-macro"] #![feature(repr128, proc_macro_quote)] #![allow(incomplete_features)] +#![allow(clippy::field_reassign_with_default)] #![allow(clippy::eq_op)] extern crate proc_macro; @@ -23,3 +24,20 @@ pub fn derive(_: TokenStream) -> TokenStream { }; output } + +#[proc_macro_derive(FieldReassignWithDefault)] +pub fn derive_foo(_input: TokenStream) -> TokenStream { + quote! { + #[derive(Default)] + struct A { + pub i: i32, + pub j: i64, + } + #[automatically_derived] + fn lint() { + let mut a: A = Default::default(); + a.i = 42; + a; + } + } +} diff --git a/tests/ui/field_reassign_with_default.rs b/tests/ui/field_reassign_with_default.rs index 2990397c03e..9fc208f5332 100644 --- a/tests/ui/field_reassign_with_default.rs +++ b/tests/ui/field_reassign_with_default.rs @@ -1,5 +1,18 @@ +// aux-build:proc_macro_derive.rs +// aux-build:macro_rules.rs + #![warn(clippy::field_reassign_with_default)] +#[macro_use] +extern crate proc_macro_derive; +#[macro_use] +extern crate macro_rules; + +// Don't lint on derives that derive `Default` +// See https://github.com/rust-lang/rust-clippy/issues/6545 +#[derive(FieldReassignWithDefault)] +struct DerivedStruct; + #[derive(Default)] struct A { i: i32, @@ -120,6 +133,9 @@ fn main() { // don't expand macros in the suggestion (#6522) let mut a: C = C::default(); a.i = vec![1]; + + // Don't lint in external macros + field_reassign_with_default!(); } mod m { diff --git a/tests/ui/field_reassign_with_default.stderr b/tests/ui/field_reassign_with_default.stderr index 59d2ac8ed69..2f0f28f7bb7 100644 --- a/tests/ui/field_reassign_with_default.stderr +++ b/tests/ui/field_reassign_with_default.stderr @@ -1,84 +1,84 @@ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:35:5 + --> $DIR/field_reassign_with_default.rs:48:5 | LL | a.i = 42; | ^^^^^^^^^ | = note: `-D clippy::field-reassign-with-default` implied by `-D warnings` -note: consider initializing the variable with `A { i: 42, ..Default::default() }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:34:5 +note: consider initializing the variable with `main::A { i: 42, ..Default::default() }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:47:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:75:5 + --> $DIR/field_reassign_with_default.rs:88:5 | LL | a.j = 43; | ^^^^^^^^^ | -note: consider initializing the variable with `A { j: 43, i: 42 }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:74:5 +note: consider initializing the variable with `main::A { j: 43, i: 42 }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:87:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:80:5 + --> $DIR/field_reassign_with_default.rs:93:5 | LL | a.i = 42; | ^^^^^^^^^ | -note: consider initializing the variable with `A { i: 42, j: 44 }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:79:5 +note: consider initializing the variable with `main::A { i: 42, j: 44 }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:92:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:86:5 + --> $DIR/field_reassign_with_default.rs:99:5 | LL | a.i = 42; | ^^^^^^^^^ | -note: consider initializing the variable with `A { i: 42, ..Default::default() }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:85:5 +note: consider initializing the variable with `main::A { i: 42, ..Default::default() }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:98:5 | LL | let mut a = A::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:96:5 + --> $DIR/field_reassign_with_default.rs:109:5 | LL | a.i = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: consider initializing the variable with `A { i: Default::default(), ..Default::default() }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:95:5 +note: consider initializing the variable with `main::A { i: Default::default(), ..Default::default() }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:108:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:100:5 + --> $DIR/field_reassign_with_default.rs:113:5 | LL | a.i = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: consider initializing the variable with `A { i: Default::default(), j: 45 }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:99:5 +note: consider initializing the variable with `main::A { i: Default::default(), j: 45 }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:112:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:122:5 + --> $DIR/field_reassign_with_default.rs:135:5 | LL | a.i = vec![1]; | ^^^^^^^^^^^^^^ | note: consider initializing the variable with `C { i: vec![1], ..Default::default() }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:121:5 + --> $DIR/field_reassign_with_default.rs:134:5 | LL | let mut a: C = C::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From f50ded059272ed3c060ea5df353966384d7df427 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Thu, 7 Jan 2021 16:41:15 +1300 Subject: Catch `pointer::cast` too in `cast_ptr_alignment` --- clippy_lints/src/types.rs | 35 +++++++++++++++++++++++++++++------ tests/ui/cast_alignment.rs | 4 ++++ tests/ui/cast_alignment.stderr | 14 +++++++++++++- 3 files changed, 46 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index b21f81bd517..2d256b38b0d 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -1637,12 +1637,8 @@ impl<'tcx> LateLintPass<'tcx> for Casts { return; } if let ExprKind::Cast(ref ex, cast_to) = expr.kind { - if let TyKind::Path(QPath::Resolved(_, path)) = cast_to.kind { - if let Res::Def(_, def_id) = path.res { - if cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr) { - return; - } - } + if is_hir_ty_cfg_dependant(cx, cast_to) { + return; } let (cast_from, cast_to) = (cx.typeck_results().expr_ty(ex), cx.typeck_results().expr_ty(expr)); lint_fn_to_numeric_cast(cx, expr, ex, cast_from, cast_to); @@ -1691,6 +1687,21 @@ impl<'tcx> LateLintPass<'tcx> for Casts { lint_numeric_casts(cx, expr, ex, cast_from, cast_to); } + lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); + } else if let ExprKind::MethodCall(method_path, _, args, _) = expr.kind { + if method_path.ident.name != sym!(cast) { + return; + } + if_chain! { + if let Some(generic_args) = method_path.args; + if let [GenericArg::Type(cast_to)] = generic_args.args; + // There probably is no obvious reason to do this, just to be consistent with `as` cases. + if is_hir_ty_cfg_dependant(cx, cast_to); + then { + return; + } + } + let (cast_from, cast_to) = (cx.typeck_results().expr_ty(&args[0]), cx.typeck_results().expr_ty(expr)); lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); } } @@ -1714,6 +1725,18 @@ fn get_numeric_literal<'e>(expr: &'e Expr<'e>) -> Option<&'e Lit> { } } +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 + } + } +} + fn show_unnecessary_cast(cx: &LateContext<'_>, expr: &Expr<'_>, literal_str: &str, cast_from: Ty<'_>, cast_to: Ty<'_>) { let literal_kind_name = if cast_from.is_integral() { "integer" } else { "float" }; span_lint_and_sugg( diff --git a/tests/ui/cast_alignment.rs b/tests/ui/cast_alignment.rs index 4c08935639f..d011e84b115 100644 --- a/tests/ui/cast_alignment.rs +++ b/tests/ui/cast_alignment.rs @@ -12,6 +12,10 @@ fn main() { (&1u8 as *const u8) as *const u16; (&mut 1u8 as *mut u8) as *mut u16; + // cast to more-strictly-aligned type, but with the `pointer::cast` function. + (&1u8 as *const u8).cast::(); + (&mut 1u8 as *mut u8).cast::(); + /* These should be ok */ // not a pointer type diff --git a/tests/ui/cast_alignment.stderr b/tests/ui/cast_alignment.stderr index 79219f86155..97e31c130a9 100644 --- a/tests/ui/cast_alignment.stderr +++ b/tests/ui/cast_alignment.stderr @@ -12,5 +12,17 @@ error: casting from `*mut u8` to a more-strictly-aligned pointer (`*mut u16`) (1 LL | (&mut 1u8 as *mut u8) as *mut u16; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: casting from `*const u8` to a more-strictly-aligned pointer (`*const u16`) (1 < 2 bytes) + --> $DIR/cast_alignment.rs:15:5 + | +LL | (&1u8 as *const u8).cast::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: casting from `*mut u8` to a more-strictly-aligned pointer (`*mut u16`) (1 < 2 bytes) + --> $DIR/cast_alignment.rs:16:5 + | +LL | (&mut 1u8 as *mut u8).cast::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From 547ce0db274c68869a300b5162c126f95f768dac Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 7 Jan 2021 12:38:10 +0100 Subject: Change env var used for testing Clippy This changes the variable used for testing Clippy in the internal test suite: ``` CLIPPY_TESTS -> __CLIPPY_INTERNAL_TESTS ``` `CLIPPY_TESTS` is understandably used in environments of Clippy users, so we shouldn't use it in our test suite. --- doc/adding_lints.md | 10 +++++++--- src/driver.rs | 2 +- tests/compile-test.rs | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/doc/adding_lints.md b/doc/adding_lints.md index 60dfdb76650..1a7a30c61be 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -147,10 +147,14 @@ add `// edition:2018` at the top of the test file (note that it's space-sensitiv Manually testing against an example file can be useful if you have added some `println!`s and the test suite output becomes unreadable. To try Clippy with -your local modifications, run `env CLIPPY_TESTS=true cargo run --bin -clippy-driver -- -L ./target/debug input.rs` from the working copy root. +your local modifications, run -With tests in place, let's have a look at implementing our lint now. +``` +env __CLIPPY_INTERNAL_TESTS=true cargo run --bin clippy-driver -- -L ./target/debug input.rs +``` + +from the working copy root. With tests in place, let's have a look at +implementing our lint now. ## Lint declaration diff --git a/src/driver.rs b/src/driver.rs index e490ee54c0b..f5f6c09ed8e 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -298,7 +298,7 @@ pub fn main() { // - IF Clippy is run on the main crate, not on deps (`!cap_lints_allow`) THEN // - IF `--no-deps` is not set (`!no_deps`) OR // - IF `--no-deps` is set and Clippy is run on the specified primary package - let clippy_tests_set = env::var("CLIPPY_TESTS").map_or(false, |val| val == "true"); + let clippy_tests_set = env::var("__CLIPPY_INTERNAL_TESTS").map_or(false, |val| val == "true"); let cap_lints_allow = arg_value(&orig_args, "--cap-lints", |val| val == "allow").is_some(); let in_primary_package = env::var("CARGO_PRIMARY_PACKAGE").is_ok(); diff --git a/tests/compile-test.rs b/tests/compile-test.rs index ec3af94b9ca..ea800336ef5 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -254,7 +254,7 @@ fn run_ui_cargo(config: &mut compiletest::Config) { fn prepare_env() { set_var("CLIPPY_DISABLE_DOCS_LINKS", "true"); - set_var("CLIPPY_TESTS", "true"); + set_var("__CLIPPY_INTERNAL_TESTS", "true"); //set_var("RUST_BACKTRACE", "0"); } -- cgit 1.4.1-3-g733a5 From 0e14a7550632f9bbe4e0f0b5589a8c38b55d649b Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Fri, 8 Jan 2021 08:37:57 +0900 Subject: Reduce the span in `from_over_into` to impl header --- clippy_lints/src/from_over_into.rs | 2 +- tests/ui/from_over_into.stderr | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index 1e7e5f53cc2..b010abda24d 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -70,7 +70,7 @@ impl LateLintPass<'_> for FromOverInto { span_lint_and_help( cx, FROM_OVER_INTO, - item.span, + cx.tcx.sess.source_map().guess_head_span(item.span), "an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true", None, "consider to implement `From` instead", diff --git a/tests/ui/from_over_into.stderr b/tests/ui/from_over_into.stderr index 18f56f85432..b101d2704fb 100644 --- a/tests/ui/from_over_into.stderr +++ b/tests/ui/from_over_into.stderr @@ -1,12 +1,8 @@ error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true --> $DIR/from_over_into.rs:6:1 | -LL | / impl Into for String { -LL | | fn into(self) -> StringWrapper { -LL | | StringWrapper(self) -LL | | } -LL | | } - | |_^ +LL | impl Into for String { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::from-over-into` implied by `-D warnings` = help: consider to implement `From` instead -- cgit 1.4.1-3-g733a5 From ee9b47dae61cd34ee7cbcb013a72b5f162148e4a Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Fri, 8 Jan 2021 14:15:12 +1300 Subject: Move `is_hir_ty_cfg_dependant` to `util`, add stuff on pointer::cast` to the document for `cast_ptr_alignment` and fix line numbers in the test. --- clippy_lints/src/types.rs | 26 ++++++++------------------ clippy_lints/src/utils/mod.rs | 12 ++++++++++++ tests/ui/cast_alignment.stderr | 4 ++-- 3 files changed, 22 insertions(+), 20 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 2d256b38b0d..765a0569007 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -8,7 +8,6 @@ use if_chain::if_chain; use rustc_ast::{FloatTy, IntTy, LitFloatType, LitIntType, LitKind, UintTy}; use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir as hir; -use rustc_hir::def::Res; use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::{ BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericBounds, GenericParamKind, HirId, @@ -33,9 +32,9 @@ use crate::consts::{constant, Constant}; use crate::utils::paths; use crate::utils::sugg::Sugg; use crate::utils::{ - clip, comparisons, differing_macro_contexts, higher, in_constant, indent_of, int_bits, is_type_diagnostic_item, - last_path_segment, match_def_path, match_path, meets_msrv, method_chain_args, multispan_sugg, - numeric_literal::NumericLiteral, qpath_res, reindent_multiline, sext, snippet, snippet_opt, + clip, comparisons, differing_macro_contexts, higher, in_constant, indent_of, int_bits, is_hir_ty_cfg_dependant, + is_type_diagnostic_item, last_path_segment, match_def_path, match_path, meets_msrv, method_chain_args, + multispan_sugg, numeric_literal::NumericLiteral, qpath_res, reindent_multiline, sext, snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext, }; @@ -1282,8 +1281,8 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for casts from a less-strictly-aligned pointer to a - /// more-strictly-aligned pointer + /// **What it does:** Checks for casts, using `as` or `pointer::cast`, + /// from a less-strictly-aligned pointer to a more-strictly-aligned pointer /// /// **Why is this bad?** Dereferencing the resulting pointer may be undefined /// behavior. @@ -1296,6 +1295,9 @@ declare_clippy_lint! { /// ```rust /// let _ = (&1u8 as *const u8) as *const u16; /// let _ = (&mut 1u8 as *mut u8) as *mut u16; + /// + /// (&1u8 as *const u8).cast::(); + /// (&mut 1u8 as *mut u8).cast::(); /// ``` pub CAST_PTR_ALIGNMENT, pedantic, @@ -1725,18 +1727,6 @@ fn get_numeric_literal<'e>(expr: &'e Expr<'e>) -> Option<&'e Lit> { } } -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 - } - } -} - fn show_unnecessary_cast(cx: &LateContext<'_>, expr: &Expr<'_>, literal_str: &str, cast_from: Ty<'_>, cast_to: Ty<'_>) { let literal_kind_name = if cast_from.is_integral() { "integer" } else { "float" }; span_lint_and_sugg( diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 8f54cad7728..27f4cb2b254 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -1686,6 +1686,18 @@ 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 + } + } +} + #[cfg(test)] mod test { use super::{reindent_multiline, without_block_comments}; diff --git a/tests/ui/cast_alignment.stderr b/tests/ui/cast_alignment.stderr index 97e31c130a9..7998b787b91 100644 --- a/tests/ui/cast_alignment.stderr +++ b/tests/ui/cast_alignment.stderr @@ -13,13 +13,13 @@ LL | (&mut 1u8 as *mut u8) as *mut u16; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting from `*const u8` to a more-strictly-aligned pointer (`*const u16`) (1 < 2 bytes) - --> $DIR/cast_alignment.rs:15:5 + --> $DIR/cast_alignment.rs:16:5 | LL | (&1u8 as *const u8).cast::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting from `*mut u8` to a more-strictly-aligned pointer (`*mut u16`) (1 < 2 bytes) - --> $DIR/cast_alignment.rs:16:5 + --> $DIR/cast_alignment.rs:17:5 | LL | (&mut 1u8 as *mut u8).cast::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From 8490862cc37d7518c79d09d7911d86bfca2cea89 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 6 Jan 2021 15:14:01 -0600 Subject: Fix path_to_res for enum inherent items --- clippy_lints/src/utils/mod.rs | 96 +++++++++++++++----------------------- tests/ui-internal/invalid_paths.rs | 3 ++ 2 files changed, 40 insertions(+), 59 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 8f54cad7728..b2c9d4f8739 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -31,7 +31,6 @@ pub use self::hir_utils::{both, eq_expr_value, over, SpanlessEq, SpanlessHash}; use std::borrow::Cow; use std::collections::hash_map::Entry; use std::hash::BuildHasherDefault; -use std::mem; use if_chain::if_chain; use rustc_ast::ast::{self, Attribute, LitKind}; @@ -40,7 +39,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; +use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::Node; use rustc_hir::{ @@ -49,6 +48,7 @@ use rustc_hir::{ }; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, Level, Lint, LintContext}; +use rustc_middle::hir::exports::Export; use rustc_middle::hir::map::Map; use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable}; @@ -309,65 +309,43 @@ pub fn match_path_ast(path: &ast::Path, segments: &[&str]) -> bool { } /// Gets the definition associated to a path. -pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Option { - let crates = cx.tcx.crates(); - let krate = crates - .iter() - .find(|&&krate| cx.tcx.crate_name(krate).as_str() == path[0]); - if let Some(krate) = krate { - let krate = DefId { - krate: *krate, - index: CRATE_DEF_INDEX, - }; - let mut current_item = None; - let mut items = cx.tcx.item_children(krate); - let mut path_it = path.iter().skip(1).peekable(); - - loop { - let segment = match path_it.next() { - Some(segment) => segment, - None => return None, - }; - - // `get_def_path` seems to generate these empty segments for extern blocks. - // We can just ignore them. - if segment.is_empty() { - continue; - } - - let result = SmallVec::<[_; 8]>::new(); - for item in mem::replace(&mut items, cx.tcx.arena.alloc_slice(&result)).iter() { - if item.ident.name.as_str() == *segment { - if path_it.peek().is_none() { - return Some(item.res); - } - - current_item = Some(item); - items = cx.tcx.item_children(item.res.def_id()); - break; - } - } +#[allow(clippy::shadow_unrelated)] // false positive #6563 +pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Option { + fn item_child_by_name<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, name: &str) -> Option<&'tcx Export> { + tcx.item_children(def_id) + .iter() + .find(|item| item.ident.name.as_str() == name) + } - // The segment isn't a child_item. - // Try to find it under an inherent impl. - if_chain! { - if path_it.peek().is_none(); - if let Some(current_item) = current_item; - let item_def_id = current_item.res.def_id(); - if cx.tcx.def_kind(item_def_id) == DefKind::Struct; - then { - // Bad `find_map` suggestion. See #4193. - #[allow(clippy::find_map)] - return cx.tcx.inherent_impls(item_def_id).iter() - .flat_map(|&impl_def_id| cx.tcx.item_children(impl_def_id)) - .find(|item| item.ident.name.as_str() == *segment) - .map(|item| item.res); - } + let (krate, first, path) = match *path { + [krate, first, ref path @ ..] => (krate, first, path), + _ => return None, + }; + let tcx = cx.tcx; + let crates = tcx.crates(); + let krate = crates.iter().find(|&&num| tcx.crate_name(num).as_str() == krate)?; + let first = item_child_by_name(tcx, krate.as_def_id(), first)?; + let last = path + .iter() + .copied() + // `get_def_path` seems to generate these empty segments for extern blocks. + // We can just ignore them. + .filter(|segment| !segment.is_empty()) + // for each segment, find the child item + .try_fold(first, |item, segment| { + let def_id = item.res.def_id(); + if let Some(item) = item_child_by_name(tcx, def_id, segment) { + Some(item) + } else if matches!(item.res, Res::Def(DefKind::Enum | DefKind::Struct, _)) { + // it is not a child item so check inherent impl items + tcx.inherent_impls(def_id) + .iter() + .find_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, segment)) + } else { + None } - } - } else { - None - } + })?; + Some(last.res) } pub fn qpath_res(cx: &LateContext<'_>, qpath: &hir::QPath<'_>, id: hir::HirId) -> Res { diff --git a/tests/ui-internal/invalid_paths.rs b/tests/ui-internal/invalid_paths.rs index 01e28ae5e9d..a3b19c2e394 100644 --- a/tests/ui-internal/invalid_paths.rs +++ b/tests/ui-internal/invalid_paths.rs @@ -18,6 +18,9 @@ mod paths { // Path with bad module pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"]; + + // Path to method on an enum inherent impl + pub const OPTION_IS_SOME: [&str; 4] = ["core", "option", "Option", "is_some"]; } fn main() {} -- cgit 1.4.1-3-g733a5 From 24c700b5d70f063521435c450ae1b48720b5f991 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 30 Dec 2020 15:38:21 -0600 Subject: Use DefId in interning defined symbol lint --- clippy_lints/src/utils/internal_lints.rs | 11 ++++++----- tests/ui-internal/interning_defined_symbol.fixed | 6 +++--- tests/ui-internal/interning_defined_symbol.stderr | 6 +++--- 3 files changed, 12 insertions(+), 11 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 9ba39f73ee8..945aaa4668c 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -10,6 +10,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::DefId; use rustc_hir::hir_id::CRATE_HIR_ID; use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; use rustc_hir::{Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Node, Path, StmtKind, Ty, TyKind}; @@ -868,8 +869,8 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths { #[derive(Default)] pub struct InterningDefinedSymbol { - // Maps the symbol value to the constant name. - symbol_map: FxHashMap, + // Maps the symbol value to the constant DefId. + symbol_map: FxHashMap, } impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL]); @@ -889,7 +890,7 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id); if let Ok(value) = value.to_u32(); then { - self.symbol_map.insert(value, item.ident.to_string()); + self.symbol_map.insert(value, item_def_id); } } } @@ -903,7 +904,7 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { if match_def_path(cx, *def_id, &paths::SYMBOL_INTERN); if let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg); let value = Symbol::intern(&arg).as_u32(); - if let Some(symbol_const) = self.symbol_map.get(&value); + if let Some(&def_id) = self.symbol_map.get(&value); then { span_lint_and_sugg( cx, @@ -911,7 +912,7 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { is_expn_of(expr.span, "sym").unwrap_or(expr.span), "interning a defined symbol", "try", - format!("rustc_span::symbol::sym::{}", symbol_const), + cx.tcx.def_path_str(def_id), Applicability::MachineApplicable, ); } diff --git a/tests/ui-internal/interning_defined_symbol.fixed b/tests/ui-internal/interning_defined_symbol.fixed index c6b84d2ef65..2af362b8f99 100644 --- a/tests/ui-internal/interning_defined_symbol.fixed +++ b/tests/ui-internal/interning_defined_symbol.fixed @@ -14,13 +14,13 @@ macro_rules! sym { fn main() { // Direct use of Symbol::intern - let _ = rustc_span::symbol::sym::f32; + let _ = rustc_span::sym::f32; // Using a sym macro - let _ = rustc_span::symbol::sym::f32; + let _ = rustc_span::sym::f32; // Correct suggestion when symbol isn't stringified constant name - let _ = rustc_span::symbol::sym::proc_dash_macro; + let _ = rustc_span::sym::proc_dash_macro; // Interning a symbol that is not defined let _ = Symbol::intern("xyz123"); diff --git a/tests/ui-internal/interning_defined_symbol.stderr b/tests/ui-internal/interning_defined_symbol.stderr index 74b906c8a57..d7e1d62d51a 100644 --- a/tests/ui-internal/interning_defined_symbol.stderr +++ b/tests/ui-internal/interning_defined_symbol.stderr @@ -2,7 +2,7 @@ error: interning a defined symbol --> $DIR/interning_defined_symbol.rs:17:13 | LL | let _ = Symbol::intern("f32"); - | ^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::symbol::sym::f32` + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::f32` | note: the lint level is defined here --> $DIR/interning_defined_symbol.rs:2:9 @@ -15,13 +15,13 @@ error: interning a defined symbol --> $DIR/interning_defined_symbol.rs:20:13 | LL | let _ = sym!(f32); - | ^^^^^^^^^ help: try: `rustc_span::symbol::sym::f32` + | ^^^^^^^^^ help: try: `rustc_span::sym::f32` error: interning a defined symbol --> $DIR/interning_defined_symbol.rs:23:13 | LL | let _ = Symbol::intern("proc-macro"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::symbol::sym::proc_dash_macro` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::proc_dash_macro` error: aborting due to 3 previous errors -- cgit 1.4.1-3-g733a5 From 121c65f0cf2bf488128c60dc6c20a947bb1bb1ca Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 29 Dec 2020 15:40:55 -0600 Subject: Add keywords to interning defined symbol lint --- clippy_lints/src/utils/internal_lints.rs | 22 ++++++++++++---------- clippy_lints/src/utils/paths.rs | 2 ++ tests/ui-internal/interning_defined_symbol.fixed | 3 +++ tests/ui-internal/interning_defined_symbol.rs | 3 +++ tests/ui-internal/interning_defined_symbol.stderr | 8 +++++++- 5 files changed, 27 insertions(+), 11 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 945aaa4668c..c0b6688fa15 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -881,16 +881,18 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { return; } - if let Some(Res::Def(_, def_id)) = path_to_res(cx, &paths::SYM_MODULE) { - for item in cx.tcx.item_children(def_id).iter() { - if_chain! { - if let Res::Def(DefKind::Const, item_def_id) = item.res; - let ty = cx.tcx.type_of(item_def_id); - if match_type(cx, ty, &paths::SYMBOL); - if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id); - if let Ok(value) = value.to_u32(); - then { - self.symbol_map.insert(value, item_def_id); + for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] { + if let Some(Res::Def(_, def_id)) = path_to_res(cx, module) { + for item in cx.tcx.item_children(def_id).iter() { + if_chain! { + if let Res::Def(DefKind::Const, item_def_id) = item.res; + let ty = cx.tcx.type_of(item_def_id); + if match_type(cx, ty, &paths::SYMBOL); + if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id); + if let Ok(value) = value.to_u32(); + then { + self.symbol_map.insert(value, item_def_id); + } } } } diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 2080a49a11c..3179be6af2a 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -65,6 +65,8 @@ pub const IPADDR_V4: [&str; 4] = ["std", "net", "IpAddr", "V4"]; pub const IPADDR_V6: [&str; 4] = ["std", "net", "IpAddr", "V6"]; pub const ITERATOR: [&str; 5] = ["core", "iter", "traits", "iterator", "Iterator"]; #[cfg(feature = "internal-lints")] +pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"]; +#[cfg(feature = "internal-lints")] pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"]; pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"]; #[cfg(feature = "internal-lints")] diff --git a/tests/ui-internal/interning_defined_symbol.fixed b/tests/ui-internal/interning_defined_symbol.fixed index 2af362b8f99..9ab845a573a 100644 --- a/tests/ui-internal/interning_defined_symbol.fixed +++ b/tests/ui-internal/interning_defined_symbol.fixed @@ -22,6 +22,9 @@ fn main() { // Correct suggestion when symbol isn't stringified constant name let _ = rustc_span::sym::proc_dash_macro; + // interning a keyword + let _ = rustc_span::symbol::kw::SelfLower; + // Interning a symbol that is not defined let _ = Symbol::intern("xyz123"); let _ = sym!(xyz123); diff --git a/tests/ui-internal/interning_defined_symbol.rs b/tests/ui-internal/interning_defined_symbol.rs index 9ec82d4ad0b..a58e182971d 100644 --- a/tests/ui-internal/interning_defined_symbol.rs +++ b/tests/ui-internal/interning_defined_symbol.rs @@ -22,6 +22,9 @@ fn main() { // Correct suggestion when symbol isn't stringified constant name let _ = Symbol::intern("proc-macro"); + // interning a keyword + let _ = Symbol::intern("self"); + // Interning a symbol that is not defined let _ = Symbol::intern("xyz123"); let _ = sym!(xyz123); diff --git a/tests/ui-internal/interning_defined_symbol.stderr b/tests/ui-internal/interning_defined_symbol.stderr index d7e1d62d51a..50c1c268eb1 100644 --- a/tests/ui-internal/interning_defined_symbol.stderr +++ b/tests/ui-internal/interning_defined_symbol.stderr @@ -23,5 +23,11 @@ error: interning a defined symbol LL | let _ = Symbol::intern("proc-macro"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::proc_dash_macro` -error: aborting due to 3 previous errors +error: interning a defined symbol + --> $DIR/interning_defined_symbol.rs:26:13 + | +LL | let _ = Symbol::intern("self"); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::symbol::kw::SelfLower` + +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From cc26919b4dcfbb31a0eb902e8cf3e009a93e5ac8 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 30 Dec 2020 15:52:15 -0600 Subject: Add unnecessary symbol string lint --- clippy_lints/src/lib.rs | 3 + clippy_lints/src/utils/internal_lints.rs | 153 +++++++++++++++++++++++- clippy_lints/src/utils/paths.rs | 8 ++ tests/ui-internal/unnecessary_symbol_str.fixed | 16 +++ tests/ui-internal/unnecessary_symbol_str.rs | 16 +++ tests/ui-internal/unnecessary_symbol_str.stderr | 39 ++++++ 6 files changed, 233 insertions(+), 2 deletions(-) create mode 100644 tests/ui-internal/unnecessary_symbol_str.fixed create mode 100644 tests/ui-internal/unnecessary_symbol_str.rs create mode 100644 tests/ui-internal/unnecessary_symbol_str.stderr (limited to 'tests') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 37a56bc20c8..f12994c7a60 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -526,6 +526,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &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, &approx_const::APPROX_CONSTANT, &arithmetic::FLOAT_ARITHMETIC, &arithmetic::INTEGER_ARITHMETIC, @@ -1372,6 +1374,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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![ diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index c0b6688fa15..59a1852aba9 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -13,7 +13,9 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::hir_id::CRATE_HIR_ID; use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; -use rustc_hir::{Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Node, Path, StmtKind, Ty, TyKind}; +use rustc_hir::{ + BinOpKind, Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Node, Path, StmtKind, Ty, TyKind, UnOp, +}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_middle::mir::interpret::ConstValue; @@ -273,6 +275,28 @@ declare_clippy_lint! { "interning a symbol that is pre-interned and defined as a constant" } +declare_clippy_lint! { + /// **What it does:** Checks for unnecessary conversion from Symbol to a string. + /// + /// **Why is this bad?** It's faster use symbols directly intead of strings. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// Bad: + /// ```rust,ignore + /// symbol.as_str() == "clippy"; + /// ``` + /// + /// Good: + /// ```rust,ignore + /// symbol == sym::clippy; + /// ``` + pub UNNECESSARY_SYMBOL_STR, + internal, + "unnecessary conversion between Symbol and string" +} + declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]); impl EarlyLintPass for ClippyLintsInternal { @@ -873,7 +897,7 @@ pub struct InterningDefinedSymbol { symbol_map: FxHashMap, } -impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL]); +impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL, UNNECESSARY_SYMBOL_STR]); impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) { @@ -919,5 +943,130 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { ); } } + if let ExprKind::Binary(op, left, right) = expr.kind { + if matches!(op.node, BinOpKind::Eq | BinOpKind::Ne) { + let data = [ + (left, self.symbol_str_expr(left, cx)), + (right, self.symbol_str_expr(right, cx)), + ]; + match data { + // both operands are a symbol string + [(_, Some(left)), (_, Some(right))] => { + span_lint_and_sugg( + cx, + UNNECESSARY_SYMBOL_STR, + expr.span, + "unnecessary `Symbol` to string conversion", + "try", + format!( + "{} {} {}", + left.as_symbol_snippet(cx), + op.node.as_str(), + right.as_symbol_snippet(cx), + ), + Applicability::MachineApplicable, + ); + }, + // one of the operands is a symbol string + [(expr, Some(symbol)), _] | [_, (expr, Some(symbol))] => { + // creating an owned string for comparison + if matches!(symbol, SymbolStrExpr::Expr { is_to_owned: true, .. }) { + span_lint_and_sugg( + cx, + UNNECESSARY_SYMBOL_STR, + expr.span, + "unnecessary string allocation", + "try", + format!("{}.as_str()", symbol.as_symbol_snippet(cx)), + Applicability::MachineApplicable, + ); + } + }, + // nothing found + [(_, None), (_, None)] => {}, + } + } + } + } +} + +impl InterningDefinedSymbol { + fn symbol_str_expr<'tcx>(&self, expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> Option> { + static IDENT_STR_PATHS: &[&[&str]] = &[&paths::IDENT_AS_STR, &paths::TO_STRING_METHOD]; + static SYMBOL_STR_PATHS: &[&[&str]] = &[ + &paths::SYMBOL_AS_STR, + &paths::SYMBOL_TO_IDENT_STRING, + &paths::TO_STRING_METHOD, + ]; + // SymbolStr might be de-referenced: `&*symbol.as_str()` + let call = if_chain! { + if let ExprKind::AddrOf(_, _, e) = expr.kind; + if let ExprKind::Unary(UnOp::UnDeref, e) = e.kind; + then { e } else { expr } + }; + if_chain! { + // is a method call + if let ExprKind::MethodCall(_, _, [item], _) = call.kind; + if let Some(did) = cx.typeck_results().type_dependent_def_id(call.hir_id); + let ty = cx.typeck_results().expr_ty(item); + // ...on either an Ident or a Symbol + if let Some(is_ident) = if match_type(cx, ty, &paths::SYMBOL) { + Some(false) + } else if match_type(cx, ty, &paths::IDENT) { + Some(true) + } else { + None + }; + // ...which converts it to a string + let paths = if is_ident { IDENT_STR_PATHS } else { SYMBOL_STR_PATHS }; + if let Some(path) = paths.iter().find(|path| match_def_path(cx, did, path)); + then { + let is_to_owned = path.last().unwrap().ends_with("string"); + return Some(SymbolStrExpr::Expr { + item, + is_ident, + is_to_owned, + }); + } + } + // is a string constant + if let Some(Constant::Str(s)) = constant_simple(cx, cx.typeck_results(), expr) { + let value = Symbol::intern(&s).as_u32(); + // ...which matches a symbol constant + if let Some(&def_id) = self.symbol_map.get(&value) { + return Some(SymbolStrExpr::Const(def_id)); + } + } + None + } +} + +enum SymbolStrExpr<'tcx> { + /// a string constant with a corresponding symbol constant + Const(DefId), + /// a "symbol to string" expression like `symbol.as_str()` + Expr { + /// part that evaluates to `Symbol` or `Ident` + item: &'tcx Expr<'tcx>, + is_ident: bool, + /// whether an owned `String` is created like `to_ident_string()` + is_to_owned: bool, + }, +} + +impl<'tcx> SymbolStrExpr<'tcx> { + /// Returns a snippet that evaluates to a `Symbol` and is const if possible + fn as_symbol_snippet(&self, cx: &LateContext<'_>) -> Cow<'tcx, str> { + match *self { + Self::Const(def_id) => cx.tcx.def_path_str(def_id).into(), + Self::Expr { item, is_ident, .. } => { + let mut snip = snippet(cx, item.span.source_callsite(), ".."); + if is_ident { + // get `Ident.name` + snip.to_mut().push_str(".name"); + } + snip + }, + } } } diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 3179be6af2a..c0b203b5388 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -54,6 +54,10 @@ pub const HASH: [&str; 3] = ["core", "hash", "Hash"]; pub const HASHMAP: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"]; pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"]; pub const HASHSET: [&str; 5] = ["std", "collections", "hash", "set", "HashSet"]; +#[cfg(feature = "internal-lints")] +pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"]; +#[cfg(feature = "internal-lints")] +pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"]; pub const INDEX: [&str; 3] = ["core", "ops", "Index"]; pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"]; pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"]; @@ -150,8 +154,12 @@ pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "", "starts_wit #[cfg(feature = "internal-lints")] pub const SYMBOL: [&str; 3] = ["rustc_span", "symbol", "Symbol"]; #[cfg(feature = "internal-lints")] +pub const SYMBOL_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Symbol", "as_str"]; +#[cfg(feature = "internal-lints")] pub const SYMBOL_INTERN: [&str; 4] = ["rustc_span", "symbol", "Symbol", "intern"]; #[cfg(feature = "internal-lints")] +pub const SYMBOL_TO_IDENT_STRING: [&str; 4] = ["rustc_span", "symbol", "Symbol", "to_ident_string"]; +#[cfg(feature = "internal-lints")] pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"]; #[cfg(feature = "internal-lints")] pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]; diff --git a/tests/ui-internal/unnecessary_symbol_str.fixed b/tests/ui-internal/unnecessary_symbol_str.fixed new file mode 100644 index 00000000000..2ec0efe4c10 --- /dev/null +++ b/tests/ui-internal/unnecessary_symbol_str.fixed @@ -0,0 +1,16 @@ +// run-rustfix +#![feature(rustc_private)] +#![deny(clippy::internal)] +#![allow(clippy::unnecessary_operation, unused_must_use)] + +extern crate rustc_span; + +use rustc_span::symbol::{Ident, Symbol}; + +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; +} diff --git a/tests/ui-internal/unnecessary_symbol_str.rs b/tests/ui-internal/unnecessary_symbol_str.rs new file mode 100644 index 00000000000..87e1b3a2ee7 --- /dev/null +++ b/tests/ui-internal/unnecessary_symbol_str.rs @@ -0,0 +1,16 @@ +// run-rustfix +#![feature(rustc_private)] +#![deny(clippy::internal)] +#![allow(clippy::unnecessary_operation, unused_must_use)] + +extern crate rustc_span; + +use rustc_span::symbol::{Ident, Symbol}; + +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(); +} diff --git a/tests/ui-internal/unnecessary_symbol_str.stderr b/tests/ui-internal/unnecessary_symbol_str.stderr new file mode 100644 index 00000000000..b1284b7c8ff --- /dev/null +++ b/tests/ui-internal/unnecessary_symbol_str.stderr @@ -0,0 +1,39 @@ +error: unnecessary `Symbol` to string conversion + --> $DIR/unnecessary_symbol_str.rs:11:5 + | +LL | Symbol::intern("foo").as_str() == "clippy"; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::sym::clippy` + | +note: the lint level is defined here + --> $DIR/unnecessary_symbol_str.rs:3:9 + | +LL | #![deny(clippy::internal)] + | ^^^^^^^^^^^^^^^^ + = note: `#[deny(clippy::unnecessary_symbol_str)]` implied by `#[deny(clippy::internal)]` + +error: unnecessary `Symbol` to string conversion + --> $DIR/unnecessary_symbol_str.rs:12:5 + | +LL | Symbol::intern("foo").to_string() == "self"; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::symbol::kw::SelfLower` + +error: unnecessary `Symbol` to string conversion + --> $DIR/unnecessary_symbol_str.rs:13:5 + | +LL | Symbol::intern("foo").to_ident_string() != "Self"; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") != rustc_span::symbol::kw::SelfUpper` + +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` + +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` + +error: aborting due to 5 previous errors + -- cgit 1.4.1-3-g733a5 From 8a6fea4fb85adb8446797f698ab92a0869ccc9c9 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sat, 9 Jan 2021 12:26:24 +0100 Subject: Fix FP for `boxed_local` lint in default trait fn impl --- clippy_lints/src/escape.rs | 33 +++++++++++++++++++++++++++++---- tests/ui/escape_analysis.rs | 20 ++++++++++++++++++++ tests/ui/escape_analysis.stderr | 14 +++++++++++++- 3 files changed, 62 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index d2dcb3e5c46..9fcd17a756a 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -1,15 +1,16 @@ use rustc_hir::intravisit; -use rustc_hir::{self, Body, FnDecl, HirId, HirIdSet, ItemKind, Node}; +use rustc_hir::{self, AssocItemKind, Body, FnDecl, HirId, HirIdSet, ItemKind, Node}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, TraitRef, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; +use rustc_span::symbol::kw; use rustc_target::abi::LayoutOf; use rustc_target::spec::abi::Abi; use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; -use crate::utils::span_lint; +use crate::utils::{contains_ty, span_lint}; #[derive(Copy, Clone)] pub struct BoxedLocal { @@ -51,6 +52,7 @@ fn is_non_trait_box(ty: Ty<'_>) -> bool { struct EscapeDelegate<'a, 'tcx> { cx: &'a LateContext<'tcx>, set: HirIdSet, + trait_self_ty: Option>, too_large_for_stack: u64, } @@ -72,19 +74,34 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal { } } - // If the method is an impl for a trait, don't warn. let parent_id = cx.tcx.hir().get_parent_item(hir_id); let parent_node = cx.tcx.hir().find(parent_id); + let mut trait_self_ty = None; if let Some(Node::Item(item)) = parent_node { + // If the method is an impl for a trait, don't warn. if let ItemKind::Impl { of_trait: Some(_), .. } = item.kind { return; } + + // find `self` ty for this trait if relevant + if let ItemKind::Trait(_, _, _, _, items) = item.kind { + 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 { + trait_self_ty = + Some(TraitRef::identity(cx.tcx, trait_item.id.hir_id.owner.to_def_id()).self_ty()); + } + } + } + } } let mut v = EscapeDelegate { cx, set: HirIdSet::default(), + trait_self_ty, too_large_for_stack: self.too_large_for_stack, }; @@ -153,6 +170,14 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> { return; } + // skip if there is a `self` parameter binding to a type + // that contains `Self` (i.e.: `self: Box`), see #4804 + if let Some(trait_self_ty) = self.trait_self_ty { + if map.name(cmt.hir_id) == kw::SelfLower && contains_ty(cmt.place.ty(), trait_self_ty) { + return; + } + } + if is_non_trait_box(cmt.place.ty()) && !self.is_large_box(cmt.place.ty()) { self.set.insert(cmt.hir_id); } diff --git a/tests/ui/escape_analysis.rs b/tests/ui/escape_analysis.rs index 07004489610..d26f48fc68f 100644 --- a/tests/ui/escape_analysis.rs +++ b/tests/ui/escape_analysis.rs @@ -182,3 +182,23 @@ pub extern "C" fn do_not_warn_me(_c_pointer: Box) -> () {} #[rustfmt::skip] // Forces rustfmt to not add ABI pub extern fn do_not_warn_me_no_abi(_c_pointer: Box) -> () {} + +// Issue #4804 - default implementation in trait +mod issue4804 { + trait DefaultTraitImplTest { + // don't warn on `self` + fn default_impl(self: Box) -> u32 { + 5 + } + + // warn on `x: Box` + fn default_impl_x(self: Box, x: Box) -> u32 { + 4 + } + } + + trait WarnTrait { + // warn on `x: Box` + fn foo(x: Box) {} + } +} diff --git a/tests/ui/escape_analysis.stderr b/tests/ui/escape_analysis.stderr index c86a769a3da..4a82b4419f9 100644 --- a/tests/ui/escape_analysis.stderr +++ b/tests/ui/escape_analysis.stderr @@ -12,5 +12,17 @@ error: local variable doesn't need to be boxed here LL | pub fn new(_needs_name: Box>) -> () {} | ^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: local variable doesn't need to be boxed here + --> $DIR/escape_analysis.rs:195:44 + | +LL | fn default_impl_x(self: Box, x: Box) -> u32 { + | ^ + +error: local variable doesn't need to be boxed here + --> $DIR/escape_analysis.rs:202:16 + | +LL | fn foo(x: Box) {} + | ^ + +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From 8d7417d8079b7f942e3a116ede6d36dc7a219e71 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 10 Jan 2021 23:32:23 -0500 Subject: Add: single_match will suggest using if .. == .. instead of if let when applicable --- clippy_lints/src/matches.rs | 79 ++++++++++++++++++++++++++++++++++++-------- tests/ui/single_match.rs | 43 ++++++++++++++++++++++++ tests/ui/single_match.stderr | 38 ++++++++++++++++++++- 3 files changed, 146 insertions(+), 14 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 04b35835c6b..2239b519632 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -2,10 +2,10 @@ use crate::consts::{constant, miri_to_const, Constant}; use crate::utils::sugg::Sugg; use crate::utils::usage::is_unused; use crate::utils::{ - expr_block, get_arg_name, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of, is_refutable, - is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, meets_msrv, multispan_sugg, remove_blocks, - snippet, snippet_block, snippet_opt, snippet_with_applicability, span_lint_and_help, span_lint_and_note, - span_lint_and_sugg, span_lint_and_then, + expr_block, get_arg_name, get_parent_expr, implements_trait, in_macro, indent_of, is_allowed, is_expn_of, + is_refutable, is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, meets_msrv, multispan_sugg, + remove_blocks, snippet, snippet_block, snippet_opt, snippet_with_applicability, span_lint_and_help, + span_lint_and_note, span_lint_and_sugg, span_lint_and_then, }; use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash}; use if_chain::if_chain; @@ -717,6 +717,28 @@ fn check_single_match_single_pattern( } } +fn peel_pat_refs(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) { + fn peel(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) { + if let PatKind::Ref(pat, _) = pat.kind { + peel(pat, count + 1) + } else { + (pat, count) + } + } + peel(pat, 0) +} + +fn peel_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) { + fn peel(ty: Ty<'_>, count: usize) -> (Ty<'_>, usize) { + if let ty::Ref(_, ty, _) = ty.kind() { + peel(ty, count + 1) + } else { + (ty, count) + } + } + peel(ty, 0) +} + fn report_single_match_single_pattern( cx: &LateContext<'_>, ex: &Expr<'_>, @@ -728,20 +750,51 @@ fn report_single_match_single_pattern( let els_str = els.map_or(String::new(), |els| { format!(" else {}", expr_block(cx, els, None, "..", Some(expr.span))) }); + + let (msg, sugg) = if_chain! { + let (pat, pat_ref_count) = peel_pat_refs(arms[0].pat); + if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind; + let (ty, ty_ref_count) = peel_ty_refs(cx.typeck_results().expr_ty(ex)); + if let Some(trait_id) = cx.tcx.lang_items().structural_peq_trait(); + if ty.is_integral() || ty.is_char() || ty.is_str() || implements_trait(cx, ty, trait_id, &[]); + then { + // scrutinee derives PartialEq and the pattern is a constant. + let pat_ref_count = match pat.kind { + // string literals are already a reference. + PatKind::Lit(Expr { kind: ExprKind::Lit(lit), .. }) if lit.node.is_str() => pat_ref_count + 1, + _ => pat_ref_count, + }; + let msg = "you seem to be trying to use match for an equality check. Consider using `if`"; + let sugg = format!( + "if {} == {}{} {}{}", + snippet(cx, ex.span, ".."), + // PartialEq for different reference counts may not exist. + "&".repeat(ty_ref_count - pat_ref_count), + snippet(cx, arms[0].pat.span, ".."), + expr_block(cx, &arms[0].body, None, "..", Some(expr.span)), + els_str, + ); + (msg, sugg) + } else { + let msg = "you seem to be trying to use match for destructuring a single pattern. Consider using `if let`"; + let sugg = format!( + "if let {} = {} {}{}", + snippet(cx, arms[0].pat.span, ".."), + snippet(cx, ex.span, ".."), + expr_block(cx, &arms[0].body, None, "..", Some(expr.span)), + els_str, + ); + (msg, sugg) + } + }; + span_lint_and_sugg( cx, lint, expr.span, - "you seem to be trying to use match for destructuring a single pattern. Consider using `if \ - let`", + msg, "try this", - format!( - "if let {} = {} {}{}", - snippet(cx, arms[0].pat.span, ".."), - snippet(cx, ex.span, ".."), - expr_block(cx, &arms[0].body, None, "..", Some(expr.span)), - els_str, - ), + sugg, Applicability::HasPlaceholders, ); } diff --git a/tests/ui/single_match.rs b/tests/ui/single_match.rs index 1c55af5dfb6..02266308fba 100644 --- a/tests/ui/single_match.rs +++ b/tests/ui/single_match.rs @@ -81,6 +81,49 @@ fn single_match_know_enum() { } } +fn issue_173() { + let x = "test"; + match x { + "test" => println!(), + _ => (), + } + + #[derive(PartialEq, Eq)] + enum Foo { + A, + B, + C(u32), + } + + let x = Foo::A; + match x { + Foo::A => println!(), + _ => (), + } + + const FOO_C: Foo = Foo::C(0); + match x { + FOO_C => println!(), + _ => (), + } + enum Bar { + A, + B, + } + impl PartialEq for Bar { + fn eq(&self, rhs: &Self) -> bool { + matches!((self, rhs), (Self::A, Self::A) | (Self::B, Self::B)) + } + } + impl Eq for Bar {} + + let x = Bar::A; + match x { + Bar::A => println!(), + _ => (), + } +} + macro_rules! single_match { ($num:literal) => { match $num { diff --git a/tests/ui/single_match.stderr b/tests/ui/single_match.stderr index f69554d75f9..5eca07ab109 100644 --- a/tests/ui/single_match.stderr +++ b/tests/ui/single_match.stderr @@ -65,5 +65,41 @@ LL | | Cow::Owned(..) => (), LL | | }; | |_____^ help: try this: `if let Cow::Borrowed(..) = c { dummy() }` -error: aborting due to 6 previous errors +error: you seem to be trying to use match for an equality check. Consider using `if` + --> $DIR/single_match.rs:86:5 + | +LL | / match x { +LL | | "test" => println!(), +LL | | _ => (), +LL | | } + | |_____^ help: try this: `if x == "test" { println!() }` + +error: you seem to be trying to use match for an equality check. Consider using `if` + --> $DIR/single_match.rs:99:5 + | +LL | / match x { +LL | | Foo::A => println!(), +LL | | _ => (), +LL | | } + | |_____^ help: try this: `if x == Foo::A { println!() }` + +error: you seem to be trying to use match for an equality check. Consider using `if` + --> $DIR/single_match.rs:105:5 + | +LL | / match x { +LL | | FOO_C => println!(), +LL | | _ => (), +LL | | } + | |_____^ help: try this: `if x == FOO_C { println!() }` + +error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match.rs:121:5 + | +LL | / match x { +LL | | Bar::A => println!(), +LL | | _ => (), +LL | | } + | |_____^ help: try this: `if let Bar::A = x { println!() }` + +error: aborting due to 10 previous errors -- cgit 1.4.1-3-g733a5 From f2d493504cb863a30f45e8a0d5717285823f931e Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Sat, 28 Nov 2020 16:56:59 -0600 Subject: Similar names ignore underscore prefixed names --- clippy_lints/src/non_expressive_names.rs | 4 ++++ tests/ui/similar_names.rs | 5 +++++ 2 files changed, 9 insertions(+) (limited to 'tests') diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index 446426b3e61..855529378e6 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -199,6 +199,10 @@ impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> { ); return; } + if interned_name.starts_with('_') { + // these bindings are typically unused or represent an ignored portion of a destructuring pattern + return; + } let count = interned_name.chars().count(); if count < 3 { if count == 1 { diff --git a/tests/ui/similar_names.rs b/tests/ui/similar_names.rs index 6796b15289e..5981980988b 100644 --- a/tests/ui/similar_names.rs +++ b/tests/ui/similar_names.rs @@ -101,3 +101,8 @@ pub(crate) struct DirSizes { pub(crate) numb_reg_cache_entries: u64, pub(crate) numb_reg_src_checkouts: u64, } + +fn ignore_underscore_prefix() { + let hello: (); + let _hello: (); +} -- cgit 1.4.1-3-g733a5 From feee45c87270a4e6cf1959315e2c4528e7da4ec7 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Wed, 13 Jan 2021 11:08:12 +1300 Subject: Fix the ICE 6539 It happened because `zero_sized_map_values` used `layout_of` with types from type aliases, which is essentially the same as the ICE 4968. --- clippy_lints/src/zero_sized_map_values.rs | 4 +++- tests/ui/crashes/ice-6539.rs | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 tests/ui/crashes/ice-6539.rs (limited to 'tests') diff --git a/clippy_lints/src/zero_sized_map_values.rs b/clippy_lints/src/zero_sized_map_values.rs index 1d5fa8d06a9..5e9ffab7dfb 100644 --- a/clippy_lints/src/zero_sized_map_values.rs +++ b/clippy_lints/src/zero_sized_map_values.rs @@ -6,7 +6,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_target::abi::LayoutOf as _; use rustc_typeck::hir_ty_to_ty; -use crate::utils::{is_type_diagnostic_item, match_type, paths, span_lint_and_help}; +use crate::utils::{is_normalizable, is_type_diagnostic_item, match_type, paths, span_lint_and_help}; declare_clippy_lint! { /// **What it does:** Checks for maps with zero-sized value types anywhere in the code. @@ -50,6 +50,8 @@ impl LateLintPass<'_> for ZeroSizedMapValues { if is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) || match_type(cx, ty, &paths::BTREEMAP); if let Adt(_, ref substs) = ty.kind(); let ty = substs.type_at(1); + // Do this to prevent `layout_of` crashing, being unable to fully normalize `ty`. + if is_normalizable(cx, cx.param_env, ty); if let Ok(layout) = cx.layout_of(ty); if layout.is_zst(); then { diff --git a/tests/ui/crashes/ice-6539.rs b/tests/ui/crashes/ice-6539.rs new file mode 100644 index 00000000000..ac6c3e4aba0 --- /dev/null +++ b/tests/ui/crashes/ice-6539.rs @@ -0,0 +1,16 @@ +// The test for the ICE 6539: https://github.com/rust-lang/rust-clippy/issues/6539. +// The cause is that `zero_sized_map_values` used `layout_of` with types from type aliases, +// which is essentially the same as the ICE 4968. +// Note that only type aliases with associated types caused the crash this time, +// not others such as trait impls. + +use std::collections::{BTreeMap, HashMap}; + +pub trait Trait { + type Assoc; +} + +type TypeAlias = HashMap<(), ::Assoc>; +type TypeAlias2 = BTreeMap<(), ::Assoc>; + +fn main() {} -- cgit 1.4.1-3-g733a5 From 85edd65bf69266dd7cec2ca6d7bb6941b6f85444 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 14 Jan 2021 14:26:26 -0500 Subject: Address review comments Add: attempt to remove address of expressions from the scrutinee expression before adding references to the pattern --- clippy_lints/src/matches.rs | 45 +++++++++++++----------------------- clippy_lints/src/utils/mod.rs | 38 +++++++++++++++++++++++++++++++ tests/ui/single_match.rs | 15 +++++++++++- tests/ui/single_match.stderr | 48 +++++++++++++++++++++++++++------------ tests/ui/single_match_else.stderr | 6 ++--- 5 files changed, 104 insertions(+), 48 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 2239b519632..6ecd738d2f0 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -4,8 +4,8 @@ use crate::utils::usage::is_unused; use crate::utils::{ expr_block, get_arg_name, get_parent_expr, implements_trait, in_macro, indent_of, is_allowed, is_expn_of, is_refutable, is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, meets_msrv, multispan_sugg, - remove_blocks, snippet, snippet_block, snippet_opt, snippet_with_applicability, span_lint_and_help, - span_lint_and_note, span_lint_and_sugg, span_lint_and_then, + peel_hir_pat_refs, peel_mid_ty_refs, peeln_hir_expr_refs, remove_blocks, snippet, snippet_block, snippet_opt, + snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, }; use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash}; use if_chain::if_chain; @@ -717,28 +717,6 @@ fn check_single_match_single_pattern( } } -fn peel_pat_refs(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) { - fn peel(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) { - if let PatKind::Ref(pat, _) = pat.kind { - peel(pat, count + 1) - } else { - (pat, count) - } - } - peel(pat, 0) -} - -fn peel_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) { - fn peel(ty: Ty<'_>, count: usize) -> (Ty<'_>, usize) { - if let ty::Ref(_, ty, _) = ty.kind() { - peel(ty, count + 1) - } else { - (ty, count) - } - } - peel(ty, 0) -} - fn report_single_match_single_pattern( cx: &LateContext<'_>, ex: &Expr<'_>, @@ -752,9 +730,9 @@ fn report_single_match_single_pattern( }); let (msg, sugg) = if_chain! { - let (pat, pat_ref_count) = peel_pat_refs(arms[0].pat); + let (pat, pat_ref_count) = peel_hir_pat_refs(arms[0].pat); if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind; - let (ty, ty_ref_count) = peel_ty_refs(cx.typeck_results().expr_ty(ex)); + let (ty, ty_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(ex)); if let Some(trait_id) = cx.tcx.lang_items().structural_peq_trait(); if ty.is_integral() || ty.is_char() || ty.is_str() || implements_trait(cx, ty, trait_id, &[]); then { @@ -764,19 +742,28 @@ fn report_single_match_single_pattern( PatKind::Lit(Expr { kind: ExprKind::Lit(lit), .. }) if lit.node.is_str() => pat_ref_count + 1, _ => pat_ref_count, }; - let msg = "you seem to be trying to use match for an equality check. Consider using `if`"; + // References are only implicitly added to the pattern, so no overflow here. + // e.g. will work: match &Some(_) { Some(_) => () } + // will not: match Some(_) { &Some(_) => () } + let ref_count_diff = ty_ref_count - pat_ref_count; + + // Try to remove address of expressions first. + let (ex, removed) = peeln_hir_expr_refs(ex, ref_count_diff); + let ref_count_diff = ref_count_diff - removed; + + let msg = "you seem to be trying to use `match` for an equality check. Consider using `if`"; let sugg = format!( "if {} == {}{} {}{}", snippet(cx, ex.span, ".."), // PartialEq for different reference counts may not exist. - "&".repeat(ty_ref_count - pat_ref_count), + "&".repeat(ref_count_diff), snippet(cx, arms[0].pat.span, ".."), expr_block(cx, &arms[0].body, None, "..", Some(expr.span)), els_str, ); (msg, sugg) } else { - let msg = "you seem to be trying to use match for destructuring a single pattern. Consider using `if let`"; + let msg = "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`"; let sugg = format!( "if let {} = {} {}{}", snippet(cx, arms[0].pat.span, ".."), diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 8f54cad7728..8f8c681ecb7 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -1668,6 +1668,44 @@ where match_expr_list } +/// Peels off all references on the pattern. Returns the underlying pattern and the number of +/// references removed. +pub fn peel_hir_pat_refs(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) { + fn peel(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) { + if let PatKind::Ref(pat, _) = pat.kind { + peel(pat, count + 1) + } else { + (pat, count) + } + } + peel(pat, 0) +} + +/// Peels off up to the given number of references on the expression. Returns the underlying +/// expression and the number of references removed. +pub fn peeln_hir_expr_refs(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) { + fn f(expr: &'a Expr<'a>, count: usize, target: usize) -> (&'a Expr<'a>, usize) { + match expr.kind { + ExprKind::AddrOf(_, _, expr) if count != target => f(expr, count + 1, target), + _ => (expr, count), + } + } + f(expr, 0, count) +} + +/// Peels off all references on the type. Returns the underlying type and the number of references +/// removed. +pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) { + fn peel(ty: Ty<'_>, count: usize) -> (Ty<'_>, usize) { + if let ty::Ref(_, ty, _) = ty.kind() { + peel(ty, count + 1) + } else { + (ty, count) + } + } + peel(ty, 0) +} + #[macro_export] macro_rules! unwrap_cargo_metadata { ($cx: ident, $lint: ident, $deps: expr) => {{ diff --git a/tests/ui/single_match.rs b/tests/ui/single_match.rs index 02266308fba..ca884b41c45 100644 --- a/tests/ui/single_match.rs +++ b/tests/ui/single_match.rs @@ -81,7 +81,8 @@ fn single_match_know_enum() { } } -fn issue_173() { +// issue #173 +fn if_suggestion() { let x = "test"; match x { "test" => println!(), @@ -106,6 +107,18 @@ fn issue_173() { FOO_C => println!(), _ => (), } + + match &&x { + Foo::A => println!(), + _ => (), + } + + let x = &x; + match &x { + Foo::A => println!(), + _ => (), + } + enum Bar { A, B, diff --git a/tests/ui/single_match.stderr b/tests/ui/single_match.stderr index 5eca07ab109..7ea6955b740 100644 --- a/tests/ui/single_match.stderr +++ b/tests/ui/single_match.stderr @@ -1,4 +1,4 @@ -error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` --> $DIR/single_match.rs:8:5 | LL | / match x { @@ -17,7 +17,7 @@ LL | println!("{:?}", y); LL | }; | -error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` --> $DIR/single_match.rs:16:5 | LL | / match x { @@ -29,7 +29,7 @@ LL | | _ => (), LL | | } | |_____^ help: try this: `if let Some(y) = x { println!("{:?}", y) }` -error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` --> $DIR/single_match.rs:25:5 | LL | / match z { @@ -38,7 +38,7 @@ LL | | _ => {}, LL | | }; | |_____^ help: try this: `if let (2..=3, 7..=9) = z { dummy() }` -error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` --> $DIR/single_match.rs:54:5 | LL | / match x { @@ -47,7 +47,7 @@ LL | | None => (), LL | | }; | |_____^ help: try this: `if let Some(y) = x { dummy() }` -error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` --> $DIR/single_match.rs:59:5 | LL | / match y { @@ -56,7 +56,7 @@ LL | | Err(..) => (), LL | | }; | |_____^ help: try this: `if let Ok(y) = y { dummy() }` -error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` --> $DIR/single_match.rs:66:5 | LL | / match c { @@ -65,8 +65,8 @@ LL | | Cow::Owned(..) => (), LL | | }; | |_____^ help: try this: `if let Cow::Borrowed(..) = c { dummy() }` -error: you seem to be trying to use match for an equality check. Consider using `if` - --> $DIR/single_match.rs:86:5 +error: you seem to be trying to use `match` for an equality check. Consider using `if` + --> $DIR/single_match.rs:87:5 | LL | / match x { LL | | "test" => println!(), @@ -74,8 +74,8 @@ LL | | _ => (), LL | | } | |_____^ help: try this: `if x == "test" { println!() }` -error: you seem to be trying to use match for an equality check. Consider using `if` - --> $DIR/single_match.rs:99:5 +error: you seem to be trying to use `match` for an equality check. Consider using `if` + --> $DIR/single_match.rs:100:5 | LL | / match x { LL | | Foo::A => println!(), @@ -83,8 +83,8 @@ LL | | _ => (), LL | | } | |_____^ help: try this: `if x == Foo::A { println!() }` -error: you seem to be trying to use match for an equality check. Consider using `if` - --> $DIR/single_match.rs:105:5 +error: you seem to be trying to use `match` for an equality check. Consider using `if` + --> $DIR/single_match.rs:106:5 | LL | / match x { LL | | FOO_C => println!(), @@ -92,8 +92,26 @@ LL | | _ => (), LL | | } | |_____^ help: try this: `if x == FOO_C { println!() }` -error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match.rs:121:5 +error: you seem to be trying to use `match` for an equality check. Consider using `if` + --> $DIR/single_match.rs:111:5 + | +LL | / match &&x { +LL | | Foo::A => println!(), +LL | | _ => (), +LL | | } + | |_____^ help: try this: `if x == Foo::A { println!() }` + +error: you seem to be trying to use `match` for an equality check. Consider using `if` + --> $DIR/single_match.rs:117:5 + | +LL | / match &x { +LL | | Foo::A => println!(), +LL | | _ => (), +LL | | } + | |_____^ help: try this: `if x == &Foo::A { println!() }` + +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match.rs:134:5 | LL | / match x { LL | | Bar::A => println!(), @@ -101,5 +119,5 @@ LL | | _ => (), LL | | } | |_____^ help: try this: `if let Bar::A = x { println!() }` -error: aborting due to 10 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/single_match_else.stderr b/tests/ui/single_match_else.stderr index 3a07c2ec542..20be4fa226c 100644 --- a/tests/ui/single_match_else.stderr +++ b/tests/ui/single_match_else.stderr @@ -1,4 +1,4 @@ -error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` --> $DIR/single_match_else.rs:14:5 | LL | / match ExprNode::Butterflies { @@ -19,7 +19,7 @@ LL | None LL | } | -error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` --> $DIR/single_match_else.rs:70:5 | LL | / match Some(1) { @@ -39,7 +39,7 @@ LL | return LL | } | -error: you seem to be trying to use match for destructuring a single pattern. Consider using `if let` +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` --> $DIR/single_match_else.rs:79:5 | LL | / match Some(1) { -- cgit 1.4.1-3-g733a5 From f18cf82ca8764d6b0b07549cdba25b91bd0243fa Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 15 Jan 2021 10:41:29 +0100 Subject: Don't trigger needless_return lint in macros --- clippy_lints/src/returns.rs | 3 +++ tests/ui/needless_return.fixed | 15 +++++++++++++++ tests/ui/needless_return.rs | 15 +++++++++++++++ 3 files changed, 33 insertions(+) (limited to 'tests') diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 0e031e6151b..63548d8fdb4 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -217,6 +217,9 @@ fn check_final_expr<'tcx>( } fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, inner_span: Option, replacement: RetReplacement) { + if ret_span.from_expansion() { + return; + } match inner_span { Some(inner_span) => { if in_external_macro(cx.tcx.sess, inner_span) || inner_span.from_expansion() { diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index d849e093da7..86bfc5b4bb2 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -86,6 +86,21 @@ fn borrows_but_not_last(value: bool) -> String { } } +macro_rules! needed_return { + ($e:expr) => { + if $e > 3 { + return; + } + }; +} + +fn test_return_in_macro() { + // This will return and the macro below won't be executed. Removing the `return` from the macro + // will change semantics. + needed_return!(10); + needed_return!(0); +} + fn main() { let _ = test_end_of_fn(); let _ = test_no_semicolon(); diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index 29f2bd1852a..51061370dfe 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -86,6 +86,21 @@ fn borrows_but_not_last(value: bool) -> String { } } +macro_rules! needed_return { + ($e:expr) => { + if $e > 3 { + return; + } + }; +} + +fn test_return_in_macro() { + // This will return and the macro below won't be executed. Removing the `return` from the macro + // will change semantics. + needed_return!(10); + needed_return!(0); +} + fn main() { let _ = test_end_of_fn(); let _ = test_no_semicolon(); -- cgit 1.4.1-3-g733a5 From 83f1abff4874a66124a6aaefec248ca4051c0d23 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Mon, 4 Jan 2021 21:20:44 +0100 Subject: Fix FP with empty return for `needless_return` lint --- clippy_lints/src/returns.rs | 11 ++++++++++- tests/ui/needless_return.fixed | 13 +++++++++++++ tests/ui/needless_return.rs | 13 +++++++++++++ tests/ui/needless_return.stderr | 20 +++++++++++++++++++- 4 files changed, 55 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 63548d8fdb4..e438f92b136 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -131,7 +131,16 @@ impl<'tcx> LateLintPass<'tcx> for Return { _: HirId, ) { match kind { - FnKind::Closure(_) => check_final_expr(cx, &body.value, Some(body.value.span), RetReplacement::Empty), + FnKind::Closure(_) => { + // when returning without value in closure, replace this `return` + // with an empty block to prevent invalid suggestion (see #6501) + let replacement = if let ExprKind::Ret(None) = &body.value.kind { + RetReplacement::Block + } else { + RetReplacement::Empty + }; + check_final_expr(cx, &body.value, Some(body.value.span), replacement) + }, FnKind::ItemFn(..) | FnKind::Method(..) => { if let ExprKind::Block(ref block, _) = body.value.kind { check_block_return(cx, block); diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index 86bfc5b4bb2..f137e8ecae9 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -101,6 +101,19 @@ fn test_return_in_macro() { needed_return!(0); } +mod issue6501 { + fn foo(bar: Result<(), ()>) { + bar.unwrap_or_else(|_| {}) + } + + fn test_closure() { + let _ = || { + + }; + let _ = || {}; + } +} + fn main() { let _ = test_end_of_fn(); let _ = test_no_semicolon(); diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index 51061370dfe..d754e4d37a8 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -101,6 +101,19 @@ fn test_return_in_macro() { needed_return!(0); } +mod issue6501 { + fn foo(bar: Result<(), ()>) { + bar.unwrap_or_else(|_| return) + } + + fn test_closure() { + let _ = || { + return; + }; + let _ = || return; + } +} + fn main() { let _ = test_end_of_fn(); let _ = test_no_semicolon(); diff --git a/tests/ui/needless_return.stderr b/tests/ui/needless_return.stderr index f73c833a801..12d94e892ed 100644 --- a/tests/ui/needless_return.stderr +++ b/tests/ui/needless_return.stderr @@ -84,5 +84,23 @@ error: unneeded `return` statement LL | return String::new(); | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()` -error: aborting due to 14 previous errors +error: unneeded `return` statement + --> $DIR/needless_return.rs:91:32 + | +LL | bar.unwrap_or_else(|_| return) + | ^^^^^^ help: replace `return` with an empty block: `{}` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:96:13 + | +LL | return; + | ^^^^^^^ help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:98:20 + | +LL | let _ = || return; + | ^^^^^^ help: replace `return` with an empty block: `{}` + +error: aborting due to 17 previous errors -- cgit 1.4.1-3-g733a5 From 46aa654c2d1792a42835d63f9a3dfcbffc20b758 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Fri, 15 Jan 2021 18:58:48 +0100 Subject: Fix test due to recent Rustup merge --- tests/ui/needless_return.stderr | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/tests/ui/needless_return.stderr b/tests/ui/needless_return.stderr index 12d94e892ed..d1240e161c0 100644 --- a/tests/ui/needless_return.stderr +++ b/tests/ui/needless_return.stderr @@ -85,19 +85,19 @@ LL | return String::new(); | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()` error: unneeded `return` statement - --> $DIR/needless_return.rs:91:32 + --> $DIR/needless_return.rs:106:32 | LL | bar.unwrap_or_else(|_| return) | ^^^^^^ help: replace `return` with an empty block: `{}` error: unneeded `return` statement - --> $DIR/needless_return.rs:96:13 + --> $DIR/needless_return.rs:111:13 | LL | return; | ^^^^^^^ help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:98:20 + --> $DIR/needless_return.rs:113:20 | LL | let _ = || return; | ^^^^^^ help: replace `return` with an empty block: `{}` -- cgit 1.4.1-3-g733a5 From 837bc9906552af5da429bef4b37be588b8d2f49d Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 31 Dec 2020 11:10:13 -0500 Subject: Initial implementation of redundant_slicing lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 ++++ clippy_lints/src/redundant_slicing.rs | 56 +++++++++++++++++++++++++++++++++++ tests/ui/redundant_slicing.rs | 11 +++++++ tests/ui/redundant_slicing.stderr | 16 ++++++++++ 5 files changed, 89 insertions(+) create mode 100644 clippy_lints/src/redundant_slicing.rs create mode 100644 tests/ui/redundant_slicing.rs create mode 100644 tests/ui/redundant_slicing.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index b0e9ad55b4f..85f6929f924 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2162,6 +2162,7 @@ Released 2018-09-13 [`redundant_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern [`redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching [`redundant_pub_crate`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pub_crate +[`redundant_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_slicing [`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes [`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref [`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 4f9ebb4af3d..70fdfd22caa 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -301,6 +301,7 @@ mod redundant_closure_call; mod redundant_else; mod redundant_field_names; mod redundant_pub_crate; +mod redundant_slicing; mod redundant_static_lifetimes; mod ref_option_ref; mod reference; @@ -849,6 +850,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &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, @@ -1229,6 +1231,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box vec_init_then_push::VecInitThenPush::default()); store.register_late_pass(move || box types::PtrAsPtr::new(msrv)); store.register_late_pass(|| box case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons); + store.register_late_pass(|| box redundant_slicing::RedundantSlicing); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1591,6 +1594,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), @@ -1835,6 +1839,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), diff --git a/clippy_lints/src/redundant_slicing.rs b/clippy_lints/src/redundant_slicing.rs new file mode 100644 index 00000000000..686298b6943 --- /dev/null +++ b/clippy_lints/src/redundant_slicing.rs @@ -0,0 +1,56 @@ +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, LangItem}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TyS; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +use crate::utils::{is_type_lang_item, snippet_with_applicability, span_lint_and_sugg}; + +declare_clippy_lint! { + /// **What it does:** Checks for redundant slicing expressions which use the full range, and + /// do not change the type. + /// + /// **Why is this bad?** It unnecessarily adds complexity to the expression. + /// + /// **Known problems:** If the type being sliced has an implementation of `Index` + /// that actually changes anything then it can't be removed. However, this would be surprising + /// to people reading the code and should have a note with it. + /// + /// **Example:** + /// + /// ```ignore + /// fn get_slice(x: &[u32]) -> &[u32] { + /// &x[..] + /// } + /// ``` + /// Use instead: + /// ```ignore + /// fn get_slice(x: &[u32]) -> &[u32] { + /// x + /// } + /// ``` + pub REDUNDANT_SLICING, + complexity, + "redundant slicing of the whole range of a type" +} + +declare_lint_pass!(RedundantSlicing => [REDUNDANT_SLICING]); + +impl LateLintPass<'_> for RedundantSlicing { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + if let ExprKind::AddrOf(_, _, addressee) = expr.kind; + if let ExprKind::Index(indexed, range) = addressee.kind; + if is_type_lang_item(cx, cx.typeck_results().expr_ty_adjusted(range), LangItem::RangeFull); + if TyS::same_type(cx.typeck_results().expr_ty(expr), cx.typeck_results().expr_ty(indexed)); + then { + let mut app = Applicability::MachineApplicable; + let hint = snippet_with_applicability(cx, indexed.span, "..", &mut app).into_owned(); + + span_lint_and_sugg(cx, REDUNDANT_SLICING, expr.span, "redundant slicing of the whole range", + "use the original slice instead", hint, app); + } + } + } +} diff --git a/tests/ui/redundant_slicing.rs b/tests/ui/redundant_slicing.rs new file mode 100644 index 00000000000..922b8b4ce57 --- /dev/null +++ b/tests/ui/redundant_slicing.rs @@ -0,0 +1,11 @@ +#![allow(unused)] +#![warn(clippy::redundant_slicing)] + +fn main() { + let x: &[u32] = &[0]; + let err = &x[..]; + + let v = vec![0]; + let ok = &v[..]; + let err = &(&v[..])[..]; +} diff --git a/tests/ui/redundant_slicing.stderr b/tests/ui/redundant_slicing.stderr new file mode 100644 index 00000000000..9efd6484ad0 --- /dev/null +++ b/tests/ui/redundant_slicing.stderr @@ -0,0 +1,16 @@ +error: redundant slicing of the whole range + --> $DIR/redundant_slicing.rs:6:15 + | +LL | let err = &x[..]; + | ^^^^^^ help: use the original slice instead: `x` + | + = note: `-D clippy::redundant-slicing` implied by `-D warnings` + +error: redundant slicing of the whole range + --> $DIR/redundant_slicing.rs:10:15 + | +LL | let err = &(&v[..])[..]; + | ^^^^^^^^^^^^^ help: use the original slice instead: `(&v[..])` + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From 0c347d3d0686d8892512dcc1e13102d15afcc6dd Mon Sep 17 00:00:00 2001 From: Marc Dominik Migge Date: Sun, 17 Jan 2021 14:42:36 +0100 Subject: Fix false positive for unit_arg lint --- clippy_lints/src/types.rs | 11 ++++++++++- tests/ui/unit_arg.rs | 20 +++++++++++++++++++- tests/ui/unit_arg.stderr | 46 ++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 71 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 3b5a83d2a0b..7d0eea37bc0 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -955,7 +955,16 @@ impl<'tcx> LateLintPass<'tcx> for UnitArg { .iter() .filter(|arg| { if is_unit(cx.typeck_results().expr_ty(arg)) && !is_unit_literal(arg) { - !matches!(&arg.kind, ExprKind::Match(.., MatchSource::TryDesugar)) + match &arg.kind { + ExprKind::Block(..) + | ExprKind::Call(..) + | ExprKind::If(..) + | ExprKind::MethodCall(..) => true, + ExprKind::Match(..) => { + !matches!(&arg.kind, ExprKind::Match(.., MatchSource::TryDesugar)) + }, + _ => false, + } } else { false } diff --git a/tests/ui/unit_arg.rs b/tests/ui/unit_arg.rs index b6a7bc5a1cc..79dac925f08 100644 --- a/tests/ui/unit_arg.rs +++ b/tests/ui/unit_arg.rs @@ -59,7 +59,18 @@ fn bad() { None.or(Some(foo(2))); // in this case, the suggestion can be inlined, no need for a surrounding block // foo(()); foo(()) instead of { foo(()); foo(()) } - foo(foo(())) + foo(foo(())); + foo(if true { + 1; + }); + foo(match Some(1) { + Some(_) => { + 1; + }, + None => { + 0; + }, + }); } fn ok() { @@ -71,6 +82,13 @@ fn ok() { b.bar({ 1 }); b.bar(()); question_mark(); + let named_unit_arg = (); + foo(named_unit_arg); + foo(if true { 1 } else { 0 }); + foo(match Some(1) { + Some(_) => 1, + None => 0, + }); } fn question_mark() -> Result<(), ()> { diff --git a/tests/ui/unit_arg.stderr b/tests/ui/unit_arg.stderr index 094cff8c985..8679706f8ec 100644 --- a/tests/ui/unit_arg.stderr +++ b/tests/ui/unit_arg.stderr @@ -156,17 +156,55 @@ LL | }); error: passing a unit value to a function --> $DIR/unit_arg.rs:62:5 | -LL | foo(foo(())) +LL | foo(foo(())); | ^^^^^^^^^^^^ | help: move the expression in front of the call and replace it with the unit literal `()` | LL | foo(()); -LL | foo(()) +LL | foo(()); + | + +error: passing a unit value to a function + --> $DIR/unit_arg.rs:63:5 + | +LL | / foo(if true { +LL | | 1; +LL | | }); + | |______^ + | +help: move the expression in front of the call and replace it with the unit literal `()` + | +LL | if true { +LL | 1; +LL | }; +LL | foo(()); + | + +error: passing a unit value to a function + --> $DIR/unit_arg.rs:66:5 | +LL | / foo(match Some(1) { +LL | | Some(_) => { +LL | | 1; +LL | | }, +... | +LL | | }, +LL | | }); + | |______^ + | +help: move the expression in front of the call and replace it with the unit literal `()` + | +LL | match Some(1) { +LL | Some(_) => { +LL | 1; +LL | }, +LL | None => { +LL | 0; + ... error: passing a unit value to a function - --> $DIR/unit_arg.rs:95:5 + --> $DIR/unit_arg.rs:113:5 | LL | Some(foo(1)) | ^^^^^^^^^^^^ @@ -177,5 +215,5 @@ LL | foo(1); LL | Some(()) | -error: aborting due to 10 previous errors +error: aborting due to 12 previous errors -- cgit 1.4.1-3-g733a5 From fb2a06dcce0680dfe8d2bed542be3c55eb2e18c7 Mon Sep 17 00:00:00 2001 From: pro-grammer1 <1df0d0d3-eed4-45fc-bc60-43a85079f3f9@anonaddy.me> Date: Sun, 17 Jan 2021 18:55:59 +0000 Subject: Remove numeric literals from print_literal and write_literal tests --- clippy_lints/src/write.rs | 2 +- tests/ui/crashes/ice-3891.stderr | 4 ++-- tests/ui/print_literal.rs | 3 --- tests/ui/print_literal.stderr | 46 ++++++++++++---------------------------- tests/ui/write_literal.rs | 3 --- tests/ui/write_literal.stderr | 46 ++++++++++++---------------------------- 6 files changed, 31 insertions(+), 73 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 60a9fb59cd2..503cb82c3e5 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -461,7 +461,7 @@ impl Write { span_lint(cx, lint, token_expr.span, "literal with an empty format string"); } idx += 1; - } + }, ExprKind::Assign(lhs, rhs, _) => { if_chain! { if let ExprKind::Lit(ref lit) = rhs.kind; diff --git a/tests/ui/crashes/ice-3891.stderr b/tests/ui/crashes/ice-3891.stderr index 5a285b0e714..59469ec5891 100644 --- a/tests/ui/crashes/ice-3891.stderr +++ b/tests/ui/crashes/ice-3891.stderr @@ -1,10 +1,10 @@ -error: invalid suffix `x` for integer literal +error: invalid suffix `x` for number literal --> $DIR/ice-3891.rs:2:5 | LL | 1x; | ^^ invalid suffix `x` | - = help: the suffix must be one of the integral types (`u32`, `isize`, etc) + = help: the suffix must be one of the numeric types (`u32`, `isize`, `f32`, etc.) error: aborting due to previous error diff --git a/tests/ui/print_literal.rs b/tests/ui/print_literal.rs index 40ed18e9302..0c8aecc2d8c 100644 --- a/tests/ui/print_literal.rs +++ b/tests/ui/print_literal.rs @@ -19,12 +19,9 @@ fn main() { println!("{number:>0width$}", number = 1, width = 6); // these should throw warnings - println!("{} of {:b} people know binary, the other half doesn't", 1, 2); print!("Hello {}", "world"); println!("Hello {} {}", world, "world"); println!("Hello {}", "world"); - println!("10 / 4 is {}", 2.5); - println!("2 + 1 = {}", 3); // positional args don't change the fact // that we're using a literal -- this should diff --git a/tests/ui/print_literal.stderr b/tests/ui/print_literal.stderr index fc502e9f71d..692abdb3054 100644 --- a/tests/ui/print_literal.stderr +++ b/tests/ui/print_literal.stderr @@ -1,88 +1,70 @@ error: literal with an empty format string - --> $DIR/print_literal.rs:22:71 - | -LL | println!("{} of {:b} people know binary, the other half doesn't", 1, 2); - | ^ - | - = note: `-D clippy::print-literal` implied by `-D warnings` - -error: literal with an empty format string - --> $DIR/print_literal.rs:23:24 + --> $DIR/print_literal.rs:22:24 | LL | print!("Hello {}", "world"); | ^^^^^^^ + | + = note: `-D clippy::print-literal` implied by `-D warnings` error: literal with an empty format string - --> $DIR/print_literal.rs:24:36 + --> $DIR/print_literal.rs:23:36 | LL | println!("Hello {} {}", world, "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/print_literal.rs:25:26 + --> $DIR/print_literal.rs:24:26 | LL | println!("Hello {}", "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/print_literal.rs:26:30 - | -LL | println!("10 / 4 is {}", 2.5); - | ^^^ - -error: literal with an empty format string - --> $DIR/print_literal.rs:27:28 - | -LL | println!("2 + 1 = {}", 3); - | ^ - -error: literal with an empty format string - --> $DIR/print_literal.rs:32:25 + --> $DIR/print_literal.rs:29:25 | LL | println!("{0} {1}", "hello", "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/print_literal.rs:32:34 + --> $DIR/print_literal.rs:29:34 | LL | println!("{0} {1}", "hello", "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/print_literal.rs:33:25 + --> $DIR/print_literal.rs:30:25 | LL | println!("{1} {0}", "hello", "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/print_literal.rs:33:34 + --> $DIR/print_literal.rs:30:34 | LL | println!("{1} {0}", "hello", "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/print_literal.rs:36:35 + --> $DIR/print_literal.rs:33:35 | LL | println!("{foo} {bar}", foo = "hello", bar = "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/print_literal.rs:36:50 + --> $DIR/print_literal.rs:33:50 | LL | println!("{foo} {bar}", foo = "hello", bar = "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/print_literal.rs:37:35 + --> $DIR/print_literal.rs:34:35 | LL | println!("{bar} {foo}", foo = "hello", bar = "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/print_literal.rs:37:50 + --> $DIR/print_literal.rs:34:50 | LL | println!("{bar} {foo}", foo = "hello", bar = "world"); | ^^^^^^^ -error: aborting due to 14 previous errors +error: aborting due to 11 previous errors diff --git a/tests/ui/write_literal.rs b/tests/ui/write_literal.rs index d8205c5eb67..f5de7ea71d3 100644 --- a/tests/ui/write_literal.rs +++ b/tests/ui/write_literal.rs @@ -24,12 +24,9 @@ fn main() { writeln!(&mut v, "{number:>0width$}", number = 1, width = 6); // these should throw warnings - writeln!(&mut v, "{} of {:b} people know binary, the other half doesn't", 1, 2); write!(&mut v, "Hello {}", "world"); writeln!(&mut v, "Hello {} {}", world, "world"); writeln!(&mut v, "Hello {}", "world"); - writeln!(&mut v, "10 / 4 is {}", 2.5); - writeln!(&mut v, "2 + 1 = {}", 3); // positional args don't change the fact // that we're using a literal -- this should diff --git a/tests/ui/write_literal.stderr b/tests/ui/write_literal.stderr index 54a787fe555..4dcaaa474a8 100644 --- a/tests/ui/write_literal.stderr +++ b/tests/ui/write_literal.stderr @@ -1,88 +1,70 @@ error: literal with an empty format string - --> $DIR/write_literal.rs:27:79 - | -LL | writeln!(&mut v, "{} of {:b} people know binary, the other half doesn't", 1, 2); - | ^ - | - = note: `-D clippy::write-literal` implied by `-D warnings` - -error: literal with an empty format string - --> $DIR/write_literal.rs:28:32 + --> $DIR/write_literal.rs:27:32 | LL | write!(&mut v, "Hello {}", "world"); | ^^^^^^^ + | + = note: `-D clippy::write-literal` implied by `-D warnings` error: literal with an empty format string - --> $DIR/write_literal.rs:29:44 + --> $DIR/write_literal.rs:28:44 | LL | writeln!(&mut v, "Hello {} {}", world, "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/write_literal.rs:30:34 + --> $DIR/write_literal.rs:29:34 | LL | writeln!(&mut v, "Hello {}", "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/write_literal.rs:31:38 - | -LL | writeln!(&mut v, "10 / 4 is {}", 2.5); - | ^^^ - -error: literal with an empty format string - --> $DIR/write_literal.rs:32:36 - | -LL | writeln!(&mut v, "2 + 1 = {}", 3); - | ^ - -error: literal with an empty format string - --> $DIR/write_literal.rs:37:33 + --> $DIR/write_literal.rs:34:33 | LL | writeln!(&mut v, "{0} {1}", "hello", "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/write_literal.rs:37:42 + --> $DIR/write_literal.rs:34:42 | LL | writeln!(&mut v, "{0} {1}", "hello", "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/write_literal.rs:38:33 + --> $DIR/write_literal.rs:35:33 | LL | writeln!(&mut v, "{1} {0}", "hello", "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/write_literal.rs:38:42 + --> $DIR/write_literal.rs:35:42 | LL | writeln!(&mut v, "{1} {0}", "hello", "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/write_literal.rs:41:43 + --> $DIR/write_literal.rs:38:43 | LL | writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/write_literal.rs:41:58 + --> $DIR/write_literal.rs:38:58 | LL | writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/write_literal.rs:42:43 + --> $DIR/write_literal.rs:39:43 | LL | writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/write_literal.rs:42:58 + --> $DIR/write_literal.rs:39:58 | LL | writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world"); | ^^^^^^^ -error: aborting due to 14 previous errors +error: aborting due to 11 previous errors -- cgit 1.4.1-3-g733a5 From 32b2a3f944f9889fd7c5fcfb7976430d1aeb7ed4 Mon Sep 17 00:00:00 2001 From: pro-grammer1 <1df0d0d3-eed4-45fc-bc60-43a85079f3f9@anonaddy.me> Date: Sun, 17 Jan 2021 19:21:33 +0000 Subject: Add numeric literals to the write_literal and print_literal tests that shouldn't fail --- tests/ui/print_literal.rs | 3 +++ tests/ui/print_literal.stderr | 22 +++++++++++----------- tests/ui/write_literal.rs | 3 +++ tests/ui/write_literal.stderr | 22 +++++++++++----------- 4 files changed, 28 insertions(+), 22 deletions(-) (limited to 'tests') diff --git a/tests/ui/print_literal.rs b/tests/ui/print_literal.rs index 0c8aecc2d8c..8665a3bb28a 100644 --- a/tests/ui/print_literal.rs +++ b/tests/ui/print_literal.rs @@ -17,6 +17,9 @@ fn main() { println!("{bar:8} {foo:>8}", foo = "hello", bar = "world"); println!("{number:>width$}", number = 1, width = 6); println!("{number:>0width$}", number = 1, width = 6); + println!("{} of {:b} people know binary, the other half doesn't", 1, 2); + println!("10 / 4 is {}", 2.5); + println!("2 + 1 = {}", 3); // these should throw warnings print!("Hello {}", "world"); diff --git a/tests/ui/print_literal.stderr b/tests/ui/print_literal.stderr index 692abdb3054..e284aece236 100644 --- a/tests/ui/print_literal.stderr +++ b/tests/ui/print_literal.stderr @@ -1,5 +1,5 @@ error: literal with an empty format string - --> $DIR/print_literal.rs:22:24 + --> $DIR/print_literal.rs:25:24 | LL | print!("Hello {}", "world"); | ^^^^^^^ @@ -7,61 +7,61 @@ LL | print!("Hello {}", "world"); = note: `-D clippy::print-literal` implied by `-D warnings` error: literal with an empty format string - --> $DIR/print_literal.rs:23:36 + --> $DIR/print_literal.rs:26:36 | LL | println!("Hello {} {}", world, "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/print_literal.rs:24:26 + --> $DIR/print_literal.rs:27:26 | LL | println!("Hello {}", "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/print_literal.rs:29:25 + --> $DIR/print_literal.rs:32:25 | LL | println!("{0} {1}", "hello", "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/print_literal.rs:29:34 + --> $DIR/print_literal.rs:32:34 | LL | println!("{0} {1}", "hello", "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/print_literal.rs:30:25 + --> $DIR/print_literal.rs:33:25 | LL | println!("{1} {0}", "hello", "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/print_literal.rs:30:34 + --> $DIR/print_literal.rs:33:34 | LL | println!("{1} {0}", "hello", "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/print_literal.rs:33:35 + --> $DIR/print_literal.rs:36:35 | LL | println!("{foo} {bar}", foo = "hello", bar = "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/print_literal.rs:33:50 + --> $DIR/print_literal.rs:36:50 | LL | println!("{foo} {bar}", foo = "hello", bar = "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/print_literal.rs:34:35 + --> $DIR/print_literal.rs:37:35 | LL | println!("{bar} {foo}", foo = "hello", bar = "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/print_literal.rs:34:50 + --> $DIR/print_literal.rs:37:50 | LL | println!("{bar} {foo}", foo = "hello", bar = "world"); | ^^^^^^^ diff --git a/tests/ui/write_literal.rs b/tests/ui/write_literal.rs index f5de7ea71d3..0a127858def 100644 --- a/tests/ui/write_literal.rs +++ b/tests/ui/write_literal.rs @@ -22,6 +22,9 @@ fn main() { writeln!(&mut v, "{bar:8} {foo:>8}", foo = "hello", bar = "world"); writeln!(&mut v, "{number:>width$}", number = 1, width = 6); writeln!(&mut v, "{number:>0width$}", number = 1, width = 6); + writeln!(&mut v, "{} of {:b} people know binary, the other half doesn't", 1, 2); + writeln!(&mut v, "10 / 4 is {}", 2.5); + writeln!(&mut v, "2 + 1 = {}", 3); // these should throw warnings write!(&mut v, "Hello {}", "world"); diff --git a/tests/ui/write_literal.stderr b/tests/ui/write_literal.stderr index 4dcaaa474a8..e54d89ecf29 100644 --- a/tests/ui/write_literal.stderr +++ b/tests/ui/write_literal.stderr @@ -1,5 +1,5 @@ error: literal with an empty format string - --> $DIR/write_literal.rs:27:32 + --> $DIR/write_literal.rs:30:32 | LL | write!(&mut v, "Hello {}", "world"); | ^^^^^^^ @@ -7,61 +7,61 @@ LL | write!(&mut v, "Hello {}", "world"); = note: `-D clippy::write-literal` implied by `-D warnings` error: literal with an empty format string - --> $DIR/write_literal.rs:28:44 + --> $DIR/write_literal.rs:31:44 | LL | writeln!(&mut v, "Hello {} {}", world, "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/write_literal.rs:29:34 + --> $DIR/write_literal.rs:32:34 | LL | writeln!(&mut v, "Hello {}", "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/write_literal.rs:34:33 + --> $DIR/write_literal.rs:37:33 | LL | writeln!(&mut v, "{0} {1}", "hello", "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/write_literal.rs:34:42 + --> $DIR/write_literal.rs:37:42 | LL | writeln!(&mut v, "{0} {1}", "hello", "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/write_literal.rs:35:33 + --> $DIR/write_literal.rs:38:33 | LL | writeln!(&mut v, "{1} {0}", "hello", "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/write_literal.rs:35:42 + --> $DIR/write_literal.rs:38:42 | LL | writeln!(&mut v, "{1} {0}", "hello", "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/write_literal.rs:38:43 + --> $DIR/write_literal.rs:41:43 | LL | writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/write_literal.rs:38:58 + --> $DIR/write_literal.rs:41:58 | LL | writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/write_literal.rs:39:43 + --> $DIR/write_literal.rs:42:43 | LL | writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world"); | ^^^^^^^ error: literal with an empty format string - --> $DIR/write_literal.rs:39:58 + --> $DIR/write_literal.rs:42:58 | LL | writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world"); | ^^^^^^^ -- cgit 1.4.1-3-g733a5 From 70704db36fd07353de58c9330397d0137afdeebe Mon Sep 17 00:00:00 2001 From: ThibsG Date: Fri, 15 Jan 2021 18:51:00 +0100 Subject: Do not lint when range is completely included into another one --- clippy_lints/src/matches.rs | 12 +++++++++++- tests/ui/match_overlapping_arm.rs | 12 ++++++++++++ tests/ui/match_overlapping_arm.stderr | 26 +------------------------- 3 files changed, 24 insertions(+), 26 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 04b35835c6b..90c63f32c13 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -1544,7 +1544,17 @@ where } }, (&Kind::End(a, _), &Kind::Start(b, _)) if a != Bound::Included(b) => (), - _ => return Some((a.range(), b.range())), + _ => { + // skip if the range `a` is completely included into the range `b` + if let Ordering::Equal | Ordering::Less = a.cmp(&b) { + let kind_a = Kind::End(a.range().node.1, a.range()); + let kind_b = Kind::End(b.range().node.1, b.range()); + if let Ordering::Equal | Ordering::Greater = kind_a.cmp(&kind_b) { + return None; + } + } + return Some((a.range(), b.range())); + }, } } diff --git a/tests/ui/match_overlapping_arm.rs b/tests/ui/match_overlapping_arm.rs index 97789bb766f..3e40f2187bf 100644 --- a/tests/ui/match_overlapping_arm.rs +++ b/tests/ui/match_overlapping_arm.rs @@ -57,6 +57,18 @@ fn overlapping() { _ => (), } + match 42 { + 5..7 => println!("5 .. 7"), + 0..10 => println!("0 .. 10"), + _ => (), + } + + match 42 { + 5..10 => println!("5 .. 10"), + 0..=10 => println!("0 ... 10"), + _ => (), + } + /* // FIXME(JohnTitor): uncomment this once rustfmt knows half-open patterns match 42 { diff --git a/tests/ui/match_overlapping_arm.stderr b/tests/ui/match_overlapping_arm.stderr index eb20d5405a9..74259cd88c7 100644 --- a/tests/ui/match_overlapping_arm.stderr +++ b/tests/ui/match_overlapping_arm.stderr @@ -23,30 +23,6 @@ note: overlaps with this LL | FOO..=11 => println!("0 ... 11"), | ^^^^^^^^ -error: some ranges overlap - --> $DIR/match_overlapping_arm.rs:26:9 - | -LL | 0..=5 => println!("0 ... 5"), - | ^^^^^ - | -note: overlaps with this - --> $DIR/match_overlapping_arm.rs:25:9 - | -LL | 2 => println!("2"), - | ^ - -error: some ranges overlap - --> $DIR/match_overlapping_arm.rs:32:9 - | -LL | 0..=2 => println!("0 ... 2"), - | ^^^^^ - | -note: overlaps with this - --> $DIR/match_overlapping_arm.rs:31:9 - | -LL | 2 => println!("2"), - | ^ - error: some ranges overlap --> $DIR/match_overlapping_arm.rs:55:9 | @@ -59,5 +35,5 @@ note: overlaps with this LL | 0..=11 => println!("0 ... 11"), | ^^^^^^ -error: aborting due to 5 previous errors +error: aborting due to 3 previous errors -- cgit 1.4.1-3-g733a5 From c3244c25ac5ccc2923105dfb903d6d4237ed1872 Mon Sep 17 00:00:00 2001 From: "kai.giebeler" Date: Sun, 17 Jan 2021 22:57:08 +0100 Subject: add test for doc_valid_idents "WebGL" --- tests/ui/doc.rs | 1 + tests/ui/doc.stderr | 44 ++++++++++++++++++++++---------------------- 2 files changed, 23 insertions(+), 22 deletions(-) (limited to 'tests') diff --git a/tests/ui/doc.rs b/tests/ui/doc.rs index 68c5d32846f..e30970ed952 100644 --- a/tests/ui/doc.rs +++ b/tests/ui/doc.rs @@ -53,6 +53,7 @@ fn test_units() { /// DirectX /// ECMAScript /// OAuth GraphQL +/// WebGL /// TeX LaTeX BibTeX BibLaTeX /// CamelCase (see also #2395) /// be_sure_we_got_to_the_end_of_it diff --git a/tests/ui/doc.stderr b/tests/ui/doc.stderr index 23fca43590b..e1c1aa85a60 100644 --- a/tests/ui/doc.stderr +++ b/tests/ui/doc.stderr @@ -55,133 +55,133 @@ LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:58:5 + --> $DIR/doc.rs:59:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `link_with_underscores` between ticks in the documentation - --> $DIR/doc.rs:62:22 + --> $DIR/doc.rs:63:22 | LL | /// This test has [a link_with_underscores][chunked-example] inside it. See #823. | ^^^^^^^^^^^^^^^^^^^^^ error: you should put `inline_link2` between ticks in the documentation - --> $DIR/doc.rs:65:21 + --> $DIR/doc.rs:66:21 | LL | /// It can also be [inline_link2]. | ^^^^^^^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:75:5 + --> $DIR/doc.rs:76:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `CamelCaseThing` between ticks in the documentation - --> $DIR/doc.rs:83:8 + --> $DIR/doc.rs:84:8 | LL | /// ## CamelCaseThing | ^^^^^^^^^^^^^^ error: you should put `CamelCaseThing` between ticks in the documentation - --> $DIR/doc.rs:86:7 + --> $DIR/doc.rs:87:7 | LL | /// # CamelCaseThing | ^^^^^^^^^^^^^^ error: you should put `CamelCaseThing` between ticks in the documentation - --> $DIR/doc.rs:88:22 + --> $DIR/doc.rs:89:22 | LL | /// Not a title #897 CamelCaseThing | ^^^^^^^^^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:89:5 + --> $DIR/doc.rs:90:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:96:5 + --> $DIR/doc.rs:97:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:109:5 + --> $DIR/doc.rs:110:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `FooBar` between ticks in the documentation - --> $DIR/doc.rs:120:43 + --> $DIR/doc.rs:121:43 | LL | /** E.g., serialization of an empty list: FooBar | ^^^^^^ error: you should put `BarQuz` between ticks in the documentation - --> $DIR/doc.rs:125:5 + --> $DIR/doc.rs:126:5 | LL | And BarQuz too. | ^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:126:1 + --> $DIR/doc.rs:127:1 | LL | be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `FooBar` between ticks in the documentation - --> $DIR/doc.rs:131:43 + --> $DIR/doc.rs:132:43 | LL | /** E.g., serialization of an empty list: FooBar | ^^^^^^ error: you should put `BarQuz` between ticks in the documentation - --> $DIR/doc.rs:136:5 + --> $DIR/doc.rs:137:5 | LL | And BarQuz too. | ^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:137:1 + --> $DIR/doc.rs:138:1 | LL | be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:148:5 + --> $DIR/doc.rs:149:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> $DIR/doc.rs:175:13 + --> $DIR/doc.rs:176:13 | LL | /// Not ok: http://www.unicode.org | ^^^^^^^^^^^^^^^^^^^^^^ error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> $DIR/doc.rs:176:13 + --> $DIR/doc.rs:177:13 | LL | /// Not ok: https://www.unicode.org | ^^^^^^^^^^^^^^^^^^^^^^^ error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> $DIR/doc.rs:177:13 + --> $DIR/doc.rs:178:13 | LL | /// Not ok: http://www.unicode.org/ | ^^^^^^^^^^^^^^^^^^^^^^ error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> $DIR/doc.rs:178:13 + --> $DIR/doc.rs:179:13 | LL | /// Not ok: http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `mycrate::Collection` between ticks in the documentation - --> $DIR/doc.rs:181:22 + --> $DIR/doc.rs:182:22 | LL | /// An iterator over mycrate::Collection's values. | ^^^^^^^^^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From eb476c6c70bccb87378462e4854f8b8fa12c40be Mon Sep 17 00:00:00 2001 From: Marc Dominik Migge Date: Mon, 18 Jan 2021 20:18:56 +0100 Subject: Split up tests for unit arg expressions --- tests/ui/unit_arg.rs | 16 -------------- tests/ui/unit_arg.stderr | 42 ++---------------------------------- tests/ui/unit_arg_expressions.rs | 35 ++++++++++++++++++++++++++++++ tests/ui/unit_arg_expressions.stderr | 41 +++++++++++++++++++++++++++++++++++ 4 files changed, 78 insertions(+), 56 deletions(-) create mode 100644 tests/ui/unit_arg_expressions.rs create mode 100644 tests/ui/unit_arg_expressions.stderr (limited to 'tests') diff --git a/tests/ui/unit_arg.rs b/tests/ui/unit_arg.rs index 79dac925f08..cce543006d7 100644 --- a/tests/ui/unit_arg.rs +++ b/tests/ui/unit_arg.rs @@ -60,17 +60,6 @@ fn bad() { // in this case, the suggestion can be inlined, no need for a surrounding block // foo(()); foo(()) instead of { foo(()); foo(()) } foo(foo(())); - foo(if true { - 1; - }); - foo(match Some(1) { - Some(_) => { - 1; - }, - None => { - 0; - }, - }); } fn ok() { @@ -84,11 +73,6 @@ fn ok() { question_mark(); let named_unit_arg = (); foo(named_unit_arg); - foo(if true { 1 } else { 0 }); - foo(match Some(1) { - Some(_) => 1, - None => 0, - }); } fn question_mark() -> Result<(), ()> { diff --git a/tests/ui/unit_arg.stderr b/tests/ui/unit_arg.stderr index 8679706f8ec..8e3f1811c65 100644 --- a/tests/ui/unit_arg.stderr +++ b/tests/ui/unit_arg.stderr @@ -166,45 +166,7 @@ LL | foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:63:5 - | -LL | / foo(if true { -LL | | 1; -LL | | }); - | |______^ - | -help: move the expression in front of the call and replace it with the unit literal `()` - | -LL | if true { -LL | 1; -LL | }; -LL | foo(()); - | - -error: passing a unit value to a function - --> $DIR/unit_arg.rs:66:5 - | -LL | / foo(match Some(1) { -LL | | Some(_) => { -LL | | 1; -LL | | }, -... | -LL | | }, -LL | | }); - | |______^ - | -help: move the expression in front of the call and replace it with the unit literal `()` - | -LL | match Some(1) { -LL | Some(_) => { -LL | 1; -LL | }, -LL | None => { -LL | 0; - ... - -error: passing a unit value to a function - --> $DIR/unit_arg.rs:113:5 + --> $DIR/unit_arg.rs:97:5 | LL | Some(foo(1)) | ^^^^^^^^^^^^ @@ -215,5 +177,5 @@ LL | foo(1); LL | Some(()) | -error: aborting due to 12 previous errors +error: aborting due to 10 previous errors diff --git a/tests/ui/unit_arg_expressions.rs b/tests/ui/unit_arg_expressions.rs new file mode 100644 index 00000000000..a6807cb2e97 --- /dev/null +++ b/tests/ui/unit_arg_expressions.rs @@ -0,0 +1,35 @@ +#![warn(clippy::unit_arg)] +#![allow(clippy::no_effect)] + +use std::fmt::Debug; + +fn foo(t: T) { + println!("{:?}", t); +} + +fn bad() { + foo(if true { + 1; + }); + foo(match Some(1) { + Some(_) => { + 1; + }, + None => { + 0; + }, + }); +} + +fn ok() { + foo(if true { 1 } else { 0 }); + foo(match Some(1) { + Some(_) => 1, + None => 0, + }); +} + +fn main() { + bad(); + ok(); +} diff --git a/tests/ui/unit_arg_expressions.stderr b/tests/ui/unit_arg_expressions.stderr new file mode 100644 index 00000000000..9fb08106b72 --- /dev/null +++ b/tests/ui/unit_arg_expressions.stderr @@ -0,0 +1,41 @@ +error: passing a unit value to a function + --> $DIR/unit_arg_expressions.rs:11:5 + | +LL | / foo(if true { +LL | | 1; +LL | | }); + | |______^ + | + = note: `-D clippy::unit-arg` implied by `-D warnings` +help: move the expression in front of the call and replace it with the unit literal `()` + | +LL | if true { +LL | 1; +LL | }; +LL | foo(()); + | + +error: passing a unit value to a function + --> $DIR/unit_arg_expressions.rs:14:5 + | +LL | / foo(match Some(1) { +LL | | Some(_) => { +LL | | 1; +LL | | }, +... | +LL | | }, +LL | | }); + | |______^ + | +help: move the expression in front of the call and replace it with the unit literal `()` + | +LL | match Some(1) { +LL | Some(_) => { +LL | 1; +LL | }, +LL | None => { +LL | 0; + ... + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From e33ab3fdd85e162909f884a2437cb42ce6e794cf Mon Sep 17 00:00:00 2001 From: ThibsG Date: Mon, 18 Jan 2021 22:33:25 +0100 Subject: Add test for `needless_return` lint --- tests/ui/needless_return.fixed | 6 ++++++ tests/ui/needless_return.rs | 6 ++++++ tests/ui/needless_return.stderr | 8 +++++++- 3 files changed, 19 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index f137e8ecae9..990475fcb58 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -112,6 +112,12 @@ mod issue6501 { }; let _ = || {}; } + + struct Foo; + #[allow(clippy::unnecessary_lazy_evaluations)] + fn bar(res: Result) -> Foo { + res.unwrap_or_else(|_| Foo) + } } fn main() { diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index d754e4d37a8..dec3d84a020 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -112,6 +112,12 @@ mod issue6501 { }; let _ = || return; } + + struct Foo; + #[allow(clippy::unnecessary_lazy_evaluations)] + fn bar(res: Result) -> Foo { + res.unwrap_or_else(|_| return Foo) + } } fn main() { diff --git a/tests/ui/needless_return.stderr b/tests/ui/needless_return.stderr index d1240e161c0..ae31d607541 100644 --- a/tests/ui/needless_return.stderr +++ b/tests/ui/needless_return.stderr @@ -102,5 +102,11 @@ error: unneeded `return` statement LL | let _ = || return; | ^^^^^^ help: replace `return` with an empty block: `{}` -error: aborting due to 17 previous errors +error: unneeded `return` statement + --> $DIR/needless_return.rs:119:32 + | +LL | res.unwrap_or_else(|_| return Foo) + | ^^^^^^^^^^ help: remove `return`: `Foo` + +error: aborting due to 18 previous errors -- cgit 1.4.1-3-g733a5 From 3269070261e8324c2b3074cd491ddf2ac6cf19d3 Mon Sep 17 00:00:00 2001 From: nahuakang Date: Mon, 11 Jan 2021 23:56:12 +0100 Subject: Create new lint for the usage of inspect for each. --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 3 +++ clippy_lints/src/methods/inspect_for_each.rs | 23 +++++++++++++++++++ clippy_lints/src/methods/mod.rs | 33 ++++++++++++++++++++++++++++ tests/ui/inspect_for_each.rs | 22 +++++++++++++++++++ tests/ui/inspect_for_each.stderr | 16 ++++++++++++++ 6 files changed, 98 insertions(+) create mode 100644 clippy_lints/src/methods/inspect_for_each.rs create mode 100644 tests/ui/inspect_for_each.rs create mode 100644 tests/ui/inspect_for_each.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 64864c2e278..a91b7c64770 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1996,6 +1996,7 @@ Released 2018-09-13 [`inline_asm_x86_att_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_att_syntax [`inline_asm_x86_intel_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_intel_syntax [`inline_fn_without_body`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_fn_without_body +[`inspect_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#inspect_for_each [`int_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#int_plus_one [`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic [`integer_division`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f12994c7a60..17662b81205 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -733,6 +733,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::FROM_ITER_INSTEAD_OF_COLLECT, &methods::GET_UNWRAP, &methods::INEFFICIENT_TO_STRING, + &methods::INSPECT_FOR_EACH, &methods::INTO_ITER_ON_REF, &methods::ITERATOR_STEP_BY_ZERO, &methods::ITER_CLONED_COLLECT, @@ -1507,6 +1508,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::FILTER_NEXT), LintId::of(&methods::FLAT_MAP_IDENTITY), LintId::of(&methods::FROM_ITER_INSTEAD_OF_COLLECT), + 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), @@ -1807,6 +1809,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::CLONE_ON_COPY), LintId::of(&methods::FILTER_NEXT), LintId::of(&methods::FLAT_MAP_IDENTITY), + LintId::of(&methods::INSPECT_FOR_EACH), LintId::of(&methods::OPTION_AS_REF_DEREF), LintId::of(&methods::SEARCH_IS_SOME), LintId::of(&methods::SKIP_WHILE_NEXT), diff --git a/clippy_lints/src/methods/inspect_for_each.rs b/clippy_lints/src/methods/inspect_for_each.rs new file mode 100644 index 00000000000..6d41ee38a27 --- /dev/null +++ b/clippy_lints/src/methods/inspect_for_each.rs @@ -0,0 +1,23 @@ +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_span::source_map::Span; + +use crate::utils::{match_trait_method, paths, span_lint_and_help}; + +use super::INSPECT_FOR_EACH; + +/// lint use of `inspect().for_each()` for `Iterators` +pub(super) fn lint<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, inspect_span: Span) { + if match_trait_method(cx, expr, &paths::ITERATOR) { + let msg = "called `inspect(..).for_each(..)` on an `Iterator`"; + let hint = "move the code from `inspect(..)` to `for_each(..)` and remove the `inspect(..)`"; + span_lint_and_help( + cx, + INSPECT_FOR_EACH, + inspect_span.with_hi(expr.span.hi()), + msg, + None, + hint, + ); + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index f13f2491d6e..018696ef0cf 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1,5 +1,6 @@ mod bind_instead_of_map; mod inefficient_to_string; +mod inspect_for_each; mod manual_saturating_arithmetic; mod option_map_unwrap_or; mod unnecessary_filter_map; @@ -1405,6 +1406,36 @@ declare_clippy_lint! { "use `.collect()` instead of `::from_iter()`" } +declare_clippy_lint! { + /// **What it does:** Checks for usage of `inspect().for_each()`. + /// + /// **Why is this bad?** It is the same as performing the computation + /// inside `inspect` at the beginning of the closure in `for_each`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// [1,2,3,4,5].iter() + /// .inspect(|&x| println!("inspect the number: {}", x)) + /// .for_each(|&x| { + /// assert!(x >= 0); + /// }); + /// ``` + /// Can be written as + /// ```rust + /// [1,2,3,4,5].iter() + /// .for_each(|&x| { + /// println!("inspect the number: {}", x); + /// assert!(x >= 0); + /// }); + /// ``` + pub INSPECT_FOR_EACH, + complexity, + "using `.inspect().for_each()`, which can be replaced with `.for_each()`" +} + pub struct Methods { msrv: Option, } @@ -1467,6 +1498,7 @@ impl_lint_pass!(Methods => [ UNNECESSARY_LAZY_EVALUATIONS, MAP_COLLECT_RESULT_UNIT, FROM_ITER_INSTEAD_OF_COLLECT, + INSPECT_FOR_EACH, ]); impl<'tcx> LateLintPass<'tcx> for Methods { @@ -1553,6 +1585,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["get_or_insert_with", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "get_or_insert"), ["ok_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "ok_or"), ["collect", "map"] => lint_map_collect(cx, expr, arg_lists[1], arg_lists[0]), + ["for_each", "inspect"] => inspect_for_each::lint(cx, expr, method_spans[1]), _ => {}, } diff --git a/tests/ui/inspect_for_each.rs b/tests/ui/inspect_for_each.rs new file mode 100644 index 00000000000..7fe45c83bca --- /dev/null +++ b/tests/ui/inspect_for_each.rs @@ -0,0 +1,22 @@ +#![warn(clippy::inspect_for_each)] + +fn main() { + let a: Vec = vec![1, 2, 3, 4, 5]; + + let mut b: Vec = Vec::new(); + a.into_iter().inspect(|x| assert!(*x > 0)).for_each(|x| { + let y = do_some(x); + let z = do_more(y); + b.push(z); + }); + + assert_eq!(b, vec![4, 5, 6, 7, 8]); +} + +fn do_some(a: usize) -> usize { + a + 1 +} + +fn do_more(a: usize) -> usize { + a + 2 +} diff --git a/tests/ui/inspect_for_each.stderr b/tests/ui/inspect_for_each.stderr new file mode 100644 index 00000000000..9f976bb7458 --- /dev/null +++ b/tests/ui/inspect_for_each.stderr @@ -0,0 +1,16 @@ +error: called `inspect(..).for_each(..)` on an `Iterator` + --> $DIR/inspect_for_each.rs:7:19 + | +LL | a.into_iter().inspect(|x| assert!(*x > 0)).for_each(|x| { + | ___________________^ +LL | | let y = do_some(x); +LL | | let z = do_more(y); +LL | | b.push(z); +LL | | }); + | |______^ + | + = note: `-D clippy::inspect-for-each` implied by `-D warnings` + = help: move the code from `inspect(..)` to `for_each(..)` and remove the `inspect(..)` + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From 966320642b157c539901ed0461d6aab6cb34729d Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Tue, 19 Jan 2021 23:51:10 +0900 Subject: Fix a wrong suggestion of `ref_in_deref` --- clippy_lints/src/reference.rs | 10 ++++++++-- tests/ui/unnecessary_ref.fixed | 11 ++++++++++- tests/ui/unnecessary_ref.rs | 11 ++++++++++- tests/ui/unnecessary_ref.stderr | 10 +++++++++- 4 files changed, 37 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index efe3237990d..2dfb947b5eb 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -1,3 +1,4 @@ +use crate::utils::sugg::Sugg; use crate::utils::{in_macro, snippet_opt, snippet_with_applicability, span_lint_and_sugg}; use if_chain::if_chain; use rustc_ast::ast::{Expr, ExprKind, Mutability, UnOp}; @@ -124,14 +125,19 @@ impl EarlyLintPass for RefInDeref { if let ExprKind::Paren(ref parened) = object.kind; if let ExprKind::AddrOf(_, _, ref inner) = parened.kind; then { - let mut applicability = Applicability::MachineApplicable; + let applicability = if inner.span.from_expansion() { + Applicability::MaybeIncorrect + } else { + Applicability::MachineApplicable + }; + let sugg = Sugg::ast(cx, inner, "_").maybe_par(); span_lint_and_sugg( cx, REF_IN_DEREF, object.span, "creating a reference that is immediately dereferenced", "try this", - snippet_with_applicability(cx, inner.span, "_", &mut applicability).to_string(), + sugg.to_string(), applicability, ); } diff --git a/tests/ui/unnecessary_ref.fixed b/tests/ui/unnecessary_ref.fixed index f7b94118d4e..d927bae976f 100644 --- a/tests/ui/unnecessary_ref.fixed +++ b/tests/ui/unnecessary_ref.fixed @@ -1,7 +1,7 @@ // run-rustfix #![feature(stmt_expr_attributes)] -#![allow(unused_variables)] +#![allow(unused_variables, dead_code)] struct Outer { inner: u32, @@ -12,3 +12,12 @@ fn main() { let outer = Outer { inner: 0 }; let inner = outer.inner; } + +struct Apple; +impl Apple { + fn hello(&self) {} +} +struct Package(pub *const Apple); +fn foobar(package: *const Package) { + unsafe { &*(*package).0 }.hello(); +} diff --git a/tests/ui/unnecessary_ref.rs b/tests/ui/unnecessary_ref.rs index 4e585b9b96b..86bfb76ec26 100644 --- a/tests/ui/unnecessary_ref.rs +++ b/tests/ui/unnecessary_ref.rs @@ -1,7 +1,7 @@ // run-rustfix #![feature(stmt_expr_attributes)] -#![allow(unused_variables)] +#![allow(unused_variables, dead_code)] struct Outer { inner: u32, @@ -12,3 +12,12 @@ fn main() { let outer = Outer { inner: 0 }; let inner = (&outer).inner; } + +struct Apple; +impl Apple { + fn hello(&self) {} +} +struct Package(pub *const Apple); +fn foobar(package: *const Package) { + unsafe { &*(&*package).0 }.hello(); +} diff --git a/tests/ui/unnecessary_ref.stderr b/tests/ui/unnecessary_ref.stderr index d0a0f219097..436f4bcf738 100644 --- a/tests/ui/unnecessary_ref.stderr +++ b/tests/ui/unnecessary_ref.stderr @@ -10,5 +10,13 @@ note: the lint level is defined here LL | #[deny(clippy::ref_in_deref)] | ^^^^^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error: creating a reference that is immediately dereferenced + --> $DIR/unnecessary_ref.rs:22:16 + | +LL | unsafe { &*(&*package).0 }.hello(); + | ^^^^^^^^^^^ help: try this: `(*package)` + | + = note: `-D clippy::ref-in-deref` implied by `-D warnings` + +error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From 391bb218b5a79f32ddfacc668838fdfb06835f77 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Tue, 19 Jan 2021 19:20:26 +0100 Subject: size_of_in_element_count: Separate test file in expressions and functions An upcoming test case for new expresssion variants make the stderr file go over 200 lines. Split this test case in two to have a clear distinction between checking whether the lint is still applying on all the functions with member counts as argument, versus validating various member-count expressions that may or may not be invalid. --- tests/ui/size_of_in_element_count.rs | 61 ------- tests/ui/size_of_in_element_count.stderr | 195 --------------------- tests/ui/size_of_in_element_count/expressions.rs | 28 +++ .../ui/size_of_in_element_count/expressions.stderr | 27 +++ tests/ui/size_of_in_element_count/functions.rs | 46 +++++ tests/ui/size_of_in_element_count/functions.stderr | 171 ++++++++++++++++++ 6 files changed, 272 insertions(+), 256 deletions(-) delete mode 100644 tests/ui/size_of_in_element_count.rs delete mode 100644 tests/ui/size_of_in_element_count.stderr create mode 100644 tests/ui/size_of_in_element_count/expressions.rs create mode 100644 tests/ui/size_of_in_element_count/expressions.stderr create mode 100644 tests/ui/size_of_in_element_count/functions.rs create mode 100644 tests/ui/size_of_in_element_count/functions.stderr (limited to 'tests') diff --git a/tests/ui/size_of_in_element_count.rs b/tests/ui/size_of_in_element_count.rs deleted file mode 100644 index b13e390705a..00000000000 --- a/tests/ui/size_of_in_element_count.rs +++ /dev/null @@ -1,61 +0,0 @@ -#![warn(clippy::size_of_in_element_count)] -#![allow(clippy::ptr_offset_with_cast)] - -use std::mem::{size_of, size_of_val}; -use std::ptr::{ - copy, copy_nonoverlapping, slice_from_raw_parts, slice_from_raw_parts_mut, swap_nonoverlapping, write_bytes, -}; -use std::slice::{from_raw_parts, from_raw_parts_mut}; - -fn main() { - const SIZE: usize = 128; - const HALF_SIZE: usize = SIZE / 2; - const DOUBLE_SIZE: usize = SIZE * 2; - let mut x = [2u8; SIZE]; - let mut y = [2u8; SIZE]; - - // Count is size_of (Should trigger the lint) - unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; - unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; - - unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; - unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::()) }; - unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; - unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::()) }; - - unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; - unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; - - unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() * SIZE) }; - unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::() * SIZE) }; - - unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::() * SIZE) }; - - slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE); - slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE); - - unsafe { from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE) }; - unsafe { from_raw_parts(y.as_ptr(), size_of::() * SIZE) }; - - unsafe { y.as_mut_ptr().sub(size_of::()) }; - y.as_ptr().wrapping_sub(size_of::()); - unsafe { y.as_ptr().add(size_of::()) }; - y.as_mut_ptr().wrapping_add(size_of::()); - unsafe { y.as_ptr().offset(size_of::() as isize) }; - y.as_mut_ptr().wrapping_offset(size_of::() as isize); - - // Count expression involving multiplication of size_of (Should trigger the lint) - unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; - - // Count expression involving nested multiplications of size_of (Should trigger the lint) - unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) }; - - // Count expression involving divisions of size_of (Should trigger the lint) - unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; - - // No size_of calls (Should not trigger the lint) - unsafe { copy(x.as_ptr(), y.as_mut_ptr(), SIZE) }; - - // Different types for pointee and size_of (Should not trigger the lint) - unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() / 2 * SIZE) }; -} diff --git a/tests/ui/size_of_in_element_count.stderr b/tests/ui/size_of_in_element_count.stderr deleted file mode 100644 index 8cf3612abda..00000000000 --- a/tests/ui/size_of_in_element_count.stderr +++ /dev/null @@ -1,195 +0,0 @@ -error: found a count of bytes instead of a count of elements of `T` - --> $DIR/size_of_in_element_count.rs:18:68 - | -LL | unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; - | ^^^^^^^^^^^^^^^ - | - = note: `-D clippy::size-of-in-element-count` implied by `-D warnings` - = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type - -error: found a count of bytes instead of a count of elements of `T` - --> $DIR/size_of_in_element_count.rs:19:62 - | -LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; - | ^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type - -error: found a count of bytes instead of a count of elements of `T` - --> $DIR/size_of_in_element_count.rs:21:49 - | -LL | unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; - | ^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type - -error: found a count of bytes instead of a count of elements of `T` - --> $DIR/size_of_in_element_count.rs:22:64 - | -LL | unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::()) }; - | ^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type - -error: found a count of bytes instead of a count of elements of `T` - --> $DIR/size_of_in_element_count.rs:23:51 - | -LL | unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; - | ^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type - -error: found a count of bytes instead of a count of elements of `T` - --> $DIR/size_of_in_element_count.rs:24:66 - | -LL | unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::()) }; - | ^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type - -error: found a count of bytes instead of a count of elements of `T` - --> $DIR/size_of_in_element_count.rs:26:47 - | -LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; - | ^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type - -error: found a count of bytes instead of a count of elements of `T` - --> $DIR/size_of_in_element_count.rs:27:47 - | -LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; - | ^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type - -error: found a count of bytes instead of a count of elements of `T` - --> $DIR/size_of_in_element_count.rs:29:46 - | -LL | unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type - -error: found a count of bytes instead of a count of elements of `T` - --> $DIR/size_of_in_element_count.rs:30:47 - | -LL | unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::() * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type - -error: found a count of bytes instead of a count of elements of `T` - --> $DIR/size_of_in_element_count.rs:32:66 - | -LL | unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::() * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type - -error: found a count of bytes instead of a count of elements of `T` - --> $DIR/size_of_in_element_count.rs:34:46 - | -LL | slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE); - | ^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type - -error: found a count of bytes instead of a count of elements of `T` - --> $DIR/size_of_in_element_count.rs:35:38 - | -LL | slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE); - | ^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type - -error: found a count of bytes instead of a count of elements of `T` - --> $DIR/size_of_in_element_count.rs:37:49 - | -LL | unsafe { from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type - -error: found a count of bytes instead of a count of elements of `T` - --> $DIR/size_of_in_element_count.rs:38:41 - | -LL | unsafe { from_raw_parts(y.as_ptr(), size_of::() * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type - -error: found a count of bytes instead of a count of elements of `T` - --> $DIR/size_of_in_element_count.rs:40:33 - | -LL | unsafe { y.as_mut_ptr().sub(size_of::()) }; - | ^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type - -error: found a count of bytes instead of a count of elements of `T` - --> $DIR/size_of_in_element_count.rs:41:29 - | -LL | y.as_ptr().wrapping_sub(size_of::()); - | ^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type - -error: found a count of bytes instead of a count of elements of `T` - --> $DIR/size_of_in_element_count.rs:42:29 - | -LL | unsafe { y.as_ptr().add(size_of::()) }; - | ^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type - -error: found a count of bytes instead of a count of elements of `T` - --> $DIR/size_of_in_element_count.rs:43:33 - | -LL | y.as_mut_ptr().wrapping_add(size_of::()); - | ^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type - -error: found a count of bytes instead of a count of elements of `T` - --> $DIR/size_of_in_element_count.rs:44:32 - | -LL | unsafe { y.as_ptr().offset(size_of::() as isize) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type - -error: found a count of bytes instead of a count of elements of `T` - --> $DIR/size_of_in_element_count.rs:45:36 - | -LL | y.as_mut_ptr().wrapping_offset(size_of::() as isize); - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type - -error: found a count of bytes instead of a count of elements of `T` - --> $DIR/size_of_in_element_count.rs:48:62 - | -LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; - | ^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type - -error: found a count of bytes instead of a count of elements of `T` - --> $DIR/size_of_in_element_count.rs:51:62 - | -LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type - -error: found a count of bytes instead of a count of elements of `T` - --> $DIR/size_of_in_element_count.rs:54:47 - | -LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type - -error: aborting due to 24 previous errors - diff --git a/tests/ui/size_of_in_element_count/expressions.rs b/tests/ui/size_of_in_element_count/expressions.rs new file mode 100644 index 00000000000..b56910917ba --- /dev/null +++ b/tests/ui/size_of_in_element_count/expressions.rs @@ -0,0 +1,28 @@ +#![warn(clippy::size_of_in_element_count)] +#![allow(clippy::ptr_offset_with_cast)] + +use std::mem::{size_of, size_of_val}; +use std::ptr::{copy, copy_nonoverlapping, write_bytes}; + +fn main() { + const SIZE: usize = 128; + const HALF_SIZE: usize = SIZE / 2; + const DOUBLE_SIZE: usize = SIZE * 2; + let mut x = [2u8; SIZE]; + let mut y = [2u8; SIZE]; + + // Count expression involving multiplication of size_of (Should trigger the lint) + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; + + // Count expression involving nested multiplications of size_of (Should trigger the lint) + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) }; + + // Count expression involving divisions of size_of (Should trigger the lint) + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; + + // No size_of calls (Should not trigger the lint) + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), SIZE) }; + + // Different types for pointee and size_of (Should not trigger the lint) + unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() / 2 * SIZE) }; +} diff --git a/tests/ui/size_of_in_element_count/expressions.stderr b/tests/ui/size_of_in_element_count/expressions.stderr new file mode 100644 index 00000000000..47b98e9d947 --- /dev/null +++ b/tests/ui/size_of_in_element_count/expressions.stderr @@ -0,0 +1,27 @@ +error: found a count of bytes instead of a count of elements of `T` + --> $DIR/expressions.rs:15:62 + | +LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::size-of-in-element-count` implied by `-D warnings` + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of `T` + --> $DIR/expressions.rs:18:62 + | +LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of `T` + --> $DIR/expressions.rs:21:47 + | +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: aborting due to 3 previous errors + diff --git a/tests/ui/size_of_in_element_count/functions.rs b/tests/ui/size_of_in_element_count/functions.rs new file mode 100644 index 00000000000..09d08ac37dc --- /dev/null +++ b/tests/ui/size_of_in_element_count/functions.rs @@ -0,0 +1,46 @@ +#![warn(clippy::size_of_in_element_count)] +#![allow(clippy::ptr_offset_with_cast)] + +use std::mem::{size_of, size_of_val}; +use std::ptr::{ + copy, copy_nonoverlapping, slice_from_raw_parts, slice_from_raw_parts_mut, swap_nonoverlapping, write_bytes, +}; +use std::slice::{from_raw_parts, from_raw_parts_mut}; + +fn main() { + const SIZE: usize = 128; + const HALF_SIZE: usize = SIZE / 2; + const DOUBLE_SIZE: usize = SIZE * 2; + let mut x = [2u8; SIZE]; + let mut y = [2u8; SIZE]; + + // Count is size_of (Should trigger the lint) + unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; + unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; + + unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; + unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::()) }; + unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; + unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::()) }; + + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; + + unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() * SIZE) }; + unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::() * SIZE) }; + + unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::() * SIZE) }; + + slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE); + slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE); + + unsafe { from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE) }; + unsafe { from_raw_parts(y.as_ptr(), size_of::() * SIZE) }; + + unsafe { y.as_mut_ptr().sub(size_of::()) }; + y.as_ptr().wrapping_sub(size_of::()); + unsafe { y.as_ptr().add(size_of::()) }; + y.as_mut_ptr().wrapping_add(size_of::()); + unsafe { y.as_ptr().offset(size_of::() as isize) }; + y.as_mut_ptr().wrapping_offset(size_of::() as isize); +} diff --git a/tests/ui/size_of_in_element_count/functions.stderr b/tests/ui/size_of_in_element_count/functions.stderr new file mode 100644 index 00000000000..c1e824167b7 --- /dev/null +++ b/tests/ui/size_of_in_element_count/functions.stderr @@ -0,0 +1,171 @@ +error: found a count of bytes instead of a count of elements of `T` + --> $DIR/functions.rs:18:68 + | +LL | unsafe { copy_nonoverlapping::(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^ + | + = note: `-D clippy::size-of-in-element-count` implied by `-D warnings` + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of `T` + --> $DIR/functions.rs:19:62 + | +LL | unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; + | ^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of `T` + --> $DIR/functions.rs:21:49 + | +LL | unsafe { x.as_ptr().copy_to(y.as_mut_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of `T` + --> $DIR/functions.rs:22:64 + | +LL | unsafe { x.as_ptr().copy_to_nonoverlapping(y.as_mut_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of `T` + --> $DIR/functions.rs:23:51 + | +LL | unsafe { y.as_mut_ptr().copy_from(x.as_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of `T` + --> $DIR/functions.rs:24:66 + | +LL | unsafe { y.as_mut_ptr().copy_from_nonoverlapping(x.as_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of `T` + --> $DIR/functions.rs:26:47 + | +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::()) }; + | ^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of `T` + --> $DIR/functions.rs:27:47 + | +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) }; + | ^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of `T` + --> $DIR/functions.rs:29:46 + | +LL | unsafe { y.as_mut_ptr().write_bytes(0u8, size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of `T` + --> $DIR/functions.rs:30:47 + | +LL | unsafe { write_bytes(y.as_mut_ptr(), 0u8, size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of `T` + --> $DIR/functions.rs:32:66 + | +LL | unsafe { swap_nonoverlapping(y.as_mut_ptr(), x.as_mut_ptr(), size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of `T` + --> $DIR/functions.rs:34:46 + | +LL | slice_from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of `T` + --> $DIR/functions.rs:35:38 + | +LL | slice_from_raw_parts(y.as_ptr(), size_of::() * SIZE); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of `T` + --> $DIR/functions.rs:37:49 + | +LL | unsafe { from_raw_parts_mut(y.as_mut_ptr(), size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of `T` + --> $DIR/functions.rs:38:41 + | +LL | unsafe { from_raw_parts(y.as_ptr(), size_of::() * SIZE) }; + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of `T` + --> $DIR/functions.rs:40:33 + | +LL | unsafe { y.as_mut_ptr().sub(size_of::()) }; + | ^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of `T` + --> $DIR/functions.rs:41:29 + | +LL | y.as_ptr().wrapping_sub(size_of::()); + | ^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of `T` + --> $DIR/functions.rs:42:29 + | +LL | unsafe { y.as_ptr().add(size_of::()) }; + | ^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of `T` + --> $DIR/functions.rs:43:33 + | +LL | y.as_mut_ptr().wrapping_add(size_of::()); + | ^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of `T` + --> $DIR/functions.rs:44:32 + | +LL | unsafe { y.as_ptr().offset(size_of::() as isize) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: found a count of bytes instead of a count of elements of `T` + --> $DIR/functions.rs:45:36 + | +LL | y.as_mut_ptr().wrapping_offset(size_of::() as isize); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: aborting due to 21 previous errors + -- cgit 1.4.1-3-g733a5 From d4bf59b6ef24042653951b69105a3a68542f3bbd Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Tue, 12 Jan 2021 11:35:44 +0100 Subject: size_of_in_element_count: Disable lint on division by byte-size It is fairly common to divide some length in bytes by the byte-size of a single element before creating a `from_raw_parts` slice or similar operation. This lint would erroneously disallow such expressions. Just in case, instead of simply disabling this lint in the RHS of a division, keep track of the inversion and enable it again on recursive division. --- clippy_lints/src/size_of_in_element_count.rs | 14 +++++++++----- tests/ui/size_of_in_element_count/expressions.rs | 9 +++++++++ tests/ui/size_of_in_element_count/expressions.stderr | 10 +++++++++- 3 files changed, 27 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/size_of_in_element_count.rs b/clippy_lints/src/size_of_in_element_count.rs index ea7a76146f5..87e386baadc 100644 --- a/clippy_lints/src/size_of_in_element_count.rs +++ b/clippy_lints/src/size_of_in_element_count.rs @@ -35,10 +35,11 @@ declare_clippy_lint! { declare_lint_pass!(SizeOfInElementCount => [SIZE_OF_IN_ELEMENT_COUNT]); -fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option> { +fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, inverted: bool) -> Option> { match expr.kind { ExprKind::Call(count_func, _func_args) => { if_chain! { + if !inverted; if let ExprKind::Path(ref count_func_qpath) = count_func.kind; if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id(); if match_def_path(cx, def_id, &paths::MEM_SIZE_OF) @@ -50,10 +51,13 @@ fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option { - get_size_of_ty(cx, left).or_else(|| get_size_of_ty(cx, right)) + ExprKind::Binary(op, left, right) if BinOpKind::Mul == op.node => { + get_size_of_ty(cx, left, inverted).or_else(|| get_size_of_ty(cx, right, inverted)) }, - ExprKind::Cast(expr, _) => get_size_of_ty(cx, expr), + ExprKind::Binary(op, left, right) if BinOpKind::Div == op.node => { + get_size_of_ty(cx, left, inverted).or_else(|| get_size_of_ty(cx, right, !inverted)) + }, + ExprKind::Cast(expr, _) => get_size_of_ty(cx, expr, inverted), _ => None, } } @@ -128,7 +132,7 @@ impl<'tcx> LateLintPass<'tcx> for SizeOfInElementCount { // Find a size_of call in the count parameter expression and // check that it's the same type - if let Some(ty_used_for_size_of) = get_size_of_ty(cx, count_expr); + if let Some(ty_used_for_size_of) = get_size_of_ty(cx, count_expr, false); if TyS::same_type(pointee_ty, ty_used_for_size_of); then { span_lint_and_help( diff --git a/tests/ui/size_of_in_element_count/expressions.rs b/tests/ui/size_of_in_element_count/expressions.rs index b56910917ba..2594e8fa6ad 100644 --- a/tests/ui/size_of_in_element_count/expressions.rs +++ b/tests/ui/size_of_in_element_count/expressions.rs @@ -20,6 +20,15 @@ fn main() { // Count expression involving divisions of size_of (Should trigger the lint) unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() / 2) }; + // Count expression involving divisions by size_of (Should not trigger the lint) + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / size_of::()) }; + + // Count expression involving divisions by multiple size_of (Should not trigger the lint) + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / (2 * size_of::())) }; + + // Count expression involving recursive divisions by size_of (Should trigger the lint) + unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / (2 / size_of::())) }; + // No size_of calls (Should not trigger the lint) unsafe { copy(x.as_ptr(), y.as_mut_ptr(), SIZE) }; diff --git a/tests/ui/size_of_in_element_count/expressions.stderr b/tests/ui/size_of_in_element_count/expressions.stderr index 47b98e9d947..0f0dff57f51 100644 --- a/tests/ui/size_of_in_element_count/expressions.stderr +++ b/tests/ui/size_of_in_element_count/expressions.stderr @@ -23,5 +23,13 @@ LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::() | = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type -error: aborting due to 3 previous errors +error: found a count of bytes instead of a count of elements of `T` + --> $DIR/expressions.rs:30:47 + | +LL | unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / (2 / size_of::())) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a count of elements instead of a count of bytes, it already gets multiplied by the size of the type + +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From 6c830ff9e4621c6e159ce8edb33fb225352e7b69 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sat, 19 Dec 2020 18:57:11 +0900 Subject: Run `cargo dev new_lint` --- CHANGELOG.md | 1 + clippy_lints/src/capitalized_acronyms.rs | 28 ++++++++++++++++++++++++++++ clippy_lints/src/lib.rs | 2 ++ tests/ui/capitalized_acronyms.rs | 5 +++++ 4 files changed, 36 insertions(+) create mode 100644 clippy_lints/src/capitalized_acronyms.rs create mode 100644 tests/ui/capitalized_acronyms.rs (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index b3adeed7720..963c9e77a18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1877,6 +1877,7 @@ Released 2018-09-13 [`box_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_vec [`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local [`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow +[`capitalized_acronyms`]: https://rust-lang.github.io/rust-clippy/master/index.html#capitalized_acronyms [`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata [`case_sensitive_file_extension_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#case_sensitive_file_extension_comparisons [`cast_lossless`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless diff --git a/clippy_lints/src/capitalized_acronyms.rs b/clippy_lints/src/capitalized_acronyms.rs new file mode 100644 index 00000000000..835e92dc04b --- /dev/null +++ b/clippy_lints/src/capitalized_acronyms.rs @@ -0,0 +1,28 @@ +use rustc_lint::{EarlyLintPass, EarlyContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_ast::ast::*; + +declare_clippy_lint! { + /// **What it does:** + /// + /// **Why is this bad?** + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // example code where clippy issues a warning + /// ``` + /// Use instead: + /// ```rust + /// // example code which does not raise clippy warning + /// ``` + pub CAPITALIZED_ACRONYMS, + style, + "default lint description" +} + +declare_lint_pass!(CapitalizedAcronyms => [CAPITALIZED_ACRONYMS]); + +impl EarlyLintPass for CapitalizedAcronyms {} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 0b1b347ce42..a236e1b54bb 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -169,6 +169,7 @@ mod blacklisted_name; mod blocks_in_if_conditions; mod booleans; mod bytecount; +mod capitalized_acronyms; mod cargo_common_metadata; mod case_sensitive_file_extension_comparisons; mod checked_conversions; @@ -559,6 +560,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &booleans::LOGIC_BUG, &booleans::NONMINIMAL_BOOL, &bytecount::NAIVE_BYTECOUNT, + &capitalized_acronyms::CAPITALIZED_ACRONYMS, &cargo_common_metadata::CARGO_COMMON_METADATA, &case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, &checked_conversions::CHECKED_CONVERSIONS, diff --git a/tests/ui/capitalized_acronyms.rs b/tests/ui/capitalized_acronyms.rs new file mode 100644 index 00000000000..bf5ab9f8bb7 --- /dev/null +++ b/tests/ui/capitalized_acronyms.rs @@ -0,0 +1,5 @@ +#![warn(clippy::capitalized_acronyms)] + +fn main() { + // test code goes here +} -- cgit 1.4.1-3-g733a5 From ab1da8f86569e55d4b33702c6a062551f8abdd6e Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sat, 19 Dec 2020 22:50:45 +0900 Subject: Add new lint `upper_case_acronyms` --- CHANGELOG.md | 2 +- clippy_lints/src/capitalized_acronyms.rs | 28 --------- clippy_lints/src/cognitive_complexity.rs | 8 +-- clippy_lints/src/int_plus_one.rs | 24 ++++---- clippy_lints/src/lib.rs | 10 +++- clippy_lints/src/serde_api.rs | 4 +- clippy_lints/src/upper_case_acronyms.rs | 93 ++++++++++++++++++++++++++++++ tests/ui/capitalized_acronyms.rs | 5 -- tests/ui/complex_types.rs | 2 +- tests/ui/complex_types.stderr | 2 +- tests/ui/crashes/ice-6256.rs | 1 + tests/ui/crashes/ice-6256.stderr | 6 +- tests/ui/enum_variants.rs | 2 +- tests/ui/needless_question_mark.fixed | 8 ++- tests/ui/needless_question_mark.rs | 8 ++- tests/ui/needless_question_mark.stderr | 28 ++++----- tests/ui/suspicious_operation_groupings.rs | 8 +-- tests/ui/transmute_ptr_to_ptr.rs | 6 +- tests/ui/unnested_or_patterns.fixed | 2 +- tests/ui/unnested_or_patterns.rs | 2 +- tests/ui/upper_case_acronyms.rs | 21 +++++++ tests/ui/upper_case_acronyms.stderr | 70 ++++++++++++++++++++++ tests/ui/use_self.fixed | 2 +- tests/ui/use_self.rs | 2 +- 24 files changed, 256 insertions(+), 88 deletions(-) delete mode 100644 clippy_lints/src/capitalized_acronyms.rs create mode 100644 clippy_lints/src/upper_case_acronyms.rs delete mode 100644 tests/ui/capitalized_acronyms.rs create mode 100644 tests/ui/upper_case_acronyms.rs create mode 100644 tests/ui/upper_case_acronyms.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 963c9e77a18..04f042b2deb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1877,7 +1877,6 @@ Released 2018-09-13 [`box_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_vec [`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local [`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow -[`capitalized_acronyms`]: https://rust-lang.github.io/rust-clippy/master/index.html#capitalized_acronyms [`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata [`case_sensitive_file_extension_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#case_sensitive_file_extension_comparisons [`cast_lossless`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless @@ -2275,6 +2274,7 @@ Released 2018-09-13 [`unusual_byte_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_groupings [`unwrap_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_in_result [`unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used +[`upper_case_acronyms`]: https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms [`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug [`use_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_self [`used_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#used_underscore_binding diff --git a/clippy_lints/src/capitalized_acronyms.rs b/clippy_lints/src/capitalized_acronyms.rs deleted file mode 100644 index 835e92dc04b..00000000000 --- a/clippy_lints/src/capitalized_acronyms.rs +++ /dev/null @@ -1,28 +0,0 @@ -use rustc_lint::{EarlyLintPass, EarlyContext}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_ast::ast::*; - -declare_clippy_lint! { - /// **What it does:** - /// - /// **Why is this bad?** - /// - /// **Known problems:** None. - /// - /// **Example:** - /// - /// ```rust - /// // example code where clippy issues a warning - /// ``` - /// Use instead: - /// ```rust - /// // example code which does not raise clippy warning - /// ``` - pub CAPITALIZED_ACRONYMS, - style, - "default lint description" -} - -declare_lint_pass!(CapitalizedAcronyms => [CAPITALIZED_ACRONYMS]); - -impl EarlyLintPass for CapitalizedAcronyms {} diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index b3ebdf4ca30..f21a734bb43 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -57,9 +57,9 @@ impl CognitiveComplexity { let expr = &body.value; - let mut helper = CCHelper { cc: 1, returns: 0 }; + let mut helper = CcHelper { cc: 1, returns: 0 }; helper.visit_expr(expr); - let CCHelper { cc, returns } = helper; + let CcHelper { cc, returns } = helper; let ret_ty = cx.typeck_results().node_type(expr.hir_id); let ret_adjust = if is_type_diagnostic_item(cx, ret_ty, sym::result_type) { returns @@ -136,12 +136,12 @@ impl<'tcx> LateLintPass<'tcx> for CognitiveComplexity { } } -struct CCHelper { +struct CcHelper { cc: u64, returns: u64, } -impl<'tcx> Visitor<'tcx> for CCHelper { +impl<'tcx> Visitor<'tcx> for CcHelper { type Map = Map<'tcx>; fn visit_expr(&mut self, e: &'tcx Expr<'_>) { diff --git a/clippy_lints/src/int_plus_one.rs b/clippy_lints/src/int_plus_one.rs index c629ee05ab9..260b8988d37 100644 --- a/clippy_lints/src/int_plus_one.rs +++ b/clippy_lints/src/int_plus_one.rs @@ -46,8 +46,8 @@ declare_lint_pass!(IntPlusOne => [INT_PLUS_ONE]); #[derive(Copy, Clone)] enum Side { - LHS, - RHS, + Lhs, + Rhs, } impl IntPlusOne { @@ -66,11 +66,11 @@ impl IntPlusOne { match (lhskind.node, &lhslhs.kind, &lhsrhs.kind) { // `-1 + x` (BinOpKind::Add, &ExprKind::Lit(ref lit), _) if Self::check_lit(lit, -1) => { - Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::LHS) + Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::Lhs) }, // `x - 1` (BinOpKind::Sub, _, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => { - Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::LHS) + Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::Lhs) }, _ => None, } @@ -82,10 +82,10 @@ impl IntPlusOne { match (&rhslhs.kind, &rhsrhs.kind) { // `y + 1` and `1 + y` (&ExprKind::Lit(ref lit), _) if Self::check_lit(lit, 1) => { - Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::RHS) + Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::Rhs) }, (_, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => { - Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::RHS) + Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::Rhs) }, _ => None, } @@ -97,10 +97,10 @@ impl IntPlusOne { match (&lhslhs.kind, &lhsrhs.kind) { // `1 + x` and `x + 1` (&ExprKind::Lit(ref lit), _) if Self::check_lit(lit, 1) => { - Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::LHS) + Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::Lhs) }, (_, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => { - Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::LHS) + Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::Lhs) }, _ => None, } @@ -110,11 +110,11 @@ impl IntPlusOne { match (rhskind.node, &rhslhs.kind, &rhsrhs.kind) { // `-1 + y` (BinOpKind::Add, &ExprKind::Lit(ref lit), _) if Self::check_lit(lit, -1) => { - Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::RHS) + Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::Rhs) }, // `y - 1` (BinOpKind::Sub, _, &ExprKind::Lit(ref lit)) if Self::check_lit(lit, 1) => { - Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::RHS) + Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::Rhs) }, _ => None, } @@ -138,8 +138,8 @@ impl IntPlusOne { if let Some(snippet) = snippet_opt(cx, node.span) { if let Some(other_side_snippet) = snippet_opt(cx, other_side.span) { let rec = match side { - Side::LHS => Some(format!("{} {} {}", snippet, binop_string, other_side_snippet)), - Side::RHS => Some(format!("{} {} {}", other_side_snippet, binop_string, snippet)), + Side::Lhs => Some(format!("{} {} {}", snippet, binop_string, other_side_snippet)), + Side::Rhs => Some(format!("{} {} {}", other_side_snippet, binop_string, snippet)), }; return rec; } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index a236e1b54bb..9b73c249643 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -6,6 +6,7 @@ #![feature(concat_idents)] #![feature(crate_visibility_modifier)] #![feature(drain_filter)] +#![feature(split_inclusive)] #![feature(in_band_lifetimes)] #![feature(once_cell)] #![feature(or_patterns)] @@ -169,7 +170,6 @@ mod blacklisted_name; mod blocks_in_if_conditions; mod booleans; mod bytecount; -mod capitalized_acronyms; mod cargo_common_metadata; mod case_sensitive_file_extension_comparisons; mod checked_conversions; @@ -342,6 +342,7 @@ mod unused_self; mod unused_unit; mod unwrap; mod unwrap_in_result; +mod upper_case_acronyms; mod use_self; mod useless_conversion; mod vec; @@ -560,7 +561,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &booleans::LOGIC_BUG, &booleans::NONMINIMAL_BOOL, &bytecount::NAIVE_BYTECOUNT, - &capitalized_acronyms::CAPITALIZED_ACRONYMS, &cargo_common_metadata::CARGO_COMMON_METADATA, &case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, &checked_conversions::CHECKED_CONVERSIONS, @@ -946,6 +946,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &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, @@ -985,7 +986,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: } store.register_late_pass(|| box utils::author::Author); store.register_late_pass(|| box await_holding_invalid::AwaitHolding); - store.register_late_pass(|| box serde_api::SerdeAPI); + store.register_late_pass(|| box serde_api::SerdeApi); let vec_box_size_threshold = conf.vec_box_size_threshold; store.register_late_pass(move || box types::Types::new(vec_box_size_threshold)); store.register_late_pass(|| box booleans::NonminimalBool); @@ -1176,6 +1177,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let enum_variant_name_threshold = conf.enum_variant_name_threshold; store.register_early_pass(move || box enum_variants::EnumVariantNames::new(enum_variant_name_threshold)); store.register_early_pass(|| box tabs_in_doc_comments::TabsInDocComments); + store.register_early_pass(|| box upper_case_acronyms::UpperCaseAcronyms); store.register_late_pass(|| box default::Default::default()); store.register_late_pass(|| box unused_self::UnusedSelf); store.register_late_pass(|| box mutable_debug_assertion::DebugAssertWithMutCall); @@ -1661,6 +1663,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), @@ -1778,6 +1781,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION), 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), diff --git a/clippy_lints/src/serde_api.rs b/clippy_lints/src/serde_api.rs index 44e739725c8..90cf1b6c861 100644 --- a/clippy_lints/src/serde_api.rs +++ b/clippy_lints/src/serde_api.rs @@ -18,9 +18,9 @@ declare_clippy_lint! { "various things that will negatively affect your serde experience" } -declare_lint_pass!(SerdeAPI => [SERDE_API_MISUSE]); +declare_lint_pass!(SerdeApi => [SERDE_API_MISUSE]); -impl<'tcx> LateLintPass<'tcx> for SerdeAPI { +impl<'tcx> LateLintPass<'tcx> for SerdeApi { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if let ItemKind::Impl(Impl { of_trait: Some(ref trait_ref), diff --git a/clippy_lints/src/upper_case_acronyms.rs b/clippy_lints/src/upper_case_acronyms.rs new file mode 100644 index 00000000000..61e7031716a --- /dev/null +++ b/clippy_lints/src/upper_case_acronyms.rs @@ -0,0 +1,93 @@ +use crate::utils::span_lint_and_sugg; +use if_chain::if_chain; +use itertools::Itertools; +use rustc_ast::ast::{Item, ItemKind, Variant}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::Ident; + +declare_clippy_lint! { + /// **What it does:** Checks for camel case name containing a capitalized acronym. + /// + /// **Why is this bad?** In CamelCase, acronyms count as one word. + /// See [naming conventions](https://rust-lang.github.io/api-guidelines/naming.html#casing-conforms-to-rfc-430-c-case) + /// for more. + /// + /// **Known problems:** When two acronyms are contiguous, the lint can't tell where + /// the first acronym ends and the second starts, so it suggests to lowercase all of + /// the letters in the second acronym. + /// + /// **Example:** + /// + /// ```rust + /// struct HTTPResponse; + /// ``` + /// Use instead: + /// ```rust + /// struct HttpResponse; + /// ``` + pub UPPER_CASE_ACRONYMS, + style, + "capitalized acronyms are against the naming convention" +} + +declare_lint_pass!(UpperCaseAcronyms => [UPPER_CASE_ACRONYMS]); + +fn correct_ident(ident: &str) -> String { + let ident = ident.chars().rev().collect::(); + let fragments = ident + .split_inclusive(|x: char| !x.is_ascii_lowercase()) + .rev() + .map(|x| x.chars().rev().collect::()); + + let mut ident = fragments.clone().next().unwrap(); + for (ref prev, ref curr) in fragments.tuple_windows() { + if [prev, curr] + .iter() + .all(|s| s.len() == 1 && s.chars().next().unwrap().is_ascii_uppercase()) + { + ident.push_str(&curr.to_ascii_lowercase()); + } else { + ident.push_str(curr); + } + } + ident +} + +fn check_ident(cx: &EarlyContext<'_>, ident: &Ident) { + let span = ident.span; + let ident = &ident.as_str(); + let corrected = correct_ident(ident); + if ident != &corrected { + span_lint_and_sugg( + cx, + UPPER_CASE_ACRONYMS, + span, + &format!("name `{}` contains a capitalized acronym", ident), + "consider making the acronym lowercase, except the initial letter", + corrected, + Applicability::MaybeIncorrect, + ) + } +} + +impl EarlyLintPass for UpperCaseAcronyms { + fn check_item(&mut self, cx: &EarlyContext<'_>, it: &Item) { + if_chain! { + if !in_external_macro(cx.sess(), it.span); + if matches!( + it.kind, + ItemKind::TyAlias(..) | ItemKind::Enum(..) | ItemKind::Struct(..) | ItemKind::Trait(..) + ); + then { + check_ident(cx, &it.ident); + } + } + } + + fn check_variant(&mut self, cx: &EarlyContext<'_>, v: &Variant) { + check_ident(cx, &v.ident); + } +} diff --git a/tests/ui/capitalized_acronyms.rs b/tests/ui/capitalized_acronyms.rs deleted file mode 100644 index bf5ab9f8bb7..00000000000 --- a/tests/ui/capitalized_acronyms.rs +++ /dev/null @@ -1,5 +0,0 @@ -#![warn(clippy::capitalized_acronyms)] - -fn main() { - // test code goes here -} diff --git a/tests/ui/complex_types.rs b/tests/ui/complex_types.rs index be61fb6b9be..383bbb49dbe 100644 --- a/tests/ui/complex_types.rs +++ b/tests/ui/complex_types.rs @@ -11,7 +11,7 @@ struct S { f: Vec>>, } -struct TS(Vec>>); +struct Ts(Vec>>); enum E { Tuple(Vec>>), diff --git a/tests/ui/complex_types.stderr b/tests/ui/complex_types.stderr index 8f5dbd27956..7fcbb4bce88 100644 --- a/tests/ui/complex_types.stderr +++ b/tests/ui/complex_types.stderr @@ -21,7 +21,7 @@ LL | f: Vec>>, error: very complex type used. Consider factoring parts into `type` definitions --> $DIR/complex_types.rs:14:11 | -LL | struct TS(Vec>>); +LL | struct Ts(Vec>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: very complex type used. Consider factoring parts into `type` definitions diff --git a/tests/ui/crashes/ice-6256.rs b/tests/ui/crashes/ice-6256.rs index 6f60d45d68a..5409f36b3f1 100644 --- a/tests/ui/crashes/ice-6256.rs +++ b/tests/ui/crashes/ice-6256.rs @@ -1,5 +1,6 @@ // originally from rustc ./src/test/ui/regions/issue-78262.rs // ICE: to get the signature of a closure, use substs.as_closure().sig() not fn_sig() +#![allow(clippy::upper_case_acronyms)] trait TT {} diff --git a/tests/ui/crashes/ice-6256.stderr b/tests/ui/crashes/ice-6256.stderr index 0e8353a418a..d1a8bdc3c8d 100644 --- a/tests/ui/crashes/ice-6256.stderr +++ b/tests/ui/crashes/ice-6256.stderr @@ -1,13 +1,13 @@ error[E0308]: mismatched types - --> $DIR/ice-6256.rs:11:28 + --> $DIR/ice-6256.rs:12:28 | LL | let f = |x: &dyn TT| x.func(); //[default]~ ERROR: mismatched types | ^^^^ lifetime mismatch | = note: expected reference `&(dyn TT + 'static)` found reference `&dyn TT` -note: the anonymous lifetime #1 defined on the body at 11:13... - --> $DIR/ice-6256.rs:11:13 +note: the anonymous lifetime #1 defined on the body at 12:13... + --> $DIR/ice-6256.rs:12:13 | LL | let f = |x: &dyn TT| x.func(); //[default]~ ERROR: mismatched types | ^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/enum_variants.rs b/tests/ui/enum_variants.rs index 01774a2a984..89d99dcf0c8 100644 --- a/tests/ui/enum_variants.rs +++ b/tests/ui/enum_variants.rs @@ -1,6 +1,6 @@ #![feature(non_ascii_idents)] #![warn(clippy::enum_variant_names, clippy::pub_enum_variant_names)] -#![allow(non_camel_case_types)] +#![allow(non_camel_case_types, clippy::upper_case_acronyms)] enum FakeCallType { CALL, diff --git a/tests/ui/needless_question_mark.fixed b/tests/ui/needless_question_mark.fixed index 70218f3f041..71fb3565224 100644 --- a/tests/ui/needless_question_mark.fixed +++ b/tests/ui/needless_question_mark.fixed @@ -1,7 +1,13 @@ // run-rustfix #![warn(clippy::needless_question_mark)] -#![allow(clippy::needless_return, clippy::unnecessary_unwrap, dead_code, unused_must_use)] +#![allow( + clippy::needless_return, + clippy::unnecessary_unwrap, + clippy::upper_case_acronyms, + dead_code, + unused_must_use +)] #![feature(custom_inner_attributes)] struct TO { diff --git a/tests/ui/needless_question_mark.rs b/tests/ui/needless_question_mark.rs index 60ac2c8d72e..e31f6f48fa7 100644 --- a/tests/ui/needless_question_mark.rs +++ b/tests/ui/needless_question_mark.rs @@ -1,7 +1,13 @@ // run-rustfix #![warn(clippy::needless_question_mark)] -#![allow(clippy::needless_return, clippy::unnecessary_unwrap, dead_code, unused_must_use)] +#![allow( + clippy::needless_return, + clippy::unnecessary_unwrap, + clippy::upper_case_acronyms, + dead_code, + unused_must_use +)] #![feature(custom_inner_attributes)] struct TO { diff --git a/tests/ui/needless_question_mark.stderr b/tests/ui/needless_question_mark.stderr index b4eb21882ec..567bc518a3f 100644 --- a/tests/ui/needless_question_mark.stderr +++ b/tests/ui/needless_question_mark.stderr @@ -1,5 +1,5 @@ error: Question mark operator is useless here - --> $DIR/needless_question_mark.rs:17:12 + --> $DIR/needless_question_mark.rs:23:12 | LL | return Some(to.magic?); | ^^^^^^^^^^^^^^^ help: try: `to.magic` @@ -7,79 +7,79 @@ LL | return Some(to.magic?); = note: `-D clippy::needless-question-mark` implied by `-D warnings` error: Question mark operator is useless here - --> $DIR/needless_question_mark.rs:25:12 + --> $DIR/needless_question_mark.rs:31:12 | LL | return Some(to.magic?) | ^^^^^^^^^^^^^^^ help: try: `to.magic` error: Question mark operator is useless here - --> $DIR/needless_question_mark.rs:30:5 + --> $DIR/needless_question_mark.rs:36:5 | LL | Some(to.magic?) | ^^^^^^^^^^^^^^^ help: try: `to.magic` error: Question mark operator is useless here - --> $DIR/needless_question_mark.rs:35:21 + --> $DIR/needless_question_mark.rs:41:21 | LL | to.and_then(|t| Some(t.magic?)) | ^^^^^^^^^^^^^^ help: try: `t.magic` error: Question mark operator is useless here - --> $DIR/needless_question_mark.rs:44:9 + --> $DIR/needless_question_mark.rs:50:9 | LL | Some(t.magic?) | ^^^^^^^^^^^^^^ help: try: `t.magic` error: Question mark operator is useless here - --> $DIR/needless_question_mark.rs:49:12 + --> $DIR/needless_question_mark.rs:55:12 | LL | return Ok(tr.magic?); | ^^^^^^^^^^^^^ help: try: `tr.magic` error: Question mark operator is useless here - --> $DIR/needless_question_mark.rs:56:12 + --> $DIR/needless_question_mark.rs:62:12 | LL | return Ok(tr.magic?) | ^^^^^^^^^^^^^ help: try: `tr.magic` error: Question mark operator is useless here - --> $DIR/needless_question_mark.rs:60:5 + --> $DIR/needless_question_mark.rs:66:5 | LL | Ok(tr.magic?) | ^^^^^^^^^^^^^ help: try: `tr.magic` error: Question mark operator is useless here - --> $DIR/needless_question_mark.rs:64:21 + --> $DIR/needless_question_mark.rs:70:21 | LL | tr.and_then(|t| Ok(t.magic?)) | ^^^^^^^^^^^^ help: try: `t.magic` error: Question mark operator is useless here - --> $DIR/needless_question_mark.rs:72:9 + --> $DIR/needless_question_mark.rs:78:9 | LL | Ok(t.magic?) | ^^^^^^^^^^^^ help: try: `t.magic` error: Question mark operator is useless here - --> $DIR/needless_question_mark.rs:79:16 + --> $DIR/needless_question_mark.rs:85:16 | LL | return Ok(t.magic?); | ^^^^^^^^^^^^ help: try: `t.magic` error: Question mark operator is useless here - --> $DIR/needless_question_mark.rs:132:9 + --> $DIR/needless_question_mark.rs:138:9 | LL | Ok(to.magic?) // should be triggered | ^^^^^^^^^^^^^ help: try: `to.magic` error: Question mark operator is useless here - --> $DIR/needless_question_mark.rs:148:9 + --> $DIR/needless_question_mark.rs:154:9 | LL | Some(to.magic?) // should be triggered | ^^^^^^^^^^^^^^^ help: try: `to.magic` error: Question mark operator is useless here - --> $DIR/needless_question_mark.rs:156:9 + --> $DIR/needless_question_mark.rs:162:9 | LL | Ok(to.magic?) // should be triggered | ^^^^^^^^^^^^^ help: try: `to.magic` diff --git a/tests/ui/suspicious_operation_groupings.rs b/tests/ui/suspicious_operation_groupings.rs index dd6f4ec7bd9..2f8c7cec50f 100644 --- a/tests/ui/suspicious_operation_groupings.rs +++ b/tests/ui/suspicious_operation_groupings.rs @@ -27,7 +27,7 @@ fn buggy_ab_cmp(s1: &S, s2: &S) -> bool { s1.a < s2.a && s1.a < s2.b } -struct SAOnly { +struct SaOnly { a: i32, } @@ -37,13 +37,13 @@ impl S { } } -fn do_not_give_bad_suggestions_for_this_unusual_expr(s1: &S, s2: &SAOnly) -> bool { +fn do_not_give_bad_suggestions_for_this_unusual_expr(s1: &S, s2: &SaOnly) -> bool { // This is superficially similar to `buggy_ab_cmp`, but we should not suggest // `s2.b` since that is invalid. s1.a < s2.a && s1.a() < s1.b } -fn do_not_give_bad_suggestions_for_this_macro_expr(s1: &S, s2: &SAOnly) -> bool { +fn do_not_give_bad_suggestions_for_this_macro_expr(s1: &S, s2: &SaOnly) -> bool { macro_rules! s1 { () => { S { @@ -60,7 +60,7 @@ fn do_not_give_bad_suggestions_for_this_macro_expr(s1: &S, s2: &SAOnly) -> bool s1.a < s2.a && s1!().a < s1.b } -fn do_not_give_bad_suggestions_for_this_incorrect_expr(s1: &S, s2: &SAOnly) -> bool { +fn do_not_give_bad_suggestions_for_this_incorrect_expr(s1: &S, s2: &SaOnly) -> bool { // There's two `s1.b`, but we should not suggest `s2.b` since that is invalid s1.a < s2.a && s1.b < s1.b } diff --git a/tests/ui/transmute_ptr_to_ptr.rs b/tests/ui/transmute_ptr_to_ptr.rs index 26b03bdc740..9e213aab68c 100644 --- a/tests/ui/transmute_ptr_to_ptr.rs +++ b/tests/ui/transmute_ptr_to_ptr.rs @@ -53,10 +53,10 @@ fn transmute_ptr_to_ptr() { // dereferencing raw pointers in const contexts, should not lint as it's unstable (issue 5959) const _: &() = { - struct ZST; - let zst = &ZST; + struct Zst; + let zst = &Zst; - unsafe { std::mem::transmute::<&'static ZST, &'static ()>(zst) } + unsafe { std::mem::transmute::<&'static Zst, &'static ()>(zst) } }; fn main() {} diff --git a/tests/ui/unnested_or_patterns.fixed b/tests/ui/unnested_or_patterns.fixed index b39e891094f..13a036cd800 100644 --- a/tests/ui/unnested_or_patterns.fixed +++ b/tests/ui/unnested_or_patterns.fixed @@ -3,7 +3,7 @@ #![feature(or_patterns)] #![feature(box_patterns)] #![warn(clippy::unnested_or_patterns)] -#![allow(clippy::cognitive_complexity, clippy::match_ref_pats)] +#![allow(clippy::cognitive_complexity, clippy::match_ref_pats, clippy::upper_case_acronyms)] #![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)] fn main() { diff --git a/tests/ui/unnested_or_patterns.rs b/tests/ui/unnested_or_patterns.rs index 096f5a71150..4a10cc702c4 100644 --- a/tests/ui/unnested_or_patterns.rs +++ b/tests/ui/unnested_or_patterns.rs @@ -3,7 +3,7 @@ #![feature(or_patterns)] #![feature(box_patterns)] #![warn(clippy::unnested_or_patterns)] -#![allow(clippy::cognitive_complexity, clippy::match_ref_pats)] +#![allow(clippy::cognitive_complexity, clippy::match_ref_pats, clippy::upper_case_acronyms)] #![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)] fn main() { diff --git a/tests/ui/upper_case_acronyms.rs b/tests/ui/upper_case_acronyms.rs new file mode 100644 index 00000000000..af0b5776348 --- /dev/null +++ b/tests/ui/upper_case_acronyms.rs @@ -0,0 +1,21 @@ +#![warn(clippy::upper_case_acronyms)] + +struct HTTPResponse; // linted + +struct CString; // not linted + +enum Flags { + NS, // linted + CWR, + ECE, + URG, + ACK, + PSH, + RST, + SYN, + FIN, +} + +struct GCCLLVMSomething; // linted, beware that lint suggests `GccllvmSomething` instead of `GccLlvmSomething` + +fn main() {} diff --git a/tests/ui/upper_case_acronyms.stderr b/tests/ui/upper_case_acronyms.stderr new file mode 100644 index 00000000000..2065fe10bb1 --- /dev/null +++ b/tests/ui/upper_case_acronyms.stderr @@ -0,0 +1,70 @@ +error: name `HTTPResponse` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:3:8 + | +LL | struct HTTPResponse; // linted + | ^^^^^^^^^^^^ help: consider making the acronym lowercase, except the initial letter: `HttpResponse` + | + = note: `-D clippy::upper-case-acronyms` implied by `-D warnings` + +error: name `NS` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:8:5 + | +LL | NS, // linted + | ^^ help: consider making the acronym lowercase, except the initial letter (notice the capitalization): `Ns` + +error: name `CWR` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:9:5 + | +LL | CWR, + | ^^^ help: consider making the acronym lowercase, except the initial letter: `Cwr` + +error: name `ECE` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:10:5 + | +LL | ECE, + | ^^^ help: consider making the acronym lowercase, except the initial letter: `Ece` + +error: name `URG` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:11:5 + | +LL | URG, + | ^^^ help: consider making the acronym lowercase, except the initial letter: `Urg` + +error: name `ACK` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:12:5 + | +LL | ACK, + | ^^^ help: consider making the acronym lowercase, except the initial letter (notice the capitalization): `Ack` + +error: name `PSH` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:13:5 + | +LL | PSH, + | ^^^ help: consider making the acronym lowercase, except the initial letter: `Psh` + +error: name `RST` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:14:5 + | +LL | RST, + | ^^^ help: consider making the acronym lowercase, except the initial letter: `Rst` + +error: name `SYN` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:15:5 + | +LL | SYN, + | ^^^ help: consider making the acronym lowercase, except the initial letter: `Syn` + +error: name `FIN` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:16:5 + | +LL | FIN, + | ^^^ help: consider making the acronym lowercase, except the initial letter: `Fin` + +error: name `GCCLLVMSomething` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:19:8 + | +LL | struct GCCLLVMSomething; // linted, beware that lint suggests `GccllvmSomething` instead of `GccLlvmSomething` + | ^^^^^^^^^^^^^^^^ help: consider making the acronym lowercase, except the initial letter: `GccllvmSomething` + +error: aborting due to 11 previous errors + diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed index d6a890014e6..bb2012441d9 100644 --- a/tests/ui/use_self.fixed +++ b/tests/ui/use_self.fixed @@ -3,7 +3,7 @@ #![warn(clippy::use_self)] #![allow(dead_code)] -#![allow(clippy::should_implement_trait)] +#![allow(clippy::should_implement_trait, clippy::upper_case_acronyms)] fn main() {} diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index b04d9ce75b2..ddfd2beba31 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -3,7 +3,7 @@ #![warn(clippy::use_self)] #![allow(dead_code)] -#![allow(clippy::should_implement_trait)] +#![allow(clippy::should_implement_trait, clippy::upper_case_acronyms)] fn main() {} -- cgit 1.4.1-3-g733a5 From e42208f1b7befe316e32c66b6dc6d242a56f4d84 Mon Sep 17 00:00:00 2001 From: pastchick3 <331604390@qq.com> Date: Wed, 20 Jan 2021 20:05:25 +0800 Subject: Improve the suggestion message of `stable_sort_primitive`. --- clippy_lints/src/stable_sort_primitive.rs | 31 +++++++++++++++++++------------ tests/ui/stable_sort_primitive.stderr | 27 ++++++++++++++++++++------- 2 files changed, 39 insertions(+), 19 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/stable_sort_primitive.rs b/clippy_lints/src/stable_sort_primitive.rs index 99e4b293ac6..276a9338819 100644 --- a/clippy_lints/src/stable_sort_primitive.rs +++ b/clippy_lints/src/stable_sort_primitive.rs @@ -1,4 +1,4 @@ -use crate::utils::{is_slice_of_primitives, span_lint_and_sugg, sugg::Sugg}; +use crate::utils::{is_slice_of_primitives, span_lint_and_then, sugg::Sugg}; use if_chain::if_chain; @@ -107,25 +107,32 @@ fn detect_stable_sort_primitive(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option impl LateLintPass<'_> for StableSortPrimitive { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { if let Some(detection) = detect_stable_sort_primitive(cx, expr) { - span_lint_and_sugg( + span_lint_and_then( cx, STABLE_SORT_PRIMITIVE, expr.span, format!( - "used {} instead of {} to sort primitive type `{}`", + "used `{}` on primitive type `{}`", detection.method.stable_name(), - detection.method.unstable_name(), detection.slice_type, ) .as_str(), - "try", - format!( - "{}.{}({})", - detection.slice_name, - detection.method.unstable_name(), - detection.method_args - ), - Applicability::MachineApplicable, + |diag| { + diag.span_suggestion( + expr.span, + "try", + format!( + "{}.{}({})", + detection.slice_name, + detection.method.unstable_name(), + detection.method_args, + ), + Applicability::MachineApplicable, + ); + diag.note( + "an unstable sort would perform faster without any observable difference for this data type", + ); + }, ); } } diff --git a/tests/ui/stable_sort_primitive.stderr b/tests/ui/stable_sort_primitive.stderr index 780389f32bc..b8d22ed2504 100644 --- a/tests/ui/stable_sort_primitive.stderr +++ b/tests/ui/stable_sort_primitive.stderr @@ -1,46 +1,59 @@ -error: used sort instead of sort_unstable to sort primitive type `i32` +error: used `sort` on primitive type `i32` --> $DIR/stable_sort_primitive.rs:7:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` | = note: `-D clippy::stable-sort-primitive` implied by `-D warnings` + = note: an unstable sort would perform faster without any observable difference for this data type -error: used sort instead of sort_unstable to sort primitive type `bool` +error: used `sort` on primitive type `bool` --> $DIR/stable_sort_primitive.rs:9:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` + | + = note: an unstable sort would perform faster without any observable difference for this data type -error: used sort instead of sort_unstable to sort primitive type `char` +error: used `sort` on primitive type `char` --> $DIR/stable_sort_primitive.rs:11:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` + | + = note: an unstable sort would perform faster without any observable difference for this data type -error: used sort instead of sort_unstable to sort primitive type `str` +error: used `sort` on primitive type `str` --> $DIR/stable_sort_primitive.rs:13:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` + | + = note: an unstable sort would perform faster without any observable difference for this data type -error: used sort instead of sort_unstable to sort primitive type `tuple` +error: used `sort` on primitive type `tuple` --> $DIR/stable_sort_primitive.rs:15:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` + | + = note: an unstable sort would perform faster without any observable difference for this data type -error: used sort instead of sort_unstable to sort primitive type `array` +error: used `sort` on primitive type `array` --> $DIR/stable_sort_primitive.rs:17:5 | LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` + | + = note: an unstable sort would perform faster without any observable difference for this data type -error: used sort instead of sort_unstable to sort primitive type `i32` +error: used `sort` on primitive type `i32` --> $DIR/stable_sort_primitive.rs:19:5 | LL | arr.sort(); | ^^^^^^^^^^ help: try: `arr.sort_unstable()` + | + = note: an unstable sort would perform faster without any observable difference for this data type error: aborting due to 7 previous errors -- cgit 1.4.1-3-g733a5 From f1ab3024b27cc7c02a80fd54382a10a1b4ef3bcd Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Thu, 21 Jan 2021 12:48:30 -0800 Subject: New lint: exhaustive_enums --- CHANGELOG.md | 1 + clippy_lints/src/exhaustive_enums.rs | 68 ++++++++++++++++++++++++++++++++++++ clippy_lints/src/lib.rs | 4 +++ tests/ui/exhaustive_enums.fixed | 24 +++++++++++++ tests/ui/exhaustive_enums.rs | 23 ++++++++++++ tests/ui/exhaustive_enums.stderr | 28 +++++++++++++++ 6 files changed, 148 insertions(+) create mode 100644 clippy_lints/src/exhaustive_enums.rs create mode 100644 tests/ui/exhaustive_enums.fixed create mode 100644 tests/ui/exhaustive_enums.rs create mode 100644 tests/ui/exhaustive_enums.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 04f042b2deb..4cf2125ea2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1938,6 +1938,7 @@ Released 2018-09-13 [`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 +[`exhaustive_enums`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_enums [`exit`]: https://rust-lang.github.io/rust-clippy/master/index.html#exit [`expect_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_fun_call [`expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_used diff --git a/clippy_lints/src/exhaustive_enums.rs b/clippy_lints/src/exhaustive_enums.rs new file mode 100644 index 00000000000..099171962d3 --- /dev/null +++ b/clippy_lints/src/exhaustive_enums.rs @@ -0,0 +1,68 @@ +use crate::utils::{snippet_opt, span_lint_and_help, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_ast::ast::{Item, ItemKind}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// **What it does:** Warns on any `enum`s that are not tagged `#[non_exhaustive]` + /// + /// **Why is this bad?** Exhaustive enums are typically fine, but a project which does + /// not wish to make a stability commitment around enums may wish to disable them by default. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// enum Foo { + /// Bar, + /// Baz + /// } + /// ``` + /// Use instead: + /// ```rust + /// #[non_exhaustive] + /// enum Foo { + /// Bar, + /// Baz + /// } /// ``` + pub EXHAUSTIVE_ENUMS, + restriction, + "default lint description" +} + +declare_lint_pass!(ExhaustiveEnums => [EXHAUSTIVE_ENUMS]); + +impl EarlyLintPass for ExhaustiveEnums { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + if_chain! { + if let ItemKind::Enum(..) = item.kind; + if !item.attrs.iter().any(|a| a.has_name(sym::non_exhaustive)); + then { + if let Some(snippet) = snippet_opt(cx, item.span) { + span_lint_and_sugg( + cx, + EXHAUSTIVE_ENUMS, + item.span, + "enums should not be exhaustive", + "try adding #[non_exhaustive]", + format!("#[non_exhaustive]\n{}", snippet), + Applicability::MaybeIncorrect, + ); + } else { + span_lint_and_help( + cx, + EXHAUSTIVE_ENUMS, + item.span, + "enums should not be exhaustive", + None, + "try adding #[non_exhaustive]", + ); + } + } + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 53764bb7390..465ad3846ce 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -200,6 +200,7 @@ mod escape; mod eta_reduction; mod eval_order_dependence; mod excessive_bools; +mod exhaustive_enums; mod exit; mod explicit_write; mod fallible_impl_from; @@ -611,6 +612,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &eval_order_dependence::EVAL_ORDER_DEPENDENCE, &excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS, &excessive_bools::STRUCT_EXCESSIVE_BOOLS, + &exhaustive_enums::EXHAUSTIVE_ENUMS, &exit::EXIT, &explicit_write::EXPLICIT_WRITE, &fallible_impl_from::FALLIBLE_IMPL_FROM, @@ -1096,6 +1098,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box eval_order_dependence::EvalOrderDependence); store.register_late_pass(|| box missing_doc::MissingDoc::new()); store.register_late_pass(|| box missing_inline::MissingInline); + store.register_early_pass(move || box exhaustive_enums::ExhaustiveEnums); store.register_late_pass(|| box if_let_some_result::OkIfLet); store.register_late_pass(|| box partialeq_ne_impl::PartialEqNeImpl); store.register_late_pass(|| box unused_io_amount::UnusedIoAmount); @@ -1246,6 +1249,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&create_dir::CREATE_DIR), LintId::of(&dbg_macro::DBG_MACRO), LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE), + LintId::of(&exhaustive_enums::EXHAUSTIVE_ENUMS), LintId::of(&exit::EXIT), LintId::of(&float_literal::LOSSY_FLOAT_LITERAL), LintId::of(&implicit_return::IMPLICIT_RETURN), diff --git a/tests/ui/exhaustive_enums.fixed b/tests/ui/exhaustive_enums.fixed new file mode 100644 index 00000000000..2d5f0474bc0 --- /dev/null +++ b/tests/ui/exhaustive_enums.fixed @@ -0,0 +1,24 @@ +// run-rustfix + +#![deny(clippy::exhaustive_enums)] +#![allow(unused)] + +fn main() { + // nop +} + +#[non_exhaustive] +enum Exhaustive { + Foo, + Bar, + Baz, + Quux(String), +} + +#[non_exhaustive] +enum NonExhaustive { + Foo, + Bar, + Baz, + Quux(String), +} diff --git a/tests/ui/exhaustive_enums.rs b/tests/ui/exhaustive_enums.rs new file mode 100644 index 00000000000..5c88454ae61 --- /dev/null +++ b/tests/ui/exhaustive_enums.rs @@ -0,0 +1,23 @@ +// run-rustfix + +#![deny(clippy::exhaustive_enums)] +#![allow(unused)] + +fn main() { + // nop +} + +enum Exhaustive { + Foo, + Bar, + Baz, + Quux(String), +} + +#[non_exhaustive] +enum NonExhaustive { + Foo, + Bar, + Baz, + Quux(String), +} diff --git a/tests/ui/exhaustive_enums.stderr b/tests/ui/exhaustive_enums.stderr new file mode 100644 index 00000000000..ee5a1836267 --- /dev/null +++ b/tests/ui/exhaustive_enums.stderr @@ -0,0 +1,28 @@ +error: enums should not be exhaustive + --> $DIR/exhaustive_enums.rs:10:1 + | +LL | / enum Exhaustive { +LL | | Foo, +LL | | Bar, +LL | | Baz, +LL | | Quux(String), +LL | | } + | |_^ + | +note: the lint level is defined here + --> $DIR/exhaustive_enums.rs:3:9 + | +LL | #![deny(clippy::exhaustive_enums)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ +help: try adding #[non_exhaustive] + | +LL | #[non_exhaustive] +LL | enum Exhaustive { +LL | Foo, +LL | Bar, +LL | Baz, +LL | Quux(String), + ... + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From f6cb96ef07ac6197dac5be16adbe2b7950c82d99 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Thu, 21 Jan 2021 13:34:44 -0800 Subject: Make exhaustive_enums only warn on exported items --- clippy_lints/src/exhaustive_enums.rs | 3 ++- tests/ui/exhaustive_enums.fixed | 22 ++++++++++++++++++++-- tests/ui/exhaustive_enums.rs | 22 ++++++++++++++++++++-- tests/ui/exhaustive_enums.stderr | 4 ++-- 4 files changed, 44 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/exhaustive_enums.rs b/clippy_lints/src/exhaustive_enums.rs index 1391ebd9e31..2e1c0728d2c 100644 --- a/clippy_lints/src/exhaustive_enums.rs +++ b/clippy_lints/src/exhaustive_enums.rs @@ -7,7 +7,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; declare_clippy_lint! { - /// **What it does:** Warns on any `enum`s that are not tagged `#[non_exhaustive]` + /// **What it does:** Warns on any exported `enum`s that are not tagged `#[non_exhaustive]` /// /// **Why is this bad?** Exhaustive enums are typically fine, but a project which does /// not wish to make a stability commitment around enums may wish to disable them by default. @@ -40,6 +40,7 @@ impl LateLintPass<'_> for ExhaustiveEnums { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { if_chain! { if let ItemKind::Enum(..) = item.kind; + if cx.access_levels.is_exported(item.hir_id); if !item.attrs.iter().any(|a| a.has_name(sym::non_exhaustive)); then { if let Some(snippet) = snippet_opt(cx, item.span) { diff --git a/tests/ui/exhaustive_enums.fixed b/tests/ui/exhaustive_enums.fixed index 2d5f0474bc0..71c4a251e3b 100644 --- a/tests/ui/exhaustive_enums.fixed +++ b/tests/ui/exhaustive_enums.fixed @@ -8,15 +8,33 @@ fn main() { } #[non_exhaustive] -enum Exhaustive { +pub enum Exhaustive { Foo, Bar, Baz, Quux(String), } +// no warning, already non_exhaustive #[non_exhaustive] -enum NonExhaustive { +pub enum NonExhaustive { + Foo, + Bar, + Baz, + Quux(String), +} + +// no warning, private +enum ExhaustivePrivate { + Foo, + Bar, + Baz, + Quux(String), +} + +// no warning, private +#[non_exhaustive] +enum NonExhaustivePrivate { Foo, Bar, Baz, diff --git a/tests/ui/exhaustive_enums.rs b/tests/ui/exhaustive_enums.rs index 5c88454ae61..45af6851dd1 100644 --- a/tests/ui/exhaustive_enums.rs +++ b/tests/ui/exhaustive_enums.rs @@ -7,15 +7,33 @@ fn main() { // nop } -enum Exhaustive { +pub enum Exhaustive { Foo, Bar, Baz, Quux(String), } +// no warning, already non_exhaustive #[non_exhaustive] -enum NonExhaustive { +pub enum NonExhaustive { + Foo, + Bar, + Baz, + Quux(String), +} + +// no warning, private +enum ExhaustivePrivate { + Foo, + Bar, + Baz, + Quux(String), +} + +// no warning, private +#[non_exhaustive] +enum NonExhaustivePrivate { Foo, Bar, Baz, diff --git a/tests/ui/exhaustive_enums.stderr b/tests/ui/exhaustive_enums.stderr index ee5a1836267..280c40b00aa 100644 --- a/tests/ui/exhaustive_enums.stderr +++ b/tests/ui/exhaustive_enums.stderr @@ -1,7 +1,7 @@ error: enums should not be exhaustive --> $DIR/exhaustive_enums.rs:10:1 | -LL | / enum Exhaustive { +LL | / pub enum Exhaustive { LL | | Foo, LL | | Bar, LL | | Baz, @@ -17,7 +17,7 @@ LL | #![deny(clippy::exhaustive_enums)] help: try adding #[non_exhaustive] | LL | #[non_exhaustive] -LL | enum Exhaustive { +LL | pub enum Exhaustive { LL | Foo, LL | Bar, LL | Baz, -- cgit 1.4.1-3-g733a5 From 09d4d49299c6614a0ae956980e709a234d21a9ef Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Thu, 21 Jan 2021 13:36:18 -0800 Subject: ExhaustiveEnums -> ExhaustiveItems --- clippy_lints/src/exhaustive_enums.rs | 69 ------------------------------------ clippy_lints/src/exhaustive_items.rs | 69 ++++++++++++++++++++++++++++++++++++ clippy_lints/src/lib.rs | 8 ++--- tests/ui/exhaustive_enums.fixed | 42 ---------------------- tests/ui/exhaustive_enums.rs | 41 --------------------- tests/ui/exhaustive_enums.stderr | 28 --------------- tests/ui/exhaustive_items.fixed | 42 ++++++++++++++++++++++ tests/ui/exhaustive_items.rs | 41 +++++++++++++++++++++ tests/ui/exhaustive_items.stderr | 28 +++++++++++++++ 9 files changed, 184 insertions(+), 184 deletions(-) delete mode 100644 clippy_lints/src/exhaustive_enums.rs create mode 100644 clippy_lints/src/exhaustive_items.rs delete mode 100644 tests/ui/exhaustive_enums.fixed delete mode 100644 tests/ui/exhaustive_enums.rs delete mode 100644 tests/ui/exhaustive_enums.stderr create mode 100644 tests/ui/exhaustive_items.fixed create mode 100644 tests/ui/exhaustive_items.rs create mode 100644 tests/ui/exhaustive_items.stderr (limited to 'tests') diff --git a/clippy_lints/src/exhaustive_enums.rs b/clippy_lints/src/exhaustive_enums.rs deleted file mode 100644 index 2e1c0728d2c..00000000000 --- a/clippy_lints/src/exhaustive_enums.rs +++ /dev/null @@ -1,69 +0,0 @@ -use crate::utils::{snippet_opt, span_lint_and_help, span_lint_and_sugg}; -use if_chain::if_chain; -use rustc_hir::{Item, ItemKind}; -use rustc_errors::Applicability; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::sym; - -declare_clippy_lint! { - /// **What it does:** Warns on any exported `enum`s that are not tagged `#[non_exhaustive]` - /// - /// **Why is this bad?** Exhaustive enums are typically fine, but a project which does - /// not wish to make a stability commitment around enums may wish to disable them by default. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// - /// ```rust - /// enum Foo { - /// Bar, - /// Baz - /// } - /// ``` - /// Use instead: - /// ```rust - /// #[non_exhaustive] - /// enum Foo { - /// Bar, - /// Baz - /// } /// ``` - pub EXHAUSTIVE_ENUMS, - restriction, - "default lint description" -} - -declare_lint_pass!(ExhaustiveEnums => [EXHAUSTIVE_ENUMS]); - -impl LateLintPass<'_> for ExhaustiveEnums { - fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - if_chain! { - if let ItemKind::Enum(..) = item.kind; - if cx.access_levels.is_exported(item.hir_id); - if !item.attrs.iter().any(|a| a.has_name(sym::non_exhaustive)); - then { - if let Some(snippet) = snippet_opt(cx, item.span) { - span_lint_and_sugg( - cx, - EXHAUSTIVE_ENUMS, - item.span, - "enums should not be exhaustive", - "try adding #[non_exhaustive]", - format!("#[non_exhaustive]\n{}", snippet), - Applicability::MaybeIncorrect, - ); - } else { - span_lint_and_help( - cx, - EXHAUSTIVE_ENUMS, - item.span, - "enums should not be exhaustive", - None, - "try adding #[non_exhaustive]", - ); - } - } - } - } -} diff --git a/clippy_lints/src/exhaustive_items.rs b/clippy_lints/src/exhaustive_items.rs new file mode 100644 index 00000000000..0fa6c4b589f --- /dev/null +++ b/clippy_lints/src/exhaustive_items.rs @@ -0,0 +1,69 @@ +use crate::utils::{snippet_opt, span_lint_and_help, span_lint_and_sugg}; +use if_chain::if_chain; +use rustc_hir::{Item, ItemKind}; +use rustc_errors::Applicability; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// **What it does:** Warns on any exported `enum`s that are not tagged `#[non_exhaustive]` + /// + /// **Why is this bad?** Exhaustive enums are typically fine, but a project which does + /// not wish to make a stability commitment around enums may wish to disable them by default. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// enum Foo { + /// Bar, + /// Baz + /// } + /// ``` + /// Use instead: + /// ```rust + /// #[non_exhaustive] + /// enum Foo { + /// Bar, + /// Baz + /// } /// ``` + pub EXHAUSTIVE_ENUMS, + restriction, + "default lint description" +} + +declare_lint_pass!(ExhaustiveItems => [EXHAUSTIVE_ENUMS]); + +impl LateLintPass<'_> for ExhaustiveItems { + fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + if_chain! { + if let ItemKind::Enum(..) = item.kind; + if cx.access_levels.is_exported(item.hir_id); + if !item.attrs.iter().any(|a| a.has_name(sym::non_exhaustive)); + then { + if let Some(snippet) = snippet_opt(cx, item.span) { + span_lint_and_sugg( + cx, + EXHAUSTIVE_ENUMS, + item.span, + "enums should not be exhaustive", + "try adding #[non_exhaustive]", + format!("#[non_exhaustive]\n{}", snippet), + Applicability::MaybeIncorrect, + ); + } else { + span_lint_and_help( + cx, + EXHAUSTIVE_ENUMS, + item.span, + "enums should not be exhaustive", + None, + "try adding #[non_exhaustive]", + ); + } + } + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index becd9f333fd..a5deebf7d5f 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -200,7 +200,7 @@ mod escape; mod eta_reduction; mod eval_order_dependence; mod excessive_bools; -mod exhaustive_enums; +mod exhaustive_items; mod exit; mod explicit_write; mod fallible_impl_from; @@ -612,7 +612,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &eval_order_dependence::EVAL_ORDER_DEPENDENCE, &excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS, &excessive_bools::STRUCT_EXCESSIVE_BOOLS, - &exhaustive_enums::EXHAUSTIVE_ENUMS, + &exhaustive_items::EXHAUSTIVE_ENUMS, &exit::EXIT, &explicit_write::EXPLICIT_WRITE, &fallible_impl_from::FALLIBLE_IMPL_FROM, @@ -1098,7 +1098,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box eval_order_dependence::EvalOrderDependence); store.register_late_pass(|| box missing_doc::MissingDoc::new()); store.register_late_pass(|| box missing_inline::MissingInline); - store.register_late_pass(move || box exhaustive_enums::ExhaustiveEnums); + store.register_late_pass(move || box exhaustive_items::ExhaustiveItems); store.register_late_pass(|| box if_let_some_result::OkIfLet); store.register_late_pass(|| box partialeq_ne_impl::PartialEqNeImpl); store.register_late_pass(|| box unused_io_amount::UnusedIoAmount); @@ -1249,7 +1249,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&create_dir::CREATE_DIR), LintId::of(&dbg_macro::DBG_MACRO), LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE), - LintId::of(&exhaustive_enums::EXHAUSTIVE_ENUMS), + LintId::of(&exhaustive_items::EXHAUSTIVE_ENUMS), LintId::of(&exit::EXIT), LintId::of(&float_literal::LOSSY_FLOAT_LITERAL), LintId::of(&implicit_return::IMPLICIT_RETURN), diff --git a/tests/ui/exhaustive_enums.fixed b/tests/ui/exhaustive_enums.fixed deleted file mode 100644 index 71c4a251e3b..00000000000 --- a/tests/ui/exhaustive_enums.fixed +++ /dev/null @@ -1,42 +0,0 @@ -// run-rustfix - -#![deny(clippy::exhaustive_enums)] -#![allow(unused)] - -fn main() { - // nop -} - -#[non_exhaustive] -pub enum Exhaustive { - Foo, - Bar, - Baz, - Quux(String), -} - -// no warning, already non_exhaustive -#[non_exhaustive] -pub enum NonExhaustive { - Foo, - Bar, - Baz, - Quux(String), -} - -// no warning, private -enum ExhaustivePrivate { - Foo, - Bar, - Baz, - Quux(String), -} - -// no warning, private -#[non_exhaustive] -enum NonExhaustivePrivate { - Foo, - Bar, - Baz, - Quux(String), -} diff --git a/tests/ui/exhaustive_enums.rs b/tests/ui/exhaustive_enums.rs deleted file mode 100644 index 45af6851dd1..00000000000 --- a/tests/ui/exhaustive_enums.rs +++ /dev/null @@ -1,41 +0,0 @@ -// run-rustfix - -#![deny(clippy::exhaustive_enums)] -#![allow(unused)] - -fn main() { - // nop -} - -pub enum Exhaustive { - Foo, - Bar, - Baz, - Quux(String), -} - -// no warning, already non_exhaustive -#[non_exhaustive] -pub enum NonExhaustive { - Foo, - Bar, - Baz, - Quux(String), -} - -// no warning, private -enum ExhaustivePrivate { - Foo, - Bar, - Baz, - Quux(String), -} - -// no warning, private -#[non_exhaustive] -enum NonExhaustivePrivate { - Foo, - Bar, - Baz, - Quux(String), -} diff --git a/tests/ui/exhaustive_enums.stderr b/tests/ui/exhaustive_enums.stderr deleted file mode 100644 index 280c40b00aa..00000000000 --- a/tests/ui/exhaustive_enums.stderr +++ /dev/null @@ -1,28 +0,0 @@ -error: enums should not be exhaustive - --> $DIR/exhaustive_enums.rs:10:1 - | -LL | / pub enum Exhaustive { -LL | | Foo, -LL | | Bar, -LL | | Baz, -LL | | Quux(String), -LL | | } - | |_^ - | -note: the lint level is defined here - --> $DIR/exhaustive_enums.rs:3:9 - | -LL | #![deny(clippy::exhaustive_enums)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ -help: try adding #[non_exhaustive] - | -LL | #[non_exhaustive] -LL | pub enum Exhaustive { -LL | Foo, -LL | Bar, -LL | Baz, -LL | Quux(String), - ... - -error: aborting due to previous error - diff --git a/tests/ui/exhaustive_items.fixed b/tests/ui/exhaustive_items.fixed new file mode 100644 index 00000000000..71c4a251e3b --- /dev/null +++ b/tests/ui/exhaustive_items.fixed @@ -0,0 +1,42 @@ +// run-rustfix + +#![deny(clippy::exhaustive_enums)] +#![allow(unused)] + +fn main() { + // nop +} + +#[non_exhaustive] +pub enum Exhaustive { + Foo, + Bar, + Baz, + Quux(String), +} + +// no warning, already non_exhaustive +#[non_exhaustive] +pub enum NonExhaustive { + Foo, + Bar, + Baz, + Quux(String), +} + +// no warning, private +enum ExhaustivePrivate { + Foo, + Bar, + Baz, + Quux(String), +} + +// no warning, private +#[non_exhaustive] +enum NonExhaustivePrivate { + Foo, + Bar, + Baz, + Quux(String), +} diff --git a/tests/ui/exhaustive_items.rs b/tests/ui/exhaustive_items.rs new file mode 100644 index 00000000000..45af6851dd1 --- /dev/null +++ b/tests/ui/exhaustive_items.rs @@ -0,0 +1,41 @@ +// run-rustfix + +#![deny(clippy::exhaustive_enums)] +#![allow(unused)] + +fn main() { + // nop +} + +pub enum Exhaustive { + Foo, + Bar, + Baz, + Quux(String), +} + +// no warning, already non_exhaustive +#[non_exhaustive] +pub enum NonExhaustive { + Foo, + Bar, + Baz, + Quux(String), +} + +// no warning, private +enum ExhaustivePrivate { + Foo, + Bar, + Baz, + Quux(String), +} + +// no warning, private +#[non_exhaustive] +enum NonExhaustivePrivate { + Foo, + Bar, + Baz, + Quux(String), +} diff --git a/tests/ui/exhaustive_items.stderr b/tests/ui/exhaustive_items.stderr new file mode 100644 index 00000000000..d00d950efc7 --- /dev/null +++ b/tests/ui/exhaustive_items.stderr @@ -0,0 +1,28 @@ +error: enums should not be exhaustive + --> $DIR/exhaustive_items.rs:10:1 + | +LL | / pub enum Exhaustive { +LL | | Foo, +LL | | Bar, +LL | | Baz, +LL | | Quux(String), +LL | | } + | |_^ + | +note: the lint level is defined here + --> $DIR/exhaustive_items.rs:3:9 + | +LL | #![deny(clippy::exhaustive_enums)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ +help: try adding #[non_exhaustive] + | +LL | #[non_exhaustive] +LL | pub enum Exhaustive { +LL | Foo, +LL | Bar, +LL | Baz, +LL | Quux(String), + ... + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From c92bdc4dbbd777f6933f7990f87066147a629c8d Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 1 Jan 2021 13:00:09 -0600 Subject: Split filter_map into manual_filter_map --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 3 ++ clippy_lints/src/methods/mod.rs | 109 +++++++++++++++++++++++++++++++++----- tests/ui/filter_methods.stderr | 12 +---- tests/ui/manual_filter_map.fixed | 37 +++++++++++++ tests/ui/manual_filter_map.rs | 37 +++++++++++++ tests/ui/manual_filter_map.stderr | 22 ++++++++ 7 files changed, 198 insertions(+), 23 deletions(-) create mode 100644 tests/ui/manual_filter_map.fixed create mode 100644 tests/ui/manual_filter_map.rs create mode 100644 tests/ui/manual_filter_map.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 04f042b2deb..8feb1a148af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2035,6 +2035,7 @@ Released 2018-09-13 [`macro_use_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_use_imports [`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion [`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn +[`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map [`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy [`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive [`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 53764bb7390..bde9c630c6c 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -745,6 +745,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::ITER_NTH, &methods::ITER_NTH_ZERO, &methods::ITER_SKIP_NEXT, + &methods::MANUAL_FILTER_MAP, &methods::MANUAL_SATURATING_ARITHMETIC, &methods::MAP_COLLECT_RESULT_UNIT, &methods::MAP_FLATTEN, @@ -1526,6 +1527,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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_SATURATING_ARITHMETIC), LintId::of(&methods::MAP_COLLECT_RESULT_UNIT), LintId::of(&methods::NEW_RET_NO_SELF), @@ -1823,6 +1825,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::FILTER_NEXT), LintId::of(&methods::FLAT_MAP_IDENTITY), LintId::of(&methods::INSPECT_FOR_EACH), + LintId::of(&methods::MANUAL_FILTER_MAP), LintId::of(&methods::OPTION_AS_REF_DEREF), LintId::of(&methods::SEARCH_IS_SOME), LintId::of(&methods::SKIP_WHILE_NEXT), diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index c8b69f4fbae..518d2e67ad1 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -15,7 +15,8 @@ use if_chain::if_chain; use rustc_ast::ast; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::{TraitItem, TraitItemKind}; +use rustc_hir::def::Res; +use rustc_hir::{Expr, ExprKind, PatKind, QPath, TraitItem, TraitItemKind, UnOp}; use rustc_lint::{LateContext, LateLintPass, Lint, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, TraitRef, Ty, TyS}; @@ -450,6 +451,32 @@ declare_clippy_lint! { "using combinations of `filter`, `map`, `filter_map` and `flat_map` which can usually be written as a single method call" } +declare_clippy_lint! { + /// **What it does:** Checks for usage of `_.filter(_).map(_)` that can be written more simply + /// as `filter_map(_)`. + /// + /// **Why is this bad?** Redundant code in the `filter` and `map` operations is poor style and + /// less performant. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// Bad: + /// ```rust + /// (0_i32..10) + /// .filter(|n| n.checked_add(1).is_some()) + /// .map(|n| n.checked_add(1).unwrap()); + /// ``` + /// + /// Good: + /// ```rust + /// (0_i32..10).filter_map(|n| n.checked_add(1)); + /// ``` + pub MANUAL_FILTER_MAP, + complexity, + "using `_.filter(_).map(_)` in a way that can be written more simply as `filter_map(_)`" +} + declare_clippy_lint! { /// **What it does:** Checks for usage of `_.filter_map(_).next()`. /// @@ -1473,6 +1500,7 @@ impl_lint_pass!(Methods => [ FILTER_NEXT, SKIP_WHILE_NEXT, FILTER_MAP, + MANUAL_FILTER_MAP, FILTER_MAP_NEXT, FLAT_MAP_IDENTITY, FIND_MAP, @@ -1540,7 +1568,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["next", "filter"] => lint_filter_next(cx, expr, arg_lists[1]), ["next", "skip_while"] => lint_skip_while_next(cx, expr, arg_lists[1]), ["next", "iter"] => lint_iter_next(cx, expr, arg_lists[1]), - ["map", "filter"] => lint_filter_map(cx, expr, arg_lists[1], arg_lists[0]), + ["map", "filter"] => lint_filter_map(cx, expr), ["map", "filter_map"] => lint_filter_map_map(cx, expr, arg_lists[1], arg_lists[0]), ["next", "filter_map"] => lint_filter_map_next(cx, expr, arg_lists[1], self.msrv.as_ref()), ["map", "find"] => lint_find_map(cx, expr, arg_lists[1], arg_lists[0]), @@ -2989,17 +3017,72 @@ fn lint_skip_while_next<'tcx>( } /// lint use of `filter().map()` for `Iterators` -fn lint_filter_map<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'_>, - _filter_args: &'tcx [hir::Expr<'_>], - _map_args: &'tcx [hir::Expr<'_>], -) { - // lint if caller of `.filter().map()` is an Iterator - if match_trait_method(cx, expr, &paths::ITERATOR) { - let msg = "called `filter(..).map(..)` on an `Iterator`"; - let hint = "this is more succinctly expressed by calling `.filter_map(..)` instead"; - span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint); +fn lint_filter_map<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if_chain! { + if let ExprKind::MethodCall(_, _, [map_recv, map_arg], map_span) = expr.kind; + if let ExprKind::MethodCall(_, _, [_, filter_arg], filter_span) = map_recv.kind; + if match_trait_method(cx, expr, &paths::ITERATOR); + + // filter(|x| ...is_some())... + if let ExprKind::Closure(_, _, filter_body_id, ..) = filter_arg.kind; + let filter_body = cx.tcx.hir().body(filter_body_id); + if let [filter_param] = filter_body.params; + // optional ref pattern: `filter(|&x| ..)` + let (filter_pat, is_filter_param_ref) = if let PatKind::Ref(ref_pat, _) = filter_param.pat.kind { + (ref_pat, true) + } else { + (filter_param.pat, false) + }; + // closure ends with is_some() or is_ok() + if let PatKind::Binding(_, filter_param_id, _, None) = filter_pat.kind; + if let ExprKind::MethodCall(path, _, [filter_arg], _) = filter_body.value.kind; + if let Some(opt_ty) = cx.typeck_results().expr_ty(filter_arg).ty_adt_def(); + if let Some(is_result) = if cx.tcx.is_diagnostic_item(sym::option_type, opt_ty.did) { + Some(false) + } else if cx.tcx.is_diagnostic_item(sym::result_type, opt_ty.did) { + Some(true) + } else { + None + }; + if path.ident.name.as_str() == if is_result { "is_ok" } else { "is_some" }; + + // ...map(|x| ...unwrap()) + if let ExprKind::Closure(_, _, map_body_id, ..) = map_arg.kind; + let map_body = cx.tcx.hir().body(map_body_id); + if let [map_param] = map_body.params; + if let PatKind::Binding(_, map_param_id, map_param_ident, None) = map_param.pat.kind; + // closure ends with expect() or unwrap() + if let ExprKind::MethodCall(seg, _, [map_arg, ..], _) = map_body.value.kind; + if matches!(seg.ident.name, sym::expect | sym::unwrap | sym::unwrap_or); + + let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| { + // in `filter(|x| ..)`, replace `*x` with `x` + let a_path = if_chain! { + if !is_filter_param_ref; + if let ExprKind::Unary(UnOp::UnDeref, expr_path) = a.kind; + then { expr_path } else { a } + }; + // let the filter closure arg and the map closure arg be equal + if_chain! { + if let ExprKind::Path(QPath::Resolved(None, a_path)) = a_path.kind; + if let ExprKind::Path(QPath::Resolved(None, b_path)) = b.kind; + if a_path.res == Res::Local(filter_param_id); + if b_path.res == Res::Local(map_param_id); + if TyS::same_type(cx.typeck_results().expr_ty_adjusted(a), cx.typeck_results().expr_ty_adjusted(b)); + then { + return true; + } + } + false + }; + if SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg); + then { + let span = filter_span.to(map_span); + let msg = "`filter(..).map(..)` can be simplified as `filter_map(..)`"; + let to_opt = if is_result { ".ok()" } else { "" }; + let sugg = format!("filter_map(|{}| {}{})", map_param_ident, snippet(cx, map_arg.span, ".."), to_opt); + span_lint_and_sugg(cx, MANUAL_FILTER_MAP, span, msg, "try", sugg, Applicability::MachineApplicable); + } } } diff --git a/tests/ui/filter_methods.stderr b/tests/ui/filter_methods.stderr index 11922681379..c7b4f28be3a 100644 --- a/tests/ui/filter_methods.stderr +++ b/tests/ui/filter_methods.stderr @@ -1,12 +1,3 @@ -error: called `filter(..).map(..)` on an `Iterator` - --> $DIR/filter_methods.rs:6:21 - | -LL | let _: Vec<_> = vec![5; 6].into_iter().filter(|&x| x == 0).map(|x| x * 2).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::filter-map` implied by `-D warnings` - = help: this is more succinctly expressed by calling `.filter_map(..)` instead - error: called `filter(..).flat_map(..)` on an `Iterator` --> $DIR/filter_methods.rs:8:21 | @@ -17,6 +8,7 @@ LL | | .filter(|&x| x == 0) LL | | .flat_map(|x| x.checked_mul(2)) | |_______________________________________^ | + = note: `-D clippy::filter-map` implied by `-D warnings` = help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()` error: called `filter_map(..).flat_map(..)` on an `Iterator` @@ -43,5 +35,5 @@ LL | | .map(|x| x.checked_mul(2)) | = help: this is more succinctly expressed by only calling `.filter_map(..)` instead -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/manual_filter_map.fixed b/tests/ui/manual_filter_map.fixed new file mode 100644 index 00000000000..fc8f58f8ea5 --- /dev/null +++ b/tests/ui/manual_filter_map.fixed @@ -0,0 +1,37 @@ +// run-rustfix +#![allow(dead_code)] +#![warn(clippy::manual_filter_map)] +#![allow(clippy::redundant_closure)] // FIXME suggestion may have redundant closure + +fn main() { + // is_some(), unwrap() + let _ = (0..).filter_map(|a| to_opt(a)); + + // ref pattern, expect() + let _ = (0..).filter_map(|a| to_opt(a)); + + // is_ok(), unwrap_or() + let _ = (0..).filter_map(|a| to_res(a).ok()); +} + +fn no_lint() { + // no shared code + let _ = (0..).filter(|n| *n > 1).map(|n| n + 1); + + // very close but different since filter() provides a reference + let _ = (0..).filter(|n| to_opt(n).is_some()).map(|a| to_opt(a).unwrap()); + + // similar but different + let _ = (0..).filter(|n| to_opt(n).is_some()).map(|n| to_res(n).unwrap()); + let _ = (0..) + .filter(|n| to_opt(n).map(|n| n + 1).is_some()) + .map(|a| to_opt(a).unwrap()); +} + +fn to_opt(_: T) -> Option { + unimplemented!() +} + +fn to_res(_: T) -> Result { + unimplemented!() +} diff --git a/tests/ui/manual_filter_map.rs b/tests/ui/manual_filter_map.rs new file mode 100644 index 00000000000..3af4bbee3bf --- /dev/null +++ b/tests/ui/manual_filter_map.rs @@ -0,0 +1,37 @@ +// run-rustfix +#![allow(dead_code)] +#![warn(clippy::manual_filter_map)] +#![allow(clippy::redundant_closure)] // FIXME suggestion may have redundant closure + +fn main() { + // is_some(), unwrap() + let _ = (0..).filter(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap()); + + // ref pattern, expect() + let _ = (0..).filter(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi")); + + // is_ok(), unwrap_or() + let _ = (0..).filter(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1)); +} + +fn no_lint() { + // no shared code + let _ = (0..).filter(|n| *n > 1).map(|n| n + 1); + + // very close but different since filter() provides a reference + let _ = (0..).filter(|n| to_opt(n).is_some()).map(|a| to_opt(a).unwrap()); + + // similar but different + let _ = (0..).filter(|n| to_opt(n).is_some()).map(|n| to_res(n).unwrap()); + let _ = (0..) + .filter(|n| to_opt(n).map(|n| n + 1).is_some()) + .map(|a| to_opt(a).unwrap()); +} + +fn to_opt(_: T) -> Option { + unimplemented!() +} + +fn to_res(_: T) -> Result { + unimplemented!() +} diff --git a/tests/ui/manual_filter_map.stderr b/tests/ui/manual_filter_map.stderr new file mode 100644 index 00000000000..4d4e2d5c12f --- /dev/null +++ b/tests/ui/manual_filter_map.stderr @@ -0,0 +1,22 @@ +error: `filter(..).map(..)` can be simplified as `filter_map(..)` + --> $DIR/manual_filter_map.rs:8:19 + | +LL | let _ = (0..).filter(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter_map(|a| to_opt(a))` + | + = note: `-D clippy::manual-filter-map` implied by `-D warnings` + +error: `filter(..).map(..)` can be simplified as `filter_map(..)` + --> $DIR/manual_filter_map.rs:11:19 + | +LL | let _ = (0..).filter(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter_map(|a| to_opt(a))` + +error: `filter(..).map(..)` can be simplified as `filter_map(..)` + --> $DIR/manual_filter_map.rs:14:19 + | +LL | let _ = (0..).filter(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter_map(|a| to_res(a).ok())` + +error: aborting due to 3 previous errors + -- cgit 1.4.1-3-g733a5 From a752d31e0a8cd8c7d8549daf421a8b791d1325a4 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 14 Jan 2021 16:36:36 -0600 Subject: Replace find_map with manual_find_map --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 3 ++ clippy_lints/src/methods/mod.rs | 64 ++++++++++++++++++++++++++--------------- tests/ui/find_map.stderr | 26 ----------------- tests/ui/manual_find_map.fixed | 37 ++++++++++++++++++++++++ tests/ui/manual_find_map.rs | 37 ++++++++++++++++++++++++ tests/ui/manual_find_map.stderr | 22 ++++++++++++++ 7 files changed, 141 insertions(+), 49 deletions(-) delete mode 100644 tests/ui/find_map.stderr create mode 100644 tests/ui/manual_find_map.fixed create mode 100644 tests/ui/manual_find_map.rs create mode 100644 tests/ui/manual_find_map.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 8feb1a148af..7f2de888d35 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2036,6 +2036,7 @@ Released 2018-09-13 [`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion [`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn [`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map +[`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map [`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy [`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive [`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index bde9c630c6c..b22ddfacf86 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -746,6 +746,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::ITER_NTH_ZERO, &methods::ITER_SKIP_NEXT, &methods::MANUAL_FILTER_MAP, + &methods::MANUAL_FIND_MAP, &methods::MANUAL_SATURATING_ARITHMETIC, &methods::MAP_COLLECT_RESULT_UNIT, &methods::MAP_FLATTEN, @@ -1528,6 +1529,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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::MAP_COLLECT_RESULT_UNIT), LintId::of(&methods::NEW_RET_NO_SELF), @@ -1826,6 +1828,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::FLAT_MAP_IDENTITY), LintId::of(&methods::INSPECT_FOR_EACH), LintId::of(&methods::MANUAL_FILTER_MAP), + LintId::of(&methods::MANUAL_FIND_MAP), LintId::of(&methods::OPTION_AS_REF_DEREF), LintId::of(&methods::SEARCH_IS_SOME), LintId::of(&methods::SKIP_WHILE_NEXT), diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 518d2e67ad1..9d078588746 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -477,6 +477,32 @@ declare_clippy_lint! { "using `_.filter(_).map(_)` in a way that can be written more simply as `filter_map(_)`" } +declare_clippy_lint! { + /// **What it does:** Checks for usage of `_.find(_).map(_)` that can be written more simply + /// as `find_map(_)`. + /// + /// **Why is this bad?** Redundant code in the `find` and `map` operations is poor style and + /// less performant. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// Bad: + /// ```rust + /// (0_i32..10) + /// .find(|n| n.checked_add(1).is_some()) + /// .map(|n| n.checked_add(1).unwrap()); + /// ``` + /// + /// Good: + /// ```rust + /// (0_i32..10).find_map(|n| n.checked_add(1)); + /// ``` + pub MANUAL_FIND_MAP, + complexity, + "using `_.find(_).map(_)` in a way that can be written more simply as `find_map(_)`" +} + declare_clippy_lint! { /// **What it does:** Checks for usage of `_.filter_map(_).next()`. /// @@ -1501,6 +1527,7 @@ impl_lint_pass!(Methods => [ SKIP_WHILE_NEXT, FILTER_MAP, MANUAL_FILTER_MAP, + MANUAL_FIND_MAP, FILTER_MAP_NEXT, FLAT_MAP_IDENTITY, FIND_MAP, @@ -1568,10 +1595,10 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["next", "filter"] => lint_filter_next(cx, expr, arg_lists[1]), ["next", "skip_while"] => lint_skip_while_next(cx, expr, arg_lists[1]), ["next", "iter"] => lint_iter_next(cx, expr, arg_lists[1]), - ["map", "filter"] => lint_filter_map(cx, expr), + ["map", "filter"] => lint_filter_map(cx, expr, false), ["map", "filter_map"] => lint_filter_map_map(cx, expr, arg_lists[1], arg_lists[0]), ["next", "filter_map"] => lint_filter_map_next(cx, expr, arg_lists[1], self.msrv.as_ref()), - ["map", "find"] => lint_find_map(cx, expr, arg_lists[1], arg_lists[0]), + ["map", "find"] => lint_filter_map(cx, expr, true), ["flat_map", "filter"] => lint_filter_flat_map(cx, expr, arg_lists[1], arg_lists[0]), ["flat_map", "filter_map"] => lint_filter_map_flat_map(cx, expr, arg_lists[1], arg_lists[0]), ["flat_map", ..] => lint_flat_map_identity(cx, expr, arg_lists[0], method_spans[0]), @@ -3016,12 +3043,12 @@ fn lint_skip_while_next<'tcx>( } } -/// lint use of `filter().map()` for `Iterators` -fn lint_filter_map<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { +/// lint use of `filter().map()` or `find().map()` for `Iterators` +fn lint_filter_map<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, is_find: bool) { if_chain! { if let ExprKind::MethodCall(_, _, [map_recv, map_arg], map_span) = expr.kind; if let ExprKind::MethodCall(_, _, [_, filter_arg], filter_span) = map_recv.kind; - if match_trait_method(cx, expr, &paths::ITERATOR); + if match_trait_method(cx, map_recv, &paths::ITERATOR); // filter(|x| ...is_some())... if let ExprKind::Closure(_, _, filter_body_id, ..) = filter_arg.kind; @@ -3078,10 +3105,16 @@ fn lint_filter_map<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg); then { let span = filter_span.to(map_span); - let msg = "`filter(..).map(..)` can be simplified as `filter_map(..)`"; + let (filter_name, lint) = if is_find { + ("find", MANUAL_FIND_MAP) + } else { + ("filter", MANUAL_FILTER_MAP) + }; + let msg = format!("`{}(..).map(..)` can be simplified as `{0}_map(..)`", filter_name); let to_opt = if is_result { ".ok()" } else { "" }; - let sugg = format!("filter_map(|{}| {}{})", map_param_ident, snippet(cx, map_arg.span, ".."), to_opt); - span_lint_and_sugg(cx, MANUAL_FILTER_MAP, span, msg, "try", sugg, Applicability::MachineApplicable); + let sugg = format!("{}_map(|{}| {}{})", filter_name, map_param_ident, + snippet(cx, map_arg.span, ".."), to_opt); + span_lint_and_sugg(cx, lint, span, &msg, "try", sugg, Applicability::MachineApplicable); } } } @@ -3120,21 +3153,6 @@ fn lint_filter_map_next<'tcx>( } } -/// lint use of `find().map()` for `Iterators` -fn lint_find_map<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'_>, - _find_args: &'tcx [hir::Expr<'_>], - map_args: &'tcx [hir::Expr<'_>], -) { - // lint if caller of `.filter().map()` is an Iterator - if match_trait_method(cx, &map_args[0], &paths::ITERATOR) { - let msg = "called `find(..).map(..)` on an `Iterator`"; - let hint = "this is more succinctly expressed by calling `.find_map(..)` instead"; - span_lint_and_help(cx, FIND_MAP, expr.span, msg, None, hint); - } -} - /// lint use of `filter_map().map()` for `Iterators` fn lint_filter_map_map<'tcx>( cx: &LateContext<'tcx>, diff --git a/tests/ui/find_map.stderr b/tests/ui/find_map.stderr deleted file mode 100644 index aea3cc62afc..00000000000 --- a/tests/ui/find_map.stderr +++ /dev/null @@ -1,26 +0,0 @@ -error: called `find(..).map(..)` on an `Iterator` - --> $DIR/find_map.rs:20:26 - | -LL | let _: Option = a.iter().find(|s| s.parse::().is_ok()).map(|s| s.parse().unwrap()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::find-map` implied by `-D warnings` - = help: this is more succinctly expressed by calling `.find_map(..)` instead - -error: called `find(..).map(..)` on an `Iterator` - --> $DIR/find_map.rs:23:29 - | -LL | let _: Option = desserts_of_the_week - | _____________________________^ -LL | | .iter() -LL | | .find(|dessert| match *dessert { -LL | | Dessert::Cake(_) => true, -... | -LL | | _ => unreachable!(), -LL | | }); - | |__________^ - | - = help: this is more succinctly expressed by calling `.find_map(..)` instead - -error: aborting due to 2 previous errors - diff --git a/tests/ui/manual_find_map.fixed b/tests/ui/manual_find_map.fixed new file mode 100644 index 00000000000..95e97c4fd1f --- /dev/null +++ b/tests/ui/manual_find_map.fixed @@ -0,0 +1,37 @@ +// run-rustfix +#![allow(dead_code)] +#![warn(clippy::manual_find_map)] +#![allow(clippy::redundant_closure)] // FIXME suggestion may have redundant closure + +fn main() { + // is_some(), unwrap() + let _ = (0..).find_map(|a| to_opt(a)); + + // ref pattern, expect() + let _ = (0..).find_map(|a| to_opt(a)); + + // is_ok(), unwrap_or() + let _ = (0..).find_map(|a| to_res(a).ok()); +} + +fn no_lint() { + // no shared code + let _ = (0..).filter(|n| *n > 1).map(|n| n + 1); + + // very close but different since filter() provides a reference + let _ = (0..).find(|n| to_opt(n).is_some()).map(|a| to_opt(a).unwrap()); + + // similar but different + let _ = (0..).find(|n| to_opt(n).is_some()).map(|n| to_res(n).unwrap()); + let _ = (0..) + .find(|n| to_opt(n).map(|n| n + 1).is_some()) + .map(|a| to_opt(a).unwrap()); +} + +fn to_opt(_: T) -> Option { + unimplemented!() +} + +fn to_res(_: T) -> Result { + unimplemented!() +} diff --git a/tests/ui/manual_find_map.rs b/tests/ui/manual_find_map.rs new file mode 100644 index 00000000000..cd3c82e3b25 --- /dev/null +++ b/tests/ui/manual_find_map.rs @@ -0,0 +1,37 @@ +// run-rustfix +#![allow(dead_code)] +#![warn(clippy::manual_find_map)] +#![allow(clippy::redundant_closure)] // FIXME suggestion may have redundant closure + +fn main() { + // is_some(), unwrap() + let _ = (0..).find(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap()); + + // ref pattern, expect() + let _ = (0..).find(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi")); + + // is_ok(), unwrap_or() + let _ = (0..).find(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1)); +} + +fn no_lint() { + // no shared code + let _ = (0..).filter(|n| *n > 1).map(|n| n + 1); + + // very close but different since filter() provides a reference + let _ = (0..).find(|n| to_opt(n).is_some()).map(|a| to_opt(a).unwrap()); + + // similar but different + let _ = (0..).find(|n| to_opt(n).is_some()).map(|n| to_res(n).unwrap()); + let _ = (0..) + .find(|n| to_opt(n).map(|n| n + 1).is_some()) + .map(|a| to_opt(a).unwrap()); +} + +fn to_opt(_: T) -> Option { + unimplemented!() +} + +fn to_res(_: T) -> Result { + unimplemented!() +} diff --git a/tests/ui/manual_find_map.stderr b/tests/ui/manual_find_map.stderr new file mode 100644 index 00000000000..9e7f798df45 --- /dev/null +++ b/tests/ui/manual_find_map.stderr @@ -0,0 +1,22 @@ +error: `find(..).map(..)` can be simplified as `find_map(..)` + --> $DIR/manual_find_map.rs:8:19 + | +LL | let _ = (0..).find(|n| to_opt(*n).is_some()).map(|a| to_opt(a).unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|a| to_opt(a))` + | + = note: `-D clippy::manual-find-map` implied by `-D warnings` + +error: `find(..).map(..)` can be simplified as `find_map(..)` + --> $DIR/manual_find_map.rs:11:19 + | +LL | let _ = (0..).find(|&n| to_opt(n).is_some()).map(|a| to_opt(a).expect("hi")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|a| to_opt(a))` + +error: `find(..).map(..)` can be simplified as `find_map(..)` + --> $DIR/manual_find_map.rs:14:19 + | +LL | let _ = (0..).find(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|a| to_res(a).ok())` + +error: aborting due to 3 previous errors + -- cgit 1.4.1-3-g733a5 From 8cb7e85006853e99e6ba2bf378c801fb53eee8fa Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Thu, 21 Jan 2021 13:41:57 -0800 Subject: Add exhaustive_structs lint --- CHANGELOG.md | 1 + clippy_lints/src/exhaustive_items.rs | 54 ++++++++++++++++++---- clippy_lints/src/lib.rs | 2 + tests/ui/exhaustive_items.fixed | 86 ++++++++++++++++++++++++------------ tests/ui/exhaustive_items.rs | 85 +++++++++++++++++++++++------------ tests/ui/exhaustive_items.stderr | 52 +++++++++++++++------- 6 files changed, 199 insertions(+), 81 deletions(-) (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 4cf2125ea2f..ccc7eb7e881 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1939,6 +1939,7 @@ Released 2018-09-13 [`eval_order_dependence`]: https://rust-lang.github.io/rust-clippy/master/index.html#eval_order_dependence [`excessive_precision`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision [`exhaustive_enums`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_enums +[`exhaustive_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_structs [`exit`]: https://rust-lang.github.io/rust-clippy/master/index.html#exit [`expect_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_fun_call [`expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_used diff --git a/clippy_lints/src/exhaustive_items.rs b/clippy_lints/src/exhaustive_items.rs index 0fa6c4b589f..d604ad0004f 100644 --- a/clippy_lints/src/exhaustive_items.rs +++ b/clippy_lints/src/exhaustive_items.rs @@ -1,7 +1,7 @@ use crate::utils::{snippet_opt, span_lint_and_help, span_lint_and_sugg}; use if_chain::if_chain; -use rustc_hir::{Item, ItemKind}; use rustc_errors::Applicability; +use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -10,7 +10,8 @@ declare_clippy_lint! { /// **What it does:** Warns on any exported `enum`s that are not tagged `#[non_exhaustive]` /// /// **Why is this bad?** Exhaustive enums are typically fine, but a project which does - /// not wish to make a stability commitment around enums may wish to disable them by default. + /// not wish to make a stability commitment around exported enums may wish to + /// disable them by default. /// /// **Known problems:** None. /// @@ -28,25 +29,62 @@ declare_clippy_lint! { /// enum Foo { /// Bar, /// Baz - /// } /// ``` + /// } + /// ``` pub EXHAUSTIVE_ENUMS, restriction, - "default lint description" + "detects exported enums that have not been marked #[non_exhaustive]" +} + +declare_clippy_lint! { + /// **What it does:** Warns on any exported `structs`s that are not tagged `#[non_exhaustive]` + /// + /// **Why is this bad?** Exhaustive structs are typically fine, but a project which does + /// not wish to make a stability commitment around exported structs may wish to + /// disable them by default. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// struct Foo { + /// bar: u8, + /// baz: String, + /// } + /// ``` + /// Use instead: + /// ```rust + /// #[non_exhaustive] + /// struct Foo { + /// bar: u8, + /// baz: String, + /// } + /// ``` + pub EXHAUSTIVE_STRUCTS, + restriction, + "detects exported structs that have not been marked #[non_exhaustive]" } -declare_lint_pass!(ExhaustiveItems => [EXHAUSTIVE_ENUMS]); +declare_lint_pass!(ExhaustiveItems => [EXHAUSTIVE_ENUMS, EXHAUSTIVE_STRUCTS]); impl LateLintPass<'_> for ExhaustiveItems { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { if_chain! { - if let ItemKind::Enum(..) = item.kind; + if let ItemKind::Enum(..) | ItemKind::Struct(..) = item.kind; if cx.access_levels.is_exported(item.hir_id); if !item.attrs.iter().any(|a| a.has_name(sym::non_exhaustive)); then { + let lint = if let ItemKind::Enum(..) = item.kind { + EXHAUSTIVE_ENUMS + } else { + EXHAUSTIVE_STRUCTS + }; + if let Some(snippet) = snippet_opt(cx, item.span) { span_lint_and_sugg( cx, - EXHAUSTIVE_ENUMS, + lint, item.span, "enums should not be exhaustive", "try adding #[non_exhaustive]", @@ -56,7 +94,7 @@ impl LateLintPass<'_> for ExhaustiveItems { } else { span_lint_and_help( cx, - EXHAUSTIVE_ENUMS, + lint, item.span, "enums should not be exhaustive", None, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index a5deebf7d5f..91c74026f64 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -613,6 +613,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &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, @@ -1250,6 +1251,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&dbg_macro::DBG_MACRO), 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(&implicit_return::IMPLICIT_RETURN), diff --git a/tests/ui/exhaustive_items.fixed b/tests/ui/exhaustive_items.fixed index 71c4a251e3b..bc146c6afc5 100644 --- a/tests/ui/exhaustive_items.fixed +++ b/tests/ui/exhaustive_items.fixed @@ -1,42 +1,72 @@ // run-rustfix -#![deny(clippy::exhaustive_enums)] +#![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)] #![allow(unused)] fn main() { // nop } -#[non_exhaustive] +pub mod enums { + #[non_exhaustive] pub enum Exhaustive { - Foo, - Bar, - Baz, - Quux(String), -} + Foo, + Bar, + Baz, + Quux(String), + } -// no warning, already non_exhaustive -#[non_exhaustive] -pub enum NonExhaustive { - Foo, - Bar, - Baz, - Quux(String), -} + // no warning, already non_exhaustive + #[non_exhaustive] + pub enum NonExhaustive { + Foo, + Bar, + Baz, + Quux(String), + } + + // no warning, private + enum ExhaustivePrivate { + Foo, + Bar, + Baz, + Quux(String), + } -// no warning, private -enum ExhaustivePrivate { - Foo, - Bar, - Baz, - Quux(String), + // no warning, private + #[non_exhaustive] + enum NonExhaustivePrivate { + Foo, + Bar, + Baz, + Quux(String), + } } -// no warning, private -#[non_exhaustive] -enum NonExhaustivePrivate { - Foo, - Bar, - Baz, - Quux(String), +pub mod structs { + #[non_exhaustive] +pub struct Exhaustive { + foo: u8, + bar: String, + } + + // no warning, already non_exhaustive + #[non_exhaustive] + pub struct NonExhaustive { + foo: u8, + bar: String, + } + + // no warning, private + struct ExhaustivePrivate { + foo: u8, + bar: String, + } + + // no warning, private + #[non_exhaustive] + struct NonExhaustivePrivate { + foo: u8, + bar: String, + } } diff --git a/tests/ui/exhaustive_items.rs b/tests/ui/exhaustive_items.rs index 45af6851dd1..ed86b50be30 100644 --- a/tests/ui/exhaustive_items.rs +++ b/tests/ui/exhaustive_items.rs @@ -1,41 +1,70 @@ // run-rustfix -#![deny(clippy::exhaustive_enums)] +#![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)] #![allow(unused)] fn main() { // nop } -pub enum Exhaustive { - Foo, - Bar, - Baz, - Quux(String), -} +pub mod enums { + pub enum Exhaustive { + Foo, + Bar, + Baz, + Quux(String), + } -// no warning, already non_exhaustive -#[non_exhaustive] -pub enum NonExhaustive { - Foo, - Bar, - Baz, - Quux(String), -} + // no warning, already non_exhaustive + #[non_exhaustive] + pub enum NonExhaustive { + Foo, + Bar, + Baz, + Quux(String), + } + + // no warning, private + enum ExhaustivePrivate { + Foo, + Bar, + Baz, + Quux(String), + } -// no warning, private -enum ExhaustivePrivate { - Foo, - Bar, - Baz, - Quux(String), + // no warning, private + #[non_exhaustive] + enum NonExhaustivePrivate { + Foo, + Bar, + Baz, + Quux(String), + } } -// no warning, private -#[non_exhaustive] -enum NonExhaustivePrivate { - Foo, - Bar, - Baz, - Quux(String), +pub mod structs { + pub struct Exhaustive { + foo: u8, + bar: String, + } + + // no warning, already non_exhaustive + #[non_exhaustive] + pub struct NonExhaustive { + foo: u8, + bar: String, + } + + // no warning, private + struct ExhaustivePrivate { + foo: u8, + bar: String, + } + + // no warning, private + #[non_exhaustive] + struct NonExhaustivePrivate { + foo: u8, + bar: String, + } } diff --git a/tests/ui/exhaustive_items.stderr b/tests/ui/exhaustive_items.stderr index d00d950efc7..7e286e65949 100644 --- a/tests/ui/exhaustive_items.stderr +++ b/tests/ui/exhaustive_items.stderr @@ -1,28 +1,46 @@ error: enums should not be exhaustive - --> $DIR/exhaustive_items.rs:10:1 + --> $DIR/exhaustive_items.rs:11:5 | -LL | / pub enum Exhaustive { -LL | | Foo, -LL | | Bar, -LL | | Baz, -LL | | Quux(String), -LL | | } - | |_^ +LL | / pub enum Exhaustive { +LL | | Foo, +LL | | Bar, +LL | | Baz, +LL | | Quux(String), +LL | | } + | |_____^ | note: the lint level is defined here - --> $DIR/exhaustive_items.rs:3:9 + --> $DIR/exhaustive_items.rs:3:35 | -LL | #![deny(clippy::exhaustive_enums)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try adding #[non_exhaustive] | -LL | #[non_exhaustive] +LL | #[non_exhaustive] LL | pub enum Exhaustive { -LL | Foo, -LL | Bar, -LL | Baz, -LL | Quux(String), +LL | Foo, +LL | Bar, +LL | Baz, +LL | Quux(String), ... -error: aborting due to previous error +error: enums should not be exhaustive + --> $DIR/exhaustive_items.rs:46:5 + | +LL | / pub struct Exhaustive { +LL | | foo: u8, +LL | | bar: String, +LL | | } + | |_____^ + | +help: try adding #[non_exhaustive] + | +LL | #[non_exhaustive] +LL | pub struct Exhaustive { +LL | foo: u8, +LL | bar: String, +LL | } + | + +error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From 752274eabdd9be991c9a4e11850890301b697032 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Thu, 21 Jan 2021 14:00:25 -0800 Subject: Fix indentation of suggestion --- clippy_lints/src/exhaustive_items.rs | 5 +++-- tests/ui/exhaustive_items.fixed | 4 ++-- tests/ui/exhaustive_items.stderr | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/exhaustive_items.rs b/clippy_lints/src/exhaustive_items.rs index d604ad0004f..bbcf9bfea27 100644 --- a/clippy_lints/src/exhaustive_items.rs +++ b/clippy_lints/src/exhaustive_items.rs @@ -1,4 +1,4 @@ -use crate::utils::{snippet_opt, span_lint_and_help, span_lint_and_sugg}; +use crate::utils::{indent_of, snippet_opt, span_lint_and_help, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; @@ -82,13 +82,14 @@ impl LateLintPass<'_> for ExhaustiveItems { }; if let Some(snippet) = snippet_opt(cx, item.span) { + let indent = " ".repeat(indent_of(cx, item.span).unwrap_or(0)); span_lint_and_sugg( cx, lint, item.span, "enums should not be exhaustive", "try adding #[non_exhaustive]", - format!("#[non_exhaustive]\n{}", snippet), + format!("#[non_exhaustive]\n{}{}", indent, snippet), Applicability::MaybeIncorrect, ); } else { diff --git a/tests/ui/exhaustive_items.fixed b/tests/ui/exhaustive_items.fixed index bc146c6afc5..7e355d2a58b 100644 --- a/tests/ui/exhaustive_items.fixed +++ b/tests/ui/exhaustive_items.fixed @@ -9,7 +9,7 @@ fn main() { pub mod enums { #[non_exhaustive] -pub enum Exhaustive { + pub enum Exhaustive { Foo, Bar, Baz, @@ -45,7 +45,7 @@ pub enum Exhaustive { pub mod structs { #[non_exhaustive] -pub struct Exhaustive { + pub struct Exhaustive { foo: u8, bar: String, } diff --git a/tests/ui/exhaustive_items.stderr b/tests/ui/exhaustive_items.stderr index 7e286e65949..1716c8e2cb5 100644 --- a/tests/ui/exhaustive_items.stderr +++ b/tests/ui/exhaustive_items.stderr @@ -17,7 +17,7 @@ LL | #![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)] help: try adding #[non_exhaustive] | LL | #[non_exhaustive] -LL | pub enum Exhaustive { +LL | pub enum Exhaustive { LL | Foo, LL | Bar, LL | Baz, @@ -36,7 +36,7 @@ LL | | } help: try adding #[non_exhaustive] | LL | #[non_exhaustive] -LL | pub struct Exhaustive { +LL | pub struct Exhaustive { LL | foo: u8, LL | bar: String, LL | } -- cgit 1.4.1-3-g733a5 From 65d003a1128e9234730a66c79065afdabb31afa0 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Fri, 22 Jan 2021 12:08:46 -0800 Subject: Clean up suggestion span; clarify help message --- clippy_lints/src/exhaustive_items.rs | 27 ++++++++++++++++----------- tests/ui/exhaustive_items.stderr | 22 ++++++++++------------ 2 files changed, 26 insertions(+), 23 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/exhaustive_items.rs b/clippy_lints/src/exhaustive_items.rs index bbcf9bfea27..4749e36238c 100644 --- a/clippy_lints/src/exhaustive_items.rs +++ b/clippy_lints/src/exhaustive_items.rs @@ -1,4 +1,4 @@ -use crate::utils::{indent_of, snippet_opt, span_lint_and_help, span_lint_and_sugg}; +use crate::utils::{indent_of, snippet_opt, span_lint_and_help, span_lint_and_then}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; @@ -75,29 +75,34 @@ impl LateLintPass<'_> for ExhaustiveItems { if cx.access_levels.is_exported(item.hir_id); if !item.attrs.iter().any(|a| a.has_name(sym::non_exhaustive)); then { - let lint = if let ItemKind::Enum(..) = item.kind { - EXHAUSTIVE_ENUMS + let (lint, msg) = if let ItemKind::Enum(..) = item.kind { + (EXHAUSTIVE_ENUMS, "exported enums should not be exhaustive") } else { - EXHAUSTIVE_STRUCTS + (EXHAUSTIVE_STRUCTS, "exported structs should not be exhaustive") }; + let suggestion_span = item.span.until(item.ident.span); - if let Some(snippet) = snippet_opt(cx, item.span) { + if let Some(snippet) = snippet_opt(cx, suggestion_span) { let indent = " ".repeat(indent_of(cx, item.span).unwrap_or(0)); - span_lint_and_sugg( + span_lint_and_then( cx, lint, item.span, - "enums should not be exhaustive", - "try adding #[non_exhaustive]", - format!("#[non_exhaustive]\n{}{}", indent, snippet), - Applicability::MaybeIncorrect, + msg, + |diag| { + let sugg = format!("#[non_exhaustive]\n{}{}", indent, snippet); + diag.span_suggestion(suggestion_span, + "try adding #[non_exhaustive]", + sugg, + Applicability::MaybeIncorrect); + } ); } else { span_lint_and_help( cx, lint, item.span, - "enums should not be exhaustive", + msg, None, "try adding #[non_exhaustive]", ); diff --git a/tests/ui/exhaustive_items.stderr b/tests/ui/exhaustive_items.stderr index 1716c8e2cb5..a24e64b6705 100644 --- a/tests/ui/exhaustive_items.stderr +++ b/tests/ui/exhaustive_items.stderr @@ -1,4 +1,4 @@ -error: enums should not be exhaustive +error: exported enums should not be exhaustive --> $DIR/exhaustive_items.rs:11:5 | LL | / pub enum Exhaustive { @@ -10,21 +10,17 @@ LL | | } | |_____^ | note: the lint level is defined here - --> $DIR/exhaustive_items.rs:3:35 + --> $DIR/exhaustive_items.rs:3:9 | LL | #![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try adding #[non_exhaustive] | LL | #[non_exhaustive] LL | pub enum Exhaustive { -LL | Foo, -LL | Bar, -LL | Baz, -LL | Quux(String), - ... + | -error: enums should not be exhaustive +error: exported structs should not be exhaustive --> $DIR/exhaustive_items.rs:46:5 | LL | / pub struct Exhaustive { @@ -33,13 +29,15 @@ LL | | bar: String, LL | | } | |_____^ | +note: the lint level is defined here + --> $DIR/exhaustive_items.rs:3:35 + | +LL | #![deny(clippy::exhaustive_enums, clippy::exhaustive_structs)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try adding #[non_exhaustive] | LL | #[non_exhaustive] LL | pub struct Exhaustive { -LL | foo: u8, -LL | bar: String, -LL | } | error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From 66afdd1f4292e7fda6ea89113c0c8343e3321d99 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 21 Jan 2021 19:21:12 -0600 Subject: Enhance collapsible_match for adjusted bindings --- clippy_lints/src/collapsible_match.rs | 22 +++++++++++++++++--- tests/ui/collapsible_match2.rs | 29 ++++++++++++++++++++++++++ tests/ui/collapsible_match2.stderr | 38 ++++++++++++++++++++++++++++++++++- 3 files changed, 85 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/collapsible_match.rs b/clippy_lints/src/collapsible_match.rs index c75aa2bde97..604ba102046 100644 --- a/clippy_lints/src/collapsible_match.rs +++ b/clippy_lints/src/collapsible_match.rs @@ -2,7 +2,7 @@ use crate::utils::visitors::LocalUsedVisitor; use crate::utils::{span_lint_and_then, SpanlessEq}; use if_chain::if_chain; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; -use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, QPath, StmtKind}; +use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, QPath, StmtKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{DefIdTree, TyCtxt}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -72,8 +72,7 @@ fn check_arm(arm: &Arm<'_>, wild_outer_arm: &Arm<'_>, cx: &LateContext<'_>) { if arms_inner.iter().all(|arm| arm.guard.is_none()); // match expression must be a local binding // match { .. } - if let ExprKind::Path(QPath::Resolved(None, path)) = expr_in.kind; - if let Res::Local(binding_id) = path.res; + if let Some(binding_id) = addr_adjusted_binding(expr_in, cx); // one of the branches must be "wild-like" if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| arm_is_wild_like(arm_inner, cx.tcx)); let (wild_inner_arm, non_wild_inner_arm) = @@ -175,3 +174,20 @@ fn is_none_ctor(res: Res, tcx: TyCtxt<'_>) -> bool { } false } + +/// Retrieves a binding ID with optional `&` and/or `*` operators removed. (e.g. `&**foo`) +/// Returns `None` if a non-reference type is de-referenced. +/// For example, if `Vec` is de-referenced to a slice, `None` is returned. +fn addr_adjusted_binding(mut expr: &Expr<'_>, cx: &LateContext<'_>) -> Option { + loop { + match expr.kind { + ExprKind::AddrOf(_, _, e) => expr = e, + ExprKind::Path(QPath::Resolved(None, path)) => match path.res { + Res::Local(binding_id) => break Some(binding_id), + _ => break None, + }, + ExprKind::Unary(UnOp::UnDeref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e, + _ => break None, + } + } +} diff --git a/tests/ui/collapsible_match2.rs b/tests/ui/collapsible_match2.rs index d571ac4ab69..8372a212477 100644 --- a/tests/ui/collapsible_match2.rs +++ b/tests/ui/collapsible_match2.rs @@ -40,6 +40,35 @@ fn lint_cases(opt_opt: Option>, res_opt: Result, String> // there is still a better way to write this. mac!(res_opt => Ok(val), val => Some(n), foo(n)); } + + // deref reference value + match Some(&[1]) { + Some(s) => match *s { + [n] => foo(n), + _ => (), + }, + _ => (), + } + + // ref pattern and deref + match Some(&[1]) { + Some(ref s) => match &*s { + [n] => foo(n), + _ => (), + }, + _ => (), + } +} + +fn no_lint() { + // deref inner value (cannot pattern match with Vec) + match Some(vec![1]) { + Some(s) => match *s { + [n] => foo(n), + _ => (), + }, + _ => (), + } } fn make() -> T { diff --git a/tests/ui/collapsible_match2.stderr b/tests/ui/collapsible_match2.stderr index 490d82d12cd..b2eb457d173 100644 --- a/tests/ui/collapsible_match2.stderr +++ b/tests/ui/collapsible_match2.stderr @@ -57,5 +57,41 @@ LL | mac!(res_opt => Ok(val), val => Some(n), foo(n)); | Replace this binding = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 3 previous errors +error: Unnecessary nested match + --> $DIR/collapsible_match2.rs:46:20 + | +LL | Some(s) => match *s { + | ____________________^ +LL | | [n] => foo(n), +LL | | _ => (), +LL | | }, + | |_________^ + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match2.rs:46:14 + | +LL | Some(s) => match *s { + | ^ Replace this binding +LL | [n] => foo(n), + | ^^^ with this pattern + +error: Unnecessary nested match + --> $DIR/collapsible_match2.rs:55:24 + | +LL | Some(ref s) => match &*s { + | ________________________^ +LL | | [n] => foo(n), +LL | | _ => (), +LL | | }, + | |_________^ + | +help: The outer pattern can be modified to include the inner pattern. + --> $DIR/collapsible_match2.rs:55:14 + | +LL | Some(ref s) => match &*s { + | ^^^^^ Replace this binding +LL | [n] => foo(n), + | ^^^ with this pattern + +error: aborting due to 5 previous errors -- cgit 1.4.1-3-g733a5 From 051891173d4018abfb353235c912e8cd649f73e3 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 24 Jan 2021 12:28:59 +0100 Subject: Add more tests for `match_overlapping_arm` lint --- tests/ui/match_overlapping_arm.rs | 18 ++++++++++++++++++ tests/ui/match_overlapping_arm.stderr | 26 +++++++++++++++++++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/ui/match_overlapping_arm.rs b/tests/ui/match_overlapping_arm.rs index 3e40f2187bf..44c51e8112a 100644 --- a/tests/ui/match_overlapping_arm.rs +++ b/tests/ui/match_overlapping_arm.rs @@ -69,6 +69,24 @@ fn overlapping() { _ => (), } + match 42 { + 0..14 => println!("0 .. 14"), + 5..10 => println!("5 .. 10"), + _ => (), + } + + match 42 { + 5..14 => println!("5 .. 14"), + 0..=10 => println!("0 ... 10"), + _ => (), + } + + match 42 { + 0..7 => println!("0 .. 7"), + 0..=10 => println!("0 ... 10"), + _ => (), + } + /* // FIXME(JohnTitor): uncomment this once rustfmt knows half-open patterns match 42 { diff --git a/tests/ui/match_overlapping_arm.stderr b/tests/ui/match_overlapping_arm.stderr index 74259cd88c7..f25a66d634e 100644 --- a/tests/ui/match_overlapping_arm.stderr +++ b/tests/ui/match_overlapping_arm.stderr @@ -35,5 +35,29 @@ note: overlaps with this LL | 0..=11 => println!("0 ... 11"), | ^^^^^^ -error: aborting due to 3 previous errors +error: some ranges overlap + --> $DIR/match_overlapping_arm.rs:80:9 + | +LL | 0..=10 => println!("0 ... 10"), + | ^^^^^^ + | +note: overlaps with this + --> $DIR/match_overlapping_arm.rs:79:9 + | +LL | 5..14 => println!("5 .. 14"), + | ^^^^^ + +error: some ranges overlap + --> $DIR/match_overlapping_arm.rs:85:9 + | +LL | 0..7 => println!("0 .. 7"), + | ^^^^ + | +note: overlaps with this + --> $DIR/match_overlapping_arm.rs:86:9 + | +LL | 0..=10 => println!("0 ... 10"), + | ^^^^^^ + +error: aborting due to 5 previous errors -- cgit 1.4.1-3-g733a5 From 3e3dff71357005556aba8d9a7893829f7510b079 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Mon, 25 Jan 2021 14:39:03 -0800 Subject: Add test with attrs --- tests/ui/exhaustive_items.fixed | 10 ++++++++++ tests/ui/exhaustive_items.rs | 9 +++++++++ tests/ui/exhaustive_items.stderr | 21 +++++++++++++++++++-- 3 files changed, 38 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/ui/exhaustive_items.fixed b/tests/ui/exhaustive_items.fixed index 7e355d2a58b..8174a0175ab 100644 --- a/tests/ui/exhaustive_items.fixed +++ b/tests/ui/exhaustive_items.fixed @@ -16,6 +16,16 @@ pub mod enums { Quux(String), } + /// Some docs + #[repr(C)] + #[non_exhaustive] + pub enum ExhaustiveWithAttrs { + Foo, + Bar, + Baz, + Quux(String), + } + // no warning, already non_exhaustive #[non_exhaustive] pub enum NonExhaustive { diff --git a/tests/ui/exhaustive_items.rs b/tests/ui/exhaustive_items.rs index ed86b50be30..b476f09f8a0 100644 --- a/tests/ui/exhaustive_items.rs +++ b/tests/ui/exhaustive_items.rs @@ -15,6 +15,15 @@ pub mod enums { Quux(String), } + /// Some docs + #[repr(C)] + pub enum ExhaustiveWithAttrs { + Foo, + Bar, + Baz, + Quux(String), + } + // no warning, already non_exhaustive #[non_exhaustive] pub enum NonExhaustive { diff --git a/tests/ui/exhaustive_items.stderr b/tests/ui/exhaustive_items.stderr index a24e64b6705..7369fe75a4f 100644 --- a/tests/ui/exhaustive_items.stderr +++ b/tests/ui/exhaustive_items.stderr @@ -20,8 +20,25 @@ LL | #[non_exhaustive] LL | pub enum Exhaustive { | +error: exported enums should not be exhaustive + --> $DIR/exhaustive_items.rs:20:5 + | +LL | / pub enum ExhaustiveWithAttrs { +LL | | Foo, +LL | | Bar, +LL | | Baz, +LL | | Quux(String), +LL | | } + | |_____^ + | +help: try adding #[non_exhaustive] + | +LL | #[non_exhaustive] +LL | pub enum ExhaustiveWithAttrs { + | + error: exported structs should not be exhaustive - --> $DIR/exhaustive_items.rs:46:5 + --> $DIR/exhaustive_items.rs:55:5 | LL | / pub struct Exhaustive { LL | | foo: u8, @@ -40,5 +57,5 @@ LL | #[non_exhaustive] LL | pub struct Exhaustive { | -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors -- cgit 1.4.1-3-g733a5 From 7f1595e18f542f2caf209a05dd090b4a2ebd3b8a Mon Sep 17 00:00:00 2001 From: Philip Hayes Date: Fri, 29 Jan 2021 22:17:18 -0800 Subject: Fix let_and_return false positive The issue: See this Rust playground link: https://play.rust-lang.org/?edition=2018&gist=12cb5d1e7527f8c37743b87fc4a53748 Run the above with clippy to see the following warning: ``` warning: returning the result of a `let` binding from a block --> src/main.rs:24:5 | 23 | let value = Foo::new(&x).value(); | --------------------------------- unnecessary `let` binding 24 | value | ^^^^^ | = note: `#[warn(clippy::let_and_return)]` on by default = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return help: return the expression directly | 23 | 24 | Foo::new(&x).value() | ``` Implementing the suggested fix, removing the temporary let binding, yields a compiler error: ``` error[E0597]: `x` does not live long enough --> src/main.rs:23:14 | 23 | Foo::new(&x).value() | ---------^^- | | | | | borrowed value does not live long enough | a temporary with access to the borrow is created here ... 24 | } | - | | | `x` dropped here while still borrowed | ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `Foo` | = note: the temporary is part of an expression at the end of a block; consider forcing this temporary to be dropped sooner, before the block's local variables are dropped help: for example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block | 23 | let x = Foo::new(&x).value(); x | ^^^^^^^ ^^^ ``` The fix: Of course, clippy looks like it should already handle this edge case; however, it appears `utils::fn_def_id` is not returning a `DefId` for `Foo::new`. Changing the `qpath_res` lookup to use the child Path `hir_id` instead of the parent Call `hir_id` fixes the issue. --- clippy_lints/src/utils/mod.rs | 3 ++- tests/ui/let_and_return.rs | 12 +++++++++++- tests/ui/let_and_return.stderr | 2 +- 3 files changed, 14 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 991fae6b1aa..caad7c88bcf 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -1546,10 +1546,11 @@ pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { ExprKind::Call( Expr { kind: ExprKind::Path(qpath), + hir_id: path_hir_id, .. }, .., - ) => cx.typeck_results().qpath_res(qpath, expr.hir_id).opt_def_id(), + ) => cx.typeck_results().qpath_res(qpath, *path_hir_id).opt_def_id(), _ => None, } } diff --git a/tests/ui/let_and_return.rs b/tests/ui/let_and_return.rs index 73e550b3df8..e3561863c1e 100644 --- a/tests/ui/let_and_return.rs +++ b/tests/ui/let_and_return.rs @@ -117,7 +117,11 @@ mod no_lint_if_stmt_borrows { fn drop(&mut self) {} } - impl Foo<'_> { + impl<'a> Foo<'a> { + fn new(inner: &'a Inner) -> Self { + Self { inner } + } + fn value(&self) -> i32 { 42 } @@ -132,6 +136,12 @@ mod no_lint_if_stmt_borrows { let value = some_foo(&x).value(); value } + + fn test2() -> i32 { + let x = Inner {}; + let value = Foo::new(&x).value(); + value + } } } diff --git a/tests/ui/let_and_return.stderr b/tests/ui/let_and_return.stderr index fe878e5f206..a6941dabeb8 100644 --- a/tests/ui/let_and_return.stderr +++ b/tests/ui/let_and_return.stderr @@ -28,7 +28,7 @@ LL | 5 | error: returning the result of a `let` binding from a block - --> $DIR/let_and_return.rs:154:13 + --> $DIR/let_and_return.rs:164:13 | LL | let clone = Arc::clone(&self.foo); | ---------------------------------- unnecessary `let` binding -- cgit 1.4.1-3-g733a5 From 949b12589112cecad9566305444527ec0738d521 Mon Sep 17 00:00:00 2001 From: nahuakang Date: Wed, 27 Jan 2021 09:34:36 +0100 Subject: Add unit tests for new lint --- tests/ui/for_loops_over_options.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 tests/ui/for_loops_over_options.rs (limited to 'tests') diff --git a/tests/ui/for_loops_over_options.rs b/tests/ui/for_loops_over_options.rs new file mode 100644 index 00000000000..d8144864a21 --- /dev/null +++ b/tests/ui/for_loops_over_options.rs @@ -0,0 +1,31 @@ +#![warn(clippy::for_loops_over_options)] + +fn main() { + let x = vec![Some(1), Some(2), Some(3)]; + for n in x { + if let Some(n) = n { + println!("{}", n); + } + } + + let y: Vec> = vec![]; + for n in y.clone() { + if let Ok(n) = n { + println!("{}", n); + } + } + + // This should not trigger the lint + for n in y.clone() { + if let Ok(n) = n { + println!("{}", n); + } else { + println!("Oops!"); + } + } + + // This should not trigger the lint + for n in vec![Some(1), Some(2), Some(3)].iter().flatten() { + println!("{}", n); + } +} -- cgit 1.4.1-3-g733a5 From 3da25ed955baffe8c14cee4950762d268f1b48e7 Mon Sep 17 00:00:00 2001 From: nahuakang Date: Wed, 27 Jan 2021 09:49:53 +0100 Subject: Rename lint --- CHANGELOG.md | 3 ++- clippy_lints/src/lib.rs | 6 +++--- clippy_lints/src/loops.rs | 6 +++--- tests/ui/for_loops_over_options.rs | 31 --------------------------- tests/ui/for_loops_over_options_or_results.rs | 31 +++++++++++++++++++++++++++ 5 files changed, 39 insertions(+), 38 deletions(-) delete mode 100644 tests/ui/for_loops_over_options.rs create mode 100644 tests/ui/for_loops_over_options_or_results.rs (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index e8e738313d4..4bd04ffbd99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1969,7 +1969,8 @@ Released 2018-09-13 [`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 -[`for_loops_over_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loops_over_options +[`for_loops_over_options_or_results`]: +https://rust-lang.github.io/rust-clippy/master/index.html#for_loops_over_options_or_results [`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 [`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 37932087355..dae6c93c7cb 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -685,7 +685,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &loops::EXPLICIT_ITER_LOOP, &loops::FOR_KV_MAP, &loops::FOR_LOOPS_OVER_FALLIBLES, - &loops::FOR_LOOPS_OVER_OPTIONS, + &loops::FOR_LOOPS_OVER_OPTIONS_OR_RESULTS, &loops::ITER_NEXT_LOOP, &loops::MANUAL_MEMCPY, &loops::MUT_RANGE_BOUND, @@ -1489,7 +1489,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::EXPLICIT_COUNTER_LOOP), LintId::of(&loops::FOR_KV_MAP), LintId::of(&loops::FOR_LOOPS_OVER_FALLIBLES), - LintId::of(&loops::FOR_LOOPS_OVER_OPTIONS), + LintId::of(&loops::FOR_LOOPS_OVER_OPTIONS_OR_RESULTS), LintId::of(&loops::ITER_NEXT_LOOP), LintId::of(&loops::MANUAL_MEMCPY), LintId::of(&loops::MUT_RANGE_BOUND), @@ -1822,7 +1822,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&lifetimes::EXTRA_UNUSED_LIFETIMES), LintId::of(&lifetimes::NEEDLESS_LIFETIMES), LintId::of(&loops::EXPLICIT_COUNTER_LOOP), - LintId::of(&loops::FOR_LOOPS_OVER_OPTIONS), + LintId::of(&loops::FOR_LOOPS_OVER_OPTIONS_OR_RESULTS), LintId::of(&loops::MUT_RANGE_BOUND), LintId::of(&loops::SINGLE_ELEMENT_LOOP), LintId::of(&loops::WHILE_LET_LOOP), diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index c1a59650cb0..e9047cce15f 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -520,7 +520,7 @@ declare_clippy_lint! { /// println!("{}", n); /// } /// ``` - pub FOR_LOOPS_OVER_OPTIONS, + pub FOR_LOOPS_OVER_OPTIONS_OR_RESULTS, complexity, "for loops over `Option`s or `Result`s with a single expression can be simplified" } @@ -532,7 +532,7 @@ declare_lint_pass!(Loops => [ EXPLICIT_INTO_ITER_LOOP, ITER_NEXT_LOOP, FOR_LOOPS_OVER_FALLIBLES, - FOR_LOOPS_OVER_OPTIONS, + FOR_LOOPS_OVER_OPTIONS_OR_RESULTS, WHILE_LET_LOOP, NEEDLESS_COLLECT, EXPLICIT_COUNTER_LOOP, @@ -2012,7 +2012,7 @@ fn check_for_loop_over_options_or_results<'tcx>( let arg_snippet = snippet(cx, arg.span, ".."); let msg = "looping over `Option`s or `Result`s with an `if let` expression."; let hint = format!("try turn {} into an `Iterator` and use `flatten`: `{}.iter().flatten()`", arg_snippet, arg_snippet); - span_lint_and_help(cx, FOR_LOOPS_OVER_OPTIONS, expr.span, msg, None, &hint); + span_lint_and_help(cx, FOR_LOOPS_OVER_OPTIONS_OR_RESULTS, expr.span, msg, None, &hint); } } } diff --git a/tests/ui/for_loops_over_options.rs b/tests/ui/for_loops_over_options.rs deleted file mode 100644 index d8144864a21..00000000000 --- a/tests/ui/for_loops_over_options.rs +++ /dev/null @@ -1,31 +0,0 @@ -#![warn(clippy::for_loops_over_options)] - -fn main() { - let x = vec![Some(1), Some(2), Some(3)]; - for n in x { - if let Some(n) = n { - println!("{}", n); - } - } - - let y: Vec> = vec![]; - for n in y.clone() { - if let Ok(n) = n { - println!("{}", n); - } - } - - // This should not trigger the lint - for n in y.clone() { - if let Ok(n) = n { - println!("{}", n); - } else { - println!("Oops!"); - } - } - - // This should not trigger the lint - for n in vec![Some(1), Some(2), Some(3)].iter().flatten() { - println!("{}", n); - } -} diff --git a/tests/ui/for_loops_over_options_or_results.rs b/tests/ui/for_loops_over_options_or_results.rs new file mode 100644 index 00000000000..02e24b250f7 --- /dev/null +++ b/tests/ui/for_loops_over_options_or_results.rs @@ -0,0 +1,31 @@ +#![warn(clippy::for_loops_over_options_or_results)] + +fn main() { + let x = vec![Some(1), Some(2), Some(3)]; + for n in x { + if let Some(n) = n { + println!("{}", n); + } + } + + let y: Vec> = vec![]; + for n in y.clone() { + if let Ok(n) = n { + println!("{}", n); + } + } + + // This should not trigger the lint + for n in y.clone() { + if let Ok(n) = n { + println!("{}", n); + } else { + println!("Oops!"); + } + } + + // This should not trigger the lint + for n in vec![Some(1), Some(2), Some(3)].iter().flatten() { + println!("{}", n); + } +} -- cgit 1.4.1-3-g733a5 From b87e189694eebb5b83d758528032cf4d4db81472 Mon Sep 17 00:00:00 2001 From: nahuakang Date: Fri, 29 Jan 2021 01:38:34 +0100 Subject: Implement manual flatten lint --- CHANGELOG.md | 2 +- clippy_lints/src/lib.rs | 6 +- clippy_lints/src/loops.rs | 85 ++++++++++++++++++--------- clippy_lints/src/mut_mut.rs | 2 +- clippy_lints/src/needless_question_mark.rs | 26 +------- clippy_lints/src/ranges.rs | 2 +- clippy_lints/src/utils/higher.rs | 9 +-- clippy_lints/src/utils/internal_lints.rs | 16 +++-- clippy_lints/src/utils/mod.rs | 28 ++++++++- clippy_lints/src/vec.rs | 2 +- tests/ui/for_loops_over_options_or_results.rs | 31 ---------- tests/ui/manual_flatten.rs | 54 +++++++++++++++++ tests/ui/manual_flatten.stderr | 51 ++++++++++++++++ 13 files changed, 208 insertions(+), 106 deletions(-) delete mode 100644 tests/ui/for_loops_over_options_or_results.rs create mode 100644 tests/ui/manual_flatten.rs create mode 100644 tests/ui/manual_flatten.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index e40cd8174fc..aceabcbbdfc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1969,7 +1969,6 @@ Released 2018-09-13 [`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 -[`for_loops_over_options_or_results`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loops_over_options_or_results [`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 [`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect @@ -2040,6 +2039,7 @@ Released 2018-09-13 [`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn [`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map [`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map +[`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten [`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy [`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive [`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index dae6c93c7cb..cd0a95a4585 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -685,8 +685,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &loops::EXPLICIT_ITER_LOOP, &loops::FOR_KV_MAP, &loops::FOR_LOOPS_OVER_FALLIBLES, - &loops::FOR_LOOPS_OVER_OPTIONS_OR_RESULTS, &loops::ITER_NEXT_LOOP, + &loops::MANUAL_FLATTEN, &loops::MANUAL_MEMCPY, &loops::MUT_RANGE_BOUND, &loops::NEEDLESS_COLLECT, @@ -1489,8 +1489,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::EXPLICIT_COUNTER_LOOP), LintId::of(&loops::FOR_KV_MAP), LintId::of(&loops::FOR_LOOPS_OVER_FALLIBLES), - LintId::of(&loops::FOR_LOOPS_OVER_OPTIONS_OR_RESULTS), 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), @@ -1822,7 +1822,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&lifetimes::EXTRA_UNUSED_LIFETIMES), LintId::of(&lifetimes::NEEDLESS_LIFETIMES), LintId::of(&loops::EXPLICIT_COUNTER_LOOP), - LintId::of(&loops::FOR_LOOPS_OVER_OPTIONS_OR_RESULTS), + LintId::of(&loops::MANUAL_FLATTEN), LintId::of(&loops::MUT_RANGE_BOUND), LintId::of(&loops::SINGLE_ELEMENT_LOOP), LintId::of(&loops::WHILE_LET_LOOP), diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index e9047cce15f..db5aec82e90 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -5,10 +5,10 @@ use crate::utils::usage::{is_unused, mutated_variables}; use crate::utils::visitors::LocalUsedVisitor; use crate::utils::{ contains_name, get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, - indent_of, is_in_panic_handler, is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, - last_path_segment, match_trait_method, match_type, match_var, multispan_sugg, single_segment_path, snippet, - snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, - span_lint_and_then, sugg, SpanlessEq, + indent_of, is_in_panic_handler, is_integer_const, is_no_std_crate, is_ok_ctor, is_refutable, is_some_ctor, + is_type_diagnostic_item, last_path_segment, match_trait_method, match_type, match_var, multispan_sugg, + single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint, + span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, SpanlessEq, }; use if_chain::if_chain; use rustc_ast::ast; @@ -495,8 +495,8 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for iteration of `Option`s with - /// a single `if let Some()` expression inside. + /// **What it does:** Check for unnecessary `if let` usage in a for loop + /// where only the `Some` or `Ok` variant of the iterator element is used. /// /// **Why is this bad?** It is verbose and can be simplified /// by first calling the `flatten` method on the `Iterator`. @@ -516,23 +516,23 @@ declare_clippy_lint! { /// Use instead: /// ```rust /// let x = vec![Some(1), Some(2), Some(3)]; - /// for n in x.iter().flatten() { + /// for n in x.into_iter().flatten() { /// println!("{}", n); /// } /// ``` - pub FOR_LOOPS_OVER_OPTIONS_OR_RESULTS, + pub MANUAL_FLATTEN, complexity, "for loops over `Option`s or `Result`s with a single expression can be simplified" } declare_lint_pass!(Loops => [ MANUAL_MEMCPY, + MANUAL_FLATTEN, NEEDLESS_RANGE_LOOP, EXPLICIT_ITER_LOOP, EXPLICIT_INTO_ITER_LOOP, ITER_NEXT_LOOP, FOR_LOOPS_OVER_FALLIBLES, - FOR_LOOPS_OVER_OPTIONS_OR_RESULTS, WHILE_LET_LOOP, NEEDLESS_COLLECT, EXPLICIT_COUNTER_LOOP, @@ -549,14 +549,14 @@ declare_lint_pass!(Loops => [ impl<'tcx> LateLintPass<'tcx> for Loops { #[allow(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let Some((pat, arg, body)) = higher::for_loop(expr) { + if let Some((pat, arg, body, span)) = higher::for_loop(expr) { // we don't want to check expanded macros // this check is not at the top of the function // since higher::for_loop expressions are marked as expansions if body.span.from_expansion() { return; } - check_for_loop(cx, pat, arg, body, expr); + check_for_loop(cx, pat, arg, body, expr, span); } // we don't want to check expanded macros @@ -851,6 +851,7 @@ fn check_for_loop<'tcx>( arg: &'tcx Expr<'_>, body: &'tcx Expr<'_>, expr: &'tcx Expr<'_>, + span: Span, ) { let is_manual_memcpy_triggered = detect_manual_memcpy(cx, pat, arg, body, expr); if !is_manual_memcpy_triggered { @@ -862,7 +863,7 @@ fn check_for_loop<'tcx>( check_for_mut_range_bound(cx, arg, body); check_for_single_element_loop(cx, pat, arg, body, expr); detect_same_item_push(cx, pat, arg, body, expr); - check_for_loop_over_options_or_results(cx, pat, arg, body, expr); + check_manual_flatten(cx, pat, arg, body, span); } // this function assumes the given expression is a `for` loop. @@ -1986,33 +1987,61 @@ fn check_for_single_element_loop<'tcx>( } } -/// Check if a for loop loops over `Option`s or `Result`s and contains only -/// a `if let Some` or `if let Ok` expression. -fn check_for_loop_over_options_or_results<'tcx>( +/// Check for unnecessary `if let` usage in a for loop where only the `Some` or `Ok` variant of the +/// iterator element is used. +fn check_manual_flatten<'tcx>( cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx Expr<'_>, body: &'tcx Expr<'_>, - expr: &'tcx Expr<'_>, + span: Span, ) { if_chain! { + // Ensure the `if let` statement is the only expression in the for-loop if let ExprKind::Block(ref block, _) = body.kind; if block.stmts.is_empty(); if let Some(inner_expr) = block.expr; - if let ExprKind::Match(ref _match_expr, ref _match_arms, MatchSource::IfLetDesugar{ contains_else_clause }) = inner_expr.kind; - if !contains_else_clause; + if let ExprKind::Match( + ref match_expr, ref match_arms, MatchSource::IfLetDesugar{ contains_else_clause: false } + ) = inner_expr.kind; + // Ensure match_expr in `if let` statement is the same as the pat from the for-loop + if let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind; + if let ExprKind::Path(QPath::Resolved(None, match_expr_path)) = match_expr.kind; + if let Res::Local(match_expr_path_id) = match_expr_path.res; + if pat_hir_id == match_expr_path_id; + // Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result` + if let PatKind::TupleStruct(QPath::Resolved(None, path), _, _) = match_arms[0].pat.kind; + if is_some_ctor(cx, path.res) || is_ok_ctor(cx, path.res); + let if_let_type = if is_some_ctor(cx, path.res) { + "Some" + } else { + "Ok" + }; + // Determine if `arg` is `Iterator` or implicitly calls `into_iter` + let arg_ty = cx.typeck_results().expr_ty(arg); + if let Some(id) = get_trait_def_id(cx, &paths::ITERATOR); + if let is_iterator = implements_trait(cx, arg_ty, id, &[]); + then { - // println!("if_let_expr:\n{:?}", snippet(cx, if_let_expr.span, "..")); - // println!("pat is:\n {:?}", snippet(cx, pat.span, "..")); - // println!("arg is:\n {:?}", snippet(cx, arg.span, "..")); - // println!("body is:\n {:?}", snippet(cx, body.span, "..")); - // println!("arg kind is: {:?}", arg.kind); - // println!("expr is:\n {:?}", snippet(cx, expr.span, "..")); - // todo!(); + // Prepare the error message + let msg = format!("Unnecessary `if let` since only the `{}` variant of the iterator element is used.", if_let_type); + + // Prepare the help message let arg_snippet = snippet(cx, arg.span, ".."); - let msg = "looping over `Option`s or `Result`s with an `if let` expression."; - let hint = format!("try turn {} into an `Iterator` and use `flatten`: `{}.iter().flatten()`", arg_snippet, arg_snippet); - span_lint_and_help(cx, FOR_LOOPS_OVER_OPTIONS_OR_RESULTS, expr.span, msg, None, &hint); + let hint = if is_iterator { + format!("try: `{}.flatten()`", arg_snippet) + } else { + format!("try: `{}.into_iter().flatten()`", arg_snippet) + }; + + span_lint_and_help( + cx, + MANUAL_FLATTEN, + span, + &msg, + Some(arg.span), + &hint, + ); } } } diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index 2f3cdb894f0..d7239b328bb 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -52,7 +52,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> { return; } - if let Some((_, arg, body)) = higher::for_loop(expr) { + if let Some((_, arg, body, _)) = higher::for_loop(expr) { // A `for` loop lowers to: // ```rust // match ::std::iter::Iterator::next(&mut iter) { diff --git a/clippy_lints/src/needless_question_mark.rs b/clippy_lints/src/needless_question_mark.rs index 9e9b79ee1cf..fe8d4d07abc 100644 --- a/clippy_lints/src/needless_question_mark.rs +++ b/clippy_lints/src/needless_question_mark.rs @@ -1,8 +1,6 @@ use rustc_errors::Applicability; -use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::{Body, Expr, ExprKind, LangItem, MatchSource, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::DefIdTree; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::sym; @@ -160,7 +158,7 @@ fn is_some_or_ok_call<'a>( // Check outer expression matches CALL_IDENT(ARGUMENT) format if let ExprKind::Call(path, args) = &expr.kind; if let ExprKind::Path(QPath::Resolved(None, path)) = &path.kind; - if is_some_ctor(cx, path.res) || is_ok_ctor(cx, path.res); + if utils::is_some_ctor(cx, path.res) || utils::is_ok_ctor(cx, path.res); // Extract inner expression from ARGUMENT if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar) = &args[0].kind; @@ -208,25 +206,3 @@ fn is_some_or_ok_call<'a>( fn has_implicit_error_from(cx: &LateContext<'_>, entire_expr: &Expr<'_>, inner_result_expr: &Expr<'_>) -> bool { return cx.typeck_results().expr_ty(entire_expr) != cx.typeck_results().expr_ty(inner_result_expr); } - -fn is_ok_ctor(cx: &LateContext<'_>, res: Res) -> bool { - if let Some(ok_id) = cx.tcx.lang_items().result_ok_variant() { - if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res { - if let Some(variant_id) = cx.tcx.parent(id) { - return variant_id == ok_id; - } - } - } - false -} - -fn is_some_ctor(cx: &LateContext<'_>, res: Res) -> bool { - if let Some(some_id) = cx.tcx.lang_items().option_some_variant() { - if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res { - if let Some(variant_id) = cx.tcx.parent(id) { - return variant_id == some_id; - } - } - } - false -} diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 3e454eecd97..59503817c0f 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -442,7 +442,7 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) { let mut cur_expr = expr; while let Some(parent_expr) = get_parent_expr(cx, cur_expr) { match higher::for_loop(parent_expr) { - Some((_, args, _)) if args.hir_id == expr.hir_id => return true, + Some((_, args, _, _)) if args.hir_id == expr.hir_id => return true, _ => cur_expr = parent_expr, } } diff --git a/clippy_lints/src/utils/higher.rs b/clippy_lints/src/utils/higher.rs index 340d340d6d3..df7f0f95782 100644 --- a/clippy_lints/src/utils/higher.rs +++ b/clippy_lints/src/utils/higher.rs @@ -9,6 +9,7 @@ use rustc_ast::ast; use rustc_hir as hir; use rustc_hir::{BorrowKind, Expr, ExprKind, StmtKind, UnOp}; use rustc_lint::LateContext; +use rustc_span::source_map::Span; /// Converts a hir binary operator to the corresponding `ast` type. #[must_use] @@ -133,11 +134,11 @@ pub fn is_from_for_desugar(local: &hir::Local<'_>) -> bool { false } -/// Recover the essential nodes of a desugared for loop: -/// `for pat in arg { body }` becomes `(pat, arg, body)`. +/// Recover 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)`. pub fn for_loop<'tcx>( expr: &'tcx hir::Expr<'tcx>, -) -> Option<(&hir::Pat<'_>, &'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>)> { +) -> Option<(&hir::Pat<'_>, &'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>, Span)> { if_chain! { if let hir::ExprKind::Match(ref iterexpr, ref arms, hir::MatchSource::ForLoopDesugar) = expr.kind; if let hir::ExprKind::Call(_, ref iterargs) = iterexpr.kind; @@ -148,7 +149,7 @@ pub fn for_loop<'tcx>( if let hir::StmtKind::Local(ref local) = let_stmt.kind; if let hir::StmtKind::Expr(ref expr) = body.kind; then { - return Some((&*local.pat, &iterargs[0], expr)); + return Some((&*local.pat, &iterargs[0], expr, arms[0].span)); } } None diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 822863ca3e2..b3eae933062 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -841,15 +841,13 @@ pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { // implementations of native types. Check lang items. let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect(); let lang_items = cx.tcx.lang_items(); - for lang_item in lang_items.items() { - if let Some(def_id) = lang_item { - let lang_item_path = cx.get_def_path(*def_id); - if path_syms.starts_with(&lang_item_path) { - if let [item] = &path_syms[lang_item_path.len()..] { - for child in cx.tcx.item_children(*def_id) { - if child.ident.name == *item { - return true; - } + for item_def_id in lang_items.items().iter().flatten() { + let lang_item_path = cx.get_def_path(*item_def_id); + if path_syms.starts_with(&lang_item_path) { + if let [item] = &path_syms[lang_item_path.len()..] { + for child in cx.tcx.item_children(*item_def_id) { + if child.ident.name == *item { + return true; } } } diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index d0db3a67533..3390c71dd8e 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -37,7 +37,7 @@ use rustc_ast::ast::{self, Attribute, LitKind}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::Node; @@ -50,7 +50,7 @@ use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::exports::Export; use rustc_middle::hir::map::Map; use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; -use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, layout::IntegerExt, DefIdTree, Ty, TyCtxt, TypeFoldable}; use rustc_semver::RustcVersion; use rustc_session::Session; use rustc_span::hygiene::{ExpnKind, MacroKind}; @@ -1700,6 +1700,30 @@ pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { } } +/// Check if the resolution of a given path is an `Ok` variant of `Result`. +pub fn is_ok_ctor(cx: &LateContext<'_>, res: Res) -> bool { + if let Some(ok_id) = cx.tcx.lang_items().result_ok_variant() { + if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res { + if let Some(variant_id) = cx.tcx.parent(id) { + return variant_id == ok_id; + } + } + } + false +} + +/// Check if the resolution of a given path is a `Some` variant of `Option`. +pub fn is_some_ctor(cx: &LateContext<'_>, res: Res) -> bool { + if let Some(some_id) = cx.tcx.lang_items().option_some_variant() { + if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res { + if let Some(variant_id) = cx.tcx.parent(id) { + return variant_id == some_id; + } + } + } + false +} + #[cfg(test)] mod test { use super::{reindent_multiline, without_block_comments}; diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index 149cceb39dd..c132e4de4f6 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -55,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { // search for `for _ in vec![…]` if_chain! { - if let Some((_, arg, _)) = higher::for_loop(expr); + if let Some((_, arg, _, _)) = higher::for_loop(expr); if let Some(vec_args) = higher::vec_macro(cx, arg); if is_copy(cx, vec_type(cx.typeck_results().expr_ty_adjusted(arg))); then { diff --git a/tests/ui/for_loops_over_options_or_results.rs b/tests/ui/for_loops_over_options_or_results.rs deleted file mode 100644 index 02e24b250f7..00000000000 --- a/tests/ui/for_loops_over_options_or_results.rs +++ /dev/null @@ -1,31 +0,0 @@ -#![warn(clippy::for_loops_over_options_or_results)] - -fn main() { - let x = vec![Some(1), Some(2), Some(3)]; - for n in x { - if let Some(n) = n { - println!("{}", n); - } - } - - let y: Vec> = vec![]; - for n in y.clone() { - if let Ok(n) = n { - println!("{}", n); - } - } - - // This should not trigger the lint - for n in y.clone() { - if let Ok(n) = n { - println!("{}", n); - } else { - println!("Oops!"); - } - } - - // This should not trigger the lint - for n in vec![Some(1), Some(2), Some(3)].iter().flatten() { - println!("{}", n); - } -} diff --git a/tests/ui/manual_flatten.rs b/tests/ui/manual_flatten.rs new file mode 100644 index 00000000000..f183ceecdd8 --- /dev/null +++ b/tests/ui/manual_flatten.rs @@ -0,0 +1,54 @@ +#![warn(clippy::manual_flatten)] + +fn main() { + let x = vec![Some(1), Some(2), Some(3)]; + for n in x { + if let Some(n) = n { + println!("{}", n); + } + } + + let y: Vec> = vec![]; + for n in y.clone() { + if let Ok(n) = n { + println!("{}", n); + } + } + + let z = vec![Some(1), Some(2), Some(3)]; + let z = z.iter(); + for n in z { + if let Some(n) = n { + println!("{}", n); + } + } + + // Using the `None` variant should not trigger the lint + let z = vec![Some(1), Some(2), Some(3)]; + for n in z { + if n.is_none() { + println!("Nada."); + } + } + + // Using the `Err` variant should not trigger the lint + for n in y.clone() { + if let Err(e) = n { + println!("Oops: {}!", e); + } + } + + // Having an else clause should not trigger the lint + for n in y.clone() { + if let Ok(n) = n { + println!("{}", n); + } else { + println!("Oops!"); + } + } + + // Using manual flatten should not trigger the lint + for n in vec![Some(1), Some(2), Some(3)].iter().flatten() { + println!("{}", n); + } +} diff --git a/tests/ui/manual_flatten.stderr b/tests/ui/manual_flatten.stderr new file mode 100644 index 00000000000..cf99a2d9ab1 --- /dev/null +++ b/tests/ui/manual_flatten.stderr @@ -0,0 +1,51 @@ +error: Unnecessary `if let` since only the `Some` variant of the iterator element is used. + --> $DIR/manual_flatten.rs:5:5 + | +LL | / for n in x { +LL | | if let Some(n) = n { +LL | | println!("{}", n); +LL | | } +LL | | } + | |_____^ + | + = note: `-D clippy::manual-flatten` implied by `-D warnings` +help: try: `x.into_iter().flatten()` + --> $DIR/manual_flatten.rs:5:14 + | +LL | for n in x { + | ^ + +error: Unnecessary `if let` since only the `Ok` variant of the iterator element is used. + --> $DIR/manual_flatten.rs:12:5 + | +LL | / for n in y.clone() { +LL | | if let Ok(n) = n { +LL | | println!("{}", n); +LL | | } +LL | | } + | |_____^ + | +help: try: `y.clone().into_iter().flatten()` + --> $DIR/manual_flatten.rs:12:14 + | +LL | for n in y.clone() { + | ^^^^^^^^^ + +error: Unnecessary `if let` since only the `Some` variant of the iterator element is used. + --> $DIR/manual_flatten.rs:20:5 + | +LL | / for n in z { +LL | | if let Some(n) = n { +LL | | println!("{}", n); +LL | | } +LL | | } + | |_____^ + | +help: try: `z.flatten()` + --> $DIR/manual_flatten.rs:20:14 + | +LL | for n in z { + | ^ + +error: aborting due to 3 previous errors + -- cgit 1.4.1-3-g733a5 From 7825bf36d8dfee0099a23eb74451783da3ce261f Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 31 Jan 2021 21:43:35 +0100 Subject: Fix suggestions that need parens --- clippy_lints/src/methods/mod.rs | 9 +++++++-- tests/ui/from_iter_instead_of_collect.rs | 8 +++++++- tests/ui/from_iter_instead_of_collect.stderr | 14 +++++++++++++- 3 files changed, 27 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 7a459a440ca..f53b2f67d1d 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -4094,14 +4094,19 @@ fn lint_from_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr< if implements_trait(cx, ty, from_iter_id, &[]) && implements_trait(cx, arg_ty, iter_id, &[]); then { // `expr` implements `FromIterator` trait - let iter_expr = snippet(cx, args[0].span, ".."); + let iter_expr = sugg::Sugg::hir(cx, &args[0], "..").maybe_par(); + let sugg = if higher::range(&args[0]).is_some() { + format!("{}.collect::<{}>()", iter_expr, ty) + } else { + format!("{}.collect()", iter_expr) + }; span_lint_and_sugg( cx, FROM_ITER_INSTEAD_OF_COLLECT, expr.span, "usage of `FromIterator::from_iter`", "use `.collect()` instead of `::from_iter()`", - format!("{}.collect()", iter_expr), + sugg, Applicability::MaybeIncorrect, ); } diff --git a/tests/ui/from_iter_instead_of_collect.rs b/tests/ui/from_iter_instead_of_collect.rs index 045eb3133d3..6c81366c4df 100644 --- a/tests/ui/from_iter_instead_of_collect.rs +++ b/tests/ui/from_iter_instead_of_collect.rs @@ -1,6 +1,6 @@ #![warn(clippy::from_iter_instead_of_collect)] -use std::collections::HashMap; +use std::collections::{HashMap, VecDeque}; use std::iter::FromIterator; fn main() { @@ -10,4 +10,10 @@ fn main() { HashMap::::from_iter(vec![5, 5, 5, 5].iter().enumerate()); Vec::from_iter(vec![42u32]); + + let a = vec![0, 1, 2]; + assert_eq!(a, Vec::from_iter(0..3)); + + let mut b = VecDeque::from_iter(0..3); + b.push_back(4); } diff --git a/tests/ui/from_iter_instead_of_collect.stderr b/tests/ui/from_iter_instead_of_collect.stderr index 46bdc2f4e19..e2161dd3b57 100644 --- a/tests/ui/from_iter_instead_of_collect.stderr +++ b/tests/ui/from_iter_instead_of_collect.stderr @@ -12,5 +12,17 @@ error: usage of `FromIterator::from_iter` LL | HashMap::::from_iter(vec![5, 5, 5, 5].iter().enumerate()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `vec![5, 5, 5, 5].iter().enumerate().collect()` -error: aborting due to 2 previous errors +error: usage of `FromIterator::from_iter` + --> $DIR/from_iter_instead_of_collect.rs:15:19 + | +LL | assert_eq!(a, Vec::from_iter(0..3)); + | ^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` + +error: usage of `FromIterator::from_iter` + --> $DIR/from_iter_instead_of_collect.rs:17:17 + | +LL | let mut b = VecDeque::from_iter(0..3); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` + +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From 0fb09d6b218b005b90fe5b3f3d5346a2b707e19e Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Mon, 1 Feb 2021 18:38:43 -0800 Subject: exhaustive_structs: don't trigger for structs with private fields --- clippy_lints/src/exhaustive_items.rs | 10 +++++++--- tests/ui/exhaustive_items.fixed | 25 +++++++++++++++++-------- tests/ui/exhaustive_items.rs | 23 ++++++++++++++++------- tests/ui/exhaustive_items.stderr | 4 ++-- 4 files changed, 42 insertions(+), 20 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/exhaustive_items.rs b/clippy_lints/src/exhaustive_items.rs index 32b1299efce..e3988d0038c 100644 --- a/clippy_lints/src/exhaustive_items.rs +++ b/clippy_lints/src/exhaustive_items.rs @@ -75,10 +75,14 @@ impl LateLintPass<'_> for ExhaustiveItems { if cx.access_levels.is_exported(item.hir_id); if !item.attrs.iter().any(|a| a.has_name(sym::non_exhaustive)); then { - let (lint, msg) = if let ItemKind::Enum(..) = item.kind { - (EXHAUSTIVE_ENUMS, "exported enums should not be exhaustive") - } else { + let (lint, msg) = if let ItemKind::Struct(ref v, ..) = item.kind { + if v.fields().iter().any(|f| !f.vis.node.is_pub()) { + // skip structs with private fields + return; + } (EXHAUSTIVE_STRUCTS, "exported structs should not be exhaustive") + } else { + (EXHAUSTIVE_ENUMS, "exported enums should not be exhaustive") }; let suggestion_span = item.span.shrink_to_lo(); let indent = " ".repeat(indent_of(cx, item.span).unwrap_or(0)); diff --git a/tests/ui/exhaustive_items.fixed b/tests/ui/exhaustive_items.fixed index 8174a0175ab..383b3d85a66 100644 --- a/tests/ui/exhaustive_items.fixed +++ b/tests/ui/exhaustive_items.fixed @@ -56,27 +56,36 @@ pub mod enums { pub mod structs { #[non_exhaustive] pub struct Exhaustive { - foo: u8, - bar: String, + pub foo: u8, + pub bar: String, } // no warning, already non_exhaustive #[non_exhaustive] pub struct NonExhaustive { - foo: u8, - bar: String, + pub foo: u8, + pub bar: String, + } + + // no warning, private fields + pub struct ExhaustivePrivateFieldTuple(u8); + + // no warning, private fields + pub struct ExhaustivePrivateField { + pub foo: u8, + bar: String } // no warning, private struct ExhaustivePrivate { - foo: u8, - bar: String, + pub foo: u8, + pub bar: String, } // no warning, private #[non_exhaustive] struct NonExhaustivePrivate { - foo: u8, - bar: String, + pub foo: u8, + pub bar: String, } } diff --git a/tests/ui/exhaustive_items.rs b/tests/ui/exhaustive_items.rs index b476f09f8a0..6f59dbf2da5 100644 --- a/tests/ui/exhaustive_items.rs +++ b/tests/ui/exhaustive_items.rs @@ -53,27 +53,36 @@ pub mod enums { pub mod structs { pub struct Exhaustive { - foo: u8, - bar: String, + pub foo: u8, + pub bar: String, } // no warning, already non_exhaustive #[non_exhaustive] pub struct NonExhaustive { - foo: u8, + pub foo: u8, + pub bar: String, + } + + // no warning, private fields + pub struct ExhaustivePrivateFieldTuple(u8); + + // no warning, private fields + pub struct ExhaustivePrivateField { + pub foo: u8, bar: String, } // no warning, private struct ExhaustivePrivate { - foo: u8, - bar: String, + pub foo: u8, + pub bar: String, } // no warning, private #[non_exhaustive] struct NonExhaustivePrivate { - foo: u8, - bar: String, + pub foo: u8, + pub bar: String, } } diff --git a/tests/ui/exhaustive_items.stderr b/tests/ui/exhaustive_items.stderr index 7369fe75a4f..8fbab535a9b 100644 --- a/tests/ui/exhaustive_items.stderr +++ b/tests/ui/exhaustive_items.stderr @@ -41,8 +41,8 @@ error: exported structs should not be exhaustive --> $DIR/exhaustive_items.rs:55:5 | LL | / pub struct Exhaustive { -LL | | foo: u8, -LL | | bar: String, +LL | | pub foo: u8, +LL | | pub bar: String, LL | | } | |_____^ | -- cgit 1.4.1-3-g733a5 From 4a13c8c22e17e74e5ae4ece07ae37ad32903dcae Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Tue, 2 Feb 2021 08:59:23 +0100 Subject: Fix test formatting --- tests/ui/exhaustive_items.fixed | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/ui/exhaustive_items.fixed b/tests/ui/exhaustive_items.fixed index 383b3d85a66..c209f5b4b72 100644 --- a/tests/ui/exhaustive_items.fixed +++ b/tests/ui/exhaustive_items.fixed @@ -73,7 +73,7 @@ pub mod structs { // no warning, private fields pub struct ExhaustivePrivateField { pub foo: u8, - bar: String + bar: String, } // no warning, private -- cgit 1.4.1-3-g733a5 From bde667af7e7d512978daff3bc2b540bb913bd6a1 Mon Sep 17 00:00:00 2001 From: Caden Haustein Date: Wed, 30 Dec 2020 16:37:59 -0600 Subject: Add missing_panics_doc lint --- CHANGELOG.md | 1 + clippy_dev/src/bless.rs | 3 + clippy_dev/src/lib.rs | 13 +++ clippy_dev/src/ra_setup.rs | 3 + clippy_dev/src/serve.rs | 3 + clippy_lints/src/doc.rs | 125 +++++++++++++++++++++++- clippy_lints/src/lib.rs | 2 + clippy_lints/src/utils/diagnostics.rs | 2 +- mini-macro/src/lib.rs | 3 + tests/ui/doc_panics.rs | 95 ++++++++++++++++++ tests/ui/doc_panics.stderr | 67 +++++++++++++ tests/ui/should_impl_trait/corner_cases.rs | 3 +- tests/ui/should_impl_trait/method_list_1.rs | 3 +- tests/ui/should_impl_trait/method_list_1.stderr | 28 +++--- tests/ui/should_impl_trait/method_list_2.rs | 3 +- tests/ui/should_impl_trait/method_list_2.stderr | 30 +++--- 16 files changed, 346 insertions(+), 38 deletions(-) create mode 100644 tests/ui/doc_panics.rs create mode 100644 tests/ui/doc_panics.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index dadb6832d1f..c1032204a22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2079,6 +2079,7 @@ Released 2018-09-13 [`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items [`missing_errors_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_errors_doc [`missing_inline_in_public_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_inline_in_public_items +[`missing_panics_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_panics_doc [`missing_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc [`mistyped_literal_suffixes`]: https://rust-lang.github.io/rust-clippy/master/index.html#mistyped_literal_suffixes [`mixed_case_hex_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_case_hex_literals diff --git a/clippy_dev/src/bless.rs b/clippy_dev/src/bless.rs index b877806946c..2a869e9d449 100644 --- a/clippy_dev/src/bless.rs +++ b/clippy_dev/src/bless.rs @@ -24,6 +24,9 @@ static CLIPPY_BUILD_TIME: SyncLazy> = SyncLazy::ne fs::metadata(path).ok()?.modified().ok() }); +/// # Panics +/// +/// Panics if the path to a test file is broken pub fn bless(ignore_timestamp: bool) { let test_suite_dirs = [ clippy_project_root().join("tests").join("ui"), diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index 24d70d433f3..01d1fc9211a 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -236,6 +236,10 @@ pub struct FileChange { /// `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, @@ -283,6 +287,10 @@ where /// .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, @@ -329,6 +337,11 @@ where } /// Returns the path to the Clippy project directory +/// +/// # Panics +/// +/// Panics if the current directory could not be retrieved, there was an error reading any of the +/// Cargo.toml files or ancestor directory is the clippy root directory #[must_use] pub fn clippy_project_root() -> PathBuf { let current_dir = std::env::current_dir().unwrap(); diff --git a/clippy_dev/src/ra_setup.rs b/clippy_dev/src/ra_setup.rs index a8a6a2cb1bd..a3c329b578b 100644 --- a/clippy_dev/src/ra_setup.rs +++ b/clippy_dev/src/ra_setup.rs @@ -8,6 +8,9 @@ use std::path::{Path, PathBuf}; // This allows rust analyzer to analyze rustc internals and show proper information inside clippy // code. See https://github.com/rust-analyzer/rust-analyzer/issues/3517 and https://github.com/rust-lang/rust-clippy/issues/5514 for details +/// # Panics +/// +/// Panics if `rustc_path` does not lead to a rustc repo or the files could not be read pub fn run(rustc_path: Option<&str>) { // we can unwrap here because the arg is required by clap let rustc_path = PathBuf::from(rustc_path.unwrap()); diff --git a/clippy_dev/src/serve.rs b/clippy_dev/src/serve.rs index a46c0e4d3f0..faa94859601 100644 --- a/clippy_dev/src/serve.rs +++ b/clippy_dev/src/serve.rs @@ -4,6 +4,9 @@ use std::process::Command; use std::thread; use std::time::{Duration, SystemTime}; +/// # Panics +/// +/// Panics if the python commands could not be spawned pub fn run(port: u16, lint: Option<&str>) -> ! { let mut url = Some(match lint { None => format!("http://localhost:{}", port), diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 3a754f49917..b3e3635c1c1 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -1,4 +1,7 @@ -use crate::utils::{implements_trait, is_entrypoint_fn, is_type_diagnostic_item, return_ty, span_lint}; +use crate::utils::{ + implements_trait, is_entrypoint_fn, is_type_diagnostic_item, match_panic_def_id, method_chain_args, return_ty, + span_lint, span_lint_and_note, +}; use if_chain::if_chain; use itertools::Itertools; use rustc_ast::ast::{Async, AttrKind, Attribute, FnRetTy, ItemKind}; @@ -8,7 +11,10 @@ use rustc_data_structures::sync::Lrc; use rustc_errors::emitter::EmitterWriter; use rustc_errors::Handler; use rustc_hir as hir; +use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_parse::maybe_new_parser_from_source_str; @@ -122,6 +128,37 @@ declare_clippy_lint! { "`pub fn` returns `Result` without `# Errors` in doc comment" } +declare_clippy_lint! { + /// **What it does:** Checks the doc comments of publicly visible functions that + /// may panic and warns if there is no `# Panics` section. + /// + /// **Why is this bad?** Documenting the scenarios in which panicking occurs + /// can help callers who do not want to panic to avoid those situations. + /// + /// **Known problems:** None. + /// + /// **Examples:** + /// + /// Since the following function may panic it has a `# Panics` section in + /// its doc comment: + /// + /// ```rust + /// /// # Panics + /// /// + /// /// Will panic if y is 0 + /// pub fn divide_by(x: i32, y: i32) -> i32 { + /// if y == 0 { + /// panic!("Cannot divide by 0") + /// } else { + /// x / y + /// } + /// } + /// ``` + pub MISSING_PANICS_DOC, + pedantic, + "`pub fn` may panic without `# Panics` in doc comment" +} + declare_clippy_lint! { /// **What it does:** Checks for `fn main() { .. }` in doctests /// @@ -166,7 +203,9 @@ impl DocMarkdown { } } -impl_lint_pass!(DocMarkdown => [DOC_MARKDOWN, MISSING_SAFETY_DOC, MISSING_ERRORS_DOC, NEEDLESS_DOCTEST_MAIN]); +impl_lint_pass!(DocMarkdown => + [DOC_MARKDOWN, MISSING_SAFETY_DOC, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, NEEDLESS_DOCTEST_MAIN] +); impl<'tcx> LateLintPass<'tcx> for DocMarkdown { fn check_crate(&mut self, cx: &LateContext<'tcx>, krate: &'tcx hir::Crate<'_>) { @@ -180,7 +219,15 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { if !(is_entrypoint_fn(cx, cx.tcx.hir().local_def_id(item.hir_id).to_def_id()) || in_external_macro(cx.tcx.sess, item.span)) { - lint_for_missing_headers(cx, item.hir_id, item.span, sig, headers, Some(body_id)); + let body = cx.tcx.hir().body(body_id); + let impl_item_def_id = cx.tcx.hir().local_def_id(item.hir_id); + let mut fpu = FindPanicUnwrap { + cx, + typeck_results: cx.tcx.typeck(impl_item_def_id), + panic_span: None, + }; + fpu.visit_expr(&body.value); + lint_for_missing_headers(cx, item.hir_id, item.span, sig, headers, Some(body_id), fpu.panic_span); } }, hir::ItemKind::Impl(ref impl_) => { @@ -200,7 +247,7 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { let headers = check_attrs(cx, &self.valid_idents, &item.attrs); if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind { if !in_external_macro(cx.tcx.sess, item.span) { - lint_for_missing_headers(cx, item.hir_id, item.span, sig, headers, None); + lint_for_missing_headers(cx, item.hir_id, item.span, sig, headers, None, None); } } } @@ -211,7 +258,15 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { return; } if let hir::ImplItemKind::Fn(ref sig, body_id) = item.kind { - lint_for_missing_headers(cx, item.hir_id, item.span, sig, headers, Some(body_id)); + let body = cx.tcx.hir().body(body_id); + let impl_item_def_id = cx.tcx.hir().local_def_id(item.hir_id); + let mut fpu = FindPanicUnwrap { + cx, + typeck_results: cx.tcx.typeck(impl_item_def_id), + panic_span: None, + }; + fpu.visit_expr(&body.value); + lint_for_missing_headers(cx, item.hir_id, item.span, sig, headers, Some(body_id), fpu.panic_span); } } } @@ -223,6 +278,7 @@ fn lint_for_missing_headers<'tcx>( sig: &hir::FnSig<'_>, headers: DocHeaders, body_id: Option, + panic_span: Option, ) { if !cx.access_levels.is_exported(hir_id) { return; // Private functions do not require doc comments @@ -235,6 +291,16 @@ fn lint_for_missing_headers<'tcx>( "unsafe function's docs miss `# Safety` section", ); } + if !headers.panics && panic_span.is_some() { + span_lint_and_note( + cx, + MISSING_PANICS_DOC, + span, + "docs for function which may panic missing `# Panics` section", + panic_span, + "first possible panic found here", + ); + } if !headers.errors { if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::result_type) { span_lint( @@ -321,6 +387,7 @@ pub fn strip_doc_comment_decoration(doc: &str, comment_kind: CommentKind, span: struct DocHeaders { safety: bool, errors: bool, + panics: bool, } fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &'a [Attribute]) -> DocHeaders { @@ -338,6 +405,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs return DocHeaders { safety: true, errors: true, + panics: true, }; } } @@ -353,6 +421,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs return DocHeaders { safety: false, errors: false, + panics: false, }; } @@ -394,6 +463,7 @@ fn check_doc<'a, Events: Iterator, Range, Range o, Err(e) => e - 1, @@ -607,3 +678,47 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span) { ); } } + +struct FindPanicUnwrap<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + panic_span: Option, + typeck_results: &'tcx ty::TypeckResults<'tcx>, +} + +impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + if self.panic_span.is_some() { + return; + } + + // check for `begin_panic` + if_chain! { + if let ExprKind::Call(ref func_expr, _) = expr.kind; + if let ExprKind::Path(QPath::Resolved(_, ref path)) = func_expr.kind; + if let Some(path_def_id) = path.res.opt_def_id(); + if match_panic_def_id(self.cx, path_def_id); + then { + self.panic_span = Some(expr.span); + } + } + + // check for `unwrap` + if let Some(arglists) = method_chain_args(expr, &["unwrap"]) { + let reciever_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs(); + if is_type_diagnostic_item(self.cx, reciever_ty, sym::option_type) + || is_type_diagnostic_item(self.cx, reciever_ty, sym::result_type) + { + self.panic_span = Some(expr.span); + } + } + + // and check sub-expressions + intravisit::walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 54007c29c6c..5a40c00bd67 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -592,6 +592,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &disallowed_method::DISALLOWED_METHOD, &doc::DOC_MARKDOWN, &doc::MISSING_ERRORS_DOC, + &doc::MISSING_PANICS_DOC, &doc::MISSING_SAFETY_DOC, &doc::NEEDLESS_DOCTEST_MAIN, &double_comparison::DOUBLE_COMPARISONS, @@ -1317,6 +1318,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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(&enum_variants::PUB_ENUM_VARIANT_NAMES), diff --git a/clippy_lints/src/utils/diagnostics.rs b/clippy_lints/src/utils/diagnostics.rs index 6caa04f651f..269be217c2d 100644 --- a/clippy_lints/src/utils/diagnostics.rs +++ b/clippy_lints/src/utils/diagnostics.rs @@ -110,7 +110,7 @@ pub fn span_lint_and_help<'a, T: LintContext>( pub fn span_lint_and_note<'a, T: LintContext>( cx: &'a T, lint: &'static Lint, - span: Span, + span: impl Into, msg: &str, note_span: Option, note: &str, diff --git a/mini-macro/src/lib.rs b/mini-macro/src/lib.rs index ba946563ec5..2b793589049 100644 --- a/mini-macro/src/lib.rs +++ b/mini-macro/src/lib.rs @@ -7,6 +7,9 @@ extern crate proc_macro; use proc_macro::{quote, TokenStream}; #[proc_macro_derive(ClippyMiniMacroTest)] +/// # Panics +/// +/// Panics if the macro derivation fails pub fn mini_macro(_: TokenStream) -> TokenStream { quote!( #[allow(unused)] diff --git a/tests/ui/doc_panics.rs b/tests/ui/doc_panics.rs new file mode 100644 index 00000000000..7ef932f367b --- /dev/null +++ b/tests/ui/doc_panics.rs @@ -0,0 +1,95 @@ +#![warn(clippy::missing_panics_doc)] +#![allow(clippy::option_map_unit_fn)] + +fn main() {} + +/// This needs to be documented +pub fn unwrap() { + let result = Err("Hi"); + result.unwrap() +} + +/// This needs to be documented +pub fn panic() { + panic!("This function panics") +} + +/// This needs to be documented +pub fn todo() { + todo!() +} + +/// This needs to be documented +pub fn inner_body(opt: Option) { + opt.map(|x| { + if x == 10 { + panic!() + } + }); +} + +/// This is documented +/// +/// # Panics +/// +/// Panics if `result` if an error +pub fn unwrap_documented() { + let result = Err("Hi"); + result.unwrap() +} + +/// This is documented +/// +/// # Panics +/// +/// Panics just because +pub fn panic_documented() { + panic!("This function panics") +} + +/// This is documented +/// +/// # Panics +/// +/// Panics if `opt` is Just(10) +pub fn inner_body_documented(opt: Option) { + opt.map(|x| { + if x == 10 { + panic!() + } + }); +} + +/// This is documented +/// +/// # Panics +/// +/// We still need to do this part +pub fn todo_documented() { + todo!() +} + +/// This is okay because it is private +fn unwrap_private() { + let result = Err("Hi"); + result.unwrap() +} + +/// This is okay because it is private +fn panic_private() { + panic!("This function panics") +} + +/// This is okay because it is private +fn todo_private() { + todo!() +} + +/// This is okay because it is private +fn inner_body_private(opt: Option) { + opt.map(|x| { + if x == 10 { + panic!() + } + }); +} diff --git a/tests/ui/doc_panics.stderr b/tests/ui/doc_panics.stderr new file mode 100644 index 00000000000..c0c4e9e4fa7 --- /dev/null +++ b/tests/ui/doc_panics.stderr @@ -0,0 +1,67 @@ +error: docs for function which may panic missing `# Panics` section + --> $DIR/doc_panics.rs:7:1 + | +LL | / pub fn unwrap() { +LL | | let result = Err("Hi"); +LL | | result.unwrap() +LL | | } + | |_^ + | + = note: `-D clippy::missing-panics-doc` implied by `-D warnings` +note: first possible panic found here + --> $DIR/doc_panics.rs:9:5 + | +LL | result.unwrap() + | ^^^^^^^^^^^^^^^ + +error: docs for function which may panic missing `# Panics` section + --> $DIR/doc_panics.rs:13:1 + | +LL | / pub fn panic() { +LL | | panic!("This function panics") +LL | | } + | |_^ + | +note: first possible panic found here + --> $DIR/doc_panics.rs:14:5 + | +LL | panic!("This function panics") + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: docs for function which may panic missing `# Panics` section + --> $DIR/doc_panics.rs:18:1 + | +LL | / pub fn todo() { +LL | | todo!() +LL | | } + | |_^ + | +note: first possible panic found here + --> $DIR/doc_panics.rs:19:5 + | +LL | todo!() + | ^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: docs for function which may panic missing `# Panics` section + --> $DIR/doc_panics.rs:23:1 + | +LL | / pub fn inner_body(opt: Option) { +LL | | opt.map(|x| { +LL | | if x == 10 { +LL | | panic!() +LL | | } +LL | | }); +LL | | } + | |_^ + | +note: first possible panic found here + --> $DIR/doc_panics.rs:26:13 + | +LL | panic!() + | ^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 4 previous errors + diff --git a/tests/ui/should_impl_trait/corner_cases.rs b/tests/ui/should_impl_trait/corner_cases.rs index 6c5ffe6aba8..a7f8f54f2be 100644 --- a/tests/ui/should_impl_trait/corner_cases.rs +++ b/tests/ui/should_impl_trait/corner_cases.rs @@ -8,7 +8,8 @@ clippy::unused_self, clippy::needless_lifetimes, clippy::missing_safety_doc, - clippy::wrong_self_convention + clippy::wrong_self_convention, + clippy::missing_panics_doc )] use std::ops::Mul; diff --git a/tests/ui/should_impl_trait/method_list_1.rs b/tests/ui/should_impl_trait/method_list_1.rs index f8d248fc98d..69a3390b03b 100644 --- a/tests/ui/should_impl_trait/method_list_1.rs +++ b/tests/ui/should_impl_trait/method_list_1.rs @@ -8,7 +8,8 @@ clippy::unused_self, clippy::needless_lifetimes, clippy::missing_safety_doc, - clippy::wrong_self_convention + clippy::wrong_self_convention, + clippy::missing_panics_doc )] use std::ops::Mul; diff --git a/tests/ui/should_impl_trait/method_list_1.stderr b/tests/ui/should_impl_trait/method_list_1.stderr index 2b7d4628c3f..86c63946516 100644 --- a/tests/ui/should_impl_trait/method_list_1.stderr +++ b/tests/ui/should_impl_trait/method_list_1.stderr @@ -1,5 +1,5 @@ error: method `add` can be confused for the standard trait method `std::ops::Add::add` - --> $DIR/method_list_1.rs:25:5 + --> $DIR/method_list_1.rs:26:5 | LL | / pub fn add(self, other: T) -> T { LL | | unimplemented!() @@ -10,7 +10,7 @@ LL | | } = help: consider implementing the trait `std::ops::Add` or choosing a less ambiguous method name error: method `as_mut` can be confused for the standard trait method `std::convert::AsMut::as_mut` - --> $DIR/method_list_1.rs:29:5 + --> $DIR/method_list_1.rs:30:5 | LL | / pub fn as_mut(&mut self) -> &mut T { LL | | unimplemented!() @@ -20,7 +20,7 @@ LL | | } = help: consider implementing the trait `std::convert::AsMut` or choosing a less ambiguous method name error: method `as_ref` can be confused for the standard trait method `std::convert::AsRef::as_ref` - --> $DIR/method_list_1.rs:33:5 + --> $DIR/method_list_1.rs:34:5 | LL | / pub fn as_ref(&self) -> &T { LL | | unimplemented!() @@ -30,7 +30,7 @@ LL | | } = help: consider implementing the trait `std::convert::AsRef` or choosing a less ambiguous method name error: method `bitand` can be confused for the standard trait method `std::ops::BitAnd::bitand` - --> $DIR/method_list_1.rs:37:5 + --> $DIR/method_list_1.rs:38:5 | LL | / pub fn bitand(self, rhs: T) -> T { LL | | unimplemented!() @@ -40,7 +40,7 @@ LL | | } = help: consider implementing the trait `std::ops::BitAnd` or choosing a less ambiguous method name error: method `bitor` can be confused for the standard trait method `std::ops::BitOr::bitor` - --> $DIR/method_list_1.rs:41:5 + --> $DIR/method_list_1.rs:42:5 | LL | / pub fn bitor(self, rhs: Self) -> Self { LL | | unimplemented!() @@ -50,7 +50,7 @@ LL | | } = help: consider implementing the trait `std::ops::BitOr` or choosing a less ambiguous method name error: method `bitxor` can be confused for the standard trait method `std::ops::BitXor::bitxor` - --> $DIR/method_list_1.rs:45:5 + --> $DIR/method_list_1.rs:46:5 | LL | / pub fn bitxor(self, rhs: Self) -> Self { LL | | unimplemented!() @@ -60,7 +60,7 @@ LL | | } = help: consider implementing the trait `std::ops::BitXor` or choosing a less ambiguous method name error: method `borrow` can be confused for the standard trait method `std::borrow::Borrow::borrow` - --> $DIR/method_list_1.rs:49:5 + --> $DIR/method_list_1.rs:50:5 | LL | / pub fn borrow(&self) -> &str { LL | | unimplemented!() @@ -70,7 +70,7 @@ LL | | } = help: consider implementing the trait `std::borrow::Borrow` or choosing a less ambiguous method name error: method `borrow_mut` can be confused for the standard trait method `std::borrow::BorrowMut::borrow_mut` - --> $DIR/method_list_1.rs:53:5 + --> $DIR/method_list_1.rs:54:5 | LL | / pub fn borrow_mut(&mut self) -> &mut str { LL | | unimplemented!() @@ -80,7 +80,7 @@ LL | | } = help: consider implementing the trait `std::borrow::BorrowMut` or choosing a less ambiguous method name error: method `clone` can be confused for the standard trait method `std::clone::Clone::clone` - --> $DIR/method_list_1.rs:57:5 + --> $DIR/method_list_1.rs:58:5 | LL | / pub fn clone(&self) -> Self { LL | | unimplemented!() @@ -90,7 +90,7 @@ LL | | } = help: consider implementing the trait `std::clone::Clone` or choosing a less ambiguous method name error: method `cmp` can be confused for the standard trait method `std::cmp::Ord::cmp` - --> $DIR/method_list_1.rs:61:5 + --> $DIR/method_list_1.rs:62:5 | LL | / pub fn cmp(&self, other: &Self) -> Self { LL | | unimplemented!() @@ -100,7 +100,7 @@ LL | | } = help: consider implementing the trait `std::cmp::Ord` or choosing a less ambiguous method name error: method `deref` can be confused for the standard trait method `std::ops::Deref::deref` - --> $DIR/method_list_1.rs:69:5 + --> $DIR/method_list_1.rs:70:5 | LL | / pub fn deref(&self) -> &Self { LL | | unimplemented!() @@ -110,7 +110,7 @@ LL | | } = help: consider implementing the trait `std::ops::Deref` or choosing a less ambiguous method name error: method `deref_mut` can be confused for the standard trait method `std::ops::DerefMut::deref_mut` - --> $DIR/method_list_1.rs:73:5 + --> $DIR/method_list_1.rs:74:5 | LL | / pub fn deref_mut(&mut self) -> &mut Self { LL | | unimplemented!() @@ -120,7 +120,7 @@ LL | | } = help: consider implementing the trait `std::ops::DerefMut` or choosing a less ambiguous method name error: method `div` can be confused for the standard trait method `std::ops::Div::div` - --> $DIR/method_list_1.rs:77:5 + --> $DIR/method_list_1.rs:78:5 | LL | / pub fn div(self, rhs: Self) -> Self { LL | | unimplemented!() @@ -130,7 +130,7 @@ LL | | } = help: consider implementing the trait `std::ops::Div` or choosing a less ambiguous method name error: method `drop` can be confused for the standard trait method `std::ops::Drop::drop` - --> $DIR/method_list_1.rs:81:5 + --> $DIR/method_list_1.rs:82:5 | LL | / pub fn drop(&mut self) { LL | | unimplemented!() diff --git a/tests/ui/should_impl_trait/method_list_2.rs b/tests/ui/should_impl_trait/method_list_2.rs index ed5e0d384bf..2cdc1a06fe6 100644 --- a/tests/ui/should_impl_trait/method_list_2.rs +++ b/tests/ui/should_impl_trait/method_list_2.rs @@ -8,7 +8,8 @@ clippy::unused_self, clippy::needless_lifetimes, clippy::missing_safety_doc, - clippy::wrong_self_convention + clippy::wrong_self_convention, + clippy::missing_panics_doc )] use std::ops::Mul; diff --git a/tests/ui/should_impl_trait/method_list_2.stderr b/tests/ui/should_impl_trait/method_list_2.stderr index b6fd4356956..0142e299108 100644 --- a/tests/ui/should_impl_trait/method_list_2.stderr +++ b/tests/ui/should_impl_trait/method_list_2.stderr @@ -1,5 +1,5 @@ error: method `eq` can be confused for the standard trait method `std::cmp::PartialEq::eq` - --> $DIR/method_list_2.rs:26:5 + --> $DIR/method_list_2.rs:27:5 | LL | / pub fn eq(&self, other: &Self) -> bool { LL | | unimplemented!() @@ -10,7 +10,7 @@ LL | | } = help: consider implementing the trait `std::cmp::PartialEq` or choosing a less ambiguous method name error: method `from_iter` can be confused for the standard trait method `std::iter::FromIterator::from_iter` - --> $DIR/method_list_2.rs:30:5 + --> $DIR/method_list_2.rs:31:5 | LL | / pub fn from_iter(iter: T) -> Self { LL | | unimplemented!() @@ -20,7 +20,7 @@ LL | | } = help: consider implementing the trait `std::iter::FromIterator` or choosing a less ambiguous method name error: method `from_str` can be confused for the standard trait method `std::str::FromStr::from_str` - --> $DIR/method_list_2.rs:34:5 + --> $DIR/method_list_2.rs:35:5 | LL | / pub fn from_str(s: &str) -> Result { LL | | unimplemented!() @@ -30,7 +30,7 @@ LL | | } = help: consider implementing the trait `std::str::FromStr` or choosing a less ambiguous method name error: method `hash` can be confused for the standard trait method `std::hash::Hash::hash` - --> $DIR/method_list_2.rs:38:5 + --> $DIR/method_list_2.rs:39:5 | LL | / pub fn hash(&self, state: &mut T) { LL | | unimplemented!() @@ -40,7 +40,7 @@ LL | | } = help: consider implementing the trait `std::hash::Hash` or choosing a less ambiguous method name error: method `index` can be confused for the standard trait method `std::ops::Index::index` - --> $DIR/method_list_2.rs:42:5 + --> $DIR/method_list_2.rs:43:5 | LL | / pub fn index(&self, index: usize) -> &Self { LL | | unimplemented!() @@ -50,7 +50,7 @@ LL | | } = help: consider implementing the trait `std::ops::Index` or choosing a less ambiguous method name error: method `index_mut` can be confused for the standard trait method `std::ops::IndexMut::index_mut` - --> $DIR/method_list_2.rs:46:5 + --> $DIR/method_list_2.rs:47:5 | LL | / pub fn index_mut(&mut self, index: usize) -> &mut Self { LL | | unimplemented!() @@ -60,7 +60,7 @@ LL | | } = help: consider implementing the trait `std::ops::IndexMut` or choosing a less ambiguous method name error: method `into_iter` can be confused for the standard trait method `std::iter::IntoIterator::into_iter` - --> $DIR/method_list_2.rs:50:5 + --> $DIR/method_list_2.rs:51:5 | LL | / pub fn into_iter(self) -> Self { LL | | unimplemented!() @@ -70,7 +70,7 @@ LL | | } = help: consider implementing the trait `std::iter::IntoIterator` or choosing a less ambiguous method name error: method `mul` can be confused for the standard trait method `std::ops::Mul::mul` - --> $DIR/method_list_2.rs:54:5 + --> $DIR/method_list_2.rs:55:5 | LL | / pub fn mul(self, rhs: Self) -> Self { LL | | unimplemented!() @@ -80,7 +80,7 @@ LL | | } = help: consider implementing the trait `std::ops::Mul` or choosing a less ambiguous method name error: method `neg` can be confused for the standard trait method `std::ops::Neg::neg` - --> $DIR/method_list_2.rs:58:5 + --> $DIR/method_list_2.rs:59:5 | LL | / pub fn neg(self) -> Self { LL | | unimplemented!() @@ -90,7 +90,7 @@ LL | | } = help: consider implementing the trait `std::ops::Neg` or choosing a less ambiguous method name error: method `next` can be confused for the standard trait method `std::iter::Iterator::next` - --> $DIR/method_list_2.rs:62:5 + --> $DIR/method_list_2.rs:63:5 | LL | / pub fn next(&mut self) -> Option { LL | | unimplemented!() @@ -100,7 +100,7 @@ LL | | } = help: consider implementing the trait `std::iter::Iterator` or choosing a less ambiguous method name error: method `not` can be confused for the standard trait method `std::ops::Not::not` - --> $DIR/method_list_2.rs:66:5 + --> $DIR/method_list_2.rs:67:5 | LL | / pub fn not(self) -> Self { LL | | unimplemented!() @@ -110,7 +110,7 @@ LL | | } = help: consider implementing the trait `std::ops::Not` or choosing a less ambiguous method name error: method `rem` can be confused for the standard trait method `std::ops::Rem::rem` - --> $DIR/method_list_2.rs:70:5 + --> $DIR/method_list_2.rs:71:5 | LL | / pub fn rem(self, rhs: Self) -> Self { LL | | unimplemented!() @@ -120,7 +120,7 @@ LL | | } = help: consider implementing the trait `std::ops::Rem` or choosing a less ambiguous method name error: method `shl` can be confused for the standard trait method `std::ops::Shl::shl` - --> $DIR/method_list_2.rs:74:5 + --> $DIR/method_list_2.rs:75:5 | LL | / pub fn shl(self, rhs: Self) -> Self { LL | | unimplemented!() @@ -130,7 +130,7 @@ LL | | } = help: consider implementing the trait `std::ops::Shl` or choosing a less ambiguous method name error: method `shr` can be confused for the standard trait method `std::ops::Shr::shr` - --> $DIR/method_list_2.rs:78:5 + --> $DIR/method_list_2.rs:79:5 | LL | / pub fn shr(self, rhs: Self) -> Self { LL | | unimplemented!() @@ -140,7 +140,7 @@ LL | | } = help: consider implementing the trait `std::ops::Shr` or choosing a less ambiguous method name error: method `sub` can be confused for the standard trait method `std::ops::Sub::sub` - --> $DIR/method_list_2.rs:82:5 + --> $DIR/method_list_2.rs:83:5 | LL | / pub fn sub(self, rhs: Self) -> Self { LL | | unimplemented!() -- cgit 1.4.1-3-g733a5 From e07cd5b6fe47b1e26f19a1bede7c2e4967cb46d7 Mon Sep 17 00:00:00 2001 From: nahuakang Date: Tue, 2 Feb 2021 19:04:20 +0100 Subject: Enhance manual flatten --- clippy_lints/src/loops.rs | 112 ++++++++++++++++++++++++++--------------- tests/ui/manual_flatten.rs | 10 ++++ tests/ui/manual_flatten.stderr | 47 ++++++++--------- 3 files changed, 101 insertions(+), 68 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index db5aec82e90..23dce283f28 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1996,52 +1996,82 @@ fn check_manual_flatten<'tcx>( body: &'tcx Expr<'_>, span: Span, ) { - if_chain! { - // Ensure the `if let` statement is the only expression in the for-loop - if let ExprKind::Block(ref block, _) = body.kind; - if block.stmts.is_empty(); - if let Some(inner_expr) = block.expr; - if let ExprKind::Match( - ref match_expr, ref match_arms, MatchSource::IfLetDesugar{ contains_else_clause: false } - ) = inner_expr.kind; - // Ensure match_expr in `if let` statement is the same as the pat from the for-loop - if let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind; - if let ExprKind::Path(QPath::Resolved(None, match_expr_path)) = match_expr.kind; - if let Res::Local(match_expr_path_id) = match_expr_path.res; - if pat_hir_id == match_expr_path_id; - // Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result` - if let PatKind::TupleStruct(QPath::Resolved(None, path), _, _) = match_arms[0].pat.kind; - if is_some_ctor(cx, path.res) || is_ok_ctor(cx, path.res); - let if_let_type = if is_some_ctor(cx, path.res) { - "Some" + if let ExprKind::Block(ref block, _) = body.kind { + // Ensure the `if let` statement is the only expression or statement in the for-loop + let inner_expr = if block.stmts.len() == 1 && block.expr.is_none() { + let match_stmt = &block.stmts[0]; + if let StmtKind::Semi(inner_expr) = match_stmt.kind { + Some(inner_expr) + } else { + None + } + } else if block.stmts.is_empty() { + block.expr } else { - "Ok" + None }; - // Determine if `arg` is `Iterator` or implicitly calls `into_iter` - let arg_ty = cx.typeck_results().expr_ty(arg); - if let Some(id) = get_trait_def_id(cx, &paths::ITERATOR); - if let is_iterator = implements_trait(cx, arg_ty, id, &[]); - then { - // Prepare the error message - let msg = format!("Unnecessary `if let` since only the `{}` variant of the iterator element is used.", if_let_type); + if_chain! { + if let Some(inner_expr) = inner_expr; + if let ExprKind::Match( + ref match_expr, ref match_arms, MatchSource::IfLetDesugar{ contains_else_clause: false } + ) = inner_expr.kind; + // Ensure match_expr in `if let` statement is the same as the pat from the for-loop + if let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind; + if let ExprKind::Path(QPath::Resolved(None, match_expr_path)) = match_expr.kind; + if let Res::Local(match_expr_path_id) = match_expr_path.res; + if pat_hir_id == match_expr_path_id; + // Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result` + if let PatKind::TupleStruct(QPath::Resolved(None, path), _, _) = match_arms[0].pat.kind; + let some_ctor = is_some_ctor(cx, path.res); + let ok_ctor = is_ok_ctor(cx, path.res); + if some_ctor || ok_ctor; + let if_let_type = if some_ctor { "Some" } else { "Ok" }; - // Prepare the help message - let arg_snippet = snippet(cx, arg.span, ".."); - let hint = if is_iterator { - format!("try: `{}.flatten()`", arg_snippet) - } else { - format!("try: `{}.into_iter().flatten()`", arg_snippet) - }; + then { + // Prepare the error message + let msg = format!("unnecessary `if let` since only the `{}` variant of the iterator element is used", if_let_type); - span_lint_and_help( - cx, - MANUAL_FLATTEN, - span, - &msg, - Some(arg.span), - &hint, - ); + // Prepare the help message + let mut applicability = Applicability::MaybeIncorrect; + let arg_snippet = snippet_with_applicability( + cx, + arg.span, + "..", + &mut applicability, + ); + // Determine if `arg` is by reference, an `Iterator`, or implicitly adjusted with `into_iter` + let hint = match arg.kind { + ExprKind::AddrOf(_, _, arg_expr) => { + format!("{}.iter().flatten()", snippet(cx, arg_expr.span, "..")) + }, + ExprKind::MethodCall(_, _, _, _) | ExprKind::Path(QPath::Resolved(None, _)) => { + // Determine if `arg` is `Iterator` or implicitly calls `into_iter` + let arg_ty = cx.typeck_results().expr_ty(arg); + if let Some(id) = get_trait_def_id(cx, &paths::ITERATOR) { + let is_iterator = implements_trait(cx, arg_ty, id, &[]); + if is_iterator { + format!("{}.flatten()", arg_snippet) + } else { + format!("{}.into_iter().flatten()", arg_snippet) + } + } else { + return + } + }, + _ => return, + }; + + span_lint_and_sugg( + cx, + MANUAL_FLATTEN, + span, + &msg, + "try", + hint, + applicability, + ) + } } } } diff --git a/tests/ui/manual_flatten.rs b/tests/ui/manual_flatten.rs index f183ceecdd8..b97cceb66f8 100644 --- a/tests/ui/manual_flatten.rs +++ b/tests/ui/manual_flatten.rs @@ -1,6 +1,7 @@ #![warn(clippy::manual_flatten)] fn main() { + // Test for loop over implicitly adjusted `Iterator` with `if let` expression let x = vec![Some(1), Some(2), Some(3)]; for n in x { if let Some(n) = n { @@ -8,13 +9,22 @@ fn main() { } } + // Test for loop over implicitly implicitly adjusted `Iterator` with `if let` statement let y: Vec> = vec![]; for n in y.clone() { + if let Ok(n) = n { + println!("{}", n); + }; + } + + // Test for loop over by reference + for n in &y { if let Ok(n) = n { println!("{}", n); } } + // Test for loop over `Iterator` with `if let` expression let z = vec![Some(1), Some(2), Some(3)]; let z = z.iter(); for n in z { diff --git a/tests/ui/manual_flatten.stderr b/tests/ui/manual_flatten.stderr index cf99a2d9ab1..754921eb739 100644 --- a/tests/ui/manual_flatten.stderr +++ b/tests/ui/manual_flatten.stderr @@ -1,51 +1,44 @@ -error: Unnecessary `if let` since only the `Some` variant of the iterator element is used. - --> $DIR/manual_flatten.rs:5:5 +error: unnecessary `if let` since only the `Some` variant of the iterator element is used + --> $DIR/manual_flatten.rs:6:5 | LL | / for n in x { LL | | if let Some(n) = n { LL | | println!("{}", n); LL | | } LL | | } - | |_____^ + | |_____^ help: try: `x.into_iter().flatten()` | = note: `-D clippy::manual-flatten` implied by `-D warnings` -help: try: `x.into_iter().flatten()` - --> $DIR/manual_flatten.rs:5:14 - | -LL | for n in x { - | ^ -error: Unnecessary `if let` since only the `Ok` variant of the iterator element is used. - --> $DIR/manual_flatten.rs:12:5 +error: unnecessary `if let` since only the `Ok` variant of the iterator element is used + --> $DIR/manual_flatten.rs:14:5 | LL | / for n in y.clone() { LL | | if let Ok(n) = n { LL | | println!("{}", n); -LL | | } +LL | | }; LL | | } - | |_____^ - | -help: try: `y.clone().into_iter().flatten()` - --> $DIR/manual_flatten.rs:12:14 + | |_____^ help: try: `y.clone().into_iter().flatten()` + +error: unnecessary `if let` since only the `Ok` variant of the iterator element is used + --> $DIR/manual_flatten.rs:21:5 | -LL | for n in y.clone() { - | ^^^^^^^^^ +LL | / for n in &y { +LL | | if let Ok(n) = n { +LL | | println!("{}", n); +LL | | } +LL | | } + | |_____^ help: try: `y.iter().flatten()` -error: Unnecessary `if let` since only the `Some` variant of the iterator element is used. - --> $DIR/manual_flatten.rs:20:5 +error: unnecessary `if let` since only the `Some` variant of the iterator element is used + --> $DIR/manual_flatten.rs:30:5 | LL | / for n in z { LL | | if let Some(n) = n { LL | | println!("{}", n); LL | | } LL | | } - | |_____^ - | -help: try: `z.flatten()` - --> $DIR/manual_flatten.rs:20:14 - | -LL | for n in z { - | ^ + | |_____^ help: try: `z.flatten()` -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From bfbc0835870f8bf6cde79a01dfe7de351dde14aa Mon Sep 17 00:00:00 2001 From: Pierre-Andre Gagnon Date: Tue, 2 Feb 2021 18:39:23 -0500 Subject: Fix for issue 6640 --- clippy_lints/src/unnecessary_wraps.rs | 92 ++++++++++++++++++---------- tests/ui/unnecessary_wraps.rs | 28 +++++++++ tests/ui/unnecessary_wraps.stderr | 109 ++-------------------------------- 3 files changed, 93 insertions(+), 136 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index 8ac5dd696b7..eec76ce8b03 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -1,6 +1,6 @@ use crate::utils::{ - contains_return, in_macro, is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_then, - visitors::find_all_ret_expressions, + contains_return, in_macro, is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_sugg, + span_lint_and_then, visitors::find_all_ret_expressions, }; use if_chain::if_chain; use rustc_errors::Applicability; @@ -64,6 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { span: Span, hir_id: HirId, ) { + // Abort if public function/method or closure. match fn_kind { FnKind::ItemFn(.., visibility, _) | FnKind::Method(.., Some(visibility), _) => { if visibility.node.is_pub() { @@ -74,6 +75,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { _ => (), } + // Abort if the method is implementing a trait or of it a trait method. if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) { if matches!( item.kind, @@ -83,22 +85,43 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { } } - let (return_type, path) = if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::option_type) { + // Check if return type is Option or Result. If neither, abort. + let return_ty = return_ty(cx, hir_id); + let (return_type_label, path) = if is_type_diagnostic_item(cx, return_ty, sym::option_type) { ("Option", &paths::OPTION_SOME) - } else if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::result_type) { + } else if is_type_diagnostic_item(cx, return_ty, sym::result_type) { ("Result", &paths::RESULT_OK) } else { return; }; + // Take the first inner type of the Option or Result. If can't, abort. + let inner_ty = if_chain! { + // Skip Option or Result and take the first outermost inner type. + if let Some(inner_ty) = return_ty.walk().nth(1); + if let GenericArgKind::Type(inner_ty) = inner_ty.unpack(); + then { + inner_ty + } else { + return; + } + }; + + // Check if all return expression respect the following condition and collect them. let mut suggs = Vec::new(); let can_sugg = find_all_ret_expressions(cx, &body.value, |ret_expr| { if_chain! { + // Abort if in macro. if !in_macro(ret_expr.span); + // Check if a function call. if let ExprKind::Call(ref func, ref args) = ret_expr.kind; + // Get the Path of the function call. if let ExprKind::Path(ref qpath) = func.kind; + // Check if OPTION_SOME or RESULT_OK, depending on return type. if match_qpath(qpath, path); + // Make sure the function call has only one argument. if args.len() == 1; + // Make sure the function argument does not contain a return expression. if !contains_return(&args[0]); then { suggs.push((ret_expr.span, snippet(cx, args[0].span.source_callsite(), "..").to_string())); @@ -110,39 +133,42 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { }); if can_sugg && !suggs.is_empty() { - span_lint_and_then( - cx, - UNNECESSARY_WRAPS, - span, - format!( - "this function's return value is unnecessarily wrapped by `{}`", - return_type - ) - .as_str(), - |diag| { - let inner_ty = return_ty(cx, hir_id) - .walk() - .skip(1) // skip `std::option::Option` or `std::result::Result` - .take(1) // take the first outermost inner type - .filter_map(|inner| match inner.unpack() { - GenericArgKind::Type(inner_ty) => Some(inner_ty.to_string()), - _ => None, - }); - inner_ty.for_each(|inner_ty| { + // Issue 6640: If the inner type is Unit, emit lint similar to clippy::unused_unit. + if inner_ty.is_unit() { + span_lint_and_sugg( + cx, + UNNECESSARY_WRAPS, + fn_decl.output.span(), + "unneeded wrapped unit return type", + format!("remove the `-> {}<()>`", return_type_label).as_str(), + String::new(), + Applicability::MaybeIncorrect, + ); + } else { + span_lint_and_then( + cx, + UNNECESSARY_WRAPS, + span, + format!( + "this function's return value is unnecessarily wrapped by `{}`", + return_type_label + ) + .as_str(), + |diag| { diag.span_suggestion( fn_decl.output.span(), - format!("remove `{}` from the return type...", return_type).as_str(), - inner_ty, + format!("remove `{}` from the return type...", return_type_label).as_str(), + inner_ty.to_string(), Applicability::MaybeIncorrect, ); - }); - diag.multipart_suggestion( - "...and change the returning expressions", - suggs, - Applicability::MaybeIncorrect, - ); - }, - ); + diag.multipart_suggestion( + "...and change the returning expressions", + suggs, + Applicability::MaybeIncorrect, + ); + }, + ); + } } } } diff --git a/tests/ui/unnecessary_wraps.rs b/tests/ui/unnecessary_wraps.rs index a4570098d71..2d6aedc97ef 100644 --- a/tests/ui/unnecessary_wraps.rs +++ b/tests/ui/unnecessary_wraps.rs @@ -116,8 +116,36 @@ fn issue_6384(s: &str) -> Option<&str> { }) } +// should be linted +fn issue_6640_1(a: bool, b: bool) -> Option<()> { + if a && b { + return Some(()); + } + if a { + Some(()); + Some(()) + } else { + return Some(()); + } +} + +// should be linted +fn issue_6640_2(a: bool, b: bool) -> Result<(), i32> { + if a && b { + return Ok(()); + } + if a { + Ok(()); + Ok(()) + } else { + return Ok(()); + } +} + fn main() { // method calls are not linted func1(true, true); func2(true, true); + issue_6640_1(true, true); + issue_6640_2(true, true); } diff --git a/tests/ui/unnecessary_wraps.stderr b/tests/ui/unnecessary_wraps.stderr index 410f054b8ef..3ca5a8d1702 100644 --- a/tests/ui/unnecessary_wraps.stderr +++ b/tests/ui/unnecessary_wraps.stderr @@ -1,106 +1,9 @@ -error: this function's return value is unnecessarily wrapped by `Option` - --> $DIR/unnecessary_wraps.rs:8:1 - | -LL | / fn func1(a: bool, b: bool) -> Option { -LL | | if a && b { -LL | | return Some(42); -LL | | } -... | -LL | | } -LL | | } - | |_^ - | - = note: `-D clippy::unnecessary-wraps` implied by `-D warnings` -help: remove `Option` from the return type... - | -LL | fn func1(a: bool, b: bool) -> i32 { - | ^^^ -help: ...and change the returning expressions - | -LL | return 42; -LL | } -LL | if a { -LL | Some(-1); -LL | 2 -LL | } else { - ... - -error: this function's return value is unnecessarily wrapped by `Option` - --> $DIR/unnecessary_wraps.rs:21:1 - | -LL | / fn func2(a: bool, b: bool) -> Option { -LL | | if a && b { -LL | | return Some(10); -LL | | } -... | -LL | | } -LL | | } - | |_^ - | -help: remove `Option` from the return type... - | -LL | fn func2(a: bool, b: bool) -> i32 { - | ^^^ -help: ...and change the returning expressions - | -LL | return 10; -LL | } -LL | if a { -LL | 20 -LL | } else { -LL | 30 - | - -error: this function's return value is unnecessarily wrapped by `Option` - --> $DIR/unnecessary_wraps.rs:51:1 - | -LL | / fn func5() -> Option { -LL | | Some(1) -LL | | } - | |_^ - | -help: remove `Option` from the return type... - | -LL | fn func5() -> i32 { - | ^^^ -help: ...and change the returning expressions - | -LL | 1 - | - -error: this function's return value is unnecessarily wrapped by `Result` - --> $DIR/unnecessary_wraps.rs:61:1 - | -LL | / fn func7() -> Result { -LL | | Ok(1) -LL | | } - | |_^ - | -help: remove `Result` from the return type... - | -LL | fn func7() -> i32 { - | ^^^ -help: ...and change the returning expressions - | -LL | 1 - | - -error: this function's return value is unnecessarily wrapped by `Option` - --> $DIR/unnecessary_wraps.rs:93:5 - | -LL | / fn func12() -> Option { -LL | | Some(1) -LL | | } - | |_____^ - | -help: remove `Option` from the return type... - | -LL | fn func12() -> i32 { - | ^^^ -help: ...and change the returning expressions - | -LL | 1 +error[E0282]: type annotations needed + --> $DIR/unnecessary_wraps.rs:138:9 | +LL | Ok(()); + | ^^ cannot infer type for type parameter `E` declared on the enum `Result` -error: aborting due to 5 previous errors +error: aborting due to previous error +For more information about this error, try `rustc --explain E0282`. -- cgit 1.4.1-3-g733a5 From e0e51e418906916123b3bd15c175d80d3dc74bf2 Mon Sep 17 00:00:00 2001 From: Pierre-Andre Gagnon Date: Tue, 2 Feb 2021 19:14:13 -0500 Subject: Fixed test --- clippy_lints/src/unnecessary_wraps.rs | 2 +- tests/ui/unnecessary_wraps.rs | 1 - tests/ui/unnecessary_wraps.stderr | 121 ++++++++++++++++++++++++++++++++-- 3 files changed, 116 insertions(+), 8 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index eec76ce8b03..7da42ba3934 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -140,7 +140,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { UNNECESSARY_WRAPS, fn_decl.output.span(), "unneeded wrapped unit return type", - format!("remove the `-> {}<()>`", return_type_label).as_str(), + format!("remove the `-> {}<[...]>`", return_type_label).as_str(), String::new(), Applicability::MaybeIncorrect, ); diff --git a/tests/ui/unnecessary_wraps.rs b/tests/ui/unnecessary_wraps.rs index 2d6aedc97ef..5aaa99bbe5a 100644 --- a/tests/ui/unnecessary_wraps.rs +++ b/tests/ui/unnecessary_wraps.rs @@ -135,7 +135,6 @@ fn issue_6640_2(a: bool, b: bool) -> Result<(), i32> { return Ok(()); } if a { - Ok(()); Ok(()) } else { return Ok(()); diff --git a/tests/ui/unnecessary_wraps.stderr b/tests/ui/unnecessary_wraps.stderr index 3ca5a8d1702..f28981f9e34 100644 --- a/tests/ui/unnecessary_wraps.stderr +++ b/tests/ui/unnecessary_wraps.stderr @@ -1,9 +1,118 @@ -error[E0282]: type annotations needed - --> $DIR/unnecessary_wraps.rs:138:9 +error: this function's return value is unnecessarily wrapped by `Option` + --> $DIR/unnecessary_wraps.rs:8:1 | -LL | Ok(()); - | ^^ cannot infer type for type parameter `E` declared on the enum `Result` +LL | / fn func1(a: bool, b: bool) -> Option { +LL | | if a && b { +LL | | return Some(42); +LL | | } +... | +LL | | } +LL | | } + | |_^ + | + = note: `-D clippy::unnecessary-wraps` implied by `-D warnings` +help: remove `Option` from the return type... + | +LL | fn func1(a: bool, b: bool) -> i32 { + | ^^^ +help: ...and change the returning expressions + | +LL | return 42; +LL | } +LL | if a { +LL | Some(-1); +LL | 2 +LL | } else { + ... + +error: this function's return value is unnecessarily wrapped by `Option` + --> $DIR/unnecessary_wraps.rs:21:1 + | +LL | / fn func2(a: bool, b: bool) -> Option { +LL | | if a && b { +LL | | return Some(10); +LL | | } +... | +LL | | } +LL | | } + | |_^ + | +help: remove `Option` from the return type... + | +LL | fn func2(a: bool, b: bool) -> i32 { + | ^^^ +help: ...and change the returning expressions + | +LL | return 10; +LL | } +LL | if a { +LL | 20 +LL | } else { +LL | 30 + | + +error: this function's return value is unnecessarily wrapped by `Option` + --> $DIR/unnecessary_wraps.rs:51:1 + | +LL | / fn func5() -> Option { +LL | | Some(1) +LL | | } + | |_^ + | +help: remove `Option` from the return type... + | +LL | fn func5() -> i32 { + | ^^^ +help: ...and change the returning expressions + | +LL | 1 + | + +error: this function's return value is unnecessarily wrapped by `Result` + --> $DIR/unnecessary_wraps.rs:61:1 + | +LL | / fn func7() -> Result { +LL | | Ok(1) +LL | | } + | |_^ + | +help: remove `Result` from the return type... + | +LL | fn func7() -> i32 { + | ^^^ +help: ...and change the returning expressions + | +LL | 1 + | + +error: this function's return value is unnecessarily wrapped by `Option` + --> $DIR/unnecessary_wraps.rs:93:5 + | +LL | / fn func12() -> Option { +LL | | Some(1) +LL | | } + | |_____^ + | +help: remove `Option` from the return type... + | +LL | fn func12() -> i32 { + | ^^^ +help: ...and change the returning expressions + | +LL | 1 + | + +error: unneeded wrapped unit return type + --> $DIR/unnecessary_wraps.rs:120:38 + | +LL | fn issue_6640_1(a: bool, b: bool) -> Option<()> { + | ^^^^^^^^^^ help: remove the `-> Option<[...]>` + +error: unneeded wrapped unit return type + --> $DIR/unnecessary_wraps.rs:133:38 + | +LL | fn issue_6640_2(a: bool, b: bool) -> Result<(), i32> { + | ^^^^^^^^^^^^^^^ help: remove the `-> Result<[...]>` -error: aborting due to previous error +error: aborting due to 7 previous errors -For more information about this error, try `rustc --explain E0282`. -- cgit 1.4.1-3-g733a5 From 6396b8fb7f095919da061ad2bfeb008ee7fb6589 Mon Sep 17 00:00:00 2001 From: Yusuke Tanaka Date: Thu, 4 Feb 2021 00:06:26 +0900 Subject: Fix file names of flat_map_identity test This commit fixes the file names of the `flat_map_identity` test. Previously, their names were started with `unnecessary_flat_map` even though the lint rule name is `flat_map_identity`. This inconsistency happened probably because the rule name was changed during the discussion in the PR where this rule was introduced. ref: https://github.com/rust-lang/rust-clippy/pull/4231 --- tests/ui/flat_map_identity.fixed | 14 ++++++++++++++ tests/ui/flat_map_identity.rs | 14 ++++++++++++++ tests/ui/flat_map_identity.stderr | 16 ++++++++++++++++ tests/ui/unnecessary_flat_map.fixed | 14 -------------- tests/ui/unnecessary_flat_map.rs | 14 -------------- tests/ui/unnecessary_flat_map.stderr | 16 ---------------- 6 files changed, 44 insertions(+), 44 deletions(-) create mode 100644 tests/ui/flat_map_identity.fixed create mode 100644 tests/ui/flat_map_identity.rs create mode 100644 tests/ui/flat_map_identity.stderr delete mode 100644 tests/ui/unnecessary_flat_map.fixed delete mode 100644 tests/ui/unnecessary_flat_map.rs delete mode 100644 tests/ui/unnecessary_flat_map.stderr (limited to 'tests') diff --git a/tests/ui/flat_map_identity.fixed b/tests/ui/flat_map_identity.fixed new file mode 100644 index 00000000000..dfe3bd47e13 --- /dev/null +++ b/tests/ui/flat_map_identity.fixed @@ -0,0 +1,14 @@ +// run-rustfix + +#![allow(unused_imports)] +#![warn(clippy::flat_map_identity)] + +use std::convert; + +fn main() { + let iterator = [[0, 1], [2, 3], [4, 5]].iter(); + let _ = iterator.flatten(); + + let iterator = [[0, 1], [2, 3], [4, 5]].iter(); + let _ = iterator.flatten(); +} diff --git a/tests/ui/flat_map_identity.rs b/tests/ui/flat_map_identity.rs new file mode 100644 index 00000000000..393b9569255 --- /dev/null +++ b/tests/ui/flat_map_identity.rs @@ -0,0 +1,14 @@ +// run-rustfix + +#![allow(unused_imports)] +#![warn(clippy::flat_map_identity)] + +use std::convert; + +fn main() { + let iterator = [[0, 1], [2, 3], [4, 5]].iter(); + let _ = iterator.flat_map(|x| x); + + let iterator = [[0, 1], [2, 3], [4, 5]].iter(); + let _ = iterator.flat_map(convert::identity); +} diff --git a/tests/ui/flat_map_identity.stderr b/tests/ui/flat_map_identity.stderr new file mode 100644 index 00000000000..e4686ae5a54 --- /dev/null +++ b/tests/ui/flat_map_identity.stderr @@ -0,0 +1,16 @@ +error: called `flat_map(|x| x)` on an `Iterator` + --> $DIR/flat_map_identity.rs:10:22 + | +LL | let _ = iterator.flat_map(|x| x); + | ^^^^^^^^^^^^^^^ help: try: `flatten()` + | + = note: `-D clippy::flat-map-identity` implied by `-D warnings` + +error: called `flat_map(std::convert::identity)` on an `Iterator` + --> $DIR/flat_map_identity.rs:13:22 + | +LL | let _ = iterator.flat_map(convert::identity); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/unnecessary_flat_map.fixed b/tests/ui/unnecessary_flat_map.fixed deleted file mode 100644 index dfe3bd47e13..00000000000 --- a/tests/ui/unnecessary_flat_map.fixed +++ /dev/null @@ -1,14 +0,0 @@ -// run-rustfix - -#![allow(unused_imports)] -#![warn(clippy::flat_map_identity)] - -use std::convert; - -fn main() { - let iterator = [[0, 1], [2, 3], [4, 5]].iter(); - let _ = iterator.flatten(); - - let iterator = [[0, 1], [2, 3], [4, 5]].iter(); - let _ = iterator.flatten(); -} diff --git a/tests/ui/unnecessary_flat_map.rs b/tests/ui/unnecessary_flat_map.rs deleted file mode 100644 index 393b9569255..00000000000 --- a/tests/ui/unnecessary_flat_map.rs +++ /dev/null @@ -1,14 +0,0 @@ -// run-rustfix - -#![allow(unused_imports)] -#![warn(clippy::flat_map_identity)] - -use std::convert; - -fn main() { - let iterator = [[0, 1], [2, 3], [4, 5]].iter(); - let _ = iterator.flat_map(|x| x); - - let iterator = [[0, 1], [2, 3], [4, 5]].iter(); - let _ = iterator.flat_map(convert::identity); -} diff --git a/tests/ui/unnecessary_flat_map.stderr b/tests/ui/unnecessary_flat_map.stderr deleted file mode 100644 index a1cd5745e49..00000000000 --- a/tests/ui/unnecessary_flat_map.stderr +++ /dev/null @@ -1,16 +0,0 @@ -error: called `flat_map(|x| x)` on an `Iterator` - --> $DIR/unnecessary_flat_map.rs:10:22 - | -LL | let _ = iterator.flat_map(|x| x); - | ^^^^^^^^^^^^^^^ help: try: `flatten()` - | - = note: `-D clippy::flat-map-identity` implied by `-D warnings` - -error: called `flat_map(std::convert::identity)` on an `Iterator` - --> $DIR/unnecessary_flat_map.rs:13:22 - | -LL | let _ = iterator.flat_map(convert::identity); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()` - -error: aborting due to 2 previous errors - -- cgit 1.4.1-3-g733a5 From 78ef0f2f6c17f5933ab4dbab544b76f7da742467 Mon Sep 17 00:00:00 2001 From: nahuakang Date: Wed, 3 Feb 2021 19:45:58 +0100 Subject: Add additional test cases and improve span lint --- clippy_lints/src/loops.rs | 47 ++++++++------------- tests/ui/manual_flatten.rs | 18 +++++++-- tests/ui/manual_flatten.stderr | 92 +++++++++++++++++++++++++++++++++++------- 3 files changed, 108 insertions(+), 49 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 5bfdc98bc6a..817230a29c6 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2034,42 +2034,27 @@ fn check_manual_flatten<'tcx>( // Prepare the help message let mut applicability = Applicability::MaybeIncorrect; - let arg_snippet = snippet_with_applicability( - cx, - arg.span, - "..", - &mut applicability, - ); - // Determine if `arg` is by reference, an `Iterator`, or implicitly adjusted with `into_iter` - let arg_ty = cx.typeck_results().expr_ty(arg); - let hint = if arg_ty.is_ref() { - if has_iter_method(cx, arg_ty).is_none() { - return; - } else if let ExprKind::AddrOf(_, _, arg_expr) = arg.kind { - format!("{}.iter().flatten()", snippet(cx, arg_expr.span, "..")) - } else { - return; - } - } else if let Some(id) = get_trait_def_id(cx, &paths::ITERATOR) { - let is_iterator = implements_trait(cx, arg_ty, id, &[]); - if is_iterator { - format!("{}.flatten()", arg_snippet) - } else { - format!("{}.into_iter().flatten()", arg_snippet) - } - } else { - return - }; + let arg_snippet = make_iterator_snippet(cx, arg, &mut applicability); - span_lint_and_sugg( + span_lint_and_then( cx, MANUAL_FLATTEN, span, &msg, - "try", - hint, - applicability, - ) + |diag| { + let sugg = format!("{}.flatten()", arg_snippet); + diag.span_suggestion( + arg.span, + "try", + sugg, + Applicability::MaybeIncorrect, + ); + diag.span_help( + inner_expr.span, + "also remove the `if let` statement in the for loop", + ); + } + ); } } } diff --git a/tests/ui/manual_flatten.rs b/tests/ui/manual_flatten.rs index b97cceb66f8..ea3440f6da2 100644 --- a/tests/ui/manual_flatten.rs +++ b/tests/ui/manual_flatten.rs @@ -4,8 +4,8 @@ fn main() { // Test for loop over implicitly adjusted `Iterator` with `if let` expression let x = vec![Some(1), Some(2), Some(3)]; for n in x { - if let Some(n) = n { - println!("{}", n); + if let Some(y) = n { + println!("{}", y); } } @@ -24,12 +24,22 @@ fn main() { } } + // Test for loop over an implicit reference + // Note: If `clippy::manual_flatten` is made autofixable, this case will + // lead to a follow-up lint `clippy::into_iter_on_ref` + let z = &y; + for n in z { + if let Ok(n) = n { + println!("{}", n); + } + } + // Test for loop over `Iterator` with `if let` expression let z = vec![Some(1), Some(2), Some(3)]; let z = z.iter(); for n in z { - if let Some(n) = n { - println!("{}", n); + if let Some(m) = n { + println!("{}", m); } } diff --git a/tests/ui/manual_flatten.stderr b/tests/ui/manual_flatten.stderr index 754921eb739..49b8ed0564a 100644 --- a/tests/ui/manual_flatten.stderr +++ b/tests/ui/manual_flatten.stderr @@ -1,44 +1,108 @@ error: unnecessary `if let` since only the `Some` variant of the iterator element is used --> $DIR/manual_flatten.rs:6:5 | -LL | / for n in x { -LL | | if let Some(n) = n { -LL | | println!("{}", n); +LL | for n in x { + | ^ - help: try: `x.into_iter().flatten()` + | _____| + | | +LL | | if let Some(y) = n { +LL | | println!("{}", y); LL | | } LL | | } - | |_____^ help: try: `x.into_iter().flatten()` + | |_____^ | = note: `-D clippy::manual-flatten` implied by `-D warnings` +help: also remove the `if let` statement in the for loop + --> $DIR/manual_flatten.rs:7:9 + | +LL | / if let Some(y) = n { +LL | | println!("{}", y); +LL | | } + | |_________^ error: unnecessary `if let` since only the `Ok` variant of the iterator element is used --> $DIR/manual_flatten.rs:14:5 | -LL | / for n in y.clone() { +LL | for n in y.clone() { + | ^ --------- help: try: `y.clone().into_iter().flatten()` + | _____| + | | LL | | if let Ok(n) = n { LL | | println!("{}", n); LL | | }; LL | | } - | |_____^ help: try: `y.clone().into_iter().flatten()` + | |_____^ + | +help: also remove the `if let` statement in the for loop + --> $DIR/manual_flatten.rs:15:9 + | +LL | / if let Ok(n) = n { +LL | | println!("{}", n); +LL | | }; + | |_________^ error: unnecessary `if let` since only the `Ok` variant of the iterator element is used --> $DIR/manual_flatten.rs:21:5 | -LL | / for n in &y { +LL | for n in &y { + | ^ -- help: try: `y.iter().flatten()` + | _____| + | | LL | | if let Ok(n) = n { LL | | println!("{}", n); LL | | } LL | | } - | |_____^ help: try: `y.iter().flatten()` + | |_____^ + | +help: also remove the `if let` statement in the for loop + --> $DIR/manual_flatten.rs:22:9 + | +LL | / if let Ok(n) = n { +LL | | println!("{}", n); +LL | | } + | |_________^ -error: unnecessary `if let` since only the `Some` variant of the iterator element is used - --> $DIR/manual_flatten.rs:30:5 +error: unnecessary `if let` since only the `Ok` variant of the iterator element is used + --> $DIR/manual_flatten.rs:31:5 | -LL | / for n in z { -LL | | if let Some(n) = n { +LL | for n in z { + | ^ - help: try: `z.into_iter().flatten()` + | _____| + | | +LL | | if let Ok(n) = n { LL | | println!("{}", n); LL | | } LL | | } - | |_____^ help: try: `z.flatten()` + | |_____^ + | +help: also remove the `if let` statement in the for loop + --> $DIR/manual_flatten.rs:32:9 + | +LL | / if let Ok(n) = n { +LL | | println!("{}", n); +LL | | } + | |_________^ + +error: unnecessary `if let` since only the `Some` variant of the iterator element is used + --> $DIR/manual_flatten.rs:40:5 + | +LL | for n in z { + | ^ - help: try: `z.flatten()` + | _____| + | | +LL | | if let Some(m) = n { +LL | | println!("{}", m); +LL | | } +LL | | } + | |_____^ + | +help: also remove the `if let` statement in the for loop + --> $DIR/manual_flatten.rs:41:9 + | +LL | / if let Some(m) = n { +LL | | println!("{}", m); +LL | | } + | |_________^ -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors -- cgit 1.4.1-3-g733a5 From e32e4dedf1781a4696c34f31d69e68c7c0eaf6a9 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Tue, 2 Feb 2021 12:26:20 +0900 Subject: New lint: default_numeric_fallback --- CHANGELOG.md | 1 + clippy_lints/src/default_numeric_fallback.rs | 388 +++++++++++++++++++++++++++ clippy_lints/src/lib.rs | 4 + tests/ui/default_numeric_fallback.rs | 99 +++++++ tests/ui/default_numeric_fallback.stderr | 187 +++++++++++++ 5 files changed, 679 insertions(+) create mode 100644 clippy_lints/src/default_numeric_fallback.rs create mode 100644 tests/ui/default_numeric_fallback.rs create mode 100644 tests/ui/default_numeric_fallback.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index c1032204a22..5eed9664088 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1909,6 +1909,7 @@ Released 2018-09-13 [`debug_assert_with_mut_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#debug_assert_with_mut_call [`decimal_literal_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation [`declare_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#declare_interior_mutable_const +[`default_numeric_fallback`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_numeric_fallback [`default_trait_access`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_trait_access [`deprecated_cfg_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_cfg_attr [`deprecated_semver`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_semver diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs new file mode 100644 index 00000000000..f049e64d0fb --- /dev/null +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -0,0 +1,388 @@ +use rustc_ast::ast::{Label, LitFloatType, LitIntType, LitKind}; +use rustc_hir::{ + self as hir, + intravisit::{walk_expr, walk_stmt, walk_ty, FnKind, NestedVisitorMap, Visitor}, + Body, Expr, ExprKind, FnDecl, FnRetTy, Guard, HirId, Lit, Stmt, StmtKind, +}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::{ + hir::map::Map, + ty::{self, subst::GenericArgKind, FloatTy, IntTy, Ty, TyCtxt}, +}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; +use rustc_typeck::hir_ty_to_ty; + +use if_chain::if_chain; + +use crate::utils::span_lint_and_help; + +declare_clippy_lint! { + /// **What it does:** Checks for usage of unconstrained numeric literals which may cause default numeric fallback in type + /// inference. + /// + /// Default numeric fallback means that if numeric types have not yet been bound to concrete + /// types at the end of type inference, then integer type is bound to `i32`, and similarly + /// floating type is bound to `f64`. + /// + /// See [RFC0212](https://github.com/rust-lang/rfcs/blob/master/text/0212-restore-int-fallback.md) for more information about the fallback. + /// + /// **Why is this bad?** For those who are very careful about types, default numeric fallback + /// can be a pitfall that cause unexpected runtime behavior. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// let i = 10; + /// let f = 1.23; + /// ``` + /// + /// Use instead: + /// ```rust + /// let i = 10i32; + /// let f = 1.23f64; + /// ``` + pub DEFAULT_NUMERIC_FALLBACK, + restriction, + "usage of unconstrained numeric literals which may cause default numeric fallback." +} + +declare_lint_pass!(DefaultNumericFallback => [DEFAULT_NUMERIC_FALLBACK]); + +fn enclosing_body_owner_opt(tcx: TyCtxt<'_>, hir_id: HirId) -> Option { + let hir_map = tcx.hir(); + for (parent, _) in hir_map.parent_iter(hir_id) { + if let Some(body) = hir_map.maybe_body_owned_by(parent) { + return Some(hir_map.body_owner(body)); + } + } + None +} + +impl LateLintPass<'_> for DefaultNumericFallback { + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + _: FnKind<'tcx>, + fn_decl: &'tcx FnDecl<'_>, + body: &'tcx Body<'_>, + _: Span, + hir_id: HirId, + ) { + let ret_ty_bound = match fn_decl.output { + FnRetTy::DefaultReturn(_) => None, + FnRetTy::Return(ty) => Some(ty), + } + .and_then(|ty| { + let mut infer_ty_finder = InferTyFinder::new(); + infer_ty_finder.visit_ty(ty); + if infer_ty_finder.found { + None + } else if enclosing_body_owner_opt(cx.tcx, hir_id).is_some() { + cx.typeck_results().node_type_opt(ty.hir_id) + } else { + Some(hir_ty_to_ty(cx.tcx, ty)) + } + }); + + let mut visitor = NumericFallbackVisitor::new(ret_ty_bound, cx); + visitor.visit_body(body); + } +} + +struct NumericFallbackVisitor<'a, 'tcx> { + /// Stack manages type bound of exprs. The top element holds current expr type. + ty_bounds: Vec>>, + + /// Ret type bound. + ret_ty_bound: Option>, + + /// Break type bounds. + break_ty_bounds: Vec<(Option) {} + fn a(_: Vec) -> Self { + unimplemented!() + } + } + + impl Trait> for Vec + where + T: Trait, + { + fn a(v: Vec) -> Self { + >::a(v).into_iter().map(Trait::a).collect() + } } } @@ -197,8 +213,8 @@ mod rustfix { fn fun_1() {} fn fun_2() { - Self::fun_1(); - Self::A; + nested::A::fun_1(); + nested::A::A; Self {}; } @@ -219,7 +235,8 @@ mod issue3567 { impl Test for TestStruct { fn test() -> TestStruct { - Self::from_something() + // FIXME: applicable here + TestStruct::from_something() } } } @@ -233,12 +250,14 @@ mod paths_created_by_lowering { const A: usize = 0; const B: usize = 1; - async fn g() -> Self { + // FIXME: applicable here + async fn g() -> S { Self {} } fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] { - &p[Self::A..Self::B] + // FIXME: applicable here twice + &p[S::A..S::B] } } @@ -252,3 +271,194 @@ mod paths_created_by_lowering { } } } + +// reused from #1997 +mod generics { + struct Foo { + value: T, + } + + impl Foo { + // `Self` is applicable here + fn foo(value: T) -> Self { + Self { value } + } + + // `Cannot` use `Self` as a return type as the generic types are different + fn bar(value: i32) -> Foo { + Foo { value } + } + } +} + +mod issue4140 { + pub struct Error { + _from: From, + _too: To, + } + + pub trait From { + type From; + type To; + + fn from(value: T) -> Self; + } + + pub trait TryFrom + where + Self: Sized, + { + type From; + type To; + + fn try_from(value: T) -> Result>; + } + + impl TryFrom for T + where + T: From, + { + type From = Self; + type To = Self; + + fn try_from(value: F) -> Result> { + Ok(From::from(value)) + } + } + + impl From for i64 { + type From = bool; + type To = Self; + + fn from(value: bool) -> Self { + if value { + 100 + } else { + 0 + } + } + } +} + +mod issue2843 { + trait Foo { + type Bar; + } + + impl Foo for usize { + type Bar = u8; + } + + impl Foo for Option { + type Bar = Option; + } +} + +mod issue3859 { + pub struct Foo; + pub struct Bar([usize; 3]); + + impl Foo { + pub const BAR: usize = 3; + + pub fn foo() { + const _X: usize = Foo::BAR; + // const _Y: usize = Self::BAR; + } + } +} + +mod issue4305 { + trait Foo: 'static {} + + struct Bar; + + impl Foo for Bar {} + + impl From for Box { + fn from(t: T) -> Self { + // FIXME: applicable here + Box::new(t) + } + } +} + +mod lint_at_item_level { + struct Foo {} + + #[allow(clippy::use_self)] + impl Foo { + fn new() -> Foo { + Foo {} + } + } + + #[allow(clippy::use_self)] + impl Default for Foo { + fn default() -> Foo { + Foo::new() + } + } +} + +mod lint_at_impl_item_level { + struct Foo {} + + impl Foo { + #[allow(clippy::use_self)] + fn new() -> Foo { + Foo {} + } + } + + impl Default for Foo { + #[allow(clippy::use_self)] + fn default() -> Foo { + Foo::new() + } + } +} + +mod issue4734 { + #[repr(C, packed)] + pub struct X { + pub x: u32, + } + + impl From for u32 { + fn from(c: X) -> Self { + unsafe { core::mem::transmute(c) } + } + } +} + +mod nested_paths { + use std::convert::Into; + mod submod { + pub struct B {} + pub struct C {} + + impl Into for B { + fn into(self) -> C { + C {} + } + } + } + + struct A { + t: T, + } + + impl A { + fn new>(v: V) -> Self { + Self { t: Into::into(v) } + } + } + + impl A { + fn test() -> Self { + // FIXME: applicable here + A::new::(submod::B {}) + } + } +} diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index ddfd2beba31..347f5e96555 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -21,6 +21,7 @@ mod use_self { impl Default for Foo { fn default() -> Foo { + // FIXME: applicable here Foo::new() } } @@ -87,7 +88,11 @@ mod existential { struct Foo; impl Foo { - fn bad(foos: &[Self]) -> impl Iterator { + // FIXME: + // TyKind::Def (used for `impl Trait` types) does not include type parameters yet. + // See documentation in rustc_hir::hir::TyKind. + // The hir tree walk stops at `impl Iterator` level and does not inspect &Foo. + fn bad(foos: &[Foo]) -> impl Iterator { foos.iter() } @@ -177,11 +182,22 @@ mod issue3410 { struct B; trait Trait { - fn a(v: T); + fn a(v: T) -> Self; } impl Trait> for Vec { - fn a(_: Vec) {} + fn a(_: Vec) -> Self { + unimplemented!() + } + } + + impl Trait> for Vec + where + T: Trait, + { + fn a(v: Vec) -> Self { + >::a(v).into_iter().map(Trait::a).collect() + } } } @@ -219,6 +235,7 @@ mod issue3567 { impl Test for TestStruct { fn test() -> TestStruct { + // FIXME: applicable here TestStruct::from_something() } } @@ -233,11 +250,13 @@ mod paths_created_by_lowering { const A: usize = 0; const B: usize = 1; + // FIXME: applicable here async fn g() -> S { S {} } fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] { + // FIXME: applicable here twice &p[S::A..S::B] } } @@ -252,3 +271,194 @@ mod paths_created_by_lowering { } } } + +// reused from #1997 +mod generics { + struct Foo { + value: T, + } + + impl Foo { + // `Self` is applicable here + fn foo(value: T) -> Foo { + Foo { value } + } + + // `Cannot` use `Self` as a return type as the generic types are different + fn bar(value: i32) -> Foo { + Foo { value } + } + } +} + +mod issue4140 { + pub struct Error { + _from: From, + _too: To, + } + + pub trait From { + type From; + type To; + + fn from(value: T) -> Self; + } + + pub trait TryFrom + where + Self: Sized, + { + type From; + type To; + + fn try_from(value: T) -> Result>; + } + + impl TryFrom for T + where + T: From, + { + type From = T::From; + type To = T::To; + + fn try_from(value: F) -> Result> { + Ok(From::from(value)) + } + } + + impl From for i64 { + type From = bool; + type To = Self; + + fn from(value: bool) -> Self { + if value { + 100 + } else { + 0 + } + } + } +} + +mod issue2843 { + trait Foo { + type Bar; + } + + impl Foo for usize { + type Bar = u8; + } + + impl Foo for Option { + type Bar = Option; + } +} + +mod issue3859 { + pub struct Foo; + pub struct Bar([usize; 3]); + + impl Foo { + pub const BAR: usize = 3; + + pub fn foo() { + const _X: usize = Foo::BAR; + // const _Y: usize = Self::BAR; + } + } +} + +mod issue4305 { + trait Foo: 'static {} + + struct Bar; + + impl Foo for Bar {} + + impl From for Box { + fn from(t: T) -> Self { + // FIXME: applicable here + Box::new(t) + } + } +} + +mod lint_at_item_level { + struct Foo {} + + #[allow(clippy::use_self)] + impl Foo { + fn new() -> Foo { + Foo {} + } + } + + #[allow(clippy::use_self)] + impl Default for Foo { + fn default() -> Foo { + Foo::new() + } + } +} + +mod lint_at_impl_item_level { + struct Foo {} + + impl Foo { + #[allow(clippy::use_self)] + fn new() -> Foo { + Foo {} + } + } + + impl Default for Foo { + #[allow(clippy::use_self)] + fn default() -> Foo { + Foo::new() + } + } +} + +mod issue4734 { + #[repr(C, packed)] + pub struct X { + pub x: u32, + } + + impl From for u32 { + fn from(c: X) -> Self { + unsafe { core::mem::transmute(c) } + } + } +} + +mod nested_paths { + use std::convert::Into; + mod submod { + pub struct B {} + pub struct C {} + + impl Into for B { + fn into(self) -> C { + C {} + } + } + } + + struct A { + t: T, + } + + impl A { + fn new>(v: V) -> Self { + Self { t: Into::into(v) } + } + } + + impl A { + fn test() -> Self { + // FIXME: applicable here + A::new::(submod::B {}) + } + } +} diff --git a/tests/ui/use_self.stderr b/tests/ui/use_self.stderr index 80e1bfc75e8..a88dd04f13d 100644 --- a/tests/ui/use_self.stderr +++ b/tests/ui/use_self.stderr @@ -18,12 +18,6 @@ error: unnecessary structure name repetition LL | fn test() -> Foo { | ^^^ help: use the applicable keyword: `Self` -error: unnecessary structure name repetition - --> $DIR/use_self.rs:18:13 - | -LL | Foo::new() - | ^^^ help: use the applicable keyword: `Self` - error: unnecessary structure name repetition --> $DIR/use_self.rs:23:25 | @@ -31,25 +25,19 @@ LL | fn default() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:24:13 - | -LL | Foo::new() - | ^^^ help: use the applicable keyword: `Self` - -error: unnecessary structure name repetition - --> $DIR/use_self.rs:90:56 + --> $DIR/use_self.rs:94:24 | -LL | fn bad(foos: &[Self]) -> impl Iterator { - | ^^^ help: use the applicable keyword: `Self` +LL | fn bad(foos: &[Foo]) -> impl Iterator { + | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:105:13 + --> $DIR/use_self.rs:109:13 | LL | TS(0) | ^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:113:25 + --> $DIR/use_self.rs:117:25 | LL | fn new() -> Foo { | ^^^ help: use the applicable keyword: `Self` @@ -60,7 +48,7 @@ LL | use_self_expand!(); // Should lint in local macros = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: unnecessary structure name repetition - --> $DIR/use_self.rs:114:17 + --> $DIR/use_self.rs:118:17 | LL | Foo {} | ^^^ help: use the applicable keyword: `Self` @@ -71,94 +59,82 @@ LL | use_self_expand!(); // Should lint in local macros = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: unnecessary structure name repetition - --> $DIR/use_self.rs:149:21 + --> $DIR/use_self.rs:141:29 | -LL | fn baz() -> Foo { - | ^^^ help: use the applicable keyword: `Self` +LL | fn bar() -> Bar { + | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:150:13 + --> $DIR/use_self.rs:142:21 | -LL | Foo {} - | ^^^ help: use the applicable keyword: `Self` +LL | Bar { foo: Foo {} } + | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:137:29 + --> $DIR/use_self.rs:153:21 | -LL | fn bar() -> Bar { - | ^^^ help: use the applicable keyword: `Self` +LL | fn baz() -> Foo { + | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:138:21 + --> $DIR/use_self.rs:154:13 | -LL | Bar { foo: Foo {} } - | ^^^ help: use the applicable keyword: `Self` +LL | Foo {} + | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:167:21 + --> $DIR/use_self.rs:171:21 | LL | let _ = Enum::B(42); | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:168:21 + --> $DIR/use_self.rs:172:21 | LL | let _ = Enum::C { field: true }; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:169:21 + --> $DIR/use_self.rs:173:21 | LL | let _ = Enum::A; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:200:13 - | -LL | nested::A::fun_1(); - | ^^^^^^^^^ help: use the applicable keyword: `Self` - -error: unnecessary structure name repetition - --> $DIR/use_self.rs:201:13 - | -LL | nested::A::A; - | ^^^^^^^^^ help: use the applicable keyword: `Self` - -error: unnecessary structure name repetition - --> $DIR/use_self.rs:203:13 + --> $DIR/use_self.rs:218:13 | LL | nested::A {}; | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:222:13 + --> $DIR/use_self.rs:254:13 | -LL | TestStruct::from_something() - | ^^^^^^^^^^ help: use the applicable keyword: `Self` +LL | S {} + | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:236:25 + --> $DIR/use_self.rs:282:29 | -LL | async fn g() -> S { - | ^ help: use the applicable keyword: `Self` +LL | fn foo(value: T) -> Foo { + | ^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:237:13 + --> $DIR/use_self.rs:283:13 | -LL | S {} - | ^ help: use the applicable keyword: `Self` +LL | Foo { value } + | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:241:16 + --> $DIR/use_self.rs:320:21 | -LL | &p[S::A..S::B] - | ^ help: use the applicable keyword: `Self` +LL | type From = T::From; + | ^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:241:22 + --> $DIR/use_self.rs:321:19 | -LL | &p[S::A..S::B] - | ^ help: use the applicable keyword: `Self` +LL | type To = T::To; + | ^^^^^ help: use the applicable keyword: `Self` -error: aborting due to 25 previous errors +error: aborting due to 21 previous errors diff --git a/tests/ui/use_self_trait.fixed b/tests/ui/use_self_trait.fixed index 1582ae114bf..d425f953a9c 100644 --- a/tests/ui/use_self_trait.fixed +++ b/tests/ui/use_self_trait.fixed @@ -33,7 +33,7 @@ impl SelfTrait for Bad { fn nested(_p1: Box, _p2: (&u8, &Self)) {} fn vals(_: Self) -> Self { - Self::default() + Bad::default() } } @@ -47,7 +47,7 @@ impl Mul for Bad { impl Clone for Bad { fn clone(&self) -> Self { - Self + Bad } } diff --git a/tests/ui/use_self_trait.stderr b/tests/ui/use_self_trait.stderr index 4f2506cc119..fa528cc5b7d 100644 --- a/tests/ui/use_self_trait.stderr +++ b/tests/ui/use_self_trait.stderr @@ -60,12 +60,6 @@ error: unnecessary structure name repetition LL | fn vals(_: Bad) -> Bad { | ^^^ help: use the applicable keyword: `Self` -error: unnecessary structure name repetition - --> $DIR/use_self_trait.rs:36:9 - | -LL | Bad::default() - | ^^^ help: use the applicable keyword: `Self` - error: unnecessary structure name repetition --> $DIR/use_self_trait.rs:41:19 | @@ -84,11 +78,5 @@ error: unnecessary structure name repetition LL | fn mul(self, rhs: Bad) -> Bad { | ^^^ help: use the applicable keyword: `Self` -error: unnecessary structure name repetition - --> $DIR/use_self_trait.rs:50:9 - | -LL | Bad - | ^^^ help: use the applicable keyword: `Self` - -error: aborting due to 15 previous errors +error: aborting due to 13 previous errors -- cgit 1.4.1-3-g733a5 From fc334fb8f4cc7e6513578d88f52b2899f624a1de Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Sat, 10 Oct 2020 00:26:12 +0200 Subject: use_self - fix issue with `hir_ty_to_ty` --- clippy_lints/src/use_self.rs | 97 ++++++++++++++++++++++++------------------ tests/ui/use_self.fixed | 6 ++- tests/ui/use_self.rs | 4 ++ tests/ui/use_self.stderr | 84 +++++++++++++++++------------------- tests/ui/use_self_trait.fixed | 12 +++--- tests/ui/use_self_trait.stderr | 76 +-------------------------------- 6 files changed, 112 insertions(+), 167 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 1b5070d1cff..023fce947b3 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -57,7 +57,7 @@ declare_lint_pass!(UseSelf => [USE_SELF]); const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element"; -fn span_lint<'tcx>(cx: &LateContext<'tcx>, span: Span) { +fn span_lint(cx: &LateContext<'_>, span: Span) { span_lint_and_sugg( cx, USE_SELF, @@ -99,12 +99,12 @@ fn span_lint_on_qpath_resolved<'tcx>(cx: &LateContext<'tcx>, qpath: &'tcx QPath< } } -struct ImplVisitor<'a, 'tcx> { +struct BodyVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, self_ty: Ty<'tcx>, } -impl<'a, 'tcx> ImplVisitor<'a, 'tcx> { +impl<'a, 'tcx> BodyVisitor<'a, 'tcx> { fn check_trait_method_impl_decl( &mut self, impl_item: &ImplItem<'tcx>, @@ -151,46 +151,13 @@ impl<'a, 'tcx> ImplVisitor<'a, 'tcx> { } } -impl<'a, 'tcx> Visitor<'tcx> for ImplVisitor<'a, 'tcx> { +impl<'a, 'tcx> Visitor<'tcx> for BodyVisitor<'a, 'tcx> { type Map = Map<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) } - fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx>) { - if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind { - match path.res { - def::Res::SelfTy(..) => {}, - _ => { - match self.cx.tcx.hir().find(self.cx.tcx.hir().get_parent_node(hir_ty.hir_id)) { - Some(Node::Expr(Expr { - kind: ExprKind::Path(QPath::TypeRelative(_, _segment)), - .. - })) => { - // The following block correctly identifies applicable lint locations - // but `hir_ty_to_ty` calls cause odd ICEs. - // - // if hir_ty_to_ty(self.cx.tcx, hir_ty) == self.self_ty { - // // FIXME: this span manipulation should not be necessary - // // @flip1995 found an ast lowering issue in - // // https://github.com/rust-lang/rust/blob/master/src/librustc_ast_lowering/path.rs#L142-L162 - // span_lint_until_last_segment(self.cx, hir_ty.span, segment); - // } - }, - _ => { - if hir_ty_to_ty(self.cx.tcx, hir_ty) == self.self_ty { - span_lint(self.cx, hir_ty.span) - } - }, - } - }, - } - } - - walk_ty(self, hir_ty); - } - fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { fn expr_ty_matches<'tcx>(expr: &'tcx Expr<'tcx>, self_ty: Ty<'tcx>, cx: &LateContext<'tcx>) -> bool { let def_id = expr.hir_id.owner; @@ -247,6 +214,52 @@ impl<'a, 'tcx> Visitor<'tcx> for ImplVisitor<'a, 'tcx> { } } +struct FnSigVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + self_ty: Ty<'tcx>, +} + +impl<'a, 'tcx> Visitor<'tcx> for FnSigVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + + fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx>) { + if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind { + match path.res { + def::Res::SelfTy(..) => {}, + _ => { + match self.cx.tcx.hir().find(self.cx.tcx.hir().get_parent_node(hir_ty.hir_id)) { + Some(Node::Expr(Expr { + kind: ExprKind::Path(QPath::TypeRelative(_, segment)), + .. + })) => { + // The following block correctly identifies applicable lint locations + // but `hir_ty_to_ty` calls cause odd ICEs. + // + if hir_ty_to_ty(self.cx.tcx, hir_ty) == self.self_ty { + // fixme: this span manipulation should not be necessary + // @flip1995 found an ast lowering issue in + // https://github.com/rust-lang/rust/blob/master/src/librustc_ast_lowering/path.rs#l142-l162 + span_lint_until_last_segment(self.cx, hir_ty.span, segment); + } + }, + _ => { + if hir_ty_to_ty(self.cx.tcx, hir_ty) == self.self_ty { + span_lint(self.cx, hir_ty.span) + } + }, + } + }, + } + } + + walk_ty(self, hir_ty); + } +} + impl<'tcx> LateLintPass<'tcx> for UseSelf { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) { if in_external_macro(cx.sess(), impl_item.span) { @@ -270,7 +283,8 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { // TODO: don't short-circuit upon lifetime parameters if should_check { let self_ty = hir_ty_to_ty(cx.tcx, hir_self_ty); - let visitor = &mut ImplVisitor { cx, self_ty }; + let body_visitor = &mut BodyVisitor { cx, self_ty }; + let fn_sig_visitor = &mut FnSigVisitor { cx, self_ty }; let tcx = cx.tcx; let impl_def_id = tcx.hir().local_def_id(imp.hir_id); @@ -279,11 +293,12 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { if let Some(impl_trait_ref) = impl_trait_ref; if let ImplItemKind::Fn(FnSig { decl: impl_decl, .. }, impl_body_id) = &impl_item.kind; then { - visitor.check_trait_method_impl_decl(impl_item, impl_decl, impl_trait_ref); + body_visitor.check_trait_method_impl_decl(impl_item, impl_decl, impl_trait_ref); let body = tcx.hir().body(*impl_body_id); - visitor.visit_body(body); + body_visitor.visit_body(body); } else { - walk_impl_item(visitor, impl_item) + walk_impl_item(body_visitor, impl_item); + walk_impl_item(fn_sig_visitor, impl_item); } } } diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed index 916484eef93..d59750cbfd8 100644 --- a/tests/ui/use_self.fixed +++ b/tests/ui/use_self.fixed @@ -15,12 +15,14 @@ mod use_self { Self {} } fn test() -> Self { + // FIXME: applicable here Foo::new() } } impl Default for Foo { - fn default() -> Self { + // FIXME: applicable here + fn default() -> Foo { // FIXME: applicable here Foo::new() } @@ -213,7 +215,9 @@ mod rustfix { fn fun_1() {} fn fun_2() { + // FIXME: applicable here nested::A::fun_1(); + // FIXME: applicable here nested::A::A; Self {}; diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index 347f5e96555..85606049774 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -15,11 +15,13 @@ mod use_self { Foo {} } fn test() -> Foo { + // FIXME: applicable here Foo::new() } } impl Default for Foo { + // FIXME: applicable here fn default() -> Foo { // FIXME: applicable here Foo::new() @@ -213,7 +215,9 @@ mod rustfix { fn fun_1() {} fn fun_2() { + // FIXME: applicable here nested::A::fun_1(); + // FIXME: applicable here nested::A::A; nested::A {}; diff --git a/tests/ui/use_self.stderr b/tests/ui/use_self.stderr index a88dd04f13d..4d213316cf5 100644 --- a/tests/ui/use_self.stderr +++ b/tests/ui/use_self.stderr @@ -1,16 +1,16 @@ error: unnecessary structure name repetition - --> $DIR/use_self.rs:14:21 + --> $DIR/use_self.rs:15:13 | -LL | fn new() -> Foo { - | ^^^ help: use the applicable keyword: `Self` +LL | Foo {} + | ^^^ help: use the applicable keyword: `Self` | = note: `-D clippy::use-self` implied by `-D warnings` error: unnecessary structure name repetition - --> $DIR/use_self.rs:15:13 + --> $DIR/use_self.rs:14:21 | -LL | Foo {} - | ^^^ help: use the applicable keyword: `Self` +LL | fn new() -> Foo { + | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition --> $DIR/use_self.rs:17:22 @@ -19,28 +19,22 @@ LL | fn test() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:23:25 - | -LL | fn default() -> Foo { - | ^^^ help: use the applicable keyword: `Self` - -error: unnecessary structure name repetition - --> $DIR/use_self.rs:94:24 + --> $DIR/use_self.rs:96:24 | LL | fn bad(foos: &[Foo]) -> impl Iterator { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:109:13 + --> $DIR/use_self.rs:111:13 | LL | TS(0) | ^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:117:25 + --> $DIR/use_self.rs:120:17 | -LL | fn new() -> Foo { - | ^^^ help: use the applicable keyword: `Self` +LL | Foo {} + | ^^^ help: use the applicable keyword: `Self` ... LL | use_self_expand!(); // Should lint in local macros | ------------------- in this macro invocation @@ -48,10 +42,10 @@ LL | use_self_expand!(); // Should lint in local macros = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: unnecessary structure name repetition - --> $DIR/use_self.rs:118:17 + --> $DIR/use_self.rs:119:25 | -LL | Foo {} - | ^^^ help: use the applicable keyword: `Self` +LL | fn new() -> Foo { + | ^^^ help: use the applicable keyword: `Self` ... LL | use_self_expand!(); // Should lint in local macros | ------------------- in this macro invocation @@ -59,82 +53,82 @@ LL | use_self_expand!(); // Should lint in local macros = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: unnecessary structure name repetition - --> $DIR/use_self.rs:141:29 - | -LL | fn bar() -> Bar { - | ^^^ help: use the applicable keyword: `Self` - -error: unnecessary structure name repetition - --> $DIR/use_self.rs:142:21 + --> $DIR/use_self.rs:144:21 | LL | Bar { foo: Foo {} } | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:153:21 + --> $DIR/use_self.rs:143:29 | -LL | fn baz() -> Foo { - | ^^^ help: use the applicable keyword: `Self` +LL | fn bar() -> Bar { + | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:154:13 + --> $DIR/use_self.rs:156:13 | LL | Foo {} | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:171:21 + --> $DIR/use_self.rs:155:21 + | +LL | fn baz() -> Foo { + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:173:21 | LL | let _ = Enum::B(42); | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:172:21 + --> $DIR/use_self.rs:174:21 | LL | let _ = Enum::C { field: true }; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:173:21 + --> $DIR/use_self.rs:175:21 | LL | let _ = Enum::A; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:218:13 + --> $DIR/use_self.rs:222:13 | LL | nested::A {}; | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:254:13 + --> $DIR/use_self.rs:258:13 | LL | S {} | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:282:29 + --> $DIR/use_self.rs:287:13 | -LL | fn foo(value: T) -> Foo { - | ^^^^^^ help: use the applicable keyword: `Self` +LL | Foo { value } + | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:283:13 + --> $DIR/use_self.rs:286:29 | -LL | Foo { value } - | ^^^ help: use the applicable keyword: `Self` +LL | fn foo(value: T) -> Foo { + | ^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:320:21 + --> $DIR/use_self.rs:324:21 | LL | type From = T::From; | ^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:321:19 + --> $DIR/use_self.rs:325:19 | LL | type To = T::To; | ^^^^^ help: use the applicable keyword: `Self` -error: aborting due to 21 previous errors +error: aborting due to 20 previous errors diff --git a/tests/ui/use_self_trait.fixed b/tests/ui/use_self_trait.fixed index d425f953a9c..680a0623839 100644 --- a/tests/ui/use_self_trait.fixed +++ b/tests/ui/use_self_trait.fixed @@ -18,21 +18,21 @@ trait SelfTrait { struct Bad; impl SelfTrait for Bad { - fn refs(p1: &Self) -> &Self { + fn refs(p1: &Bad) -> &Bad { p1 } - fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self { + fn ref_refs<'a>(p1: &'a &'a Bad) -> &'a &'a Bad { p1 } - fn mut_refs(p1: &mut Self) -> &mut Self { + fn mut_refs(p1: &mut Bad) -> &mut Bad { p1 } - fn nested(_p1: Box, _p2: (&u8, &Self)) {} + fn nested(_p1: Box, _p2: (&u8, &Bad)) {} - fn vals(_: Self) -> Self { + fn vals(_: Bad) -> Bad { Bad::default() } } @@ -40,7 +40,7 @@ impl SelfTrait for Bad { impl Mul for Bad { type Output = Self; - fn mul(self, rhs: Self) -> Self { + fn mul(self, rhs: Bad) -> Bad { rhs } } diff --git a/tests/ui/use_self_trait.stderr b/tests/ui/use_self_trait.stderr index fa528cc5b7d..5409ccedf85 100644 --- a/tests/ui/use_self_trait.stderr +++ b/tests/ui/use_self_trait.stderr @@ -1,82 +1,10 @@ -error: unnecessary structure name repetition - --> $DIR/use_self_trait.rs:21:18 - | -LL | fn refs(p1: &Bad) -> &Bad { - | ^^^ help: use the applicable keyword: `Self` - | - = note: `-D clippy::use-self` implied by `-D warnings` - -error: unnecessary structure name repetition - --> $DIR/use_self_trait.rs:21:27 - | -LL | fn refs(p1: &Bad) -> &Bad { - | ^^^ help: use the applicable keyword: `Self` - -error: unnecessary structure name repetition - --> $DIR/use_self_trait.rs:25:33 - | -LL | fn ref_refs<'a>(p1: &'a &'a Bad) -> &'a &'a Bad { - | ^^^ help: use the applicable keyword: `Self` - -error: unnecessary structure name repetition - --> $DIR/use_self_trait.rs:25:49 - | -LL | fn ref_refs<'a>(p1: &'a &'a Bad) -> &'a &'a Bad { - | ^^^ help: use the applicable keyword: `Self` - -error: unnecessary structure name repetition - --> $DIR/use_self_trait.rs:29:26 - | -LL | fn mut_refs(p1: &mut Bad) -> &mut Bad { - | ^^^ help: use the applicable keyword: `Self` - -error: unnecessary structure name repetition - --> $DIR/use_self_trait.rs:29:39 - | -LL | fn mut_refs(p1: &mut Bad) -> &mut Bad { - | ^^^ help: use the applicable keyword: `Self` - -error: unnecessary structure name repetition - --> $DIR/use_self_trait.rs:33:24 - | -LL | fn nested(_p1: Box, _p2: (&u8, &Bad)) {} - | ^^^ help: use the applicable keyword: `Self` - -error: unnecessary structure name repetition - --> $DIR/use_self_trait.rs:33:42 - | -LL | fn nested(_p1: Box, _p2: (&u8, &Bad)) {} - | ^^^ help: use the applicable keyword: `Self` - -error: unnecessary structure name repetition - --> $DIR/use_self_trait.rs:35:16 - | -LL | fn vals(_: Bad) -> Bad { - | ^^^ help: use the applicable keyword: `Self` - -error: unnecessary structure name repetition - --> $DIR/use_self_trait.rs:35:24 - | -LL | fn vals(_: Bad) -> Bad { - | ^^^ help: use the applicable keyword: `Self` - error: unnecessary structure name repetition --> $DIR/use_self_trait.rs:41:19 | LL | type Output = Bad; | ^^^ help: use the applicable keyword: `Self` - -error: unnecessary structure name repetition - --> $DIR/use_self_trait.rs:43:23 | -LL | fn mul(self, rhs: Bad) -> Bad { - | ^^^ help: use the applicable keyword: `Self` - -error: unnecessary structure name repetition - --> $DIR/use_self_trait.rs:43:31 - | -LL | fn mul(self, rhs: Bad) -> Bad { - | ^^^ help: use the applicable keyword: `Self` + = note: `-D clippy::use-self` implied by `-D warnings` -error: aborting due to 13 previous errors +error: aborting due to previous error -- cgit 1.4.1-3-g733a5 From bb40db7adc6c42fb675559b7ac92468ed416747f Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 16 Oct 2020 13:26:02 +0200 Subject: Update test files --- tests/ui/use_self.fixed | 41 ++++-------- tests/ui/use_self.rs | 21 +------ tests/ui/use_self.stderr | 140 ++++++++++++++++++++++++++++++----------- tests/ui/use_self_trait.fixed | 15 ++--- tests/ui/use_self_trait.rs | 1 + tests/ui/use_self_trait.stderr | 82 +++++++++++++++++++++++- 6 files changed, 208 insertions(+), 92 deletions(-) (limited to 'tests') diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed index d59750cbfd8..1632e6aca44 100644 --- a/tests/ui/use_self.fixed +++ b/tests/ui/use_self.fixed @@ -3,7 +3,7 @@ #![warn(clippy::use_self)] #![allow(dead_code)] -#![allow(clippy::should_implement_trait, clippy::upper_case_acronyms)] +#![allow(clippy::should_implement_trait, clippy::upper_case_acronyms, clippy::from_over_into)] fn main() {} @@ -15,16 +15,13 @@ mod use_self { Self {} } fn test() -> Self { - // FIXME: applicable here - Foo::new() + Self::new() } } impl Default for Foo { - // FIXME: applicable here - fn default() -> Foo { - // FIXME: applicable here - Foo::new() + fn default() -> Self { + Self::new() } } } @@ -74,13 +71,12 @@ mod lifetimes { mod issue2894 { trait IntoBytes { - #[allow(clippy::wrong_self_convention)] - fn into_bytes(&self) -> Vec; + fn to_bytes(&self) -> Vec; } // This should not be linted impl IntoBytes for u8 { - fn into_bytes(&self) -> Vec { + fn to_bytes(&self) -> Vec { vec![*self] } } @@ -90,11 +86,7 @@ mod existential { struct Foo; impl Foo { - // FIXME: - // TyKind::Def (used for `impl Trait` types) does not include type parameters yet. - // See documentation in rustc_hir::hir::TyKind. - // The hir tree walk stops at `impl Iterator` level and does not inspect &Foo. - fn bad(foos: &[Self]) -> impl Iterator { + fn bad(foos: &[Self]) -> impl Iterator { foos.iter() } @@ -215,10 +207,8 @@ mod rustfix { fn fun_1() {} fn fun_2() { - // FIXME: applicable here - nested::A::fun_1(); - // FIXME: applicable here - nested::A::A; + Self::fun_1(); + Self::A; Self {}; } @@ -239,8 +229,7 @@ mod issue3567 { impl Test for TestStruct { fn test() -> TestStruct { - // FIXME: applicable here - TestStruct::from_something() + Self::from_something() } } } @@ -254,14 +243,12 @@ mod paths_created_by_lowering { const A: usize = 0; const B: usize = 1; - // FIXME: applicable here - async fn g() -> S { + async fn g() -> Self { Self {} } fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] { - // FIXME: applicable here twice - &p[S::A..S::B] + &p[Self::A..Self::B] } } @@ -381,7 +368,6 @@ mod issue4305 { impl From for Box { fn from(t: T) -> Self { - // FIXME: applicable here Box::new(t) } } @@ -461,8 +447,7 @@ mod nested_paths { impl A { fn test() -> Self { - // FIXME: applicable here - A::new::(submod::B {}) + Self::new::(submod::B {}) } } } diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index 85606049774..bbe92c9e338 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -3,7 +3,7 @@ #![warn(clippy::use_self)] #![allow(dead_code)] -#![allow(clippy::should_implement_trait, clippy::upper_case_acronyms)] +#![allow(clippy::should_implement_trait, clippy::upper_case_acronyms, clippy::from_over_into)] fn main() {} @@ -15,15 +15,12 @@ mod use_self { Foo {} } fn test() -> Foo { - // FIXME: applicable here Foo::new() } } impl Default for Foo { - // FIXME: applicable here fn default() -> Foo { - // FIXME: applicable here Foo::new() } } @@ -74,13 +71,12 @@ mod lifetimes { mod issue2894 { trait IntoBytes { - #[allow(clippy::wrong_self_convention)] - fn into_bytes(&self) -> Vec; + fn to_bytes(&self) -> Vec; } // This should not be linted impl IntoBytes for u8 { - fn into_bytes(&self) -> Vec { + fn to_bytes(&self) -> Vec { vec![*self] } } @@ -90,10 +86,6 @@ mod existential { struct Foo; impl Foo { - // FIXME: - // TyKind::Def (used for `impl Trait` types) does not include type parameters yet. - // See documentation in rustc_hir::hir::TyKind. - // The hir tree walk stops at `impl Iterator` level and does not inspect &Foo. fn bad(foos: &[Foo]) -> impl Iterator { foos.iter() } @@ -215,9 +207,7 @@ mod rustfix { fn fun_1() {} fn fun_2() { - // FIXME: applicable here nested::A::fun_1(); - // FIXME: applicable here nested::A::A; nested::A {}; @@ -239,7 +229,6 @@ mod issue3567 { impl Test for TestStruct { fn test() -> TestStruct { - // FIXME: applicable here TestStruct::from_something() } } @@ -254,13 +243,11 @@ mod paths_created_by_lowering { const A: usize = 0; const B: usize = 1; - // FIXME: applicable here async fn g() -> S { S {} } fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] { - // FIXME: applicable here twice &p[S::A..S::B] } } @@ -381,7 +368,6 @@ mod issue4305 { impl From for Box { fn from(t: T) -> Self { - // FIXME: applicable here Box::new(t) } } @@ -461,7 +447,6 @@ mod nested_paths { impl A { fn test() -> Self { - // FIXME: applicable here A::new::(submod::B {}) } } diff --git a/tests/ui/use_self.stderr b/tests/ui/use_self.stderr index 4d213316cf5..d86453eb2f0 100644 --- a/tests/ui/use_self.stderr +++ b/tests/ui/use_self.stderr @@ -1,16 +1,16 @@ error: unnecessary structure name repetition - --> $DIR/use_self.rs:15:13 + --> $DIR/use_self.rs:14:21 | -LL | Foo {} - | ^^^ help: use the applicable keyword: `Self` +LL | fn new() -> Foo { + | ^^^ help: use the applicable keyword: `Self` | = note: `-D clippy::use-self` implied by `-D warnings` error: unnecessary structure name repetition - --> $DIR/use_self.rs:14:21 + --> $DIR/use_self.rs:15:13 | -LL | fn new() -> Foo { - | ^^^ help: use the applicable keyword: `Self` +LL | Foo {} + | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition --> $DIR/use_self.rs:17:22 @@ -19,22 +19,46 @@ LL | fn test() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:96:24 + --> $DIR/use_self.rs:18:13 + | +LL | Foo::new() + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:23:25 + | +LL | fn default() -> Foo { + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:24:13 + | +LL | Foo::new() + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:89:24 | LL | fn bad(foos: &[Foo]) -> impl Iterator { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:111:13 + --> $DIR/use_self.rs:89:55 + | +LL | fn bad(foos: &[Foo]) -> impl Iterator { + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:104:13 | LL | TS(0) | ^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:120:17 + --> $DIR/use_self.rs:112:25 | -LL | Foo {} - | ^^^ help: use the applicable keyword: `Self` +LL | fn new() -> Foo { + | ^^^ help: use the applicable keyword: `Self` ... LL | use_self_expand!(); // Should lint in local macros | ------------------- in this macro invocation @@ -42,10 +66,10 @@ LL | use_self_expand!(); // Should lint in local macros = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: unnecessary structure name repetition - --> $DIR/use_self.rs:119:25 + --> $DIR/use_self.rs:113:17 | -LL | fn new() -> Foo { - | ^^^ help: use the applicable keyword: `Self` +LL | Foo {} + | ^^^ help: use the applicable keyword: `Self` ... LL | use_self_expand!(); // Should lint in local macros | ------------------- in this macro invocation @@ -53,82 +77,124 @@ LL | use_self_expand!(); // Should lint in local macros = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: unnecessary structure name repetition - --> $DIR/use_self.rs:144:21 - | -LL | Bar { foo: Foo {} } - | ^^^ help: use the applicable keyword: `Self` - -error: unnecessary structure name repetition - --> $DIR/use_self.rs:143:29 + --> $DIR/use_self.rs:136:29 | LL | fn bar() -> Bar { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:156:13 + --> $DIR/use_self.rs:137:21 | -LL | Foo {} - | ^^^ help: use the applicable keyword: `Self` +LL | Bar { foo: Foo {} } + | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:155:21 + --> $DIR/use_self.rs:148:21 | LL | fn baz() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:173:21 + --> $DIR/use_self.rs:149:13 + | +LL | Foo {} + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:166:21 | LL | let _ = Enum::B(42); | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:174:21 + --> $DIR/use_self.rs:167:21 | LL | let _ = Enum::C { field: true }; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:175:21 + --> $DIR/use_self.rs:168:21 | LL | let _ = Enum::A; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:222:13 + --> $DIR/use_self.rs:210:13 + | +LL | nested::A::fun_1(); + | ^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:211:13 + | +LL | nested::A::A; + | ^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:213:13 | LL | nested::A {}; | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:258:13 + --> $DIR/use_self.rs:232:13 + | +LL | TestStruct::from_something() + | ^^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:246:25 + | +LL | async fn g() -> S { + | ^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:247:13 | LL | S {} | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:287:13 + --> $DIR/use_self.rs:251:16 | -LL | Foo { value } - | ^^^ help: use the applicable keyword: `Self` +LL | &p[S::A..S::B] + | ^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:251:22 + | +LL | &p[S::A..S::B] + | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:286:29 + --> $DIR/use_self.rs:274:29 | LL | fn foo(value: T) -> Foo { | ^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:324:21 + --> $DIR/use_self.rs:275:13 + | +LL | Foo { value } + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:312:21 | LL | type From = T::From; | ^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:325:19 + --> $DIR/use_self.rs:313:19 | LL | type To = T::To; | ^^^^^ help: use the applicable keyword: `Self` -error: aborting due to 20 previous errors +error: unnecessary structure name repetition + --> $DIR/use_self.rs:450:13 + | +LL | A::new::(submod::B {}) + | ^ help: use the applicable keyword: `Self` + +error: aborting due to 31 previous errors diff --git a/tests/ui/use_self_trait.fixed b/tests/ui/use_self_trait.fixed index 680a0623839..9bcd692fb35 100644 --- a/tests/ui/use_self_trait.fixed +++ b/tests/ui/use_self_trait.fixed @@ -18,35 +18,36 @@ trait SelfTrait { struct Bad; impl SelfTrait for Bad { - fn refs(p1: &Bad) -> &Bad { + fn refs(p1: &Self) -> &Self { p1 } - fn ref_refs<'a>(p1: &'a &'a Bad) -> &'a &'a Bad { + fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self { p1 } - fn mut_refs(p1: &mut Bad) -> &mut Bad { + fn mut_refs(p1: &mut Self) -> &mut Self { p1 } - fn nested(_p1: Box, _p2: (&u8, &Bad)) {} + fn nested(_p1: Box, _p2: (&u8, &Self)) {} - fn vals(_: Bad) -> Bad { - Bad::default() + fn vals(_: Self) -> Self { + Self::default() } } impl Mul for Bad { type Output = Self; - fn mul(self, rhs: Bad) -> Bad { + fn mul(self, rhs: Self) -> Self { rhs } } impl Clone for Bad { fn clone(&self) -> Self { + // FIXME: applicable here Bad } } diff --git a/tests/ui/use_self_trait.rs b/tests/ui/use_self_trait.rs index 70667b9797e..de305d40f33 100644 --- a/tests/ui/use_self_trait.rs +++ b/tests/ui/use_self_trait.rs @@ -47,6 +47,7 @@ impl Mul for Bad { impl Clone for Bad { fn clone(&self) -> Self { + // FIXME: applicable here Bad } } diff --git a/tests/ui/use_self_trait.stderr b/tests/ui/use_self_trait.stderr index 5409ccedf85..55af3ff2a93 100644 --- a/tests/ui/use_self_trait.stderr +++ b/tests/ui/use_self_trait.stderr @@ -1,10 +1,88 @@ +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:21:18 + | +LL | fn refs(p1: &Bad) -> &Bad { + | ^^^ help: use the applicable keyword: `Self` + | + = note: `-D clippy::use-self` implied by `-D warnings` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:21:27 + | +LL | fn refs(p1: &Bad) -> &Bad { + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:25:33 + | +LL | fn ref_refs<'a>(p1: &'a &'a Bad) -> &'a &'a Bad { + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:25:49 + | +LL | fn ref_refs<'a>(p1: &'a &'a Bad) -> &'a &'a Bad { + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:29:26 + | +LL | fn mut_refs(p1: &mut Bad) -> &mut Bad { + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:29:39 + | +LL | fn mut_refs(p1: &mut Bad) -> &mut Bad { + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:33:24 + | +LL | fn nested(_p1: Box, _p2: (&u8, &Bad)) {} + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:33:42 + | +LL | fn nested(_p1: Box, _p2: (&u8, &Bad)) {} + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:35:16 + | +LL | fn vals(_: Bad) -> Bad { + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:35:24 + | +LL | fn vals(_: Bad) -> Bad { + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:36:9 + | +LL | Bad::default() + | ^^^ help: use the applicable keyword: `Self` + error: unnecessary structure name repetition --> $DIR/use_self_trait.rs:41:19 | LL | type Output = Bad; | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:43:23 | - = note: `-D clippy::use-self` implied by `-D warnings` +LL | fn mul(self, rhs: Bad) -> Bad { + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self_trait.rs:43:31 + | +LL | fn mul(self, rhs: Bad) -> Bad { + | ^^^ help: use the applicable keyword: `Self` -error: aborting due to previous error +error: aborting due to 14 previous errors -- cgit 1.4.1-3-g733a5 From da65d8166fe50a817a49d542460986234e94dc6d Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sun, 17 Jan 2021 19:09:24 +0100 Subject: Don't trigger use_self in macros --- clippy_lints/src/use_self.rs | 7 ++- tests/ui/auxiliary/proc_macro_derive.rs | 12 +++++ tests/ui/use_self.fixed | 13 ++++-- tests/ui/use_self.rs | 9 +++- tests/ui/use_self.stderr | 82 ++++++++++++--------------------- 5 files changed, 63 insertions(+), 60 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index fbd996ad0e9..3dfec190541 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -1,4 +1,4 @@ -use crate::utils::{meets_msrv, qpath_res, snippet_opt, span_lint_and_sugg}; +use crate::utils::{in_macro, meets_msrv, qpath_res, snippet_opt, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; @@ -13,7 +13,6 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; -use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{AssocKind, Ty}; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -232,7 +231,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { } fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) { - if in_external_macro(cx.sess(), hir_ty.span) + if in_macro(hir_ty.span) | in_impl(cx, hir_ty) | self.types_to_skip.contains(&hir_ty.hir_id) | !meets_msrv(self.msrv.as_ref(), &USE_SELF_MSRV) @@ -274,7 +273,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { } } - if in_external_macro(cx.sess(), expr.span) | !meets_msrv(self.msrv.as_ref(), &USE_SELF_MSRV) { + if in_macro(expr.span) | !meets_msrv(self.msrv.as_ref(), &USE_SELF_MSRV) { return; } diff --git a/tests/ui/auxiliary/proc_macro_derive.rs b/tests/ui/auxiliary/proc_macro_derive.rs index 24891682d36..aebeaf34679 100644 --- a/tests/ui/auxiliary/proc_macro_derive.rs +++ b/tests/ui/auxiliary/proc_macro_derive.rs @@ -41,3 +41,15 @@ pub fn derive_foo(_input: TokenStream) -> TokenStream { } } } + +#[proc_macro_derive(StructAUseSelf)] +pub fn derive_use_self(_input: TokenStream) -> proc_macro::TokenStream { + quote! { + struct A; + impl A { + fn new() -> A { + A + } + } + } +} diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed index 1632e6aca44..95e7bc75431 100644 --- a/tests/ui/use_self.fixed +++ b/tests/ui/use_self.fixed @@ -1,10 +1,14 @@ // run-rustfix // edition:2018 +// aux-build:proc_macro_derive.rs #![warn(clippy::use_self)] #![allow(dead_code)] #![allow(clippy::should_implement_trait, clippy::upper_case_acronyms, clippy::from_over_into)] +#[macro_use] +extern crate proc_macro_derive; + fn main() {} mod use_self { @@ -109,8 +113,8 @@ mod tuple_structs { mod macros { macro_rules! use_self_expand { () => { - fn new() -> Self { - Self {} + fn new() -> Foo { + Foo {} } }; } @@ -118,8 +122,11 @@ mod macros { struct Foo {} impl Foo { - use_self_expand!(); // Should lint in local macros + use_self_expand!(); // Should not lint in local macros } + + #[derive(StructAUseSelf)] // Should not lint in derives + struct A; } mod nesting { diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index bbe92c9e338..75424f34159 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -1,10 +1,14 @@ // run-rustfix // edition:2018 +// aux-build:proc_macro_derive.rs #![warn(clippy::use_self)] #![allow(dead_code)] #![allow(clippy::should_implement_trait, clippy::upper_case_acronyms, clippy::from_over_into)] +#[macro_use] +extern crate proc_macro_derive; + fn main() {} mod use_self { @@ -118,8 +122,11 @@ mod macros { struct Foo {} impl Foo { - use_self_expand!(); // Should lint in local macros + use_self_expand!(); // Should not lint in local macros } + + #[derive(StructAUseSelf)] // Should not lint in derives + struct A; } mod nesting { diff --git a/tests/ui/use_self.stderr b/tests/ui/use_self.stderr index d86453eb2f0..37dfef7cfe0 100644 --- a/tests/ui/use_self.stderr +++ b/tests/ui/use_self.stderr @@ -1,5 +1,5 @@ error: unnecessary structure name repetition - --> $DIR/use_self.rs:14:21 + --> $DIR/use_self.rs:18:21 | LL | fn new() -> Foo { | ^^^ help: use the applicable keyword: `Self` @@ -7,194 +7,172 @@ LL | fn new() -> Foo { = note: `-D clippy::use-self` implied by `-D warnings` error: unnecessary structure name repetition - --> $DIR/use_self.rs:15:13 + --> $DIR/use_self.rs:19:13 | LL | Foo {} | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:17:22 + --> $DIR/use_self.rs:21:22 | LL | fn test() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:18:13 + --> $DIR/use_self.rs:22:13 | LL | Foo::new() | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:23:25 + --> $DIR/use_self.rs:27:25 | LL | fn default() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:24:13 + --> $DIR/use_self.rs:28:13 | LL | Foo::new() | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:89:24 + --> $DIR/use_self.rs:93:24 | LL | fn bad(foos: &[Foo]) -> impl Iterator { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:89:55 + --> $DIR/use_self.rs:93:55 | LL | fn bad(foos: &[Foo]) -> impl Iterator { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:104:13 + --> $DIR/use_self.rs:108:13 | LL | TS(0) | ^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:112:25 - | -LL | fn new() -> Foo { - | ^^^ help: use the applicable keyword: `Self` -... -LL | use_self_expand!(); // Should lint in local macros - | ------------------- in this macro invocation - | - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: unnecessary structure name repetition - --> $DIR/use_self.rs:113:17 - | -LL | Foo {} - | ^^^ help: use the applicable keyword: `Self` -... -LL | use_self_expand!(); // Should lint in local macros - | ------------------- in this macro invocation - | - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: unnecessary structure name repetition - --> $DIR/use_self.rs:136:29 + --> $DIR/use_self.rs:143:29 | LL | fn bar() -> Bar { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:137:21 + --> $DIR/use_self.rs:144:21 | LL | Bar { foo: Foo {} } | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:148:21 + --> $DIR/use_self.rs:155:21 | LL | fn baz() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:149:13 + --> $DIR/use_self.rs:156:13 | LL | Foo {} | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:166:21 + --> $DIR/use_self.rs:173:21 | LL | let _ = Enum::B(42); | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:167:21 + --> $DIR/use_self.rs:174:21 | LL | let _ = Enum::C { field: true }; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:168:21 + --> $DIR/use_self.rs:175:21 | LL | let _ = Enum::A; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:210:13 + --> $DIR/use_self.rs:217:13 | LL | nested::A::fun_1(); | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:211:13 + --> $DIR/use_self.rs:218:13 | LL | nested::A::A; | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:213:13 + --> $DIR/use_self.rs:220:13 | LL | nested::A {}; | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:232:13 + --> $DIR/use_self.rs:239:13 | LL | TestStruct::from_something() | ^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:246:25 + --> $DIR/use_self.rs:253:25 | LL | async fn g() -> S { | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:247:13 + --> $DIR/use_self.rs:254:13 | LL | S {} | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:251:16 + --> $DIR/use_self.rs:258:16 | LL | &p[S::A..S::B] | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:251:22 + --> $DIR/use_self.rs:258:22 | LL | &p[S::A..S::B] | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:274:29 + --> $DIR/use_self.rs:281:29 | LL | fn foo(value: T) -> Foo { | ^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:275:13 + --> $DIR/use_self.rs:282:13 | LL | Foo { value } | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:312:21 + --> $DIR/use_self.rs:319:21 | LL | type From = T::From; | ^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:313:19 + --> $DIR/use_self.rs:320:19 | LL | type To = T::To; | ^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:450:13 + --> $DIR/use_self.rs:457:13 | LL | A::new::(submod::B {}) | ^ help: use the applicable keyword: `Self` -error: aborting due to 31 previous errors +error: aborting due to 29 previous errors -- cgit 1.4.1-3-g733a5 From 37f978299e514c977e747d0212fa72c2b1a86d11 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Wed, 10 Feb 2021 20:55:31 +0100 Subject: Add test for checking a combination of unreachable and panic. --- tests/ui/doc_panics.rs | 22 ++++++++++++++++++++++ tests/ui/doc_panics.stderr | 21 ++++++++++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/ui/doc_panics.rs b/tests/ui/doc_panics.rs index a3b9cbb5114..3008c2d5b85 100644 --- a/tests/ui/doc_panics.rs +++ b/tests/ui/doc_panics.rs @@ -28,6 +28,15 @@ pub fn inner_body(opt: Option) { }); } +/// This needs to be documented +pub fn unreachable_and_panic() { + if true { + unreachable!() + } else { + panic!() + } +} + /// This is documented /// /// # Panics @@ -69,6 +78,19 @@ pub fn todo_documented() { todo!() } +/// This is documented +/// +/// # Panics +/// +/// We still need to do this part +pub fn unreachable_amd_panic_documented() { + if true { + unreachable!() + } else { + panic!() + } +} + /// This is okay because it is private fn unwrap_private() { let result = Err("Hi"); diff --git a/tests/ui/doc_panics.stderr b/tests/ui/doc_panics.stderr index c0c4e9e4fa7..287148690d2 100644 --- a/tests/ui/doc_panics.stderr +++ b/tests/ui/doc_panics.stderr @@ -63,5 +63,24 @@ LL | panic!() | ^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 4 previous errors +error: docs for function which may panic missing `# Panics` section + --> $DIR/doc_panics.rs:32:1 + | +LL | / pub fn unreachable_and_panic() { +LL | | if true { +LL | | unreachable!() +LL | | } else { +LL | | panic!() +LL | | } +LL | | } + | |_^ + | +note: first possible panic found here + --> $DIR/doc_panics.rs:36:9 + | +LL | panic!() + | ^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 5 previous errors -- cgit 1.4.1-3-g733a5 From b80ac2af9c0939428054e7d018f19fc16b920b05 Mon Sep 17 00:00:00 2001 From: boolean_coercion Date: Wed, 10 Feb 2021 21:47:04 +0200 Subject: Added boilerplate --- CHANGELOG.md | 1 + clippy_lints/src/from_str_radix_10.rs | 34 ++++++++++++++++++++++++++++++++++ clippy_lints/src/lib.rs | 4 ++++ tests/ui/from_str_radix_10.rs | 5 +++++ 4 files changed, 44 insertions(+) create mode 100644 clippy_lints/src/from_str_radix_10.rs create mode 100644 tests/ui/from_str_radix_10.rs (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 4dcf9c3529b..56b74a7d0ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2092,6 +2092,7 @@ Released 2018-09-13 [`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref [`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 [`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send [`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len [`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs new file mode 100644 index 00000000000..ec2a60ec47c --- /dev/null +++ b/clippy_lints/src/from_str_radix_10.rs @@ -0,0 +1,34 @@ +use rustc_lint::{EarlyLintPass, EarlyContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_ast::ast::*; + +declare_clippy_lint! { + /// **What it does:** + /// Checks for function invocations of the form `primitive::from_str_radix(s, 10)` + /// + /// **Why is this bad?** + /// This specific common use case can be rewritten as `s.parse::()` + /// (and in most cases, the turbofish can be removed), which reduces code length + /// and complexity. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let input: &str = get_input(); + /// let num = u16::from_str_radix(input, 10)?; + /// ``` + /// Use instead: + /// ```rust + /// let input: &str = get_input(); + /// let num: u16 = input.parse()?; + /// ``` + pub FROM_STR_RADIX_10, + style, + "default lint description" +} + +declare_lint_pass!(FromStrRadix10 => [FROM_STR_RADIX_10]); + +impl EarlyLintPass for FromStrRadix10 {} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d96911fac1a..5b84422458f 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -210,6 +210,7 @@ mod floating_point_arithmetic; mod format; mod formatting; mod from_over_into; +mod from_str_radix_10; mod functions; mod future_not_send; mod get_last_with_len; @@ -637,6 +638,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &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, @@ -1468,6 +1470,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), @@ -1724,6 +1727,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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::RESULT_UNIT_ERR), diff --git a/tests/ui/from_str_radix_10.rs b/tests/ui/from_str_radix_10.rs new file mode 100644 index 00000000000..70eaa8d666c --- /dev/null +++ b/tests/ui/from_str_radix_10.rs @@ -0,0 +1,5 @@ +#![warn(clippy::from_str_radix_10)] + +fn main() { + // test code goes here +} -- cgit 1.4.1-3-g733a5 From 64729390a1b2b6b05f8a4407658163ddff4d017e Mon Sep 17 00:00:00 2001 From: boolean_coercion Date: Thu, 11 Feb 2021 02:30:37 +0200 Subject: Implemented majority of from_str_radix_10 --- clippy_lints/src/from_str_radix_10.rs | 59 ++++++++++++++++++++++++++++++++--- clippy_lints/src/lib.rs | 1 + tests/ui/from_str_radix_10.rs | 32 +++++++++++++++++-- 3 files changed, 86 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs index ec2a60ec47c..612ea9ae62c 100644 --- a/clippy_lints/src/from_str_radix_10.rs +++ b/clippy_lints/src/from_str_radix_10.rs @@ -1,6 +1,10 @@ -use rustc_lint::{EarlyLintPass, EarlyContext}; +use rustc_lint::{LateLintPass, LateContext}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_ast::ast::*; +use rustc_hir::*; +use rustc_errors::Applicability; +use if_chain::if_chain; + +use crate::utils::span_lint_and_sugg; declare_clippy_lint! { /// **What it does:** @@ -26,9 +30,56 @@ declare_clippy_lint! { /// ``` pub FROM_STR_RADIX_10, style, - "default lint description" + "from_str_radix with radix 10" } declare_lint_pass!(FromStrRadix10 => [FROM_STR_RADIX_10]); -impl EarlyLintPass for FromStrRadix10 {} +impl LateLintPass<'tcx> for FromStrRadix10 { + fn check_expr(&mut self, cx: &LateContext<'tcx>, exp: &Expr<'tcx>) { + if_chain! { + if let ExprKind::Call(maybe_path, arguments) = &exp.kind; + if let ExprKind::Path(qpath) = &maybe_path.kind; + if let QPath::TypeRelative(ty, pathseg) = &qpath; + + // check if the first part of the path is some integer primitive + if let TyKind::Path(ty_qpath) = &ty.kind; + let ty_res = cx.qpath_res(ty_qpath, ty.hir_id); + if let def::Res::PrimTy(prim_ty) = ty_res; + if is_primitive_integer_ty(prim_ty); + + // check if the second part of the path indeed calls the associated + // function `from_str_radix` + if pathseg.ident.name.as_str() == "from_str_radix"; + + // check if the second argument resolves to a constant `10` + if arguments.len() == 2; + if is_constant_10(&arguments[1]); + + then { + span_lint_and_sugg( + cx, + FROM_STR_RADIX_10, + exp.span, + "This call to `from_str_radix` can be shortened to a call to str::parse", + "try", + format!("TODO"), + Applicability::MachineApplicable + ); + } + } + } +} + +fn is_primitive_integer_ty(ty: PrimTy) -> bool { + match ty { + PrimTy::Int(_) => true, + PrimTy::Uint(_) => true, + _ => false + } +} + +fn is_constant_10<'tcx>(expr: &Expr<'tcx>) -> bool { + // TODO + true +} \ No newline at end of file diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 5b84422458f..dae686b1229 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1258,6 +1258,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box types::PtrAsPtr::new(msrv)); store.register_late_pass(|| box case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons); store.register_late_pass(|| box redundant_slicing::RedundantSlicing); + store.register_late_pass(|| box from_str_radix_10::FromStrRadix10); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), diff --git a/tests/ui/from_str_radix_10.rs b/tests/ui/from_str_radix_10.rs index 70eaa8d666c..086616d09ff 100644 --- a/tests/ui/from_str_radix_10.rs +++ b/tests/ui/from_str_radix_10.rs @@ -1,5 +1,33 @@ #![warn(clippy::from_str_radix_10)] -fn main() { - // test code goes here +mod some_mod { + // fake function that shouldn't trigger the lint + pub fn from_str_radix(_: &str, _: u32) -> Result<(), std::num::ParseIntError> { + unimplemented!() + } } + +// fake function that shouldn't trigger the lint +fn from_str_radix(_: &str, _: u32) -> Result<(), std::num::ParseIntError> { + unimplemented!() +} + +fn main() -> Result<(), Box> { + // all of these should trigger the lint + u32::from_str_radix("30", 10)?; + i64::from_str_radix("24", 10)?; + isize::from_str_radix("100", 10)?; + u8::from_str_radix("7", 10)?; + + // none of these should trigger the lint + u16::from_str_radix("20", 3)?; + i32::from_str_radix("45", 12)?; + usize::from_str_radix("10", 16)?; + i128::from_str_radix("10", 13)?; + some_mod::from_str_radix("50", 10)?; + some_mod::from_str_radix("50", 6)?; + from_str_radix("50", 10)?; + from_str_radix("50", 6)?; + + Ok(()) +} \ No newline at end of file -- cgit 1.4.1-3-g733a5 From 0b31b470ad743fc758a1c47a17d7241948cd4a3b Mon Sep 17 00:00:00 2001 From: boolean_coercion Date: Thu, 11 Feb 2021 12:42:20 +0200 Subject: Changed applicability to MaybeIncorrect because of surrounding braces --- clippy_lints/src/from_str_radix_10.rs | 2 +- tests/ui/from_str_radix_10.rs | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs index 57661e0f9bc..9371104bf9b 100644 --- a/clippy_lints/src/from_str_radix_10.rs +++ b/clippy_lints/src/from_str_radix_10.rs @@ -66,7 +66,7 @@ impl LateLintPass<'tcx> for FromStrRadix10 { "This call to `from_str_radix` can be shortened to a call to str::parse", "try", format!("({}).parse()", orig_string), - Applicability::MachineApplicable + Applicability::MaybeIncorrect ); } } diff --git a/tests/ui/from_str_radix_10.rs b/tests/ui/from_str_radix_10.rs index 086616d09ff..795e795fcb3 100644 --- a/tests/ui/from_str_radix_10.rs +++ b/tests/ui/from_str_radix_10.rs @@ -19,6 +19,9 @@ fn main() -> Result<(), Box> { isize::from_str_radix("100", 10)?; u8::from_str_radix("7", 10)?; + let string = "300"; + i32::from_str_radix(string, 10)?; + // none of these should trigger the lint u16::from_str_radix("20", 3)?; i32::from_str_radix("45", 12)?; -- cgit 1.4.1-3-g733a5 From d1a627ab3bd801879a565cc9e68a322d8d28bcbf Mon Sep 17 00:00:00 2001 From: boolean_coercion Date: Thu, 11 Feb 2021 12:46:11 +0200 Subject: Ran bless and rustfmt --- clippy_lints/src/from_str_radix_10.rs | 14 +++++++------- tests/ui/from_str_radix_10.rs | 2 +- tests/ui/from_str_radix_10.stderr | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 8 deletions(-) create mode 100644 tests/ui/from_str_radix_10.stderr (limited to 'tests') diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs index 9371104bf9b..9a8d4542616 100644 --- a/clippy_lints/src/from_str_radix_10.rs +++ b/clippy_lints/src/from_str_radix_10.rs @@ -1,8 +1,8 @@ -use rustc_lint::{LateLintPass, LateContext}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_hir::*; -use rustc_errors::Applicability; use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::*; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; use crate::utils::span_lint_and_sugg; @@ -16,7 +16,7 @@ declare_clippy_lint! { /// and complexity. /// /// **Known problems:** None. - /// + /// /// **Example:** /// /// ```rust @@ -77,6 +77,6 @@ fn is_primitive_integer_ty(ty: PrimTy) -> bool { match ty { PrimTy::Int(_) => true, PrimTy::Uint(_) => true, - _ => false + _ => false, } -} \ No newline at end of file +} diff --git a/tests/ui/from_str_radix_10.rs b/tests/ui/from_str_radix_10.rs index 795e795fcb3..2d8106da7ba 100644 --- a/tests/ui/from_str_radix_10.rs +++ b/tests/ui/from_str_radix_10.rs @@ -33,4 +33,4 @@ fn main() -> Result<(), Box> { from_str_radix("50", 6)?; Ok(()) -} \ No newline at end of file +} diff --git a/tests/ui/from_str_radix_10.stderr b/tests/ui/from_str_radix_10.stderr new file mode 100644 index 00000000000..376d0dd56ea --- /dev/null +++ b/tests/ui/from_str_radix_10.stderr @@ -0,0 +1,34 @@ +error: This call to `from_str_radix` can be shortened to a call to str::parse + --> $DIR/from_str_radix_10.rs:17:5 + | +LL | u32::from_str_radix("30", 10)?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `("30").parse()` + | + = note: `-D clippy::from-str-radix-10` implied by `-D warnings` + +error: This call to `from_str_radix` can be shortened to a call to str::parse + --> $DIR/from_str_radix_10.rs:18:5 + | +LL | i64::from_str_radix("24", 10)?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `("24").parse()` + +error: This call to `from_str_radix` can be shortened to a call to str::parse + --> $DIR/from_str_radix_10.rs:19:5 + | +LL | isize::from_str_radix("100", 10)?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `("100").parse()` + +error: This call to `from_str_radix` can be shortened to a call to str::parse + --> $DIR/from_str_radix_10.rs:20:5 + | +LL | u8::from_str_radix("7", 10)?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `("7").parse()` + +error: This call to `from_str_radix` can be shortened to a call to str::parse + --> $DIR/from_str_radix_10.rs:23:5 + | +LL | i32::from_str_radix(string, 10)?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(string).parse()` + +error: aborting due to 5 previous errors + -- cgit 1.4.1-3-g733a5 From 642efabfbbba6577a3698a305bac8ce0c693e67f Mon Sep 17 00:00:00 2001 From: boolean_coercion Date: Fri, 12 Feb 2021 11:53:52 +0200 Subject: Fixed typos and updated to matches! where applicable --- clippy_lints/src/from_str_radix_10.rs | 12 ++---------- tests/ui/from_str_radix_10.stderr | 10 +++++----- 2 files changed, 7 insertions(+), 15 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs index 53cb1e3ecd9..de9add4b6b6 100644 --- a/clippy_lints/src/from_str_radix_10.rs +++ b/clippy_lints/src/from_str_radix_10.rs @@ -46,7 +46,7 @@ impl LateLintPass<'tcx> for FromStrRadix10 { if let TyKind::Path(ty_qpath) = &ty.kind; let ty_res = cx.qpath_res(ty_qpath, ty.hir_id); if let def::Res::PrimTy(prim_ty) = ty_res; - if is_primitive_integer_ty(prim_ty); + if matches!(prim_ty, PrimTy::Int(_) | PrimTy::Uint(_)); // check if the second part of the path indeed calls the associated // function `from_str_radix` @@ -63,7 +63,7 @@ impl LateLintPass<'tcx> for FromStrRadix10 { cx, FROM_STR_RADIX_10, exp.span, - "This call to `from_str_radix` can be shortened to a call to str::parse", + "this call to `from_str_radix` can be replaced with a call to `str::parse`", "try", format!("({}).parse()", orig_string), Applicability::MaybeIncorrect @@ -72,11 +72,3 @@ impl LateLintPass<'tcx> for FromStrRadix10 { } } } - -fn is_primitive_integer_ty(ty: PrimTy) -> bool { - match ty { - PrimTy::Int(_) => true, - PrimTy::Uint(_) => true, - _ => false, - } -} diff --git a/tests/ui/from_str_radix_10.stderr b/tests/ui/from_str_radix_10.stderr index 376d0dd56ea..5557cd3b9ef 100644 --- a/tests/ui/from_str_radix_10.stderr +++ b/tests/ui/from_str_radix_10.stderr @@ -1,4 +1,4 @@ -error: This call to `from_str_radix` can be shortened to a call to str::parse +error: this call to `from_str_radix` can be replaced with a call to `str::parse` --> $DIR/from_str_radix_10.rs:17:5 | LL | u32::from_str_radix("30", 10)?; @@ -6,25 +6,25 @@ LL | u32::from_str_radix("30", 10)?; | = note: `-D clippy::from-str-radix-10` implied by `-D warnings` -error: This call to `from_str_radix` can be shortened to a call to str::parse +error: this call to `from_str_radix` can be replaced with a call to `str::parse` --> $DIR/from_str_radix_10.rs:18:5 | LL | i64::from_str_radix("24", 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `("24").parse()` -error: This call to `from_str_radix` can be shortened to a call to str::parse +error: this call to `from_str_radix` can be replaced with a call to `str::parse` --> $DIR/from_str_radix_10.rs:19:5 | LL | isize::from_str_radix("100", 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `("100").parse()` -error: This call to `from_str_radix` can be shortened to a call to str::parse +error: this call to `from_str_radix` can be replaced with a call to `str::parse` --> $DIR/from_str_radix_10.rs:20:5 | LL | u8::from_str_radix("7", 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `("7").parse()` -error: This call to `from_str_radix` can be shortened to a call to str::parse +error: this call to `from_str_radix` can be replaced with a call to `str::parse` --> $DIR/from_str_radix_10.rs:23:5 | LL | i32::from_str_radix(string, 10)?; -- cgit 1.4.1-3-g733a5 From d36fe8556962467545f5e92bf896b6672f4e88ae Mon Sep 17 00:00:00 2001 From: boolean_coercion Date: Sat, 13 Feb 2021 00:33:08 +0200 Subject: Made parens addition smarter and added tests with bless --- clippy_lints/src/from_str_radix_10.rs | 11 +++++++++-- tests/ui/from_str_radix_10.rs | 13 +++++++++++++ tests/ui/from_str_radix_10.stderr | 34 +++++++++++++++++++++++----------- 3 files changed, 45 insertions(+), 13 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs index de9add4b6b6..993b85ed998 100644 --- a/clippy_lints/src/from_str_radix_10.rs +++ b/clippy_lints/src/from_str_radix_10.rs @@ -5,6 +5,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use crate::utils::span_lint_and_sugg; +use crate::utils::sugg::Sugg; declare_clippy_lint! { /// **What it does:** @@ -58,14 +59,20 @@ impl LateLintPass<'tcx> for FromStrRadix10 { if let rustc_ast::ast::LitKind::Int(10, _) = lit.node; then { - let orig_string = crate::utils::snippet(cx, arguments[0].span, "string"); + let sugg = Sugg::hir_with_applicability( + cx, + &arguments[0], + "", + &mut Applicability::MachineApplicable + ).maybe_par(); + span_lint_and_sugg( cx, FROM_STR_RADIX_10, exp.span, "this call to `from_str_radix` can be replaced with a call to `str::parse`", "try", - format!("({}).parse()", orig_string), + format!("{}.parse::<{}>()", sugg, prim_ty.name_str()), Applicability::MaybeIncorrect ); } diff --git a/tests/ui/from_str_radix_10.rs b/tests/ui/from_str_radix_10.rs index 2d8106da7ba..0a973128664 100644 --- a/tests/ui/from_str_radix_10.rs +++ b/tests/ui/from_str_radix_10.rs @@ -12,12 +12,25 @@ fn from_str_radix(_: &str, _: u32) -> Result<(), std::num::ParseIntError> { unimplemented!() } +// to test parenthesis addition +struct Test; + +impl std::ops::Add for Test { + type Output = &'static str; + + fn add(self, _: Self) -> Self::Output { + "304" + } +} + fn main() -> Result<(), Box> { // all of these should trigger the lint u32::from_str_radix("30", 10)?; i64::from_str_radix("24", 10)?; isize::from_str_radix("100", 10)?; u8::from_str_radix("7", 10)?; + u16::from_str_radix(&("10".to_owned() + "5"), 10)?; + i128::from_str_radix(Test + Test, 10)?; let string = "300"; i32::from_str_radix(string, 10)?; diff --git a/tests/ui/from_str_radix_10.stderr b/tests/ui/from_str_radix_10.stderr index 5557cd3b9ef..d6669001915 100644 --- a/tests/ui/from_str_radix_10.stderr +++ b/tests/ui/from_str_radix_10.stderr @@ -1,34 +1,46 @@ error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> $DIR/from_str_radix_10.rs:17:5 + --> $DIR/from_str_radix_10.rs:28:5 | LL | u32::from_str_radix("30", 10)?; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `("30").parse()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"30".parse::()` | = note: `-D clippy::from-str-radix-10` implied by `-D warnings` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> $DIR/from_str_radix_10.rs:18:5 + --> $DIR/from_str_radix_10.rs:29:5 | LL | i64::from_str_radix("24", 10)?; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `("24").parse()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"24".parse::()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> $DIR/from_str_radix_10.rs:19:5 + --> $DIR/from_str_radix_10.rs:30:5 | LL | isize::from_str_radix("100", 10)?; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `("100").parse()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"100".parse::()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> $DIR/from_str_radix_10.rs:20:5 + --> $DIR/from_str_radix_10.rs:31:5 | LL | u8::from_str_radix("7", 10)?; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `("7").parse()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"7".parse::()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` - --> $DIR/from_str_radix_10.rs:23:5 + --> $DIR/from_str_radix_10.rs:32:5 + | +LL | u16::from_str_radix(&("10".to_owned() + "5"), 10)?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(&("10".to_owned() + "5")).parse::()` + +error: this call to `from_str_radix` can be replaced with a call to `str::parse` + --> $DIR/from_str_radix_10.rs:33:5 + | +LL | i128::from_str_radix(Test + Test, 10)?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(Test + Test).parse::()` + +error: this call to `from_str_radix` can be replaced with a call to `str::parse` + --> $DIR/from_str_radix_10.rs:36:5 | LL | i32::from_str_radix(string, 10)?; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(string).parse()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.parse::()` -error: aborting due to 5 previous errors +error: aborting due to 7 previous errors -- cgit 1.4.1-3-g733a5 From 87109bb4f599bb8ac7b1c3a970337ac92cb3031c Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Sat, 13 Feb 2021 22:12:59 +0900 Subject: Add minimal reproducer for ICE in #6179 --- tests/ui/crashes/ice-6179.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 tests/ui/crashes/ice-6179.rs (limited to 'tests') diff --git a/tests/ui/crashes/ice-6179.rs b/tests/ui/crashes/ice-6179.rs new file mode 100644 index 00000000000..f8c866a49aa --- /dev/null +++ b/tests/ui/crashes/ice-6179.rs @@ -0,0 +1,21 @@ +//! This is a minimal reproducer for the ICE in https://github.com/rust-lang/rust-clippy/pull/6179. +//! The ICE is mainly caused by using `hir_ty_to_ty`. See the discussion in the PR for details. + +#![warn(clippy::use_self)] +#![allow(dead_code)] + +struct Foo {} + +impl Foo { + fn foo() -> Self { + impl Foo { + fn bar() {} + } + + let _: _ = 1; + + Self {} + } +} + +fn main() {} -- cgit 1.4.1-3-g733a5 From 12025506d61bc05a10455ae96a623515663c09fe Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 14 Feb 2021 16:21:12 +0100 Subject: Do not lint when the closure is called using an iterator --- clippy_lints/src/blocks_in_if_conditions.rs | 18 +++++++++++++++++- tests/ui/blocks_in_if_conditions_closure.rs | 11 ++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/blocks_in_if_conditions.rs b/clippy_lints/src/blocks_in_if_conditions.rs index 4efca10bcdf..b53f80fd8bc 100644 --- a/clippy_lints/src/blocks_in_if_conditions.rs +++ b/clippy_lints/src/blocks_in_if_conditions.rs @@ -1,4 +1,8 @@ -use crate::utils::{differing_macro_contexts, snippet_block_with_applicability, span_lint, span_lint_and_sugg}; +use crate::utils::{ + differing_macro_contexts, get_parent_expr, get_trait_def_id, implements_trait, paths, + snippet_block_with_applicability, span_lint, span_lint_and_sugg, +}; +use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::{BlockCheckMode, Expr, ExprKind}; @@ -52,6 +56,18 @@ impl<'a, 'tcx> Visitor<'tcx> for ExVisitor<'a, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { if let ExprKind::Closure(_, _, eid, _, _) = expr.kind { + // do not lint if the closure is called using an iterator (see #1141) + if_chain! { + if let Some(parent) = get_parent_expr(self.cx, expr); + if let ExprKind::MethodCall(_, _, args, _) = parent.kind; + let caller = self.cx.typeck_results().expr_ty(&args[0]); + if let Some(iter_id) = get_trait_def_id(self.cx, &paths::ITERATOR); + if implements_trait(self.cx, caller, iter_id, &[]); + then { + return; + } + } + let body = self.cx.tcx.hir().body(eid); let ex = &body.value; if matches!(ex.kind, ExprKind::Block(_, _)) && !body.value.span.from_expansion() { diff --git a/tests/ui/blocks_in_if_conditions_closure.rs b/tests/ui/blocks_in_if_conditions_closure.rs index acbabfa20d7..2856943b9be 100644 --- a/tests/ui/blocks_in_if_conditions_closure.rs +++ b/tests/ui/blocks_in_if_conditions_closure.rs @@ -44,4 +44,13 @@ fn macro_in_closure() { } } -fn main() {} +#[rustfmt::skip] +fn main() { + let mut range = 0..10; + range.all(|i| {i < 10} ); + + let v = vec![1, 2, 3]; + if v.into_iter().any(|x| {x == 4}) { + println!("contains 4!"); + } +} -- cgit 1.4.1-3-g733a5 From 93796b2346a1177809628ea141f0dd958f4c4d29 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Mon, 15 Feb 2021 14:22:31 +0900 Subject: Add some restrictions to default_numeric_fallback to avoid FNs --- clippy_lints/src/default_numeric_fallback.rs | 171 +++++++++++++++++++++------ clippy_lints/src/lib.rs | 2 +- tests/ui/default_numeric_fallback.rs | 40 +++++++ tests/ui/default_numeric_fallback.stderr | 50 +++++++- 4 files changed, 218 insertions(+), 45 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index b3b5a4a8251..3102d032057 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -1,9 +1,14 @@ -use rustc_ast::{LitFloatType, LitIntType, LitKind}; -use rustc_data_structures::fx::FxHashSet; -use rustc_hir::{Expr, ExprKind, HirId, Stmt, StmtKind}; +use rustc_ast::ast::{LitFloatType, LitIntType, LitKind}; +use rustc_hir::{ + intravisit::{walk_expr, walk_stmt, NestedVisitorMap, Visitor}, + Body, Expr, ExprKind, Lit, Stmt, StmtKind, +}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, FloatTy, IntTy}; -use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_middle::{ + hir::map::Map, + ty::{self, FloatTy, IntTy, Ty}, +}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; use if_chain::if_chain; @@ -33,53 +38,141 @@ declare_clippy_lint! { /// Use instead: /// ```rust /// let i = 10i32; - /// let f: f64 = 1.23; + /// let f = 1.23f64; /// ``` pub DEFAULT_NUMERIC_FALLBACK, restriction, "usage of unconstrained numeric literals which may cause default numeric fallback." } -#[derive(Default)] -pub struct DefaultNumericFallback { - /// Hold `init` in `Local` if `Local` has a type annotation. - bounded_inits: FxHashSet, +declare_lint_pass!(DefaultNumericFallback => [DEFAULT_NUMERIC_FALLBACK]); + +impl LateLintPass<'_> for DefaultNumericFallback { + fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) { + let mut visitor = NumericFallbackVisitor::new(cx); + visitor.visit_body(body); + } } -impl_lint_pass!(DefaultNumericFallback => [DEFAULT_NUMERIC_FALLBACK]); +struct NumericFallbackVisitor<'a, 'tcx> { + /// Stack manages type bound of exprs. The top element holds current expr type. + ty_bounds: Vec>, -impl LateLintPass<'_> for DefaultNumericFallback { - fn check_stmt(&mut self, _: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - if_chain! { - if let StmtKind::Local(local) = stmt.kind; - if local.ty.is_some(); - if let Some(init) = local.init; - then { - self.bounded_inits.insert(init.hir_id); - } + cx: &'a LateContext<'tcx>, +} + +impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { + fn new(cx: &'a LateContext<'tcx>) -> Self { + Self { + ty_bounds: vec![TyBound::Nothing], + cx, } } - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let expr_ty = cx.typeck_results().expr_ty(expr); - let hir_id = expr.hir_id; + /// Check whether a passed literal has potential to cause fallback or not. + fn check_lit(&self, lit: &Lit, lit_ty: Ty<'tcx>) { + let ty_bound = self.ty_bounds.last().unwrap(); if_chain! { - if let ExprKind::Lit(ref lit) = expr.kind; - if matches!(lit.node, - LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed)); - if matches!(expr_ty.kind(), ty::Int(IntTy::I32) | ty::Float(FloatTy::F64)); - if !self.bounded_inits.contains(&hir_id); - if !cx.tcx.hir().parent_iter(hir_id).any(|(ref hir_id, _)| self.bounded_inits.contains(hir_id)); - then { - span_lint_and_help( - cx, - DEFAULT_NUMERIC_FALLBACK, - lit.span, - "default numeric fallback might occur", - None, - "consider adding suffix to avoid default numeric fallback", - ) - } + if matches!(lit.node, + LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed)); + if matches!(lit_ty.kind(), ty::Int(IntTy::I32) | ty::Float(FloatTy::F64)); + if !ty_bound.is_integral(); + then { + span_lint_and_help( + self.cx, + DEFAULT_NUMERIC_FALLBACK, + lit.span, + "default numeric fallback might occur", + None, + "consider adding suffix to avoid default numeric fallback", + ); + } + } + } +} + +impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + #[allow(clippy::too_many_lines)] + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { + match &expr.kind { + ExprKind::Call(func, args) => { + if_chain! { + if let ExprKind::Path(ref func_path) = func.kind; + if let Some(def_id) = self.cx.qpath_res(func_path, func.hir_id).opt_def_id(); + then { + let fn_sig = self.cx.tcx.fn_sig(def_id).skip_binder(); + for (expr, bound) in args.iter().zip(fn_sig.inputs().iter()) { + // Push found arg type, then visit arg. + self.ty_bounds.push(TyBound::Ty(bound)); + self.visit_expr(expr); + self.ty_bounds.pop(); + } + return; + } + } + }, + + ExprKind::MethodCall(_, _, args, _) => { + if let Some(def_id) = self.cx.typeck_results().type_dependent_def_id(expr.hir_id) { + let fn_sig = self.cx.tcx.fn_sig(def_id).skip_binder(); + for (expr, bound) in args.iter().zip(fn_sig.inputs().iter()) { + self.ty_bounds.push(TyBound::Ty(bound)); + self.visit_expr(expr); + self.ty_bounds.pop(); + } + return; + } + }, + + ExprKind::Lit(lit) => { + let ty = self.cx.typeck_results().expr_ty(expr); + self.check_lit(lit, ty); + return; + }, + + _ => {}, + } + + walk_expr(self, expr); + } + + fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) { + match stmt.kind { + StmtKind::Local(local) => { + if local.ty.is_some() { + self.ty_bounds.push(TyBound::Any) + } else { + self.ty_bounds.push(TyBound::Nothing) + } + }, + + _ => self.ty_bounds.push(TyBound::Nothing), + } + + walk_stmt(self, stmt); + self.ty_bounds.pop(); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +#[derive(Debug, Clone, Copy)] +enum TyBound<'ctx> { + Any, + Ty(Ty<'ctx>), + Nothing, +} + +impl<'ctx> TyBound<'ctx> { + fn is_integral(self) -> bool { + match self { + TyBound::Any => true, + TyBound::Ty(t) => t.is_integral(), + TyBound::Nothing => false, } } } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 6590613b93a..642c1e68dac 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1028,7 +1028,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box strings::StringAdd); store.register_late_pass(|| box implicit_return::ImplicitReturn); store.register_late_pass(|| box implicit_saturating_sub::ImplicitSaturatingSub); - store.register_late_pass(|| box default_numeric_fallback::DefaultNumericFallback::default()); + store.register_late_pass(|| box default_numeric_fallback::DefaultNumericFallback); let msrv = conf.msrv.as_ref().and_then(|s| { parse_msrv(s, None, None).or_else(|| { diff --git a/tests/ui/default_numeric_fallback.rs b/tests/ui/default_numeric_fallback.rs index 5ca5bc1e907..420c1f0c931 100644 --- a/tests/ui/default_numeric_fallback.rs +++ b/tests/ui/default_numeric_fallback.rs @@ -1,12 +1,46 @@ #![warn(clippy::default_numeric_fallback)] #![allow(unused)] +#![allow(clippy::never_loop)] +#![allow(clippy::no_effect)] +#![allow(clippy::unnecessary_operation)] + +fn concrete_arg(x: i32) {} + +fn generic_arg(t: T) {} + +struct ConcreteStruct { + x: i32, +} + +struct StructForMethodCallTest { + x: i32, +} + +impl StructForMethodCallTest { + fn concrete_arg(&self, x: i32) {} + + fn generic_arg(&self, t: T) {} +} fn main() { + let s = StructForMethodCallTest { x: 10_i32 }; + // Bad. let x = 1; let x = 0.1; + let x = if true { 1 } else { 2 }; + let x: _ = { + let y = 1; + 1 + }; + + generic_arg(10); + s.generic_arg(10); + let x: _ = generic_arg(10); + let x: _ = s.generic_arg(10); + // Good. let x = 1_i32; let x: i32 = 1; @@ -14,5 +48,11 @@ fn main() { let x = 0.1_f64; let x: f64 = 0.1; let x: _ = 0.1; + let x: _ = if true { 1 } else { 2 }; + + concrete_arg(10); + s.concrete_arg(10); + let x = concrete_arg(10); + let x = s.concrete_arg(10); } diff --git a/tests/ui/default_numeric_fallback.stderr b/tests/ui/default_numeric_fallback.stderr index c36409a052c..cb7c174ad8d 100644 --- a/tests/ui/default_numeric_fallback.stderr +++ b/tests/ui/default_numeric_fallback.stderr @@ -1,5 +1,5 @@ error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:6:13 + --> $DIR/default_numeric_fallback.rs:29:13 | LL | let x = 1; | ^ @@ -8,7 +8,7 @@ LL | let x = 1; = help: consider adding suffix to avoid default numeric fallback error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:7:13 + --> $DIR/default_numeric_fallback.rs:30:13 | LL | let x = 0.1; | ^^^ @@ -16,7 +16,7 @@ LL | let x = 0.1; = help: consider adding suffix to avoid default numeric fallback error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:8:23 + --> $DIR/default_numeric_fallback.rs:32:23 | LL | let x = if true { 1 } else { 2 }; | ^ @@ -24,12 +24,52 @@ LL | let x = if true { 1 } else { 2 }; = help: consider adding suffix to avoid default numeric fallback error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:8:34 + --> $DIR/default_numeric_fallback.rs:32:34 | LL | let x = if true { 1 } else { 2 }; | ^ | = help: consider adding suffix to avoid default numeric fallback -error: aborting due to 4 previous errors +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:35:17 + | +LL | let y = 1; + | ^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:39:17 + | +LL | generic_arg(10); + | ^^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:40:19 + | +LL | s.generic_arg(10); + | ^^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:41:28 + | +LL | let x: _ = generic_arg(10); + | ^^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:42:30 + | +LL | let x: _ = s.generic_arg(10); + | ^^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: aborting due to 9 previous errors -- cgit 1.4.1-3-g733a5 From fb91c76586a3a01fef6a8c49edb9e4598fbd138f Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Mon, 15 Feb 2021 23:33:27 +0900 Subject: Add more tests for default_numeric_fallback --- clippy_lints/src/default_numeric_fallback.rs | 2 +- tests/ui/default_numeric_fallback.rs | 112 +++++++++++++++++-------- tests/ui/default_numeric_fallback.stderr | 118 +++++++++++++++++++++------ 3 files changed, 169 insertions(+), 63 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index b6730afa4af..d755112a178 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -27,7 +27,7 @@ declare_clippy_lint! { /// **Why is this bad?** For those who are very careful about types, default numeric fallback /// can be a pitfall that cause unexpected runtime behavior. /// - /// **Known problems:** None. + /// **Known problems:** This lint can only be allowed at the function level or above. /// /// **Example:** /// ```rust diff --git a/tests/ui/default_numeric_fallback.rs b/tests/ui/default_numeric_fallback.rs index 420c1f0c931..c4881094469 100644 --- a/tests/ui/default_numeric_fallback.rs +++ b/tests/ui/default_numeric_fallback.rs @@ -4,55 +4,97 @@ #![allow(clippy::no_effect)] #![allow(clippy::unnecessary_operation)] -fn concrete_arg(x: i32) {} +mod basic_expr { + fn test() { + // Should lint unsuffixed literals typed `i32`. + let x = 22; + let x = [1, 2, 3]; + let x = if true { (1, 2) } else { (3, 4) }; + let x = match 1 { + 1 => 1, + _ => 2, + }; -fn generic_arg(t: T) {} + // Should lint unsuffixed literals typed `f64`. + let x = 0.12; -struct ConcreteStruct { - x: i32, + // Should NOT lint suffixed literals. + let x = 22_i32; + let x = 0.12_f64; + + // Should NOT lint literals in init expr if `Local` has a type annotation. + let x: f64 = 0.1; + let x: [i32; 3] = [1, 2, 3]; + let x: (i32, i32) = if true { (1, 2) } else { (3, 4) }; + let x: _ = 1; + } } -struct StructForMethodCallTest { - x: i32, +mod nested_local { + fn test() { + let x: _ = { + // Should lint this because this literal is not bound to any types. + let y = 1; + + // Should NOT lint this because this literal is bound to `_` of outer `Local`. + 1 + }; + } } -impl StructForMethodCallTest { - fn concrete_arg(&self, x: i32) {} +mod function_def { + fn ret_i32() -> i32 { + // Even though the output type is specified, + // this unsuffixed literal is linted to reduce heuristics and keep codebase simple. + 23 + } - fn generic_arg(&self, t: T) {} + fn test() { + // Should lint this because return type is inferred to `i32` and NOT bound to a concrete + // type. + let f = || -> _ { 1 }; + + // Even though the output type is specified, + // this unsuffixed literal is linted to reduce heuristics and keep codebase simple. + let f = || -> i32 { 1 }; + } } -fn main() { - let s = StructForMethodCallTest { x: 10_i32 }; +mod function_calls { + fn concrete_arg(x: i32) {} + + fn generic_arg(t: T) {} - // Bad. - let x = 1; - let x = 0.1; + fn test() { + // Should NOT lint this because the argument type is bound to a concrete type. + concrete_arg(1); - let x = if true { 1 } else { 2 }; + // Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type. + generic_arg(1); - let x: _ = { - let y = 1; - 1 - }; + // Should lint this because the argument type is inferred to `i32` and NOT bound to a concrete type. + let x: _ = generic_arg(1); + } +} + +mod method_calls { + struct StructForMethodCallTest {} - generic_arg(10); - s.generic_arg(10); - let x: _ = generic_arg(10); - let x: _ = s.generic_arg(10); + impl StructForMethodCallTest { + fn concrete_arg(&self, x: i32) {} - // Good. - let x = 1_i32; - let x: i32 = 1; - let x: _ = 1; - let x = 0.1_f64; - let x: f64 = 0.1; - let x: _ = 0.1; + fn generic_arg(&self, t: T) {} + } - let x: _ = if true { 1 } else { 2 }; + fn test() { + let s = StructForMethodCallTest {}; - concrete_arg(10); - s.concrete_arg(10); - let x = concrete_arg(10); - let x = s.concrete_arg(10); + // Should NOT lint this because the argument type is bound to a concrete type. + s.concrete_arg(1); + + // Should lint this because the argument type is bound to a concrete type. + s.generic_arg(1); + } } + +fn main() {} diff --git a/tests/ui/default_numeric_fallback.stderr b/tests/ui/default_numeric_fallback.stderr index cb7c174ad8d..c71d05d7993 100644 --- a/tests/ui/default_numeric_fallback.stderr +++ b/tests/ui/default_numeric_fallback.stderr @@ -1,75 +1,139 @@ error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:29:13 + --> $DIR/default_numeric_fallback.rs:10:17 | -LL | let x = 1; - | ^ +LL | let x = 22; + | ^^ | = note: `-D clippy::default-numeric-fallback` implied by `-D warnings` = help: consider adding suffix to avoid default numeric fallback error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:30:13 + --> $DIR/default_numeric_fallback.rs:11:18 + | +LL | let x = [1, 2, 3]; + | ^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:11:21 + | +LL | let x = [1, 2, 3]; + | ^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:11:24 | -LL | let x = 0.1; - | ^^^ +LL | let x = [1, 2, 3]; + | ^ | = help: consider adding suffix to avoid default numeric fallback error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:32:23 + --> $DIR/default_numeric_fallback.rs:12:28 + | +LL | let x = if true { (1, 2) } else { (3, 4) }; + | ^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:12:31 + | +LL | let x = if true { (1, 2) } else { (3, 4) }; + | ^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:12:44 + | +LL | let x = if true { (1, 2) } else { (3, 4) }; + | ^ | -LL | let x = if true { 1 } else { 2 }; + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:12:47 + | +LL | let x = if true { (1, 2) } else { (3, 4) }; + | ^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:13:23 + | +LL | let x = match 1 { | ^ | = help: consider adding suffix to avoid default numeric fallback error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:32:34 + --> $DIR/default_numeric_fallback.rs:14:13 | -LL | let x = if true { 1 } else { 2 }; - | ^ +LL | 1 => 1, + | ^ | = help: consider adding suffix to avoid default numeric fallback error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:35:17 + --> $DIR/default_numeric_fallback.rs:14:18 | -LL | let y = 1; - | ^ +LL | 1 => 1, + | ^ | = help: consider adding suffix to avoid default numeric fallback error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:39:17 + --> $DIR/default_numeric_fallback.rs:15:18 | -LL | generic_arg(10); - | ^^ +LL | _ => 2, + | ^ | = help: consider adding suffix to avoid default numeric fallback error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:40:19 + --> $DIR/default_numeric_fallback.rs:19:17 | -LL | s.generic_arg(10); - | ^^ +LL | let x = 0.12; + | ^^^^ | = help: consider adding suffix to avoid default numeric fallback error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:41:28 + --> $DIR/default_numeric_fallback.rs:37:21 | -LL | let x: _ = generic_arg(10); - | ^^ +LL | let y = 1; + | ^ | = help: consider adding suffix to avoid default numeric fallback error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:42:30 + --> $DIR/default_numeric_fallback.rs:73:21 | -LL | let x: _ = s.generic_arg(10); - | ^^ +LL | generic_arg(1); + | ^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:76:32 + | +LL | let x: _ = generic_arg(1); + | ^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:96:23 + | +LL | s.generic_arg(1); + | ^ | = help: consider adding suffix to avoid default numeric fallback -error: aborting due to 9 previous errors +error: aborting due to 17 previous errors -- cgit 1.4.1-3-g733a5 From 7226291025e6a367e60365fa1653999d240777b4 Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Mon, 15 Feb 2021 07:36:25 +0100 Subject: Upgrade compiletest-rs to 0.6 and tester to 0.9 These updates allow us to specify multiple testnames for `TESTNAME`. The new version of compiletest-rs also includes `bless` support, but is not enabled with this PR. --- Cargo.toml | 4 ++-- doc/adding_lints.md | 3 +++ tests/compile-test.rs | 16 ++++++++-------- 3 files changed, 13 insertions(+), 10 deletions(-) (limited to 'tests') diff --git a/Cargo.toml b/Cargo.toml index e7755c46eb8..ea32a8edd1f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,8 +37,8 @@ tempfile = { version = "3.1.0", optional = true } [dev-dependencies] cargo_metadata = "0.12" -compiletest_rs = { version = "0.5.0", features = ["tmp"] } -tester = "0.7" +compiletest_rs = { version = "0.6.0", features = ["tmp"] } +tester = "0.9" clippy-mini-macro-test = { version = "0.2", path = "mini-macro" } serde = { version = "1.0", features = ["derive"] } derive-new = "0.5" diff --git a/doc/adding_lints.md b/doc/adding_lints.md index 8fd1dea9aee..e12e75d4a2b 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -108,6 +108,9 @@ should only commit files changed by `cargo dev bless` for the specific lint you are creating/editing. Note that if the generated files are empty, they should be removed. +Note that you can run multiple test files by specifying a comma separated list: +`TESTNAME=foo_functions,test2,test3`. + ### Cargo lints For cargo lints, the process of testing differs in that we are interested in diff --git a/tests/compile-test.rs b/tests/compile-test.rs index c0b40add109..0594663786c 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -66,8 +66,8 @@ fn third_party_crates() -> String { fn default_config() -> compiletest::Config { let mut config = compiletest::Config::default(); - if let Ok(name) = env::var("TESTNAME") { - config.filter = Some(name); + if let Ok(filters) = env::var("TESTNAME") { + config.filters = filters.split(',').map(std::string::ToString::to_string).collect(); } if let Some(path) = option_env!("RUSTC_LIB_PATH") { @@ -167,7 +167,7 @@ fn run_ui_toml(config: &mut compiletest::Config) { fn run_ui_cargo(config: &mut compiletest::Config) { fn run_tests( config: &compiletest::Config, - filter: &Option, + filters: &[String], mut tests: Vec, ) -> Result { let mut result = true; @@ -181,9 +181,10 @@ fn run_ui_cargo(config: &mut compiletest::Config) { // Use the filter if provided let dir_path = dir.path(); - match &filter { - Some(name) if !dir_path.ends_with(name) => continue, - _ => {}, + for filter in filters { + if !dir_path.ends_with(filter) { + continue; + } } for case in fs::read_dir(&dir_path)? { @@ -243,8 +244,7 @@ fn run_ui_cargo(config: &mut compiletest::Config) { let current_dir = env::current_dir().unwrap(); let conf_dir = var("CLIPPY_CONF_DIR").unwrap_or_default(); - let filter = env::var("TESTNAME").ok(); - let res = run_tests(&config, &filter, tests); + let res = run_tests(&config, &config.filters, tests); env::set_current_dir(current_dir).unwrap(); set_var("CLIPPY_CONF_DIR", conf_dir); -- cgit 1.4.1-3-g733a5 From 1f8ee3cc19f9259eea5e633affef968280d2ed1a Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Tue, 16 Feb 2021 11:20:51 +0900 Subject: Handle struct ctor case --- clippy_lints/src/default_numeric_fallback.rs | 53 +++++++++++++++++++++-- tests/ui/default_numeric_fallback.rs | 37 +++++++++++++++- tests/ui/default_numeric_fallback.stderr | 64 ++++++++++++++++++++++++++-- 3 files changed, 145 insertions(+), 9 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index d755112a178..33a577f336d 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -64,7 +64,7 @@ struct NumericFallbackVisitor<'a, 'tcx> { impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { fn new(cx: &'a LateContext<'tcx>) -> Self { Self { - ty_bounds: Vec::new(), + ty_bounds: vec![TyBound::Nothing], cx, } } @@ -121,6 +121,42 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { } }, + ExprKind::Struct(qpath, fields, base) => { + if_chain! { + if let Some(def_id) = self.cx.qpath_res(qpath, expr.hir_id).opt_def_id(); + let ty = self.cx.tcx.type_of(def_id); + if let Some(adt_def) = ty.ty_adt_def(); + if adt_def.is_struct(); + if let Some(variant) = adt_def.variants.iter().next(); + then { + let fields_def = &variant.fields; + + // Push field type then visit each field expr. + for field in fields.iter() { + let bound = + fields_def + .iter() + .find_map(|f_def| { + if f_def.ident == field.ident + { Some(self.cx.tcx.type_of(f_def.did)) } + else { None } + }); + self.ty_bounds.push(bound.into()); + self.visit_expr(field.expr); + self.ty_bounds.pop(); + } + + // Visit base with no bound. + if let Some(base) = base { + self.ty_bounds.push(TyBound::Nothing); + self.visit_expr(base); + self.ty_bounds.pop(); + } + return; + } + } + }, + ExprKind::Lit(lit) => { let ty = self.cx.typeck_results().expr_ty(expr); self.check_lit(lit, ty); @@ -166,13 +202,13 @@ fn fn_sig_opt<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option { +enum TyBound<'tcx> { Any, - Ty(Ty<'ctx>), + Ty(Ty<'tcx>), Nothing, } -impl<'ctx> TyBound<'ctx> { +impl<'tcx> TyBound<'tcx> { fn is_integral(self) -> bool { match self { TyBound::Any => true, @@ -181,3 +217,12 @@ impl<'ctx> TyBound<'ctx> { } } } + +impl<'tcx> From>> for TyBound<'tcx> { + fn from(v: Option>) -> Self { + match v { + Some(t) => TyBound::Ty(t), + None => TyBound::Nothing, + } + } +} diff --git a/tests/ui/default_numeric_fallback.rs b/tests/ui/default_numeric_fallback.rs index c4881094469..0b3758952ac 100644 --- a/tests/ui/default_numeric_fallback.rs +++ b/tests/ui/default_numeric_fallback.rs @@ -39,6 +39,20 @@ mod nested_local { // Should NOT lint this because this literal is bound to `_` of outer `Local`. 1 }; + + let x: _ = if true { + // Should lint this because this literal is not bound to any types. + let y = 1; + + // Should NOT lint this because this literal is bound to `_` of outer `Local`. + 1 + } else { + // Should lint this because this literal is not bound to any types. + let y = 1; + + // Should NOT lint this because this literal is bound to `_` of outer `Local`. + 2 + }; } } @@ -46,7 +60,7 @@ mod function_def { fn ret_i32() -> i32 { // Even though the output type is specified, // this unsuffixed literal is linted to reduce heuristics and keep codebase simple. - 23 + 1 } fn test() { @@ -77,6 +91,27 @@ mod function_calls { } } +mod struct_ctor { + struct ConcreteStruct { + x: i32, + } + + struct GenericStruct { + x: T, + } + + fn test() { + // Should NOT lint this because the field type is bound to a concrete type. + ConcreteStruct { x: 1 }; + + // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type. + GenericStruct { x: 1 }; + + // Should lint this because the field type is inferred to `i32` and NOT bound to a concrete type. + let _ = GenericStruct { x: 1 }; + } +} + mod method_calls { struct StructForMethodCallTest {} diff --git a/tests/ui/default_numeric_fallback.stderr b/tests/ui/default_numeric_fallback.stderr index c71d05d7993..50fcdfb510e 100644 --- a/tests/ui/default_numeric_fallback.stderr +++ b/tests/ui/default_numeric_fallback.stderr @@ -112,7 +112,47 @@ LL | let y = 1; = help: consider adding suffix to avoid default numeric fallback error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:73:21 + --> $DIR/default_numeric_fallback.rs:45:21 + | +LL | let y = 1; + | ^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:51:21 + | +LL | let y = 1; + | ^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:63:9 + | +LL | 1 + | ^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:69:27 + | +LL | let f = || -> _ { 1 }; + | ^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:73:29 + | +LL | let f = || -> i32 { 1 }; + | ^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:87:21 | LL | generic_arg(1); | ^ @@ -120,7 +160,7 @@ LL | generic_arg(1); = help: consider adding suffix to avoid default numeric fallback error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:76:32 + --> $DIR/default_numeric_fallback.rs:90:32 | LL | let x: _ = generic_arg(1); | ^ @@ -128,12 +168,28 @@ LL | let x: _ = generic_arg(1); = help: consider adding suffix to avoid default numeric fallback error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:96:23 + --> $DIR/default_numeric_fallback.rs:108:28 + | +LL | GenericStruct { x: 1 }; + | ^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:111:36 + | +LL | let _ = GenericStruct { x: 1 }; + | ^ + | + = help: consider adding suffix to avoid default numeric fallback + +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:131:23 | LL | s.generic_arg(1); | ^ | = help: consider adding suffix to avoid default numeric fallback -error: aborting due to 17 previous errors +error: aborting due to 24 previous errors -- cgit 1.4.1-3-g733a5 From 9b0c1ebc183bbf0fab5fbbe5ac8b2f94e5e56b87 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Tue, 16 Feb 2021 18:07:09 +0900 Subject: Change to span_lint_and_sugg from span_lint_and_help --- clippy_lints/src/default_numeric_fallback.rs | 19 ++++-- tests/ui/default_numeric_fallback.stderr | 95 +++++++--------------------- 2 files changed, 38 insertions(+), 76 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index 33a577f336d..6ace9aa6bdf 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -1,4 +1,5 @@ use rustc_ast::ast::{LitFloatType, LitIntType, LitKind}; +use rustc_errors::Applicability; use rustc_hir::{ intravisit::{walk_expr, walk_stmt, NestedVisitorMap, Visitor}, Body, Expr, ExprKind, HirId, Lit, Stmt, StmtKind, @@ -12,7 +13,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; use if_chain::if_chain; -use crate::utils::span_lint_and_help; +use crate::utils::{snippet, span_lint_and_sugg}; declare_clippy_lint! { /// **What it does:** Checks for usage of unconstrained numeric literals which may cause default numeric fallback in type @@ -75,16 +76,24 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { if let Some(ty_bound) = self.ty_bounds.last(); if matches!(lit.node, LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed)); - if matches!(lit_ty.kind(), ty::Int(IntTy::I32) | ty::Float(FloatTy::F64)); if !ty_bound.is_integral(); then { - span_lint_and_help( + let suffix = match lit_ty.kind() { + ty::Int(IntTy::I32) => "i32", + ty::Float(FloatTy::F64) => "f64", + // Default numeric fallback never results in other types. + _ => return, + }; + + let sugg = format!("{}_{}", snippet(self.cx, lit.span, ""), suffix); + span_lint_and_sugg( self.cx, DEFAULT_NUMERIC_FALLBACK, lit.span, "default numeric fallback might occur", - None, - "consider adding suffix to avoid default numeric fallback", + "consider adding suffix", + sugg, + Applicability::MaybeIncorrect, ); } } diff --git a/tests/ui/default_numeric_fallback.stderr b/tests/ui/default_numeric_fallback.stderr index 50fcdfb510e..b31aa4ebcf8 100644 --- a/tests/ui/default_numeric_fallback.stderr +++ b/tests/ui/default_numeric_fallback.stderr @@ -2,194 +2,147 @@ error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:10:17 | LL | let x = 22; - | ^^ + | ^^ help: consider adding suffix: `22_i32` | = note: `-D clippy::default-numeric-fallback` implied by `-D warnings` - = help: consider adding suffix to avoid default numeric fallback error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:11:18 | LL | let x = [1, 2, 3]; - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:11:21 | LL | let x = [1, 2, 3]; - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:11:24 | LL | let x = [1, 2, 3]; - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `3_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:12:28 | LL | let x = if true { (1, 2) } else { (3, 4) }; - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:12:31 | LL | let x = if true { (1, 2) } else { (3, 4) }; - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:12:44 | LL | let x = if true { (1, 2) } else { (3, 4) }; - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `3_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:12:47 | LL | let x = if true { (1, 2) } else { (3, 4) }; - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `4_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:13:23 | LL | let x = match 1 { - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:14:13 | LL | 1 => 1, - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:14:18 | LL | 1 => 1, - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:15:18 | LL | _ => 2, - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:19:17 | LL | let x = 0.12; - | ^^^^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^^^^ help: consider adding suffix: `0.12_f64` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:37:21 | LL | let y = 1; - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:45:21 | LL | let y = 1; - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:51:21 | LL | let y = 1; - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:63:9 | LL | 1 - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:69:27 | LL | let f = || -> _ { 1 }; - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:73:29 | LL | let f = || -> i32 { 1 }; - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:87:21 | LL | generic_arg(1); - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:90:32 | LL | let x: _ = generic_arg(1); - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:108:28 | LL | GenericStruct { x: 1 }; - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:111:36 | LL | let _ = GenericStruct { x: 1 }; - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur --> $DIR/default_numeric_fallback.rs:131:23 | LL | s.generic_arg(1); - | ^ - | - = help: consider adding suffix to avoid default numeric fallback + | ^ help: consider adding suffix: `1_i32` error: aborting due to 24 previous errors -- cgit 1.4.1-3-g733a5 From 4ac14f9e63ba71e7f15204f9cff208f022563996 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 17 Feb 2021 09:34:35 -0600 Subject: Teach SpanlessEq binding IDs --- clippy_lints/src/utils/hir_utils.rs | 134 ++++++++++++++++++++++++------------ tests/ui/collapsible_match.rs | 8 +++ tests/ui/if_same_then_else2.rs | 6 +- tests/ui/if_same_then_else2.stderr | 4 +- 4 files changed, 102 insertions(+), 50 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/utils/hir_utils.rs b/clippy_lints/src/utils/hir_utils.rs index c5870dc5124..0a9719901f0 100644 --- a/clippy_lints/src/utils/hir_utils.rs +++ b/clippy_lints/src/utils/hir_utils.rs @@ -1,10 +1,12 @@ use crate::consts::{constant_context, constant_simple}; use crate::utils::differing_macro_contexts; use rustc_ast::ast::InlineAsmTemplatePiece; +use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_hir::def::Res; use rustc_hir::{ BinOpKind, Block, BlockCheckMode, BodyId, BorrowKind, CaptureBy, Expr, ExprKind, Field, FieldPat, FnRetTy, - GenericArg, GenericArgs, Guard, InlineAsmOperand, Lifetime, LifetimeName, ParamName, Pat, PatKind, Path, + GenericArg, GenericArgs, Guard, HirId, InlineAsmOperand, Lifetime, LifetimeName, ParamName, Pat, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding, }; use rustc_lint::LateContext; @@ -52,8 +54,47 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { } } - /// Checks whether two statements are the same. - pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool { + /// Use this method to wrap comparisons that may involve inter-expression context. + /// See `self.locals`. + fn inter_expr(&mut self) -> HirEqInterExpr<'_, 'a, 'tcx> { + HirEqInterExpr { + inner: self, + locals: FxHashMap::default(), + } + } + + pub fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool { + self.inter_expr().eq_block(left, right) + } + + pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool { + self.inter_expr().eq_expr(left, right) + } + + pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool { + self.inter_expr().eq_path_segment(left, right) + } + + pub fn eq_path_segments(&mut self, left: &[PathSegment<'_>], right: &[PathSegment<'_>]) -> bool { + self.inter_expr().eq_path_segments(left, right) + } + + pub fn eq_ty_kind(&mut self, left: &TyKind<'_>, right: &TyKind<'_>) -> bool { + self.inter_expr().eq_ty_kind(left, right) + } +} + +struct HirEqInterExpr<'a, 'b, 'tcx> { + inner: &'a mut SpanlessEq<'b, 'tcx>, + + // When binding are declared, the binding ID in the left expression is mapped to the one on the + // right. For example, when comparing `{ let x = 1; x + 2 }` and `{ let y = 1; y + 2 }`, + // these blocks are considered equal since `x` is mapped to `y`. + locals: FxHashMap, +} + +impl HirEqInterExpr<'_, '_, '_> { + fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool { match (&left.kind, &right.kind) { (&StmtKind::Local(ref l), &StmtKind::Local(ref r)) => { self.eq_pat(&l.pat, &r.pat) @@ -68,21 +109,21 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { } /// Checks whether two blocks are the same. - pub fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool { + fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool { over(&left.stmts, &right.stmts, |l, r| self.eq_stmt(l, r)) && both(&left.expr, &right.expr, |l, r| self.eq_expr(l, r)) } #[allow(clippy::similar_names)] - pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool { - if !self.allow_side_effects && differing_macro_contexts(left.span, right.span) { + fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool { + if !self.inner.allow_side_effects && differing_macro_contexts(left.span, right.span) { return false; } - if let Some(typeck_results) = self.maybe_typeck_results { + if let Some(typeck_results) = self.inner.maybe_typeck_results { if let (Some(l), Some(r)) = ( - constant_simple(self.cx, typeck_results, left), - constant_simple(self.cx, typeck_results, right), + constant_simple(self.inner.cx, typeck_results, left), + constant_simple(self.inner.cx, typeck_results, right), ) { if l == r { return true; @@ -98,10 +139,10 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { both(&li.label, &ri.label, |l, r| l.ident.name == r.ident.name) }, (&ExprKind::Assign(ref ll, ref lr, _), &ExprKind::Assign(ref rl, ref rr, _)) => { - self.allow_side_effects && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) + self.inner.allow_side_effects && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) }, (&ExprKind::AssignOp(ref lo, ref ll, ref lr), &ExprKind::AssignOp(ref ro, ref rl, ref rr)) => { - self.allow_side_effects && lo.node == ro.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) + self.inner.allow_side_effects && lo.node == ro.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) }, (&ExprKind::Block(ref l, _), &ExprKind::Block(ref r, _)) => self.eq_block(l, r), (&ExprKind::Binary(l_op, ref ll, ref lr), &ExprKind::Binary(r_op, ref rl, ref rr)) => { @@ -116,7 +157,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { }, (&ExprKind::Box(ref l), &ExprKind::Box(ref r)) => self.eq_expr(l, r), (&ExprKind::Call(l_fun, l_args), &ExprKind::Call(r_fun, r_args)) => { - self.allow_side_effects && self.eq_expr(l_fun, r_fun) && self.eq_exprs(l_args, r_args) + self.inner.allow_side_effects && self.eq_expr(l_fun, r_fun) && self.eq_exprs(l_args, r_args) }, (&ExprKind::Cast(ref lx, ref lt), &ExprKind::Cast(ref rx, ref rt)) | (&ExprKind::Type(ref lx, ref lt), &ExprKind::Type(ref rx, ref rt)) => { @@ -139,19 +180,19 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { ls == rs && self.eq_expr(le, re) && over(la, ra, |l, r| { - self.eq_expr(&l.body, &r.body) + self.eq_pat(&l.pat, &r.pat) && both(&l.guard, &r.guard, |l, r| self.eq_guard(l, r)) - && self.eq_pat(&l.pat, &r.pat) + && self.eq_expr(&l.body, &r.body) }) }, (&ExprKind::MethodCall(l_path, _, l_args, _), &ExprKind::MethodCall(r_path, _, r_args, _)) => { - self.allow_side_effects && self.eq_path_segment(l_path, r_path) && self.eq_exprs(l_args, r_args) + self.inner.allow_side_effects && self.eq_path_segment(l_path, r_path) && self.eq_exprs(l_args, r_args) }, (&ExprKind::Repeat(ref le, ref ll_id), &ExprKind::Repeat(ref re, ref rl_id)) => { - let mut celcx = constant_context(self.cx, self.cx.tcx.typeck_body(ll_id.body)); - let ll = celcx.expr(&self.cx.tcx.hir().body(ll_id.body).value); - let mut celcx = constant_context(self.cx, self.cx.tcx.typeck_body(rl_id.body)); - let rl = celcx.expr(&self.cx.tcx.hir().body(rl_id.body).value); + let mut celcx = constant_context(self.inner.cx, self.inner.cx.tcx.typeck_body(ll_id.body)); + let ll = celcx.expr(&self.inner.cx.tcx.hir().body(ll_id.body).value); + let mut celcx = constant_context(self.inner.cx, self.inner.cx.tcx.typeck_body(rl_id.body)); + let rl = celcx.expr(&self.inner.cx.tcx.hir().body(rl_id.body).value); self.eq_expr(le, re) && ll == rl }, @@ -168,7 +209,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { (&ExprKind::DropTemps(ref le), &ExprKind::DropTemps(ref re)) => self.eq_expr(le, re), _ => false, }; - is_eq || self.expr_fallback.as_ref().map_or(false, |f| f(left, right)) + is_eq || self.inner.expr_fallback.as_ref().map_or(false, |f| f(left, right)) } fn eq_exprs(&mut self, left: &[Expr<'_>], right: &[Expr<'_>]) -> bool { @@ -199,13 +240,13 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { left.name == right.name } - pub fn eq_fieldpat(&mut self, left: &FieldPat<'_>, right: &FieldPat<'_>) -> bool { + fn eq_fieldpat(&mut self, left: &FieldPat<'_>, right: &FieldPat<'_>) -> bool { let (FieldPat { ident: li, pat: lp, .. }, FieldPat { ident: ri, pat: rp, .. }) = (&left, &right); li.name == ri.name && self.eq_pat(lp, rp) } /// Checks whether two patterns are the same. - pub fn eq_pat(&mut self, left: &Pat<'_>, right: &Pat<'_>) -> bool { + fn eq_pat(&mut self, left: &Pat<'_>, right: &Pat<'_>) -> bool { match (&left.kind, &right.kind) { (&PatKind::Box(ref l), &PatKind::Box(ref r)) => self.eq_pat(l, r), (&PatKind::Struct(ref lp, ref la, ..), &PatKind::Struct(ref rp, ref ra, ..)) => { @@ -214,8 +255,12 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { (&PatKind::TupleStruct(ref lp, ref la, ls), &PatKind::TupleStruct(ref rp, ref ra, rs)) => { self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat(l, r)) && ls == rs }, - (&PatKind::Binding(ref lb, .., ref li, ref lp), &PatKind::Binding(ref rb, .., ref ri, ref rp)) => { - lb == rb && li.name == ri.name && both(lp, rp, |l, r| self.eq_pat(l, r)) + (&PatKind::Binding(lb, li, _, ref lp), &PatKind::Binding(rb, ri, _, ref rp)) => { + let eq = lb == rb && both(lp, rp, |l, r| self.eq_pat(l, r)); + if eq { + self.locals.insert(li, ri); + } + eq }, (&PatKind::Path(ref l), &PatKind::Path(ref r)) => self.eq_qpath(l, r), (&PatKind::Lit(ref l), &PatKind::Lit(ref r)) => self.eq_expr(l, r), @@ -251,8 +296,11 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { } fn eq_path(&mut self, left: &Path<'_>, right: &Path<'_>) -> bool { - left.is_global() == right.is_global() - && over(&left.segments, &right.segments, |l, r| self.eq_path_segment(l, r)) + match (left.res, right.res) { + (Res::Local(l), Res::Local(r)) => l == r || self.locals.get(&l) == Some(&r), + (Res::Local(_), _) | (_, Res::Local(_)) => false, + _ => over(&left.segments, &right.segments, |l, r| self.eq_path_segment(l, r)), + } } fn eq_path_parameters(&mut self, left: &GenericArgs<'_>, right: &GenericArgs<'_>) -> bool { @@ -279,28 +327,19 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { left.ident.name == right.ident.name && both(&left.args, &right.args, |l, r| self.eq_path_parameters(l, r)) } - pub fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool { + fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool { self.eq_ty_kind(&left.kind, &right.kind) } #[allow(clippy::similar_names)] - pub fn eq_ty_kind(&mut self, left: &TyKind<'_>, right: &TyKind<'_>) -> bool { + fn eq_ty_kind(&mut self, left: &TyKind<'_>, right: &TyKind<'_>) -> bool { match (left, right) { (&TyKind::Slice(ref l_vec), &TyKind::Slice(ref r_vec)) => self.eq_ty(l_vec, r_vec), (&TyKind::Array(ref lt, ref ll_id), &TyKind::Array(ref rt, ref rl_id)) => { - let old_maybe_typeck_results = self.maybe_typeck_results; - - let mut celcx = constant_context(self.cx, self.cx.tcx.typeck_body(ll_id.body)); - self.maybe_typeck_results = Some(self.cx.tcx.typeck_body(ll_id.body)); - let ll = celcx.expr(&self.cx.tcx.hir().body(ll_id.body).value); - - let mut celcx = constant_context(self.cx, self.cx.tcx.typeck_body(rl_id.body)); - self.maybe_typeck_results = Some(self.cx.tcx.typeck_body(rl_id.body)); - let rl = celcx.expr(&self.cx.tcx.hir().body(rl_id.body).value); - - let eq_ty = self.eq_ty(lt, rt); - self.maybe_typeck_results = old_maybe_typeck_results; - eq_ty && ll == rl + let cx = self.inner.cx; + let eval_const = + |body| constant_context(cx, cx.tcx.typeck_body(body)).expr(&cx.tcx.hir().body(body).value); + self.eq_ty(lt, rt) && eval_const(ll_id.body) == eval_const(rl_id.body) }, (&TyKind::Ptr(ref l_mut), &TyKind::Ptr(ref r_mut)) => { l_mut.mutbl == r_mut.mutbl && self.eq_ty(&*l_mut.ty, &*r_mut.ty) @@ -667,10 +706,15 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { // self.maybe_typeck_results.unwrap().qpath_res(p, id).hash(&mut self.s); } - pub fn hash_path(&mut self, p: &Path<'_>) { - p.is_global().hash(&mut self.s); - for p in p.segments { - self.hash_name(p.ident.name); + pub fn hash_path(&mut self, path: &Path<'_>) { + match path.res { + // constant hash since equality is dependant on inter-expression context + Res::Local(_) => 1_usize.hash(&mut self.s), + _ => { + for seg in path.segments { + self.hash_name(seg.ident.name); + } + }, } } diff --git a/tests/ui/collapsible_match.rs b/tests/ui/collapsible_match.rs index 3294da7e814..55467cf4229 100644 --- a/tests/ui/collapsible_match.rs +++ b/tests/ui/collapsible_match.rs @@ -232,6 +232,14 @@ fn negative_cases(res_opt: Result, String>, res_res: Result match e { + Some(e) => e, + e => e, + }, + // else branch looks the same but the binding is different + e => e, + }; } fn make() -> T { diff --git a/tests/ui/if_same_then_else2.rs b/tests/ui/if_same_then_else2.rs index e83ce47e563..a2ff1b741ca 100644 --- a/tests/ui/if_same_then_else2.rs +++ b/tests/ui/if_same_then_else2.rs @@ -12,7 +12,7 @@ fn if_same_then_else2() -> Result<&'static str, ()> { if true { for _ in &[42] { let foo: &Option<_> = &Some::(42); - if true { + if foo.is_some() { break; } else { continue; @@ -21,8 +21,8 @@ fn if_same_then_else2() -> Result<&'static str, ()> { } else { //~ ERROR same body as `if` block for _ in &[42] { - let foo: &Option<_> = &Some::(42); - if true { + let bar: &Option<_> = &Some::(42); + if bar.is_some() { break; } else { continue; diff --git a/tests/ui/if_same_then_else2.stderr b/tests/ui/if_same_then_else2.stderr index f98e30fa376..454322d8aac 100644 --- a/tests/ui/if_same_then_else2.stderr +++ b/tests/ui/if_same_then_else2.stderr @@ -5,7 +5,7 @@ LL | } else { | ____________^ LL | | //~ ERROR same body as `if` block LL | | for _ in &[42] { -LL | | let foo: &Option<_> = &Some::(42); +LL | | let bar: &Option<_> = &Some::(42); ... | LL | | } LL | | } @@ -19,7 +19,7 @@ LL | if true { | _____________^ LL | | for _ in &[42] { LL | | let foo: &Option<_> = &Some::(42); -LL | | if true { +LL | | if foo.is_some() { ... | LL | | } LL | | } else { -- cgit 1.4.1-3-g733a5 From 6165cccf7e43cf4ca2234e84dcf7060c7d89ef45 Mon Sep 17 00:00:00 2001 From: Pierre-Andre Gagnon Date: Wed, 17 Feb 2021 16:41:50 -0500 Subject: Added detailled suggs for new case --- clippy_lints/src/unnecessary_wraps.rs | 100 ++++++++++++++++------------------ tests/ui/unnecessary_wraps.stderr | 62 +++++++++++++++++---- 2 files changed, 97 insertions(+), 65 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index 7da42ba3934..b097d531bc4 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -1,13 +1,13 @@ use crate::utils::{ - contains_return, in_macro, is_type_diagnostic_item, match_qpath, paths, return_ty, snippet, span_lint_and_sugg, - span_lint_and_then, visitors::find_all_ret_expressions, + contains_return, in_macro, match_qpath, paths, return_ty, snippet, span_lint_and_then, + visitors::find_all_ret_expressions, }; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, ExprKind, FnDecl, HirId, Impl, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::subst::GenericArgKind; +use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::sym; use rustc_span::Span; @@ -85,33 +85,23 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { } } - // Check if return type is Option or Result. If neither, abort. - let return_ty = return_ty(cx, hir_id); - let (return_type_label, path) = if is_type_diagnostic_item(cx, return_ty, sym::option_type) { - ("Option", &paths::OPTION_SOME) - } else if is_type_diagnostic_item(cx, return_ty, sym::result_type) { - ("Result", &paths::RESULT_OK) - } else { - return; - }; - - // Take the first inner type of the Option or Result. If can't, abort. - let inner_ty = if_chain! { - // Skip Option or Result and take the first outermost inner type. - if let Some(inner_ty) = return_ty.walk().nth(1); - if let GenericArgKind::Type(inner_ty) = inner_ty.unpack(); - then { - inner_ty + // Get the wrapper and inner types, if can't, abort. + let (return_type_label, path, inner_type) = if let ty::Adt(adt_def, subst) = return_ty(cx, hir_id).kind() { + if cx.tcx.is_diagnostic_item(sym::option_type, adt_def.did) { + ("Option", &paths::OPTION_SOME, subst.type_at(0)) + } else if cx.tcx.is_diagnostic_item(sym::result_type, adt_def.did) { + ("Result", &paths::RESULT_OK, subst.type_at(0)) } else { return; } + } else { + return; }; // Check if all return expression respect the following condition and collect them. let mut suggs = Vec::new(); let can_sugg = find_all_ret_expressions(cx, &body.value, |ret_expr| { if_chain! { - // Abort if in macro. if !in_macro(ret_expr.span); // Check if a function call. if let ExprKind::Call(ref func, ref args) = ret_expr.kind; @@ -119,12 +109,20 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { if let ExprKind::Path(ref qpath) = func.kind; // Check if OPTION_SOME or RESULT_OK, depending on return type. if match_qpath(qpath, path); - // Make sure the function call has only one argument. if args.len() == 1; // Make sure the function argument does not contain a return expression. if !contains_return(&args[0]); then { - suggs.push((ret_expr.span, snippet(cx, args[0].span.source_callsite(), "..").to_string())); + suggs.push( + ( + ret_expr.span, + if inner_type.is_unit() { + "".to_string() + } else { + snippet(cx, args[0].span.source_callsite(), "..").to_string() + } + ) + ); true } else { false @@ -133,42 +131,36 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { }); if can_sugg && !suggs.is_empty() { - // Issue 6640: If the inner type is Unit, emit lint similar to clippy::unused_unit. - if inner_ty.is_unit() { - span_lint_and_sugg( - cx, - UNNECESSARY_WRAPS, - fn_decl.output.span(), - "unneeded wrapped unit return type", - format!("remove the `-> {}<[...]>`", return_type_label).as_str(), - String::new(), - Applicability::MaybeIncorrect, - ); + let (lint_msg, return_type_suggestion_msg, return_type_suggestion) = if inner_type.is_unit() { + ( + "this function's return value is unnecessary".to_string(), + "remove the return type...".to_string(), + snippet(cx, fn_decl.output.span(), "..").to_string(), + ) } else { - span_lint_and_then( - cx, - UNNECESSARY_WRAPS, - span, + ( format!( "this function's return value is unnecessarily wrapped by `{}`", return_type_label - ) - .as_str(), - |diag| { - diag.span_suggestion( - fn_decl.output.span(), - format!("remove `{}` from the return type...", return_type_label).as_str(), - inner_ty.to_string(), - Applicability::MaybeIncorrect, - ); - diag.multipart_suggestion( - "...and change the returning expressions", - suggs, - Applicability::MaybeIncorrect, - ); - }, + ), + format!("remove `{}` from the return type...", return_type_label), + inner_type.to_string(), + ) + }; + + span_lint_and_then(cx, UNNECESSARY_WRAPS, span, lint_msg.as_str(), |diag| { + diag.span_suggestion( + fn_decl.output.span(), + return_type_suggestion_msg.as_str(), + return_type_suggestion, + Applicability::MaybeIncorrect, ); - } + diag.multipart_suggestion( + "...and then change the returning expressions", + suggs, + Applicability::MaybeIncorrect, + ); + }); } } } diff --git a/tests/ui/unnecessary_wraps.stderr b/tests/ui/unnecessary_wraps.stderr index f28981f9e34..40effb89499 100644 --- a/tests/ui/unnecessary_wraps.stderr +++ b/tests/ui/unnecessary_wraps.stderr @@ -15,7 +15,7 @@ help: remove `Option` from the return type... | LL | fn func1(a: bool, b: bool) -> i32 { | ^^^ -help: ...and change the returning expressions +help: ...and then change the returning expressions | LL | return 42; LL | } @@ -41,7 +41,7 @@ help: remove `Option` from the return type... | LL | fn func2(a: bool, b: bool) -> i32 { | ^^^ -help: ...and change the returning expressions +help: ...and then change the returning expressions | LL | return 10; LL | } @@ -63,7 +63,7 @@ help: remove `Option` from the return type... | LL | fn func5() -> i32 { | ^^^ -help: ...and change the returning expressions +help: ...and then change the returning expressions | LL | 1 | @@ -80,7 +80,7 @@ help: remove `Result` from the return type... | LL | fn func7() -> i32 { | ^^^ -help: ...and change the returning expressions +help: ...and then change the returning expressions | LL | 1 | @@ -97,22 +97,62 @@ help: remove `Option` from the return type... | LL | fn func12() -> i32 { | ^^^ -help: ...and change the returning expressions +help: ...and then change the returning expressions | LL | 1 | -error: unneeded wrapped unit return type - --> $DIR/unnecessary_wraps.rs:120:38 +error: this function's return value is unnecessary + --> $DIR/unnecessary_wraps.rs:120:1 + | +LL | / fn issue_6640_1(a: bool, b: bool) -> Option<()> { +LL | | if a && b { +LL | | return Some(()); +LL | | } +... | +LL | | } +LL | | } + | |_^ + | +help: remove the return type... | LL | fn issue_6640_1(a: bool, b: bool) -> Option<()> { - | ^^^^^^^^^^ help: remove the `-> Option<[...]>` + | ^^^^^^^^^^ +help: ...and then change the returning expressions + | +LL | return ; +LL | } +LL | if a { +LL | Some(()); +LL | +LL | } else { + ... -error: unneeded wrapped unit return type - --> $DIR/unnecessary_wraps.rs:133:38 +error: this function's return value is unnecessary + --> $DIR/unnecessary_wraps.rs:133:1 + | +LL | / fn issue_6640_2(a: bool, b: bool) -> Result<(), i32> { +LL | | if a && b { +LL | | return Ok(()); +LL | | } +... | +LL | | } +LL | | } + | |_^ + | +help: remove the return type... | LL | fn issue_6640_2(a: bool, b: bool) -> Result<(), i32> { - | ^^^^^^^^^^^^^^^ help: remove the `-> Result<[...]>` + | ^^^^^^^^^^^^^^^ +help: ...and then change the returning expressions + | +LL | return ; +LL | } +LL | if a { +LL | +LL | } else { +LL | return ; + | error: aborting due to 7 previous errors -- cgit 1.4.1-3-g733a5 From a87fa0e35049470aff2dc8f9ddc8399bb6c0718d Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Thu, 18 Feb 2021 18:35:25 +0900 Subject: Fix FP of result_unit_err when using type aliases --- clippy_lints/src/functions.rs | 15 +++++++-------- tests/ui/result_unit_error.rs | 23 ++++++++++++++++++++--- tests/ui/result_unit_error.stderr | 18 +++++++++++++----- 3 files changed, 40 insertions(+), 16 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/functions.rs b/clippy_lints/src/functions.rs index 94200a15420..30814e869e6 100644 --- a/clippy_lints/src/functions.rs +++ b/clippy_lints/src/functions.rs @@ -1,7 +1,7 @@ use crate::utils::{ attr_by_name, attrs::is_proc_macro, is_must_use_ty, is_trait_impl_item, is_type_diagnostic_item, iter_input_pats, - last_path_segment, match_def_path, must_use_attr, path_to_local, return_ty, snippet, snippet_opt, span_lint, - span_lint_and_help, span_lint_and_then, trait_ref_of_method, type_is_unsafe_function, + match_def_path, must_use_attr, path_to_local, return_ty, snippet, snippet_opt, span_lint, span_lint_and_help, + span_lint_and_then, trait_ref_of_method, type_is_unsafe_function, }; use if_chain::if_chain; use rustc_ast::ast::Attribute; @@ -470,12 +470,11 @@ fn check_result_unit_err(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, item_span if_chain! { if !in_external_macro(cx.sess(), item_span); if let hir::FnRetTy::Return(ref ty) = decl.output; - if let hir::TyKind::Path(ref qpath) = ty.kind; - if is_type_diagnostic_item(cx, hir_ty_to_ty(cx.tcx, ty), sym::result_type); - if let Some(ref args) = last_path_segment(qpath).args; - if let [_, hir::GenericArg::Type(ref err_ty)] = args.args; - if let hir::TyKind::Tup(t) = err_ty.kind; - if t.is_empty(); + let ty = hir_ty_to_ty(cx.tcx, ty); + if is_type_diagnostic_item(cx, ty, sym::result_type); + if let ty::Adt(_, substs) = ty.kind(); + let err_ty = substs.type_at(1); + if err_ty.is_unit(); then { span_lint_and_help( cx, diff --git a/tests/ui/result_unit_error.rs b/tests/ui/result_unit_error.rs index 5e57c752b5a..a4ec803024e 100644 --- a/tests/ui/result_unit_error.rs +++ b/tests/ui/result_unit_error.rs @@ -1,6 +1,4 @@ -#![allow(clippy::unnecessary_wraps)] -#[warn(clippy::result_unit_err)] -#[allow(unused)] +#![warn(clippy::result_unit_err)] pub fn returns_unit_error() -> Result { Err(()) @@ -36,4 +34,23 @@ impl UnitErrorHolder { } } +// https://github.com/rust-lang/rust-clippy/issues/6546 +pub mod issue_6546 { + type ResInv = Result; + + pub fn should_lint() -> ResInv<(), usize> { + Ok(0) + } + + pub fn should_not_lint() -> ResInv { + Ok(()) + } + + type MyRes = Result<(A, B), Box>; + + pub fn should_not_lint2(x: i32) -> MyRes { + Ok((x, ())) + } +} + fn main() {} diff --git a/tests/ui/result_unit_error.stderr b/tests/ui/result_unit_error.stderr index 12901b354f9..41d8b0a7cb7 100644 --- a/tests/ui/result_unit_error.stderr +++ b/tests/ui/result_unit_error.stderr @@ -1,5 +1,5 @@ error: this returns a `Result<_, ()> - --> $DIR/result_unit_error.rs:5:1 + --> $DIR/result_unit_error.rs:3:1 | LL | pub fn returns_unit_error() -> Result { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | pub fn returns_unit_error() -> Result { = help: use a custom Error type instead error: this returns a `Result<_, ()> - --> $DIR/result_unit_error.rs:14:5 + --> $DIR/result_unit_error.rs:12:5 | LL | fn get_that_error(&self) -> Result; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | fn get_that_error(&self) -> Result; = help: use a custom Error type instead error: this returns a `Result<_, ()> - --> $DIR/result_unit_error.rs:16:5 + --> $DIR/result_unit_error.rs:14:5 | LL | fn get_this_one_too(&self) -> Result { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -24,12 +24,20 @@ LL | fn get_this_one_too(&self) -> Result { = help: use a custom Error type instead error: this returns a `Result<_, ()> - --> $DIR/result_unit_error.rs:34:5 + --> $DIR/result_unit_error.rs:32:5 | LL | pub fn unit_error(&self) -> Result { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: use a custom Error type instead -error: aborting due to 4 previous errors +error: this returns a `Result<_, ()> + --> $DIR/result_unit_error.rs:41:5 + | +LL | pub fn should_lint() -> ResInv<(), usize> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a custom Error type instead + +error: aborting due to 5 previous errors -- cgit 1.4.1-3-g733a5 From a78271b8611d7ab7709976dfb94ae36b668ac42b Mon Sep 17 00:00:00 2001 From: Pierre-Andre Gagnon Date: Thu, 18 Feb 2021 17:32:55 -0500 Subject: Changed fn body suggestion msg --- clippy_lints/src/unnecessary_wraps.rs | 14 ++++++-------- tests/ui/unnecessary_wraps.rs | 18 ++++++++++++++++++ tests/ui/unnecessary_wraps.stderr | 14 +++++++------- 3 files changed, 31 insertions(+), 15 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index b097d531bc4..607585125a4 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -131,11 +131,12 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { }); if can_sugg && !suggs.is_empty() { - let (lint_msg, return_type_suggestion_msg, return_type_suggestion) = if inner_type.is_unit() { + let (lint_msg, return_type_sugg_msg, return_type_sugg, body_sugg_msg) = if inner_type.is_unit() { ( "this function's return value is unnecessary".to_string(), "remove the return type...".to_string(), snippet(cx, fn_decl.output.span(), "..").to_string(), + "...and then remove returned values", ) } else { ( @@ -145,21 +146,18 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { ), format!("remove `{}` from the return type...", return_type_label), inner_type.to_string(), + "...and then change returning expressions", ) }; span_lint_and_then(cx, UNNECESSARY_WRAPS, span, lint_msg.as_str(), |diag| { diag.span_suggestion( fn_decl.output.span(), - return_type_suggestion_msg.as_str(), - return_type_suggestion, - Applicability::MaybeIncorrect, - ); - diag.multipart_suggestion( - "...and then change the returning expressions", - suggs, + return_type_sugg_msg.as_str(), + return_type_sugg, Applicability::MaybeIncorrect, ); + diag.multipart_suggestion(body_sugg_msg, suggs, Applicability::MaybeIncorrect); }); } } diff --git a/tests/ui/unnecessary_wraps.rs b/tests/ui/unnecessary_wraps.rs index 5aaa99bbe5a..a510263e67d 100644 --- a/tests/ui/unnecessary_wraps.rs +++ b/tests/ui/unnecessary_wraps.rs @@ -141,6 +141,24 @@ fn issue_6640_2(a: bool, b: bool) -> Result<(), i32> { } } +// should not be linted +fn issue_6640_3() -> Option<()> { + if true { + Some(()) + } else { + None + } +} + +// should not be linted +fn issue_6640_4() -> Result<(), ()> { + if true { + Ok(()) + } else { + Err(()) + } +} + fn main() { // method calls are not linted func1(true, true); diff --git a/tests/ui/unnecessary_wraps.stderr b/tests/ui/unnecessary_wraps.stderr index 40effb89499..9a861c61a46 100644 --- a/tests/ui/unnecessary_wraps.stderr +++ b/tests/ui/unnecessary_wraps.stderr @@ -15,7 +15,7 @@ help: remove `Option` from the return type... | LL | fn func1(a: bool, b: bool) -> i32 { | ^^^ -help: ...and then change the returning expressions +help: ...and then change returning expressions | LL | return 42; LL | } @@ -41,7 +41,7 @@ help: remove `Option` from the return type... | LL | fn func2(a: bool, b: bool) -> i32 { | ^^^ -help: ...and then change the returning expressions +help: ...and then change returning expressions | LL | return 10; LL | } @@ -63,7 +63,7 @@ help: remove `Option` from the return type... | LL | fn func5() -> i32 { | ^^^ -help: ...and then change the returning expressions +help: ...and then change returning expressions | LL | 1 | @@ -80,7 +80,7 @@ help: remove `Result` from the return type... | LL | fn func7() -> i32 { | ^^^ -help: ...and then change the returning expressions +help: ...and then change returning expressions | LL | 1 | @@ -97,7 +97,7 @@ help: remove `Option` from the return type... | LL | fn func12() -> i32 { | ^^^ -help: ...and then change the returning expressions +help: ...and then change returning expressions | LL | 1 | @@ -118,7 +118,7 @@ help: remove the return type... | LL | fn issue_6640_1(a: bool, b: bool) -> Option<()> { | ^^^^^^^^^^ -help: ...and then change the returning expressions +help: ...and then remove returned values | LL | return ; LL | } @@ -144,7 +144,7 @@ help: remove the return type... | LL | fn issue_6640_2(a: bool, b: bool) -> Result<(), i32> { | ^^^^^^^^^^^^^^^ -help: ...and then change the returning expressions +help: ...and then remove returned values | LL | return ; LL | } -- cgit 1.4.1-3-g733a5 From 1f4153aa1e2fc4a59527c17cde43f3feb3088463 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Fri, 19 Feb 2021 10:02:17 +0100 Subject: collapsible_match: fix lint message capitalization (see https://rustc-dev-guide.rust-lang.org/diagnostics.html for details) --- clippy_lints/src/collapsible_match.rs | 6 ++-- tests/ui/collapsible_match.stderr | 60 +++++++++++++++++------------------ tests/ui/collapsible_match2.stderr | 30 +++++++++--------- 3 files changed, 48 insertions(+), 48 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/collapsible_match.rs b/clippy_lints/src/collapsible_match.rs index 67282cb7900..3c45525684b 100644 --- a/clippy_lints/src/collapsible_match.rs +++ b/clippy_lints/src/collapsible_match.rs @@ -96,12 +96,12 @@ fn check_arm<'tcx>(arm: &Arm<'tcx>, wild_outer_arm: &Arm<'tcx>, cx: &LateContext cx, COLLAPSIBLE_MATCH, expr.span, - "Unnecessary nested match", + "unnecessary nested match", |diag| { let mut help_span = MultiSpan::from_spans(vec![binding_span, non_wild_inner_arm.pat.span]); - help_span.push_span_label(binding_span, "Replace this binding".into()); + help_span.push_span_label(binding_span, "replace this binding".into()); help_span.push_span_label(non_wild_inner_arm.pat.span, "with this pattern".into()); - diag.span_help(help_span, "The outer pattern can be modified to include the inner pattern."); + diag.span_help(help_span, "the outer pattern can be modified to include the inner pattern"); }, ); } diff --git a/tests/ui/collapsible_match.stderr b/tests/ui/collapsible_match.stderr index 63ac6a1613d..77978884900 100644 --- a/tests/ui/collapsible_match.stderr +++ b/tests/ui/collapsible_match.stderr @@ -1,4 +1,4 @@ -error: Unnecessary nested match +error: unnecessary nested match --> $DIR/collapsible_match.rs:7:20 | LL | Ok(val) => match val { @@ -9,15 +9,15 @@ LL | | }, | |_________^ | = note: `-D clippy::collapsible-match` implied by `-D warnings` -help: The outer pattern can be modified to include the inner pattern. +help: the outer pattern can be modified to include the inner pattern --> $DIR/collapsible_match.rs:7:12 | LL | Ok(val) => match val { - | ^^^ Replace this binding + | ^^^ replace this binding LL | Some(n) => foo(n), | ^^^^^^^ with this pattern -error: Unnecessary nested match +error: unnecessary nested match --> $DIR/collapsible_match.rs:16:20 | LL | Ok(val) => match val { @@ -27,15 +27,15 @@ LL | | _ => return, LL | | }, | |_________^ | -help: The outer pattern can be modified to include the inner pattern. +help: the outer pattern can be modified to include the inner pattern --> $DIR/collapsible_match.rs:16:12 | LL | Ok(val) => match val { - | ^^^ Replace this binding + | ^^^ replace this binding LL | Some(n) => foo(n), | ^^^^^^^ with this pattern -error: Unnecessary nested match +error: unnecessary nested match --> $DIR/collapsible_match.rs:25:9 | LL | / if let Some(n) = val { @@ -43,15 +43,15 @@ LL | | take(n); LL | | } | |_________^ | -help: The outer pattern can be modified to include the inner pattern. +help: the outer pattern can be modified to include the inner pattern --> $DIR/collapsible_match.rs:24:15 | LL | if let Ok(val) = res_opt { - | ^^^ Replace this binding + | ^^^ replace this binding LL | if let Some(n) = val { | ^^^^^^^ with this pattern -error: Unnecessary nested match +error: unnecessary nested match --> $DIR/collapsible_match.rs:32:9 | LL | / if let Some(n) = val { @@ -61,15 +61,15 @@ LL | | return; LL | | } | |_________^ | -help: The outer pattern can be modified to include the inner pattern. +help: the outer pattern can be modified to include the inner pattern --> $DIR/collapsible_match.rs:31:15 | LL | if let Ok(val) = res_opt { - | ^^^ Replace this binding + | ^^^ replace this binding LL | if let Some(n) = val { | ^^^^^^^ with this pattern -error: Unnecessary nested match +error: unnecessary nested match --> $DIR/collapsible_match.rs:43:9 | LL | / match val { @@ -78,16 +78,16 @@ LL | | _ => (), LL | | } | |_________^ | -help: The outer pattern can be modified to include the inner pattern. +help: the outer pattern can be modified to include the inner pattern --> $DIR/collapsible_match.rs:42:15 | LL | if let Ok(val) = res_opt { - | ^^^ Replace this binding + | ^^^ replace this binding LL | match val { LL | Some(n) => foo(n), | ^^^^^^^ with this pattern -error: Unnecessary nested match +error: unnecessary nested match --> $DIR/collapsible_match.rs:52:13 | LL | / if let Some(n) = val { @@ -95,15 +95,15 @@ LL | | take(n); LL | | } | |_____________^ | -help: The outer pattern can be modified to include the inner pattern. +help: the outer pattern can be modified to include the inner pattern --> $DIR/collapsible_match.rs:51:12 | LL | Ok(val) => { - | ^^^ Replace this binding + | ^^^ replace this binding LL | if let Some(n) = val { | ^^^^^^^ with this pattern -error: Unnecessary nested match +error: unnecessary nested match --> $DIR/collapsible_match.rs:61:9 | LL | / match val { @@ -112,16 +112,16 @@ LL | | _ => return, LL | | } | |_________^ | -help: The outer pattern can be modified to include the inner pattern. +help: the outer pattern can be modified to include the inner pattern --> $DIR/collapsible_match.rs:60:15 | LL | if let Ok(val) = res_opt { - | ^^^ Replace this binding + | ^^^ replace this binding LL | match val { LL | Some(n) => foo(n), | ^^^^^^^ with this pattern -error: Unnecessary nested match +error: unnecessary nested match --> $DIR/collapsible_match.rs:72:13 | LL | / if let Some(n) = val { @@ -131,15 +131,15 @@ LL | | return; LL | | } | |_____________^ | -help: The outer pattern can be modified to include the inner pattern. +help: the outer pattern can be modified to include the inner pattern --> $DIR/collapsible_match.rs:71:12 | LL | Ok(val) => { - | ^^^ Replace this binding + | ^^^ replace this binding LL | if let Some(n) = val { | ^^^^^^^ with this pattern -error: Unnecessary nested match +error: unnecessary nested match --> $DIR/collapsible_match.rs:83:20 | LL | Ok(val) => match val { @@ -149,15 +149,15 @@ LL | | None => return, LL | | }, | |_________^ | -help: The outer pattern can be modified to include the inner pattern. +help: the outer pattern can be modified to include the inner pattern --> $DIR/collapsible_match.rs:83:12 | LL | Ok(val) => match val { - | ^^^ Replace this binding + | ^^^ replace this binding LL | Some(n) => foo(n), | ^^^^^^^ with this pattern -error: Unnecessary nested match +error: unnecessary nested match --> $DIR/collapsible_match.rs:92:22 | LL | Some(val) => match val { @@ -167,11 +167,11 @@ LL | | _ => return, LL | | }, | |_________^ | -help: The outer pattern can be modified to include the inner pattern. +help: the outer pattern can be modified to include the inner pattern --> $DIR/collapsible_match.rs:92:14 | LL | Some(val) => match val { - | ^^^ Replace this binding + | ^^^ replace this binding LL | Some(n) => foo(n), | ^^^^^^^ with this pattern diff --git a/tests/ui/collapsible_match2.stderr b/tests/ui/collapsible_match2.stderr index b2eb457d173..c8a445ef369 100644 --- a/tests/ui/collapsible_match2.stderr +++ b/tests/ui/collapsible_match2.stderr @@ -1,4 +1,4 @@ -error: Unnecessary nested match +error: unnecessary nested match --> $DIR/collapsible_match2.rs:8:34 | LL | Ok(val) if make() => match val { @@ -9,15 +9,15 @@ LL | | }, | |_____________^ | = note: `-D clippy::collapsible-match` implied by `-D warnings` -help: The outer pattern can be modified to include the inner pattern. +help: the outer pattern can be modified to include the inner pattern --> $DIR/collapsible_match2.rs:8:16 | LL | Ok(val) if make() => match val { - | ^^^ Replace this binding + | ^^^ replace this binding LL | Some(n) => foo(n), | ^^^^^^^ with this pattern -error: Unnecessary nested match +error: unnecessary nested match --> $DIR/collapsible_match2.rs:15:24 | LL | Ok(val) => match val { @@ -27,15 +27,15 @@ LL | | _ => return, LL | | }, | |_____________^ | -help: The outer pattern can be modified to include the inner pattern. +help: the outer pattern can be modified to include the inner pattern --> $DIR/collapsible_match2.rs:15:16 | LL | Ok(val) => match val { - | ^^^ Replace this binding + | ^^^ replace this binding LL | Some(n) => foo(n), | ^^^^^^^ with this pattern -error: Unnecessary nested match +error: unnecessary nested match --> $DIR/collapsible_match2.rs:29:29 | LL | $pat => match $e { @@ -48,16 +48,16 @@ LL | | }, LL | mac!(res_opt => Ok(val), val => Some(n), foo(n)); | ------------------------------------------------- in this macro invocation | -help: The outer pattern can be modified to include the inner pattern. +help: the outer pattern can be modified to include the inner pattern --> $DIR/collapsible_match2.rs:41:28 | LL | mac!(res_opt => Ok(val), val => Some(n), foo(n)); | ^^^ ^^^^^^^ with this pattern | | - | Replace this binding + | replace this binding = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: Unnecessary nested match +error: unnecessary nested match --> $DIR/collapsible_match2.rs:46:20 | LL | Some(s) => match *s { @@ -67,15 +67,15 @@ LL | | _ => (), LL | | }, | |_________^ | -help: The outer pattern can be modified to include the inner pattern. +help: the outer pattern can be modified to include the inner pattern --> $DIR/collapsible_match2.rs:46:14 | LL | Some(s) => match *s { - | ^ Replace this binding + | ^ replace this binding LL | [n] => foo(n), | ^^^ with this pattern -error: Unnecessary nested match +error: unnecessary nested match --> $DIR/collapsible_match2.rs:55:24 | LL | Some(ref s) => match &*s { @@ -85,11 +85,11 @@ LL | | _ => (), LL | | }, | |_________^ | -help: The outer pattern can be modified to include the inner pattern. +help: the outer pattern can be modified to include the inner pattern --> $DIR/collapsible_match2.rs:55:14 | LL | Some(ref s) => match &*s { - | ^^^^^ Replace this binding + | ^^^^^ replace this binding LL | [n] => foo(n), | ^^^ with this pattern -- cgit 1.4.1-3-g733a5 From bf55aee7b142ad14d102de3260e314a84bf8c35c Mon Sep 17 00:00:00 2001 From: bool Date: Fri, 19 Feb 2021 19:36:28 +0200 Subject: Updated from_str_radix_10 sugg to be slightly smarter and ran bless --- clippy_lints/src/from_str_radix_10.rs | 26 ++++++++++++++++++++++---- tests/ui/from_str_radix_10.rs | 3 +++ tests/ui/from_str_radix_10.stderr | 10 ++++++++-- 3 files changed, 33 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs index 993b85ed998..d0a170acb4f 100644 --- a/clippy_lints/src/from_str_radix_10.rs +++ b/clippy_lints/src/from_str_radix_10.rs @@ -1,9 +1,12 @@ use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::*; +use rustc_hir::{def, Expr, ExprKind, PrimTy, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::Ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::sym; +use crate::utils::is_type_diagnostic_item; use crate::utils::span_lint_and_sugg; use crate::utils::sugg::Sugg; @@ -40,8 +43,7 @@ impl LateLintPass<'tcx> for FromStrRadix10 { fn check_expr(&mut self, cx: &LateContext<'tcx>, exp: &Expr<'tcx>) { if_chain! { if let ExprKind::Call(maybe_path, arguments) = &exp.kind; - if let ExprKind::Path(qpath) = &maybe_path.kind; - if let QPath::TypeRelative(ty, pathseg) = &qpath; + if let ExprKind::Path(QPath::TypeRelative(ty, pathseg)) = &maybe_path.kind; // check if the first part of the path is some integer primitive if let TyKind::Path(ty_qpath) = &ty.kind; @@ -59,9 +61,20 @@ impl LateLintPass<'tcx> for FromStrRadix10 { if let rustc_ast::ast::LitKind::Int(10, _) = lit.node; then { + let expr = if let ExprKind::AddrOf(_, _, expr) = &arguments[0].kind { + let ty = cx.typeck_results().expr_ty(expr); + if is_ty_stringish(cx, ty) { + expr + } else { + &arguments[0] + } + } else { + &arguments[0] + }; + let sugg = Sugg::hir_with_applicability( cx, - &arguments[0], + expr, "", &mut Applicability::MachineApplicable ).maybe_par(); @@ -79,3 +92,8 @@ impl LateLintPass<'tcx> for FromStrRadix10 { } } } + +/// Checks if a Ty is `String` or `&str` +fn is_ty_stringish(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { + is_type_diagnostic_item(cx, ty, sym::string_type) || is_type_diagnostic_item(cx, ty, sym::str) +} diff --git a/tests/ui/from_str_radix_10.rs b/tests/ui/from_str_radix_10.rs index 0a973128664..2f2ea04847a 100644 --- a/tests/ui/from_str_radix_10.rs +++ b/tests/ui/from_str_radix_10.rs @@ -35,6 +35,9 @@ fn main() -> Result<(), Box> { let string = "300"; i32::from_str_radix(string, 10)?; + let stringier = "400".to_string(); + i32::from_str_radix(&stringier, 10)?; + // none of these should trigger the lint u16::from_str_radix("20", 3)?; i32::from_str_radix("45", 12)?; diff --git a/tests/ui/from_str_radix_10.stderr b/tests/ui/from_str_radix_10.stderr index d6669001915..471bf52a9a7 100644 --- a/tests/ui/from_str_radix_10.stderr +++ b/tests/ui/from_str_radix_10.stderr @@ -28,7 +28,7 @@ error: this call to `from_str_radix` can be replaced with a call to `str::parse` --> $DIR/from_str_radix_10.rs:32:5 | LL | u16::from_str_radix(&("10".to_owned() + "5"), 10)?; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(&("10".to_owned() + "5")).parse::()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(("10".to_owned() + "5")).parse::()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` --> $DIR/from_str_radix_10.rs:33:5 @@ -42,5 +42,11 @@ error: this call to `from_str_radix` can be replaced with a call to `str::parse` LL | i32::from_str_radix(string, 10)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.parse::()` -error: aborting due to 7 previous errors +error: this call to `from_str_radix` can be replaced with a call to `str::parse` + --> $DIR/from_str_radix_10.rs:39:5 + | +LL | i32::from_str_radix(&stringier, 10)?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `stringier.parse::()` + +error: aborting due to 8 previous errors -- cgit 1.4.1-3-g733a5 From 5af6f96c8fe029ae4f9696084f6a22f5d750f3a9 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sat, 20 Feb 2021 19:48:04 +0100 Subject: Fix camel case postfix for `enum_variant_names` lint --- clippy_lints/src/utils/camel_case.rs | 6 ++++++ tests/ui/enum_variants.rs | 13 +++++++++++++ tests/ui/enum_variants.stderr | 26 +++++++++++++++++++++++++- 3 files changed, 44 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/clippy_lints/src/utils/camel_case.rs b/clippy_lints/src/utils/camel_case.rs index 4192a26d3c8..ba1c01ebc9f 100644 --- a/clippy_lints/src/utils/camel_case.rs +++ b/clippy_lints/src/utils/camel_case.rs @@ -55,6 +55,8 @@ pub fn from(s: &str) -> usize { } } else if c.is_lowercase() { down = true; + } else if c.is_uppercase() { + last_i = i; } else { return last_i; } @@ -70,12 +72,16 @@ mod test { fn from_full() { assert_eq!(from("AbcDef"), 0); assert_eq!(from("Abc"), 0); + assert_eq!(from("ABcd"), 0); + assert_eq!(from("ABcdEf"), 0); + assert_eq!(from("AabABcd"), 0); } #[test] fn from_partial() { assert_eq!(from("abcDef"), 3); assert_eq!(from("aDbc"), 1); + assert_eq!(from("aabABcd"), 3); } #[test] diff --git a/tests/ui/enum_variants.rs b/tests/ui/enum_variants.rs index 89d99dcf0c8..4fefc0b43f1 100644 --- a/tests/ui/enum_variants.rs +++ b/tests/ui/enum_variants.rs @@ -133,4 +133,17 @@ pub enum NetworkLayer { Layer3, } +// should lint suggesting `IData`, not only `Data` (see #4639) +enum IDataRequest { + PutIData(String), + GetIData(String), + DeleteUnpubIData(String), +} + +enum HIDataRequest { + PutHIData(String), + GetHIData(String), + DeleteUnpubHIData(String), +} + fn main() {} diff --git a/tests/ui/enum_variants.stderr b/tests/ui/enum_variants.stderr index b1d481190ff..ab7fff4507a 100644 --- a/tests/ui/enum_variants.stderr +++ b/tests/ui/enum_variants.stderr @@ -97,5 +97,29 @@ LL | | } = note: `-D clippy::pub-enum-variant-names` implied by `-D warnings` = help: remove the prefixes and use full paths to the variants instead of glob imports -error: aborting due to 10 previous errors +error: all variants have the same postfix: `IData` + --> $DIR/enum_variants.rs:137:1 + | +LL | / enum IDataRequest { +LL | | PutIData(String), +LL | | GetIData(String), +LL | | DeleteUnpubIData(String), +LL | | } + | |_^ + | + = help: remove the postfixes and use full paths to the variants instead of glob imports + +error: all variants have the same postfix: `HIData` + --> $DIR/enum_variants.rs:143:1 + | +LL | / enum HIDataRequest { +LL | | PutHIData(String), +LL | | GetHIData(String), +LL | | DeleteUnpubHIData(String), +LL | | } + | |_^ + | + = help: remove the postfixes and use full paths to the variants instead of glob imports + +error: aborting due to 12 previous errors -- cgit 1.4.1-3-g733a5 From d23038944a0ab7aaf8566196f9680b918f7ec155 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Sat, 20 Feb 2021 22:52:56 +0900 Subject: New lint: inconsistent_struct_constructor --- CHANGELOG.md | 1 + .../src/inconsistent_struct_constructor.rs | 118 +++++++++++++++++++++ clippy_lints/src/lib.rs | 5 + tests/ui/inconsistent_struct_constructor.fixed | 61 +++++++++++ tests/ui/inconsistent_struct_constructor.rs | 65 ++++++++++++ tests/ui/inconsistent_struct_constructor.stderr | 20 ++++ 6 files changed, 270 insertions(+) create mode 100644 clippy_lints/src/inconsistent_struct_constructor.rs create mode 100644 tests/ui/inconsistent_struct_constructor.fixed create mode 100644 tests/ui/inconsistent_struct_constructor.rs create mode 100644 tests/ui/inconsistent_struct_constructor.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ec8b3d98f8..c51557943e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2109,6 +2109,7 @@ Released 2018-09-13 [`implicit_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_sub [`imprecise_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#imprecise_flops [`inconsistent_digit_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_digit_grouping +[`inconsistent_struct_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_struct_constructor [`indexing_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing [`ineffective_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#ineffective_bit_mask [`inefficient_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inefficient_to_string diff --git a/clippy_lints/src/inconsistent_struct_constructor.rs b/clippy_lints/src/inconsistent_struct_constructor.rs new file mode 100644 index 00000000000..24dbfbbae2f --- /dev/null +++ b/clippy_lints/src/inconsistent_struct_constructor.rs @@ -0,0 +1,118 @@ +use rustc_data_structures::fx::FxHashMap; +use rustc_errors::Applicability; +use rustc_hir::{self as hir, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::Symbol; + +use if_chain::if_chain; + +use crate::utils::{snippet, span_lint_and_sugg}; + +declare_clippy_lint! { + /// **What it does:** Checks for struct constructors where the order of the field init + /// shorthand in the constructor is inconsistent with the order in the struct definition. + /// + /// **Why is this bad?** It decreases readability and consistency. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// struct Foo { + /// x: i32, + /// y: i32, + /// } + /// let x = 1; + /// let y = 2; + /// Foo { y, x }; + /// ``` + /// + /// Use instead: + /// ```rust + /// # struct Foo { + /// # x: i32, + /// # y: i32, + /// # } + /// # let x = 1; + /// # let y = 2; + /// Foo { x, y }; + /// ``` + pub INCONSISTENT_STRUCT_CONSTRUCTOR, + style, + "the order of the field init shorthand is inconsistent with the order in the struct definition" +} + +declare_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRUCTOR]); + +impl LateLintPass<'_> for InconsistentStructConstructor { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if_chain! { + if let ExprKind::Struct(qpath, fields, base) = expr.kind; + if let Some(def_id) = cx.qpath_res(qpath, expr.hir_id).opt_def_id(); + let ty = cx.tcx.type_of(def_id); + if let Some(adt_def) = ty.ty_adt_def(); + if adt_def.is_struct(); + if let Some(variant) = adt_def.variants.iter().next(); + if fields.iter().all(|f| f.is_shorthand); + then { + let mut def_order_map = FxHashMap::default(); + for (idx, field) in variant.fields.iter().enumerate() { + def_order_map.insert(field.ident.name, idx); + } + + if is_consistent_order(fields, &def_order_map) { + return; + } + + let mut ordered_fields: Vec<_> = fields.iter().map(|f| f.ident.name).collect(); + ordered_fields.sort_unstable_by_key(|id| def_order_map[id]); + + let mut fields_snippet = String::new(); + let (last_ident, idents) = ordered_fields.split_last().unwrap(); + for ident in idents { + fields_snippet.push_str(&format!("{}, ", ident)); + } + fields_snippet.push_str(&format!("{}", last_ident)); + + let base_snippet = if let Some(base) = base { + format!(", ..{}", snippet(cx, base.span, "..")) + } else { + "".to_string() + }; + + let sugg = format!("{} {{ {}{} }}", + snippet(cx, qpath.span(), ".."), + fields_snippet, + base_snippet, + ); + + span_lint_and_sugg( + cx, + INCONSISTENT_STRUCT_CONSTRUCTOR, + expr.span, + "inconsistent struct constructor", + "try", + sugg, + Applicability::MachineApplicable, + ) + } + } + } +} + +// Check whether the order of the fields in the constructor is consistent with the order in the +// definition. +fn is_consistent_order<'tcx>(fields: &'tcx [hir::Field<'tcx>], def_order_map: &FxHashMap) -> bool { + let mut cur_idx = usize::MIN; + for f in fields { + let next_idx = def_order_map[&f.ident.name]; + if cur_idx > next_idx { + return false; + } + cur_idx = next_idx; + } + + true +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 67e490584e8..c846dc187a4 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -221,6 +221,7 @@ mod if_let_some_result; mod if_not_else; mod implicit_return; mod implicit_saturating_sub; +mod inconsistent_struct_constructor; mod indexing_slicing; mod infinite_iter; mod inherent_impl; @@ -656,6 +657,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &if_not_else::IF_NOT_ELSE, &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, @@ -1036,6 +1038,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box implicit_return::ImplicitReturn); store.register_late_pass(|| box implicit_saturating_sub::ImplicitSaturatingSub); store.register_late_pass(|| box default_numeric_fallback::DefaultNumericFallback); + store.register_late_pass(|| box inconsistent_struct_constructor::InconsistentStructConstructor); let msrv = conf.msrv.as_ref().and_then(|s| { parse_msrv(s, None, None).or_else(|| { @@ -1485,6 +1488,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&identity_op::IDENTITY_OP), LintId::of(&if_let_mutex::IF_LET_MUTEX), LintId::of(&if_let_some_result::IF_LET_SOME_RESULT), + LintId::of(&inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR), LintId::of(&indexing_slicing::OUT_OF_BOUNDS_INDEXING), LintId::of(&infinite_iter::INFINITE_ITER), LintId::of(&inherent_to_string::INHERENT_TO_STRING), @@ -1737,6 +1741,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&functions::MUST_USE_UNIT), LintId::of(&functions::RESULT_UNIT_ERR), LintId::of(&if_let_some_result::IF_LET_SOME_RESULT), + LintId::of(&inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR), LintId::of(&inherent_to_string::INHERENT_TO_STRING), LintId::of(&len_zero::COMPARISON_TO_EMPTY), LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), diff --git a/tests/ui/inconsistent_struct_constructor.fixed b/tests/ui/inconsistent_struct_constructor.fixed new file mode 100644 index 00000000000..8d9c3110035 --- /dev/null +++ b/tests/ui/inconsistent_struct_constructor.fixed @@ -0,0 +1,61 @@ +// run-rustfix +// edition:2018 +#![warn(clippy::inconsistent_struct_constructor)] +#![allow(clippy::redundant_field_names)] +#![allow(clippy::unnecessary_operation)] +#![allow(clippy::no_effect)] +#![allow(dead_code)] + +#[derive(Default)] +struct Foo { + x: i32, + y: i32, + z: i32, +} + +mod without_base { + use super::Foo; + + fn test() { + let x = 1; + let y = 1; + let z = 1; + + // Should lint. + Foo { x, y, z }; + + // Shoule NOT lint because the order is the same as in the definition. + Foo { x, y, z }; + + // Should NOT lint because z is not a shorthand init. + Foo { y, x, z: z }; + } +} + +mod with_base { + use super::Foo; + + fn test() { + let x = 1; + let z = 1; + + // Should lint. + Foo { x, z, ..Default::default() }; + + // Should NOT lint because the order is consistent with the definition. + Foo { + x, + z, + ..Default::default() + }; + + // Should NOT lint because z is not a shorthand init. + Foo { + z: z, + x, + ..Default::default() + }; + } +} + +fn main() {} diff --git a/tests/ui/inconsistent_struct_constructor.rs b/tests/ui/inconsistent_struct_constructor.rs new file mode 100644 index 00000000000..63fac910501 --- /dev/null +++ b/tests/ui/inconsistent_struct_constructor.rs @@ -0,0 +1,65 @@ +// run-rustfix +// edition:2018 +#![warn(clippy::inconsistent_struct_constructor)] +#![allow(clippy::redundant_field_names)] +#![allow(clippy::unnecessary_operation)] +#![allow(clippy::no_effect)] +#![allow(dead_code)] + +#[derive(Default)] +struct Foo { + x: i32, + y: i32, + z: i32, +} + +mod without_base { + use super::Foo; + + fn test() { + let x = 1; + let y = 1; + let z = 1; + + // Should lint. + Foo { y, x, z }; + + // Shoule NOT lint because the order is the same as in the definition. + Foo { x, y, z }; + + // Should NOT lint because z is not a shorthand init. + Foo { y, x, z: z }; + } +} + +mod with_base { + use super::Foo; + + fn test() { + let x = 1; + let z = 1; + + // Should lint. + Foo { + z, + x, + ..Default::default() + }; + + // Should NOT lint because the order is consistent with the definition. + Foo { + x, + z, + ..Default::default() + }; + + // Should NOT lint because z is not a shorthand init. + Foo { + z: z, + x, + ..Default::default() + }; + } +} + +fn main() {} diff --git a/tests/ui/inconsistent_struct_constructor.stderr b/tests/ui/inconsistent_struct_constructor.stderr new file mode 100644 index 00000000000..d7abe44f254 --- /dev/null +++ b/tests/ui/inconsistent_struct_constructor.stderr @@ -0,0 +1,20 @@ +error: inconsistent struct constructor + --> $DIR/inconsistent_struct_constructor.rs:25:9 + | +LL | Foo { y, x, z }; + | ^^^^^^^^^^^^^^^ help: try: `Foo { x, y, z }` + | + = note: `-D clippy::inconsistent-struct-constructor` implied by `-D warnings` + +error: inconsistent struct constructor + --> $DIR/inconsistent_struct_constructor.rs:43:9 + | +LL | / Foo { +LL | | z, +LL | | x, +LL | | ..Default::default() +LL | | }; + | |_________^ help: try: `Foo { x, z, ..Default::default() }` + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From 19a377510c3de9a7404f690d52a5de761ce2aafd Mon Sep 17 00:00:00 2001 From: Morten Lohne Date: Sat, 20 Feb 2021 23:47:49 +0100 Subject: Fix FP in inherent_to_string when the function has generic parameters --- clippy_lints/src/inherent_to_string.rs | 1 + tests/ui/inherent_to_string.rs | 11 +++++++++++ tests/ui/inherent_to_string.stderr | 4 ++-- 3 files changed, 14 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/inherent_to_string.rs b/clippy_lints/src/inherent_to_string.rs index b723d06a688..0b23cdaa9f0 100644 --- a/clippy_lints/src/inherent_to_string.rs +++ b/clippy_lints/src/inherent_to_string.rs @@ -106,6 +106,7 @@ impl<'tcx> LateLintPass<'tcx> for InherentToString { let decl = &signature.decl; if decl.implicit_self.has_implicit_self(); if decl.inputs.len() == 1; + if impl_item.generics.params.is_empty(); // Check if return type is String if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym::string_type); diff --git a/tests/ui/inherent_to_string.rs b/tests/ui/inherent_to_string.rs index e6cf337d1bb..6e65fdbd04e 100644 --- a/tests/ui/inherent_to_string.rs +++ b/tests/ui/inherent_to_string.rs @@ -14,6 +14,7 @@ struct C; struct D; struct E; struct F; +struct G; impl A { // Should be detected; emit warning @@ -73,6 +74,13 @@ impl F { } } +impl G { + // Should not be detected, as it does not match the function signature + fn to_string(&self) -> String { + "G.to_string()".to_string() + } +} + fn main() { let a = A; a.to_string(); @@ -93,4 +101,7 @@ fn main() { let f = F; f.to_string(1); + + let g = G; + g.to_string::<1>(); } diff --git a/tests/ui/inherent_to_string.stderr b/tests/ui/inherent_to_string.stderr index 4f331f5bec9..f5fcc193b4d 100644 --- a/tests/ui/inherent_to_string.stderr +++ b/tests/ui/inherent_to_string.stderr @@ -1,5 +1,5 @@ error: implementation of inherent method `to_string(&self) -> String` for type `A` - --> $DIR/inherent_to_string.rs:20:5 + --> $DIR/inherent_to_string.rs:21:5 | LL | / fn to_string(&self) -> String { LL | | "A.to_string()".to_string() @@ -10,7 +10,7 @@ LL | | } = help: implement trait `Display` for type `A` instead error: type `C` implements inherent method `to_string(&self) -> String` which shadows the implementation of `Display` - --> $DIR/inherent_to_string.rs:44:5 + --> $DIR/inherent_to_string.rs:45:5 | LL | / fn to_string(&self) -> String { LL | | "C.to_string()".to_string() -- cgit 1.4.1-3-g733a5 From efe33f9fe4bf6b644f26f51500d557614a5f2c93 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 10 Jan 2021 09:46:03 -0500 Subject: Add: option_manual_map lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 + clippy_lints/src/manual_map.rs | 262 ++++++++++++++++++++++++++++++++++++++ clippy_lints/src/utils/mod.rs | 27 +++- tests/ui/manual_map_option.fixed | 61 +++++++++ tests/ui/manual_map_option.rs | 113 ++++++++++++++++ tests/ui/manual_map_option.stderr | 158 +++++++++++++++++++++++ 7 files changed, 626 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/manual_map.rs create mode 100644 tests/ui/manual_map_option.fixed create mode 100644 tests/ui/manual_map_option.rs create mode 100644 tests/ui/manual_map_option.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ec8b3d98f8..edc5cea1dd6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2161,6 +2161,7 @@ Released 2018-09-13 [`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map [`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map [`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten +[`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map [`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy [`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive [`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 2e0bc4de801..b151c178afa 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -241,6 +241,7 @@ mod loops; mod macro_use; mod main_recursion; mod manual_async_fn; +mod manual_map; mod manual_non_exhaustive; mod manual_ok_or; mod manual_strip; @@ -706,6 +707,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ¯o_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, @@ -1262,6 +1264,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons); store.register_late_pass(|| box redundant_slicing::RedundantSlicing); store.register_late_pass(|| box from_str_radix_10::FromStrRadix10); + store.register_late_pass(|| box manual_map::ManualMap); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1521,6 +1524,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), @@ -1750,6 +1754,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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(&matches::INFALLIBLE_DESTRUCTURING_MATCH), diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs new file mode 100644 index 00000000000..e399c8fda55 --- /dev/null +++ b/clippy_lints/src/manual_map.rs @@ -0,0 +1,262 @@ +use crate::utils::{ + is_type_diagnostic_item, match_def_path, paths, peel_hir_expr_refs, peel_mid_ty_refs_is_mutable, + snippet_with_applicability, span_lint_and_sugg, +}; +use rustc_ast::util::parser::PREC_POSTFIX; +use rustc_errors::Applicability; +use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, Mutability, Pat, PatKind, Path, QPath}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::{sym, Ident}; + +declare_clippy_lint! { + /// **What it does:** Checks for usages of `match` which could be implemented using `map` + /// + /// **Why is this bad?** Using the `map` method is clearer and more concise. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// match Some(0) { + /// Some(x) => Some(x + 1), + /// None => None, + /// }; + /// ``` + /// Use instead: + /// ```rust + /// Some(0).map(|x| x + 1); + /// ``` + pub MANUAL_MAP, + style, + "reimplementation of `map`" +} + +declare_lint_pass!(ManualMap => [MANUAL_MAP]); + +impl LateLintPass<'_> for ManualMap { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if in_external_macro(cx.sess(), expr.span) { + return; + } + + if let ExprKind::Match(scrutinee, [arm1 @ Arm { guard: None, .. }, arm2 @ Arm { guard: None, .. }], _) = + expr.kind + { + let (scrutinee_ty, ty_ref_count, ty_mutability) = + peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee)); + if !is_type_diagnostic_item(cx, scrutinee_ty, sym::option_type) + || !is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::option_type) + { + return; + } + + let (some_expr, some_pat, pat_ref_count, is_wild_none) = + match (try_parse_pattern(cx, arm1.pat), try_parse_pattern(cx, arm2.pat)) { + (Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) + if is_none_expr(cx, arm1.body) => + { + (arm2.body, pattern, ref_count, true) + }, + (Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) + if is_none_expr(cx, arm1.body) => + { + (arm2.body, pattern, ref_count, false) + }, + (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) + if is_none_expr(cx, arm2.body) => + { + (arm1.body, pattern, ref_count, true) + }, + (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) + if is_none_expr(cx, arm2.body) => + { + (arm1.body, pattern, ref_count, false) + }, + _ => return, + }; + + // Top level or patterns aren't allowed in closures. + if matches!(some_pat.kind, PatKind::Or(_)) { + return; + } + + let some_expr = match get_some_expr(cx, some_expr) { + Some(expr) => expr, + None => return, + }; + + // Determine which binding mode to use. + let explicit_ref = some_pat.contains_explicit_ref_binding(); + let binding_mutability = explicit_ref.or(if ty_ref_count != pat_ref_count { + Some(ty_mutability) + } else { + None + }); + let as_ref_str = match binding_mutability { + Some(Mutability::Mut) => ".as_mut()", + Some(Mutability::Not) => ".as_ref()", + None => "", + }; + + let mut app = Applicability::MachineApplicable; + + // Remove address-of expressions from the scrutinee. `as_ref` will be called, + // the type is copyable, or the option is being passed by value. + let scrutinee = peel_hir_expr_refs(scrutinee).0; + let scrutinee_str = snippet_with_applicability(cx, scrutinee.span, "_", &mut app); + let scrutinee_str = if expr.precedence().order() < PREC_POSTFIX { + // Parens are needed to chain method calls. + format!("({})", scrutinee_str) + } else { + scrutinee_str.into() + }; + + let body_str = if let PatKind::Binding(annotation, _, some_binding, None) = some_pat.kind { + if let Some(func) = can_pass_as_func(cx, some_binding, some_expr) { + snippet_with_applicability(cx, func.span, "..", &mut app).into_owned() + } else { + // `ref` and `ref mut` annotations were handled earlier. + let annotation = if matches!(annotation, BindingAnnotation::Mutable) { + "mut " + } else { + "" + }; + format!( + "|{}{}| {}", + annotation, + some_binding, + snippet_with_applicability(cx, some_expr.span, "..", &mut app) + ) + } + } else if !is_wild_none && explicit_ref.is_none() { + // TODO: handle explicit reference annotations. + format!( + "|{}| {}", + snippet_with_applicability(cx, some_pat.span, "..", &mut app), + snippet_with_applicability(cx, some_expr.span, "..", &mut app) + ) + } else { + // Refutable bindings and mixed reference annotations can't be handled by `map`. + return; + }; + + span_lint_and_sugg( + cx, + MANUAL_MAP, + expr.span, + "manual implementation of `Option::map`", + "try this", + format!("{}{}.map({})", scrutinee_str, as_ref_str, body_str), + app, + ); + } + } +} + +// Checks whether the expression could be passed as a function, or whether a closure is needed. +// Returns the function to be passed to `map` if it exists. +fn can_pass_as_func(cx: &LateContext<'tcx>, binding: Ident, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { + match expr.kind { + ExprKind::Call(func, [arg]) + if matches!(arg.kind, + ExprKind::Path(QPath::Resolved(None, Path { segments: [path], ..})) + if path.ident == binding + ) && cx.typeck_results().expr_adjustments(arg).is_empty() => + { + Some(func) + }, + _ => None, + } +} + +enum OptionPat<'a> { + Wild, + None, + Some { + // The pattern contained in the `Some` tuple. + pattern: &'a Pat<'a>, + // The number of references before the `Some` tuple. + // e.g. `&&Some(_)` has a ref count of 2. + ref_count: usize, + }, +} + +// Try to parse into a recognized `Option` pattern. +// i.e. `_`, `None`, `Some(..)`, or a reference to any of those. +fn try_parse_pattern(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) -> Option> { + fn f(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ref_count: usize) -> Option> { + match pat.kind { + PatKind::Wild => Some(OptionPat::Wild), + PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1), + PatKind::Path(QPath::Resolved(None, path)) + if path + .res + .opt_def_id() + .map_or(false, |id| match_def_path(cx, id, &paths::OPTION_NONE)) => + { + Some(OptionPat::None) + }, + PatKind::TupleStruct(QPath::Resolved(None, path), [pattern], _) + if path + .res + .opt_def_id() + .map_or(false, |id| match_def_path(cx, id, &paths::OPTION_SOME)) => + { + Some(OptionPat::Some { pattern, ref_count }) + }, + _ => None, + } + } + f(cx, pat, 0) +} + +// Checks for an expression wrapped by the `Some` constructor. Returns the contained expression. +fn get_some_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { + // TODO: Allow more complex expressions. + match expr.kind { + ExprKind::Call( + Expr { + kind: ExprKind::Path(QPath::Resolved(None, path)), + .. + }, + [arg], + ) => { + if match_def_path(cx, path.res.opt_def_id()?, &paths::OPTION_SOME) { + Some(arg) + } else { + None + } + }, + ExprKind::Block( + Block { + stmts: [], + expr: Some(expr), + .. + }, + _, + ) => get_some_expr(cx, expr), + _ => None, + } +} + +// Checks for the `None` value. +fn is_none_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { + match expr.kind { + ExprKind::Path(QPath::Resolved(None, path)) => path + .res + .opt_def_id() + .map_or(false, |id| match_def_path(cx, id, &paths::OPTION_NONE)), + ExprKind::Block( + Block { + stmts: [], + expr: Some(expr), + .. + }, + _, + ) => is_none_expr(cx, expr), + _ => false, + } +} diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index fafa1400156..40771449264 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -32,7 +32,7 @@ use std::collections::hash_map::Entry; use std::hash::BuildHasherDefault; use if_chain::if_chain; -use rustc_ast::ast::{self, Attribute, LitKind}; +use rustc_ast::ast::{self, Attribute, BorrowKind, LitKind, Mutability}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir as hir; @@ -1672,6 +1672,18 @@ pub fn peel_n_hir_expr_refs(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, f(expr, 0, count) } +/// Peels off all references on the expression. Returns the underlying expression and the number of +/// references removed. +pub fn peel_hir_expr_refs(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) { + fn f(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) { + match expr.kind { + ExprKind::AddrOf(BorrowKind::Ref, _, expr) => f(expr, count + 1), + _ => (expr, count), + } + } + f(expr, 0) +} + /// Peels off all references on the type. Returns the underlying type and the number of references /// removed. pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) { @@ -1685,6 +1697,19 @@ pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) { peel(ty, 0) } +/// Peels off all references on the type.Returns the underlying type, the number of references +/// removed, and whether the pointer is ultimately mutable or not. +pub fn peel_mid_ty_refs_is_mutable(ty: Ty<'_>) -> (Ty<'_>, usize, Mutability) { + fn f(ty: Ty<'_>, count: usize, mutability: Mutability) -> (Ty<'_>, usize, Mutability) { + match ty.kind() { + ty::Ref(_, ty, Mutability::Mut) => f(ty, count + 1, mutability), + ty::Ref(_, ty, Mutability::Not) => f(ty, count + 1, Mutability::Not), + _ => (ty, count, mutability), + } + } + f(ty, 0, Mutability::Mut) +} + #[macro_export] macro_rules! unwrap_cargo_metadata { ($cx: ident, $lint: ident, $deps: expr) => {{ diff --git a/tests/ui/manual_map_option.fixed b/tests/ui/manual_map_option.fixed new file mode 100644 index 00000000000..6cb9a37b230 --- /dev/null +++ b/tests/ui/manual_map_option.fixed @@ -0,0 +1,61 @@ +// run-rustfix + +#![warn(clippy::manual_map)] +#![allow(clippy::no_effect, clippy::map_identity, clippy::unit_arg, clippy::match_ref_pats)] + +fn main() { + Some(0).map(|_| 2); + + Some(0).map(|x| x + 1); + + Some("").map(|x| x.is_empty()); + + Some(0).map(|x| !x); + + #[rustfmt::skip] + Some(0).map(std::convert::identity); + + Some(&String::new()).map(|x| str::len(x)); + + match Some(0) { + Some(x) if false => Some(x + 1), + _ => None, + }; + + Some([0, 1]).as_ref().map(|x| x[0]); + + Some(0).map(|x| x * 2); + + Some(String::new()).as_ref().map(|x| x.is_empty()); + + Some(String::new()).as_ref().map(|x| x.len()); + + Some(0).map(|x| x + x); + + Some(String::new()).as_mut().map(|x| x.push_str("")); + + Some(String::new()).as_ref().map(|x| &**x); + + Some(String::new()).as_ref().map(|x| x.is_empty()); + + Some((0, 1, 2)).map(|(x, y, z)| x + y + z); + + Some([1, 2, 3]).map(|[first, ..]| first); + + Some((String::new(), "test")).as_ref().map(|(x, y)| (y, x)); + + match Some((String::new(), 0)) { + Some((ref x, y)) => Some((y, x)), + None => None, + }; + + match Some(Some(0)) { + Some(Some(_)) | Some(None) => Some(0), + None => None, + }; + + match Some(Some((0, 1))) { + Some(Some((x, 1))) => Some(x), + _ => None, + }; +} diff --git a/tests/ui/manual_map_option.rs b/tests/ui/manual_map_option.rs new file mode 100644 index 00000000000..b9753060b99 --- /dev/null +++ b/tests/ui/manual_map_option.rs @@ -0,0 +1,113 @@ +// run-rustfix + +#![warn(clippy::manual_map)] +#![allow(clippy::no_effect, clippy::map_identity, clippy::unit_arg, clippy::match_ref_pats)] + +fn main() { + match Some(0) { + Some(_) => Some(2), + None:: => None, + }; + + match Some(0) { + Some(x) => Some(x + 1), + _ => None, + }; + + match Some("") { + Some(x) => Some(x.is_empty()), + None => None, + }; + + if let Some(x) = Some(0) { + Some(!x) + } else { + None + }; + + #[rustfmt::skip] + match Some(0) { + Some(x) => { Some(std::convert::identity(x)) } + None => { None } + }; + + match Some(&String::new()) { + Some(x) => Some(str::len(x)), + None => None, + }; + + match Some(0) { + Some(x) if false => Some(x + 1), + _ => None, + }; + + match &Some([0, 1]) { + Some(x) => Some(x[0]), + &None => None, + }; + + match &Some(0) { + &Some(x) => Some(x * 2), + None => None, + }; + + match Some(String::new()) { + Some(ref x) => Some(x.is_empty()), + _ => None, + }; + + match &&Some(String::new()) { + Some(x) => Some(x.len()), + _ => None, + }; + + match &&Some(0) { + &&Some(x) => Some(x + x), + &&_ => None, + }; + + match &mut Some(String::new()) { + Some(x) => Some(x.push_str("")), + None => None, + }; + + match &mut Some(String::new()) { + Some(ref x) => Some(&**x), + None => None, + }; + + match &mut &Some(String::new()) { + Some(x) => Some(x.is_empty()), + &mut _ => None, + }; + + match Some((0, 1, 2)) { + Some((x, y, z)) => Some(x + y + z), + None => None, + }; + + match Some([1, 2, 3]) { + Some([first, ..]) => Some(first), + None => None, + }; + + match &Some((String::new(), "test")) { + Some((x, y)) => Some((y, x)), + None => None, + }; + + match Some((String::new(), 0)) { + Some((ref x, y)) => Some((y, x)), + None => None, + }; + + match Some(Some(0)) { + Some(Some(_)) | Some(None) => Some(0), + None => None, + }; + + match Some(Some((0, 1))) { + Some(Some((x, 1))) => Some(x), + _ => None, + }; +} diff --git a/tests/ui/manual_map_option.stderr b/tests/ui/manual_map_option.stderr new file mode 100644 index 00000000000..f8e1bda83b2 --- /dev/null +++ b/tests/ui/manual_map_option.stderr @@ -0,0 +1,158 @@ +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:7:5 + | +LL | / match Some(0) { +LL | | Some(_) => Some(2), +LL | | None:: => None, +LL | | }; + | |_____^ help: try this: `Some(0).map(|_| 2)` + | + = note: `-D clippy::manual-map` implied by `-D warnings` + +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:12:5 + | +LL | / match Some(0) { +LL | | Some(x) => Some(x + 1), +LL | | _ => None, +LL | | }; + | |_____^ help: try this: `Some(0).map(|x| x + 1)` + +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:17:5 + | +LL | / match Some("") { +LL | | Some(x) => Some(x.is_empty()), +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some("").map(|x| x.is_empty())` + +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:22:5 + | +LL | / if let Some(x) = Some(0) { +LL | | Some(!x) +LL | | } else { +LL | | None +LL | | }; + | |_____^ help: try this: `Some(0).map(|x| !x)` + +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:29:5 + | +LL | / match Some(0) { +LL | | Some(x) => { Some(std::convert::identity(x)) } +LL | | None => { None } +LL | | }; + | |_____^ help: try this: `Some(0).map(std::convert::identity)` + +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:34:5 + | +LL | / match Some(&String::new()) { +LL | | Some(x) => Some(str::len(x)), +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some(&String::new()).map(|x| str::len(x))` + +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:44:5 + | +LL | / match &Some([0, 1]) { +LL | | Some(x) => Some(x[0]), +LL | | &None => None, +LL | | }; + | |_____^ help: try this: `Some([0, 1]).as_ref().map(|x| x[0])` + +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:49:5 + | +LL | / match &Some(0) { +LL | | &Some(x) => Some(x * 2), +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some(0).map(|x| x * 2)` + +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:54:5 + | +LL | / match Some(String::new()) { +LL | | Some(ref x) => Some(x.is_empty()), +LL | | _ => None, +LL | | }; + | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.is_empty())` + +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:59:5 + | +LL | / match &&Some(String::new()) { +LL | | Some(x) => Some(x.len()), +LL | | _ => None, +LL | | }; + | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.len())` + +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:64:5 + | +LL | / match &&Some(0) { +LL | | &&Some(x) => Some(x + x), +LL | | &&_ => None, +LL | | }; + | |_____^ help: try this: `Some(0).map(|x| x + x)` + +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:69:5 + | +LL | / match &mut Some(String::new()) { +LL | | Some(x) => Some(x.push_str("")), +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some(String::new()).as_mut().map(|x| x.push_str(""))` + +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:74:5 + | +LL | / match &mut Some(String::new()) { +LL | | Some(ref x) => Some(&**x), +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| &**x)` + +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:79:5 + | +LL | / match &mut &Some(String::new()) { +LL | | Some(x) => Some(x.is_empty()), +LL | | &mut _ => None, +LL | | }; + | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.is_empty())` + +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:84:5 + | +LL | / match Some((0, 1, 2)) { +LL | | Some((x, y, z)) => Some(x + y + z), +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some((0, 1, 2)).map(|(x, y, z)| x + y + z)` + +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:89:5 + | +LL | / match Some([1, 2, 3]) { +LL | | Some([first, ..]) => Some(first), +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some([1, 2, 3]).map(|[first, ..]| first)` + +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:94:5 + | +LL | / match &Some((String::new(), "test")) { +LL | | Some((x, y)) => Some((y, x)), +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some((String::new(), "test")).as_ref().map(|(x, y)| (y, x))` + +error: aborting due to 17 previous errors + -- cgit 1.4.1-3-g733a5 From 23aa2f880cc0bda7ea3bbef1391f7c4d86467d65 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 19 Feb 2021 14:04:37 -0500 Subject: Fix dogfood errors --- clippy_lints/src/manual_map.rs | 40 +++++++++++++++++++++++++-------------- tests/ui/manual_map_option.fixed | 13 +++++++++++-- tests/ui/manual_map_option.rs | 11 ++++++++++- tests/ui/manual_map_option.stderr | 26 ++++++++++++------------- 4 files changed, 60 insertions(+), 30 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs index e399c8fda55..a50a3943bab 100644 --- a/clippy_lints/src/manual_map.rs +++ b/clippy_lints/src/manual_map.rs @@ -1,10 +1,14 @@ -use crate::utils::{ - is_type_diagnostic_item, match_def_path, paths, peel_hir_expr_refs, peel_mid_ty_refs_is_mutable, - snippet_with_applicability, span_lint_and_sugg, +use crate::{ + map_unit_fn::OPTION_MAP_UNIT_FN, + matches::MATCH_AS_REF, + utils::{ + is_allowed, is_type_diagnostic_item, match_def_path, match_var, paths, peel_hir_expr_refs, + peel_mid_ty_refs_is_mutable, snippet_with_applicability, span_lint_and_sugg, + }, }; use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::Applicability; -use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, Mutability, Pat, PatKind, Path, QPath}; +use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, Mutability, Pat, PatKind, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -37,6 +41,7 @@ declare_clippy_lint! { declare_lint_pass!(ManualMap => [MANUAL_MAP]); impl LateLintPass<'_> for ManualMap { + #[allow(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if in_external_macro(cx.sess(), expr.span) { return; @@ -88,14 +93,17 @@ impl LateLintPass<'_> for ManualMap { None => return, }; + if cx.typeck_results().expr_ty(some_expr) == cx.tcx.types.unit + && !is_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id) + { + return; + } + // Determine which binding mode to use. let explicit_ref = some_pat.contains_explicit_ref_binding(); - let binding_mutability = explicit_ref.or(if ty_ref_count != pat_ref_count { - Some(ty_mutability) - } else { - None - }); - let as_ref_str = match binding_mutability { + let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then(|| ty_mutability)); + + let as_ref_str = match binding_ref { Some(Mutability::Mut) => ".as_mut()", Some(Mutability::Not) => ".as_ref()", None => "", @@ -118,6 +126,13 @@ impl LateLintPass<'_> for ManualMap { if let Some(func) = can_pass_as_func(cx, some_binding, some_expr) { snippet_with_applicability(cx, func.span, "..", &mut app).into_owned() } else { + if match_var(some_expr, some_binding.name) + && !is_allowed(cx, MATCH_AS_REF, expr.hir_id) + && binding_ref.is_some() + { + return; + } + // `ref` and `ref mut` annotations were handled earlier. let annotation = if matches!(annotation, BindingAnnotation::Mutable) { "mut " @@ -161,10 +176,7 @@ impl LateLintPass<'_> for ManualMap { fn can_pass_as_func(cx: &LateContext<'tcx>, binding: Ident, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { match expr.kind { ExprKind::Call(func, [arg]) - if matches!(arg.kind, - ExprKind::Path(QPath::Resolved(None, Path { segments: [path], ..})) - if path.ident == binding - ) && cx.typeck_results().expr_adjustments(arg).is_empty() => + if match_var(arg, binding.name) && cx.typeck_results().expr_adjustments(arg).is_empty() => { Some(func) }, diff --git a/tests/ui/manual_map_option.fixed b/tests/ui/manual_map_option.fixed index 6cb9a37b230..19350906758 100644 --- a/tests/ui/manual_map_option.fixed +++ b/tests/ui/manual_map_option.fixed @@ -32,9 +32,18 @@ fn main() { Some(0).map(|x| x + x); - Some(String::new()).as_mut().map(|x| x.push_str("")); + #[warn(clippy::option_map_unit_fn)] + match &mut Some(String::new()) { + Some(x) => Some(x.push_str("")), + None => None, + }; - Some(String::new()).as_ref().map(|x| &**x); + #[allow(clippy::option_map_unit_fn)] + { + Some(String::new()).as_mut().map(|x| x.push_str("")); + } + + Some(String::new()).as_ref().map(|x| x.len()); Some(String::new()).as_ref().map(|x| x.is_empty()); diff --git a/tests/ui/manual_map_option.rs b/tests/ui/manual_map_option.rs index b9753060b99..8b8187db0a9 100644 --- a/tests/ui/manual_map_option.rs +++ b/tests/ui/manual_map_option.rs @@ -66,13 +66,22 @@ fn main() { &&_ => None, }; + #[warn(clippy::option_map_unit_fn)] match &mut Some(String::new()) { Some(x) => Some(x.push_str("")), None => None, }; + #[allow(clippy::option_map_unit_fn)] + { + match &mut Some(String::new()) { + Some(x) => Some(x.push_str("")), + None => None, + }; + } + match &mut Some(String::new()) { - Some(ref x) => Some(&**x), + Some(ref x) => Some(x.len()), None => None, }; diff --git a/tests/ui/manual_map_option.stderr b/tests/ui/manual_map_option.stderr index f8e1bda83b2..210a30d05d4 100644 --- a/tests/ui/manual_map_option.stderr +++ b/tests/ui/manual_map_option.stderr @@ -101,25 +101,25 @@ LL | | }; | |_____^ help: try this: `Some(0).map(|x| x + x)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:69:5 + --> $DIR/manual_map_option.rs:77:9 | -LL | / match &mut Some(String::new()) { -LL | | Some(x) => Some(x.push_str("")), -LL | | None => None, -LL | | }; - | |_____^ help: try this: `Some(String::new()).as_mut().map(|x| x.push_str(""))` +LL | / match &mut Some(String::new()) { +LL | | Some(x) => Some(x.push_str("")), +LL | | None => None, +LL | | }; + | |_________^ help: try this: `Some(String::new()).as_mut().map(|x| x.push_str(""))` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:74:5 + --> $DIR/manual_map_option.rs:83:5 | LL | / match &mut Some(String::new()) { -LL | | Some(ref x) => Some(&**x), +LL | | Some(ref x) => Some(x.len()), LL | | None => None, LL | | }; - | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| &**x)` + | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.len())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:79:5 + --> $DIR/manual_map_option.rs:88:5 | LL | / match &mut &Some(String::new()) { LL | | Some(x) => Some(x.is_empty()), @@ -128,7 +128,7 @@ LL | | }; | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.is_empty())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:84:5 + --> $DIR/manual_map_option.rs:93:5 | LL | / match Some((0, 1, 2)) { LL | | Some((x, y, z)) => Some(x + y + z), @@ -137,7 +137,7 @@ LL | | }; | |_____^ help: try this: `Some((0, 1, 2)).map(|(x, y, z)| x + y + z)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:89:5 + --> $DIR/manual_map_option.rs:98:5 | LL | / match Some([1, 2, 3]) { LL | | Some([first, ..]) => Some(first), @@ -146,7 +146,7 @@ LL | | }; | |_____^ help: try this: `Some([1, 2, 3]).map(|[first, ..]| first)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:94:5 + --> $DIR/manual_map_option.rs:103:5 | LL | / match &Some((String::new(), "test")) { LL | | Some((x, y)) => Some((y, x)), -- cgit 1.4.1-3-g733a5 From ff157ae1f442d574317cd4d8cd6a6eaf27c8fbc4 Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius III" Date: Wed, 17 Feb 2021 12:30:48 -0500 Subject: Improve tests --- clippy_utils/Cargo.toml | 4 ++-- tests/versioncheck.rs | 25 ++++++++++++++----------- 2 files changed, 16 insertions(+), 13 deletions(-) (limited to 'tests') diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index cfa0661d87a..d99dbbd7aa3 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_utils" -version = "0.1.0" -authors = ["The Rust Project Developers"] +version = "0.1.51" +authors = ["The Rust Clippy Developers"] edition = "2018" [dependencies] diff --git a/tests/versioncheck.rs b/tests/versioncheck.rs index bc5ed0816cc..aadd2c1fb7f 100644 --- a/tests/versioncheck.rs +++ b/tests/versioncheck.rs @@ -2,21 +2,24 @@ use rustc_tools_util::VersionInfo; #[test] -fn check_that_clippy_lints_has_the_same_version_as_clippy() { +fn check_that_clippy_lints_and_clippy_utils_have_the_same_version_as_clippy() { let clippy_meta = cargo_metadata::MetadataCommand::new() .no_deps() .exec() .expect("could not obtain cargo metadata"); - std::env::set_current_dir(std::env::current_dir().unwrap().join("clippy_lints")).unwrap(); - let clippy_lints_meta = cargo_metadata::MetadataCommand::new() - .no_deps() - .exec() - .expect("could not obtain cargo metadata"); - assert_eq!(clippy_lints_meta.packages[0].version, clippy_meta.packages[0].version); - for package in &clippy_meta.packages[0].dependencies { - if package.name == "clippy_lints" { - assert!(package.req.matches(&clippy_lints_meta.packages[0].version)); - return; + + for krate in &["clippy_lints", "clippy_utils"] { + let krate_meta = cargo_metadata::MetadataCommand::new() + .current_dir(std::env::current_dir().unwrap().join(krate)) + .no_deps() + .exec() + .expect("could not obtain cargo metadata"); + assert_eq!(krate_meta.packages[0].version, clippy_meta.packages[0].version); + for package in &clippy_meta.packages[0].dependencies { + if package.name == *krate { + assert!(package.req.matches(&krate_meta.packages[0].version)); + break; + } } } } -- cgit 1.4.1-3-g733a5 From 1e7b1ccb2a05f80ae0a580401e7565fb1c0a4917 Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius III" Date: Wed, 17 Feb 2021 17:05:32 -0500 Subject: Fix versioncheck test --- tests/versioncheck.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/tests/versioncheck.rs b/tests/versioncheck.rs index aadd2c1fb7f..1c954c57a85 100644 --- a/tests/versioncheck.rs +++ b/tests/versioncheck.rs @@ -9,15 +9,15 @@ fn check_that_clippy_lints_and_clippy_utils_have_the_same_version_as_clippy() { .expect("could not obtain cargo metadata"); for krate in &["clippy_lints", "clippy_utils"] { - let krate_meta = cargo_metadata::MetadataCommand::new() - .current_dir(std::env::current_dir().unwrap().join(krate)) - .no_deps() - .exec() + let krate_meta = clippy_meta + .packages + .iter() + .find(|package| package.name == *krate) .expect("could not obtain cargo metadata"); - assert_eq!(krate_meta.packages[0].version, clippy_meta.packages[0].version); + assert_eq!(krate_meta.version, clippy_meta.packages[0].version); for package in &clippy_meta.packages[0].dependencies { if package.name == *krate { - assert!(package.req.matches(&krate_meta.packages[0].version)); + assert!(package.req.matches(&krate_meta.version)); break; } } -- cgit 1.4.1-3-g733a5 From 2c26c0f621ec0c94ae17c09191de2ed802a2fa93 Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius III" Date: Wed, 17 Feb 2021 21:04:38 -0500 Subject: Update custom_ice_message.stderr --- tests/ui-internal/custom_ice_message.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/ui-internal/custom_ice_message.stderr b/tests/ui-internal/custom_ice_message.stderr index a1b8e2ee162..29dee73c946 100644 --- a/tests/ui-internal/custom_ice_message.stderr +++ b/tests/ui-internal/custom_ice_message.stderr @@ -1,4 +1,4 @@ -thread 'rustc' panicked at 'Would you like some help with that?', clippy_lints/src/utils/internal_lints.rs +thread 'rustc' panicked at 'Would you like some help with that?', clippy_utils/src/internal_lints.rs note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace error: internal compiler error: unexpected panic -- cgit 1.4.1-3-g733a5 From ab7381f085d587f49949e31a02847b5a78b9ea36 Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius III" Date: Thu, 18 Feb 2021 12:25:35 -0500 Subject: Move `declare_clippy_lint` back into clippy_lints --- clippy_dev/src/lib.rs | 18 +- clippy_lints/src/lib.rs | 96 ++- clippy_lints/src/utils.rs | 1 - clippy_lints/src/utils/author.rs | 779 +++++++++++++++++++ clippy_lints/src/utils/inspector.rs | 578 +++++++++++++++ clippy_lints/src/utils/internal_lints.rs | 1070 +++++++++++++++++++++++++++ clippy_lints/src/utils/mod.rs | 6 + clippy_utils/src/author.rs | 779 ------------------- clippy_utils/src/inspector.rs | 578 --------------- clippy_utils/src/internal_lints.rs | 1070 --------------------------- clippy_utils/src/lib.rs | 103 --- tests/ui-internal/custom_ice_message.stderr | 2 +- 12 files changed, 2534 insertions(+), 2546 deletions(-) delete mode 100644 clippy_lints/src/utils.rs create mode 100644 clippy_lints/src/utils/author.rs create mode 100644 clippy_lints/src/utils/inspector.rs create mode 100644 clippy_lints/src/utils/internal_lints.rs create mode 100644 clippy_lints/src/utils/mod.rs delete mode 100644 clippy_utils/src/author.rs delete mode 100644 clippy_utils/src/inspector.rs delete mode 100644 clippy_utils/src/internal_lints.rs (limited to 'tests') diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index 61e423d82f3..01d1fc9211a 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -186,17 +186,11 @@ fn gather_from_file(dir_entry: &walkdir::DirEntry) -> impl Iterator let path_buf = path.with_file_name(filename); let mut rel_path = path_buf .strip_prefix(clippy_project_root().join("clippy_lints/src")) - .map(PathBuf::from) - .or_else(|_| { - path_buf - .strip_prefix(clippy_project_root().join("clippy_utils/src")) - .map(|c| Path::new("utils").join(c)) - }) - .expect("only files in `clippy_lints/src` or `clippy_utils/src` should be looked at"); + .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().to_path_buf(); + rel_path = rel_path.parent().unwrap(); } let module = rel_path @@ -219,15 +213,13 @@ fn parse_contents(content: &str, module: &str) -> impl Iterator { lints.chain(deprecated).collect::>().into_iter() } -/// Collects all .rs files in the `clippy_lints/src` and `clippy_utils/src` directories +/// 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 clippy_lints_path = clippy_project_root().join("clippy_lints/src"); - let clippy_utils_path = clippy_project_root().join("clippy_utils/src"); - WalkDir::new(clippy_lints_path) + let path = clippy_project_root().join("clippy_lints/src"); + WalkDir::new(path) .into_iter() - .chain(WalkDir::new(clippy_utils_path).into_iter()) .filter_map(Result::ok) .filter(|f| f.path().extension() == Some(OsStr::new("rs"))) } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 8a395e3a6e6..c8502e9cc74 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -46,9 +46,103 @@ use rustc_data_structures::fx::FxHashSet; use rustc_lint::LintId; use rustc_session::Session; +/// Macro used to declare a Clippy lint. +/// +/// Every lint declaration consists of 4 parts: +/// +/// 1. The documentation, which is used for the website +/// 2. The `LINT_NAME`. See [lint naming][lint_naming] on lint naming conventions. +/// 3. The `lint_level`, which is a mapping from *one* of our lint groups to `Allow`, `Warn` or +/// `Deny`. The lint level here has nothing to do with what lint groups the lint is a part of. +/// 4. The `description` that contains a short explanation on what's wrong with code where the +/// lint is triggered. +/// +/// Currently the categories `style`, `correctness`, `complexity` and `perf` are enabled by default. +/// As said in the README.md of this repository, if the lint level mapping changes, please update +/// README.md. +/// +/// # Example +/// +/// ``` +/// #![feature(rustc_private)] +/// extern crate rustc_session; +/// use rustc_session::declare_tool_lint; +/// use clippy_lints::declare_clippy_lint; +/// +/// declare_clippy_lint! { +/// /// **What it does:** Checks for ... (describe what the lint matches). +/// /// +/// /// **Why is this bad?** Supply the reason for linting the code. +/// /// +/// /// **Known problems:** None. (Or describe where it could go wrong.) +/// /// +/// /// **Example:** +/// /// +/// /// ```rust +/// /// // Bad +/// /// Insert a short example of code that triggers the lint +/// /// +/// /// // Good +/// /// Insert a short example of improved code that doesn't trigger the lint +/// /// ``` +/// pub LINT_NAME, +/// pedantic, +/// "description" +/// } +/// ``` +/// [lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints #[macro_export] macro_rules! declare_clippy_lint { - ( $($x:tt)* ) => { clippy_utils::declare_clippy_lint!($($x)*); } + { $(#[$attr:meta])* pub $name:tt, style, $description:tt } => { + declare_tool_lint! { + $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true + } + }; + { $(#[$attr:meta])* pub $name:tt, correctness, $description:tt } => { + declare_tool_lint! { + $(#[$attr])* pub clippy::$name, Deny, $description, report_in_external_macro: true + } + }; + { $(#[$attr:meta])* pub $name:tt, complexity, $description:tt } => { + declare_tool_lint! { + $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true + } + }; + { $(#[$attr:meta])* pub $name:tt, perf, $description:tt } => { + declare_tool_lint! { + $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true + } + }; + { $(#[$attr:meta])* pub $name:tt, pedantic, $description:tt } => { + declare_tool_lint! { + $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true + } + }; + { $(#[$attr:meta])* pub $name:tt, restriction, $description:tt } => { + declare_tool_lint! { + $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true + } + }; + { $(#[$attr:meta])* pub $name:tt, cargo, $description:tt } => { + declare_tool_lint! { + $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true + } + }; + { $(#[$attr:meta])* pub $name:tt, nursery, $description:tt } => { + declare_tool_lint! { + $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true + } + }; + { $(#[$attr:meta])* pub $name:tt, internal, $description:tt } => { + declare_tool_lint! { + $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true + } + }; + { $(#[$attr:meta])* pub $name:tt, internal_warn, $description:tt } => { + declare_tool_lint! { + $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true + } + }; } #[macro_export] diff --git a/clippy_lints/src/utils.rs b/clippy_lints/src/utils.rs deleted file mode 100644 index bf54e39769c..00000000000 --- a/clippy_lints/src/utils.rs +++ /dev/null @@ -1 +0,0 @@ -pub use clippy_utils::*; diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs new file mode 100644 index 00000000000..ca60d335262 --- /dev/null +++ b/clippy_lints/src/utils/author.rs @@ -0,0 +1,779 @@ +//! A group of attributes that can be attached to Rust code in order +//! to generate a clippy lint detecting said code automatically. + +use crate::utils::get_attr; +use rustc_ast::ast::{Attribute, LitFloatType, LitKind}; +use rustc_ast::walk_list; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir as hir; +use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; +use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, Pat, PatKind, QPath, Stmt, StmtKind, TyKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::hir::map::Map; +use rustc_session::Session; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Generates clippy code that detects the offending pattern + /// + /// **Example:** + /// ```rust,ignore + /// // ./tests/ui/my_lint.rs + /// fn foo() { + /// // detect the following pattern + /// #[clippy::author] + /// if x == 42 { + /// // but ignore everything from here on + /// #![clippy::author = "ignore"] + /// } + /// () + /// } + /// ``` + /// + /// Running `TESTNAME=ui/my_lint cargo uitest` will produce + /// a `./tests/ui/new_lint.stdout` file with the generated code: + /// + /// ```rust,ignore + /// // ./tests/ui/new_lint.stdout + /// if_chain! { + /// if let ExprKind::If(ref cond, ref then, None) = item.kind, + /// if let ExprKind::Binary(BinOp::Eq, ref left, ref right) = cond.kind, + /// if let ExprKind::Path(ref path) = left.kind, + /// if let ExprKind::Lit(ref lit) = right.kind, + /// if let LitKind::Int(42, _) = lit.node, + /// then { + /// // report your lint here + /// } + /// } + /// ``` + pub LINT_AUTHOR, + internal_warn, + "helper for writing lints" +} + +declare_lint_pass!(Author => [LINT_AUTHOR]); + +fn prelude() { + println!("if_chain! {{"); +} + +fn done() { + println!(" then {{"); + println!(" // report your lint here"); + println!(" }}"); + println!("}}"); +} + +impl<'tcx> LateLintPass<'tcx> for Author { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { + if !has_attr(cx.sess(), &item.attrs) { + return; + } + prelude(); + PrintVisitor::new("item").visit_item(item); + done(); + } + + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { + if !has_attr(cx.sess(), &item.attrs) { + return; + } + prelude(); + PrintVisitor::new("item").visit_impl_item(item); + done(); + } + + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { + if !has_attr(cx.sess(), &item.attrs) { + return; + } + prelude(); + PrintVisitor::new("item").visit_trait_item(item); + done(); + } + + fn check_variant(&mut self, cx: &LateContext<'tcx>, var: &'tcx hir::Variant<'_>) { + if !has_attr(cx.sess(), &var.attrs) { + return; + } + prelude(); + let parent_hir_id = cx.tcx.hir().get_parent_node(var.id); + PrintVisitor::new("var").visit_variant(var, &hir::Generics::empty(), parent_hir_id); + done(); + } + + fn check_struct_field(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::StructField<'_>) { + if !has_attr(cx.sess(), &field.attrs) { + return; + } + prelude(); + PrintVisitor::new("field").visit_struct_field(field); + done(); + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if !has_attr(cx.sess(), &expr.attrs) { + return; + } + prelude(); + PrintVisitor::new("expr").visit_expr(expr); + done(); + } + + fn check_arm(&mut self, cx: &LateContext<'tcx>, arm: &'tcx hir::Arm<'_>) { + if !has_attr(cx.sess(), &arm.attrs) { + return; + } + prelude(); + PrintVisitor::new("arm").visit_arm(arm); + done(); + } + + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'_>) { + if !has_attr(cx.sess(), stmt.kind.attrs(|id| cx.tcx.hir().item(id.id))) { + return; + } + prelude(); + PrintVisitor::new("stmt").visit_stmt(stmt); + done(); + } + + fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ForeignItem<'_>) { + if !has_attr(cx.sess(), &item.attrs) { + return; + } + prelude(); + PrintVisitor::new("item").visit_foreign_item(item); + done(); + } +} + +impl PrintVisitor { + #[must_use] + fn new(s: &'static str) -> Self { + Self { + ids: FxHashMap::default(), + current: s.to_owned(), + } + } + + fn next(&mut self, s: &'static str) -> String { + use std::collections::hash_map::Entry::{Occupied, Vacant}; + match self.ids.entry(s) { + // already there: start numbering from `1` + Occupied(mut occ) => { + let val = occ.get_mut(); + *val += 1; + format!("{}{}", s, *val) + }, + // not there: insert and return name as given + Vacant(vac) => { + vac.insert(0); + s.to_owned() + }, + } + } + + fn print_qpath(&mut self, path: &QPath<'_>) { + if let QPath::LangItem(lang_item, _) = *path { + println!( + " if matches!({}, QPath::LangItem(LangItem::{:?}, _));", + self.current, lang_item, + ); + } else { + print!(" if match_qpath({}, &[", self.current); + print_path(path, &mut true); + println!("]);"); + } + } +} + +struct PrintVisitor { + /// Fields are the current index that needs to be appended to pattern + /// binding names + ids: FxHashMap<&'static str, usize>, + /// the name that needs to be destructured + current: String, +} + +impl<'tcx> Visitor<'tcx> for PrintVisitor { + type Map = Map<'tcx>; + + #[allow(clippy::too_many_lines)] + fn visit_expr(&mut self, expr: &Expr<'_>) { + print!(" if let ExprKind::"); + let current = format!("{}.kind", self.current); + match expr.kind { + ExprKind::Box(ref inner) => { + let inner_pat = self.next("inner"); + println!("Box(ref {}) = {};", inner_pat, current); + self.current = inner_pat; + self.visit_expr(inner); + }, + ExprKind::Array(ref elements) => { + let elements_pat = self.next("elements"); + println!("Array(ref {}) = {};", elements_pat, current); + println!(" if {}.len() == {};", elements_pat, elements.len()); + for (i, element) in elements.iter().enumerate() { + self.current = format!("{}[{}]", elements_pat, i); + self.visit_expr(element); + } + }, + ExprKind::Call(ref func, ref args) => { + let func_pat = self.next("func"); + let args_pat = self.next("args"); + println!("Call(ref {}, ref {}) = {};", func_pat, args_pat, current); + self.current = func_pat; + self.visit_expr(func); + println!(" if {}.len() == {};", args_pat, args.len()); + for (i, arg) in args.iter().enumerate() { + self.current = format!("{}[{}]", args_pat, i); + self.visit_expr(arg); + } + }, + ExprKind::MethodCall(ref _method_name, ref _generics, ref _args, ref _fn_span) => { + println!( + "MethodCall(ref method_name, ref generics, ref args, ref fn_span) = {};", + current + ); + println!(" // unimplemented: `ExprKind::MethodCall` is not further destructured at the moment"); + }, + ExprKind::Tup(ref elements) => { + let elements_pat = self.next("elements"); + println!("Tup(ref {}) = {};", elements_pat, current); + println!(" if {}.len() == {};", elements_pat, elements.len()); + for (i, element) in elements.iter().enumerate() { + self.current = format!("{}[{}]", elements_pat, i); + self.visit_expr(element); + } + }, + ExprKind::Binary(ref op, ref left, ref right) => { + let op_pat = self.next("op"); + let left_pat = self.next("left"); + let right_pat = self.next("right"); + println!( + "Binary(ref {}, ref {}, ref {}) = {};", + op_pat, left_pat, right_pat, current + ); + println!(" if BinOpKind::{:?} == {}.node;", op.node, op_pat); + self.current = left_pat; + self.visit_expr(left); + self.current = right_pat; + self.visit_expr(right); + }, + ExprKind::Unary(ref op, ref inner) => { + let inner_pat = self.next("inner"); + println!("Unary(UnOp::{:?}, ref {}) = {};", op, inner_pat, current); + self.current = inner_pat; + self.visit_expr(inner); + }, + ExprKind::Lit(ref lit) => { + let lit_pat = self.next("lit"); + println!("Lit(ref {}) = {};", lit_pat, current); + match lit.node { + LitKind::Bool(val) => println!(" if let LitKind::Bool({:?}) = {}.node;", val, lit_pat), + LitKind::Char(c) => println!(" if let LitKind::Char({:?}) = {}.node;", c, lit_pat), + LitKind::Err(val) => println!(" if let LitKind::Err({}) = {}.node;", val, lit_pat), + LitKind::Byte(b) => println!(" if let LitKind::Byte({}) = {}.node;", b, lit_pat), + // FIXME: also check int type + LitKind::Int(i, _) => println!(" if let LitKind::Int({}, _) = {}.node;", i, lit_pat), + LitKind::Float(_, LitFloatType::Suffixed(_)) => println!( + " if let LitKind::Float(_, LitFloatType::Suffixed(_)) = {}.node;", + lit_pat + ), + LitKind::Float(_, LitFloatType::Unsuffixed) => println!( + " if let LitKind::Float(_, LitFloatType::Unsuffixed) = {}.node;", + lit_pat + ), + LitKind::ByteStr(ref vec) => { + let vec_pat = self.next("vec"); + println!(" if let LitKind::ByteStr(ref {}) = {}.node;", vec_pat, lit_pat); + println!(" if let [{:?}] = **{};", vec, vec_pat); + }, + LitKind::Str(ref text, _) => { + let str_pat = self.next("s"); + println!(" if let LitKind::Str(ref {}, _) = {}.node;", str_pat, lit_pat); + println!(" if {}.as_str() == {:?}", str_pat, &*text.as_str()) + }, + } + }, + ExprKind::Cast(ref expr, ref ty) => { + let cast_pat = self.next("expr"); + let cast_ty = self.next("cast_ty"); + let qp_label = self.next("qp"); + + println!("Cast(ref {}, ref {}) = {};", cast_pat, cast_ty, current); + if let TyKind::Path(ref qp) = ty.kind { + println!(" if let TyKind::Path(ref {}) = {}.kind;", qp_label, cast_ty); + self.current = qp_label; + self.print_qpath(qp); + } + self.current = cast_pat; + self.visit_expr(expr); + }, + ExprKind::Type(ref expr, ref _ty) => { + let cast_pat = self.next("expr"); + println!("Type(ref {}, _) = {};", cast_pat, current); + self.current = cast_pat; + self.visit_expr(expr); + }, + ExprKind::Loop(ref body, _, desugaring, _) => { + let body_pat = self.next("body"); + let des = loop_desugaring_name(desugaring); + let label_pat = self.next("label"); + println!("Loop(ref {}, ref {}, {}) = {};", body_pat, label_pat, des, current); + self.current = body_pat; + self.visit_block(body); + }, + ExprKind::If(ref cond, ref then, ref opt_else) => { + let cond_pat = self.next("cond"); + let then_pat = self.next("then"); + if let Some(ref else_) = *opt_else { + let else_pat = self.next("else_"); + println!( + "If(ref {}, ref {}, Some(ref {})) = {};", + cond_pat, then_pat, else_pat, current + ); + self.current = else_pat; + self.visit_expr(else_); + } else { + println!("If(ref {}, ref {}, None) = {};", cond_pat, then_pat, current); + } + self.current = cond_pat; + self.visit_expr(cond); + self.current = then_pat; + self.visit_expr(then); + }, + ExprKind::Match(ref expr, ref arms, desugaring) => { + let des = desugaring_name(desugaring); + let expr_pat = self.next("expr"); + let arms_pat = self.next("arms"); + println!("Match(ref {}, ref {}, {}) = {};", expr_pat, arms_pat, des, current); + self.current = expr_pat; + self.visit_expr(expr); + println!(" if {}.len() == {};", arms_pat, arms.len()); + for (i, arm) in arms.iter().enumerate() { + self.current = format!("{}[{}].body", arms_pat, i); + self.visit_expr(&arm.body); + if let Some(ref guard) = arm.guard { + let guard_pat = self.next("guard"); + println!(" if let Some(ref {}) = {}[{}].guard;", guard_pat, arms_pat, i); + match guard { + hir::Guard::If(ref if_expr) => { + let if_expr_pat = self.next("expr"); + println!(" if let Guard::If(ref {}) = {};", if_expr_pat, guard_pat); + self.current = if_expr_pat; + self.visit_expr(if_expr); + }, + hir::Guard::IfLet(ref if_let_pat, ref if_let_expr) => { + let if_let_pat_pat = self.next("pat"); + let if_let_expr_pat = self.next("expr"); + println!( + " if let Guard::IfLet(ref {}, ref {}) = {};", + if_let_pat_pat, if_let_expr_pat, guard_pat + ); + self.current = if_let_expr_pat; + self.visit_expr(if_let_expr); + self.current = if_let_pat_pat; + self.visit_pat(if_let_pat); + }, + } + } + self.current = format!("{}[{}].pat", arms_pat, i); + self.visit_pat(&arm.pat); + } + }, + ExprKind::Closure(ref _capture_clause, ref _func, _, _, _) => { + println!("Closure(ref capture_clause, ref func, _, _, _) = {};", current); + println!(" // unimplemented: `ExprKind::Closure` is not further destructured at the moment"); + }, + ExprKind::Yield(ref sub, _) => { + let sub_pat = self.next("sub"); + println!("Yield(ref sub) = {};", current); + self.current = sub_pat; + self.visit_expr(sub); + }, + ExprKind::Block(ref block, _) => { + let block_pat = self.next("block"); + println!("Block(ref {}) = {};", block_pat, current); + self.current = block_pat; + self.visit_block(block); + }, + ExprKind::Assign(ref target, ref value, _) => { + let target_pat = self.next("target"); + let value_pat = self.next("value"); + println!( + "Assign(ref {}, ref {}, ref _span) = {};", + target_pat, value_pat, current + ); + self.current = target_pat; + self.visit_expr(target); + self.current = value_pat; + self.visit_expr(value); + }, + ExprKind::AssignOp(ref op, ref target, ref value) => { + let op_pat = self.next("op"); + let target_pat = self.next("target"); + let value_pat = self.next("value"); + println!( + "AssignOp(ref {}, ref {}, ref {}) = {};", + op_pat, target_pat, value_pat, current + ); + println!(" if BinOpKind::{:?} == {}.node;", op.node, op_pat); + self.current = target_pat; + self.visit_expr(target); + self.current = value_pat; + self.visit_expr(value); + }, + ExprKind::Field(ref object, ref field_ident) => { + let obj_pat = self.next("object"); + let field_name_pat = self.next("field_name"); + println!("Field(ref {}, ref {}) = {};", obj_pat, field_name_pat, current); + println!(" if {}.as_str() == {:?}", field_name_pat, field_ident.as_str()); + self.current = obj_pat; + self.visit_expr(object); + }, + ExprKind::Index(ref object, ref index) => { + let object_pat = self.next("object"); + let index_pat = self.next("index"); + println!("Index(ref {}, ref {}) = {};", object_pat, index_pat, current); + self.current = object_pat; + self.visit_expr(object); + self.current = index_pat; + self.visit_expr(index); + }, + ExprKind::Path(ref path) => { + let path_pat = self.next("path"); + println!("Path(ref {}) = {};", path_pat, current); + self.current = path_pat; + self.print_qpath(path); + }, + ExprKind::AddrOf(kind, mutability, ref inner) => { + let inner_pat = self.next("inner"); + println!( + "AddrOf(BorrowKind::{:?}, Mutability::{:?}, ref {}) = {};", + kind, mutability, inner_pat, current + ); + self.current = inner_pat; + self.visit_expr(inner); + }, + ExprKind::Break(ref _destination, ref opt_value) => { + let destination_pat = self.next("destination"); + if let Some(ref value) = *opt_value { + let value_pat = self.next("value"); + println!("Break(ref {}, Some(ref {})) = {};", destination_pat, value_pat, current); + self.current = value_pat; + self.visit_expr(value); + } else { + println!("Break(ref {}, None) = {};", destination_pat, current); + } + // FIXME: implement label printing + }, + ExprKind::Continue(ref _destination) => { + let destination_pat = self.next("destination"); + println!("Again(ref {}) = {};", destination_pat, current); + // FIXME: implement label printing + }, + ExprKind::Ret(ref opt_value) => { + if let Some(ref value) = *opt_value { + let value_pat = self.next("value"); + println!("Ret(Some(ref {})) = {};", value_pat, current); + self.current = value_pat; + self.visit_expr(value); + } else { + println!("Ret(None) = {};", current); + } + }, + ExprKind::InlineAsm(_) => { + println!("InlineAsm(_) = {};", current); + println!(" // unimplemented: `ExprKind::InlineAsm` is not further destructured at the moment"); + }, + ExprKind::LlvmInlineAsm(_) => { + println!("LlvmInlineAsm(_) = {};", current); + println!(" // unimplemented: `ExprKind::LlvmInlineAsm` is not further destructured at the moment"); + }, + ExprKind::Struct(ref path, ref fields, ref opt_base) => { + let path_pat = self.next("path"); + let fields_pat = self.next("fields"); + if let Some(ref base) = *opt_base { + let base_pat = self.next("base"); + println!( + "Struct(ref {}, ref {}, Some(ref {})) = {};", + path_pat, fields_pat, base_pat, current + ); + self.current = base_pat; + self.visit_expr(base); + } else { + println!("Struct(ref {}, ref {}, None) = {};", path_pat, fields_pat, current); + } + self.current = path_pat; + self.print_qpath(path); + println!(" if {}.len() == {};", fields_pat, fields.len()); + println!(" // unimplemented: field checks"); + }, + ExprKind::ConstBlock(_) => { + let value_pat = self.next("value"); + println!("Const({})", value_pat); + self.current = value_pat; + }, + // FIXME: compute length (needs type info) + ExprKind::Repeat(ref value, _) => { + let value_pat = self.next("value"); + println!("Repeat(ref {}, _) = {};", value_pat, current); + println!("// unimplemented: repeat count check"); + self.current = value_pat; + self.visit_expr(value); + }, + ExprKind::Err => { + println!("Err = {}", current); + }, + ExprKind::DropTemps(ref expr) => { + let expr_pat = self.next("expr"); + println!("DropTemps(ref {}) = {};", expr_pat, current); + self.current = expr_pat; + self.visit_expr(expr); + }, + } + } + + fn visit_block(&mut self, block: &Block<'_>) { + let trailing_pat = self.next("trailing_expr"); + println!(" if let Some({}) = &{}.expr;", trailing_pat, self.current); + println!(" if {}.stmts.len() == {};", self.current, block.stmts.len()); + let current = self.current.clone(); + for (i, stmt) in block.stmts.iter().enumerate() { + self.current = format!("{}.stmts[{}]", current, i); + self.visit_stmt(stmt); + } + } + + #[allow(clippy::too_many_lines)] + fn visit_pat(&mut self, pat: &Pat<'_>) { + print!(" if let PatKind::"); + let current = format!("{}.kind", self.current); + match pat.kind { + PatKind::Wild => println!("Wild = {};", current), + PatKind::Binding(anno, .., ident, ref sub) => { + let anno_pat = match anno { + BindingAnnotation::Unannotated => "BindingAnnotation::Unannotated", + BindingAnnotation::Mutable => "BindingAnnotation::Mutable", + BindingAnnotation::Ref => "BindingAnnotation::Ref", + BindingAnnotation::RefMut => "BindingAnnotation::RefMut", + }; + let name_pat = self.next("name"); + if let Some(ref sub) = *sub { + let sub_pat = self.next("sub"); + println!( + "Binding({}, _, {}, Some(ref {})) = {};", + anno_pat, name_pat, sub_pat, current + ); + self.current = sub_pat; + self.visit_pat(sub); + } else { + println!("Binding({}, _, {}, None) = {};", anno_pat, name_pat, current); + } + println!(" if {}.as_str() == \"{}\";", name_pat, ident.as_str()); + }, + PatKind::Struct(ref path, ref fields, ignore) => { + let path_pat = self.next("path"); + let fields_pat = self.next("fields"); + println!( + "Struct(ref {}, ref {}, {}) = {};", + path_pat, fields_pat, ignore, current + ); + self.current = path_pat; + self.print_qpath(path); + println!(" if {}.len() == {};", fields_pat, fields.len()); + println!(" // unimplemented: field checks"); + }, + PatKind::Or(ref fields) => { + let fields_pat = self.next("fields"); + println!("Or(ref {}) = {};", fields_pat, current); + println!(" if {}.len() == {};", fields_pat, fields.len()); + println!(" // unimplemented: field checks"); + }, + PatKind::TupleStruct(ref path, ref fields, skip_pos) => { + let path_pat = self.next("path"); + let fields_pat = self.next("fields"); + println!( + "TupleStruct(ref {}, ref {}, {:?}) = {};", + path_pat, fields_pat, skip_pos, current + ); + self.current = path_pat; + self.print_qpath(path); + println!(" if {}.len() == {};", fields_pat, fields.len()); + println!(" // unimplemented: field checks"); + }, + PatKind::Path(ref path) => { + let path_pat = self.next("path"); + println!("Path(ref {}) = {};", path_pat, current); + self.current = path_pat; + self.print_qpath(path); + }, + PatKind::Tuple(ref fields, skip_pos) => { + let fields_pat = self.next("fields"); + println!("Tuple(ref {}, {:?}) = {};", fields_pat, skip_pos, current); + println!(" if {}.len() == {};", fields_pat, fields.len()); + println!(" // unimplemented: field checks"); + }, + PatKind::Box(ref pat) => { + let pat_pat = self.next("pat"); + println!("Box(ref {}) = {};", pat_pat, current); + self.current = pat_pat; + self.visit_pat(pat); + }, + PatKind::Ref(ref pat, muta) => { + let pat_pat = self.next("pat"); + println!("Ref(ref {}, Mutability::{:?}) = {};", pat_pat, muta, current); + self.current = pat_pat; + self.visit_pat(pat); + }, + PatKind::Lit(ref lit_expr) => { + let lit_expr_pat = self.next("lit_expr"); + println!("Lit(ref {}) = {}", lit_expr_pat, current); + self.current = lit_expr_pat; + self.visit_expr(lit_expr); + }, + PatKind::Range(ref start, ref end, end_kind) => { + let start_pat = self.next("start"); + let end_pat = self.next("end"); + println!( + "Range(ref {}, ref {}, RangeEnd::{:?}) = {};", + start_pat, end_pat, end_kind, current + ); + self.current = start_pat; + walk_list!(self, visit_expr, start); + self.current = end_pat; + walk_list!(self, visit_expr, end); + }, + PatKind::Slice(ref start, ref middle, ref end) => { + let start_pat = self.next("start"); + let end_pat = self.next("end"); + if let Some(ref middle) = middle { + let middle_pat = self.next("middle"); + println!( + "Slice(ref {}, Some(ref {}), ref {}) = {};", + start_pat, middle_pat, end_pat, current + ); + self.current = middle_pat; + self.visit_pat(middle); + } else { + println!("Slice(ref {}, None, ref {}) = {};", start_pat, end_pat, current); + } + println!(" if {}.len() == {};", start_pat, start.len()); + for (i, pat) in start.iter().enumerate() { + self.current = format!("{}[{}]", start_pat, i); + self.visit_pat(pat); + } + println!(" if {}.len() == {};", end_pat, end.len()); + for (i, pat) in end.iter().enumerate() { + self.current = format!("{}[{}]", end_pat, i); + self.visit_pat(pat); + } + }, + } + } + + fn visit_stmt(&mut self, s: &Stmt<'_>) { + print!(" if let StmtKind::"); + let current = format!("{}.kind", self.current); + match s.kind { + // A local (let) binding: + StmtKind::Local(ref local) => { + let local_pat = self.next("local"); + println!("Local(ref {}) = {};", local_pat, current); + if let Some(ref init) = local.init { + let init_pat = self.next("init"); + println!(" if let Some(ref {}) = {}.init;", init_pat, local_pat); + self.current = init_pat; + self.visit_expr(init); + } + self.current = format!("{}.pat", local_pat); + self.visit_pat(&local.pat); + }, + // An item binding: + StmtKind::Item(_) => { + println!("Item(item_id) = {};", current); + }, + + // Expr without trailing semi-colon (must have unit type): + StmtKind::Expr(ref e) => { + let e_pat = self.next("e"); + println!("Expr(ref {}, _) = {}", e_pat, current); + self.current = e_pat; + self.visit_expr(e); + }, + + // Expr with trailing semi-colon (may have any type): + StmtKind::Semi(ref e) => { + let e_pat = self.next("e"); + println!("Semi(ref {}, _) = {}", e_pat, current); + self.current = e_pat; + self.visit_expr(e); + }, + } + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} + +fn has_attr(sess: &Session, attrs: &[Attribute]) -> bool { + get_attr(sess, attrs, "author").count() > 0 +} + +#[must_use] +fn desugaring_name(des: hir::MatchSource) -> String { + match des { + hir::MatchSource::ForLoopDesugar => "MatchSource::ForLoopDesugar".to_string(), + hir::MatchSource::TryDesugar => "MatchSource::TryDesugar".to_string(), + hir::MatchSource::WhileDesugar => "MatchSource::WhileDesugar".to_string(), + hir::MatchSource::WhileLetDesugar => "MatchSource::WhileLetDesugar".to_string(), + hir::MatchSource::Normal => "MatchSource::Normal".to_string(), + hir::MatchSource::IfLetDesugar { contains_else_clause } => format!( + "MatchSource::IfLetDesugar {{ contains_else_clause: {} }}", + contains_else_clause + ), + hir::MatchSource::IfLetGuardDesugar => "MatchSource::IfLetGuardDesugar".to_string(), + hir::MatchSource::AwaitDesugar => "MatchSource::AwaitDesugar".to_string(), + } +} + +#[must_use] +fn loop_desugaring_name(des: hir::LoopSource) -> &'static str { + match des { + hir::LoopSource::ForLoop => "LoopSource::ForLoop", + hir::LoopSource::Loop => "LoopSource::Loop", + hir::LoopSource::While => "LoopSource::While", + hir::LoopSource::WhileLet => "LoopSource::WhileLet", + } +} + +fn print_path(path: &QPath<'_>, first: &mut bool) { + match *path { + QPath::Resolved(_, ref path) => { + for segment in path.segments { + if *first { + *first = false; + } else { + print!(", "); + } + print!("{:?}", segment.ident.as_str()); + } + }, + QPath::TypeRelative(ref ty, ref segment) => match ty.kind { + hir::TyKind::Path(ref inner_path) => { + print_path(inner_path, first); + if *first { + *first = false; + } else { + print!(", "); + } + print!("{:?}", segment.ident.as_str()); + }, + ref other => print!("/* unimplemented: {:?}*/", other), + }, + QPath::LangItem(..) => panic!("print_path: called for lang item qpath"), + } +} diff --git a/clippy_lints/src/utils/inspector.rs b/clippy_lints/src/utils/inspector.rs new file mode 100644 index 00000000000..9bec24be9e4 --- /dev/null +++ b/clippy_lints/src/utils/inspector.rs @@ -0,0 +1,578 @@ +//! checks for attributes + +use crate::utils::get_attr; +use rustc_ast::ast::{Attribute, InlineAsmTemplatePiece}; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_session::Session; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Dumps every ast/hir node which has the `#[clippy::dump]` + /// attribute + /// + /// **Example:** + /// ```rust,ignore + /// #[clippy::dump] + /// extern crate foo; + /// ``` + /// + /// prints + /// + /// ```text + /// item `foo` + /// visibility inherited from outer item + /// extern crate dylib source: "/path/to/foo.so" + /// ``` + pub DEEP_CODE_INSPECTION, + internal_warn, + "helper to dump info about code" +} + +declare_lint_pass!(DeepCodeInspector => [DEEP_CODE_INSPECTION]); + +impl<'tcx> LateLintPass<'tcx> for DeepCodeInspector { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { + if !has_attr(cx.sess(), &item.attrs) { + return; + } + print_item(cx, item); + } + + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { + if !has_attr(cx.sess(), &item.attrs) { + return; + } + println!("impl item `{}`", item.ident.name); + match item.vis.node { + hir::VisibilityKind::Public => println!("public"), + hir::VisibilityKind::Crate(_) => println!("visible crate wide"), + hir::VisibilityKind::Restricted { ref path, .. } => println!( + "visible in module `{}`", + rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(path, false)) + ), + hir::VisibilityKind::Inherited => println!("visibility inherited from outer item"), + } + if item.defaultness.is_default() { + println!("default"); + } + match item.kind { + hir::ImplItemKind::Const(_, body_id) => { + println!("associated constant"); + print_expr(cx, &cx.tcx.hir().body(body_id).value, 1); + }, + hir::ImplItemKind::Fn(..) => println!("method"), + hir::ImplItemKind::TyAlias(_) => println!("associated type"), + } + } + // fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx + // hir::TraitItem) { + // if !has_attr(&item.attrs) { + // return; + // } + // } + // + // fn check_variant(&mut self, cx: &LateContext<'tcx>, var: &'tcx + // hir::Variant, _: + // &hir::Generics) { + // if !has_attr(&var.node.attrs) { + // return; + // } + // } + // + // fn check_struct_field(&mut self, cx: &LateContext<'tcx>, field: &'tcx + // hir::StructField) { + // if !has_attr(&field.attrs) { + // return; + // } + // } + // + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if !has_attr(cx.sess(), &expr.attrs) { + return; + } + print_expr(cx, expr, 0); + } + + fn check_arm(&mut self, cx: &LateContext<'tcx>, arm: &'tcx hir::Arm<'_>) { + if !has_attr(cx.sess(), &arm.attrs) { + return; + } + print_pat(cx, &arm.pat, 1); + if let Some(ref guard) = arm.guard { + println!("guard:"); + print_guard(cx, guard, 1); + } + println!("body:"); + print_expr(cx, &arm.body, 1); + } + + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'_>) { + if !has_attr(cx.sess(), stmt.kind.attrs(|id| cx.tcx.hir().item(id.id))) { + return; + } + match stmt.kind { + hir::StmtKind::Local(ref local) => { + println!("local variable of type {}", cx.typeck_results().node_type(local.hir_id)); + println!("pattern:"); + print_pat(cx, &local.pat, 0); + if let Some(ref e) = local.init { + println!("init expression:"); + print_expr(cx, e, 0); + } + }, + hir::StmtKind::Item(_) => println!("item decl"), + hir::StmtKind::Expr(ref e) | hir::StmtKind::Semi(ref e) => print_expr(cx, e, 0), + } + } + // fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx + // hir::ForeignItem) { + // if !has_attr(&item.attrs) { + // return; + // } + // } + // +} + +fn has_attr(sess: &Session, attrs: &[Attribute]) -> bool { + get_attr(sess, attrs, "dump").count() > 0 +} + +#[allow(clippy::similar_names)] +#[allow(clippy::too_many_lines)] +fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { + let ind = " ".repeat(indent); + println!("{}+", ind); + println!("{}ty: {}", ind, cx.typeck_results().expr_ty(expr)); + println!( + "{}adjustments: {:?}", + ind, + cx.typeck_results().adjustments().get(expr.hir_id) + ); + match expr.kind { + hir::ExprKind::Box(ref e) => { + println!("{}Box", ind); + print_expr(cx, e, indent + 1); + }, + hir::ExprKind::Array(v) => { + println!("{}Array", ind); + for e in v { + print_expr(cx, e, indent + 1); + } + }, + hir::ExprKind::Call(ref func, args) => { + println!("{}Call", ind); + println!("{}function:", ind); + print_expr(cx, func, indent + 1); + println!("{}arguments:", ind); + for arg in args { + print_expr(cx, arg, indent + 1); + } + }, + hir::ExprKind::MethodCall(ref path, _, args, _) => { + println!("{}MethodCall", ind); + println!("{}method name: {}", ind, path.ident.name); + for arg in args { + print_expr(cx, arg, indent + 1); + } + }, + hir::ExprKind::Tup(v) => { + println!("{}Tup", ind); + for e in v { + print_expr(cx, e, indent + 1); + } + }, + hir::ExprKind::Binary(op, ref lhs, ref rhs) => { + println!("{}Binary", ind); + println!("{}op: {:?}", ind, op.node); + println!("{}lhs:", ind); + print_expr(cx, lhs, indent + 1); + println!("{}rhs:", ind); + print_expr(cx, rhs, indent + 1); + }, + hir::ExprKind::Unary(op, ref inner) => { + println!("{}Unary", ind); + println!("{}op: {:?}", ind, op); + print_expr(cx, inner, indent + 1); + }, + hir::ExprKind::Lit(ref lit) => { + println!("{}Lit", ind); + println!("{}{:?}", ind, lit); + }, + hir::ExprKind::Cast(ref e, ref target) => { + println!("{}Cast", ind); + print_expr(cx, e, indent + 1); + println!("{}target type: {:?}", ind, target); + }, + hir::ExprKind::Type(ref e, ref target) => { + println!("{}Type", ind); + print_expr(cx, e, indent + 1); + println!("{}target type: {:?}", ind, target); + }, + hir::ExprKind::Loop(..) => { + println!("{}Loop", ind); + }, + hir::ExprKind::If(ref cond, _, ref else_opt) => { + println!("{}If", ind); + println!("{}condition:", ind); + print_expr(cx, cond, indent + 1); + if let Some(ref els) = *else_opt { + println!("{}else:", ind); + print_expr(cx, els, indent + 1); + } + }, + hir::ExprKind::Match(ref cond, _, ref source) => { + println!("{}Match", ind); + println!("{}condition:", ind); + print_expr(cx, cond, indent + 1); + println!("{}source: {:?}", ind, source); + }, + hir::ExprKind::Closure(ref clause, _, _, _, _) => { + println!("{}Closure", ind); + println!("{}clause: {:?}", ind, clause); + }, + hir::ExprKind::Yield(ref sub, _) => { + println!("{}Yield", ind); + print_expr(cx, sub, indent + 1); + }, + hir::ExprKind::Block(_, _) => { + println!("{}Block", ind); + }, + hir::ExprKind::Assign(ref lhs, ref rhs, _) => { + println!("{}Assign", ind); + println!("{}lhs:", ind); + print_expr(cx, lhs, indent + 1); + println!("{}rhs:", ind); + print_expr(cx, rhs, indent + 1); + }, + hir::ExprKind::AssignOp(ref binop, ref lhs, ref rhs) => { + println!("{}AssignOp", ind); + println!("{}op: {:?}", ind, binop.node); + println!("{}lhs:", ind); + print_expr(cx, lhs, indent + 1); + println!("{}rhs:", ind); + print_expr(cx, rhs, indent + 1); + }, + hir::ExprKind::Field(ref e, ident) => { + println!("{}Field", ind); + println!("{}field name: {}", ind, ident.name); + println!("{}struct expr:", ind); + print_expr(cx, e, indent + 1); + }, + hir::ExprKind::Index(ref arr, ref idx) => { + println!("{}Index", ind); + println!("{}array expr:", ind); + print_expr(cx, arr, indent + 1); + println!("{}index expr:", ind); + print_expr(cx, idx, indent + 1); + }, + hir::ExprKind::Path(hir::QPath::Resolved(ref ty, ref path)) => { + println!("{}Resolved Path, {:?}", ind, ty); + println!("{}path: {:?}", ind, path); + }, + hir::ExprKind::Path(hir::QPath::TypeRelative(ref ty, ref seg)) => { + println!("{}Relative Path, {:?}", ind, ty); + println!("{}seg: {:?}", ind, seg); + }, + hir::ExprKind::Path(hir::QPath::LangItem(lang_item, ..)) => { + println!("{}Lang Item Path, {:?}", ind, lang_item.name()); + }, + hir::ExprKind::AddrOf(kind, ref muta, ref e) => { + println!("{}AddrOf", ind); + println!("kind: {:?}", kind); + println!("mutability: {:?}", muta); + print_expr(cx, e, indent + 1); + }, + hir::ExprKind::Break(_, ref e) => { + println!("{}Break", ind); + if let Some(ref e) = *e { + print_expr(cx, e, indent + 1); + } + }, + hir::ExprKind::Continue(_) => println!("{}Again", ind), + hir::ExprKind::Ret(ref e) => { + println!("{}Ret", ind); + if let Some(ref e) = *e { + print_expr(cx, e, indent + 1); + } + }, + hir::ExprKind::InlineAsm(ref asm) => { + println!("{}InlineAsm", ind); + println!("{}template: {}", ind, InlineAsmTemplatePiece::to_string(asm.template)); + println!("{}options: {:?}", ind, asm.options); + println!("{}operands:", ind); + for (op, _op_sp) in asm.operands { + match op { + hir::InlineAsmOperand::In { expr, .. } + | hir::InlineAsmOperand::InOut { expr, .. } + | hir::InlineAsmOperand::Const { expr } + | hir::InlineAsmOperand::Sym { expr } => print_expr(cx, expr, indent + 1), + hir::InlineAsmOperand::Out { expr, .. } => { + if let Some(expr) = expr { + print_expr(cx, expr, indent + 1); + } + }, + hir::InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { + print_expr(cx, in_expr, indent + 1); + if let Some(out_expr) = out_expr { + print_expr(cx, out_expr, indent + 1); + } + }, + } + } + }, + hir::ExprKind::LlvmInlineAsm(ref asm) => { + let inputs = &asm.inputs_exprs; + let outputs = &asm.outputs_exprs; + println!("{}LlvmInlineAsm", ind); + println!("{}inputs:", ind); + for e in inputs.iter() { + print_expr(cx, e, indent + 1); + } + println!("{}outputs:", ind); + for e in outputs.iter() { + print_expr(cx, e, indent + 1); + } + }, + hir::ExprKind::Struct(ref path, fields, ref base) => { + println!("{}Struct", ind); + println!("{}path: {:?}", ind, path); + for field in fields { + println!("{}field \"{}\":", ind, field.ident.name); + print_expr(cx, &field.expr, indent + 1); + } + if let Some(ref base) = *base { + println!("{}base:", ind); + print_expr(cx, base, indent + 1); + } + }, + hir::ExprKind::ConstBlock(ref anon_const) => { + println!("{}ConstBlock", ind); + println!("{}anon_const:", ind); + print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1); + }, + hir::ExprKind::Repeat(ref val, ref anon_const) => { + println!("{}Repeat", ind); + println!("{}value:", ind); + print_expr(cx, val, indent + 1); + println!("{}repeat count:", ind); + print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1); + }, + hir::ExprKind::Err => { + println!("{}Err", ind); + }, + hir::ExprKind::DropTemps(ref e) => { + println!("{}DropTemps", ind); + print_expr(cx, e, indent + 1); + }, + } +} + +fn print_item(cx: &LateContext<'_>, item: &hir::Item<'_>) { + let did = cx.tcx.hir().local_def_id(item.hir_id); + println!("item `{}`", item.ident.name); + match item.vis.node { + hir::VisibilityKind::Public => println!("public"), + hir::VisibilityKind::Crate(_) => println!("visible crate wide"), + hir::VisibilityKind::Restricted { ref path, .. } => println!( + "visible in module `{}`", + rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(path, false)) + ), + hir::VisibilityKind::Inherited => println!("visibility inherited from outer item"), + } + match item.kind { + hir::ItemKind::ExternCrate(ref _renamed_from) => { + let def_id = cx.tcx.hir().local_def_id(item.hir_id); + if let Some(crate_id) = cx.tcx.extern_mod_stmt_cnum(def_id) { + let source = cx.tcx.used_crate_source(crate_id); + if let Some(ref src) = source.dylib { + println!("extern crate dylib source: {:?}", src.0); + } + if let Some(ref src) = source.rlib { + println!("extern crate rlib source: {:?}", src.0); + } + } else { + println!("weird extern crate without a crate id"); + } + }, + hir::ItemKind::Use(ref path, ref kind) => println!("{:?}, {:?}", path, kind), + hir::ItemKind::Static(..) => println!("static item of type {:#?}", cx.tcx.type_of(did)), + hir::ItemKind::Const(..) => println!("const item of type {:#?}", cx.tcx.type_of(did)), + hir::ItemKind::Fn(..) => { + let item_ty = cx.tcx.type_of(did); + println!("function of type {:#?}", item_ty); + }, + hir::ItemKind::Mod(..) => println!("module"), + hir::ItemKind::ForeignMod { abi, .. } => println!("foreign module with abi: {}", abi), + hir::ItemKind::GlobalAsm(ref asm) => println!("global asm: {:?}", asm), + hir::ItemKind::TyAlias(..) => { + println!("type alias for {:?}", cx.tcx.type_of(did)); + }, + hir::ItemKind::OpaqueTy(..) => { + println!("existential type with real type {:?}", cx.tcx.type_of(did)); + }, + hir::ItemKind::Enum(..) => { + println!("enum definition of type {:?}", cx.tcx.type_of(did)); + }, + hir::ItemKind::Struct(..) => { + println!("struct definition of type {:?}", cx.tcx.type_of(did)); + }, + hir::ItemKind::Union(..) => { + println!("union definition of type {:?}", cx.tcx.type_of(did)); + }, + hir::ItemKind::Trait(..) => { + println!("trait decl"); + if cx.tcx.trait_is_auto(did.to_def_id()) { + println!("trait is auto"); + } else { + println!("trait is not auto"); + } + }, + hir::ItemKind::TraitAlias(..) => { + println!("trait alias"); + }, + hir::ItemKind::Impl(hir::Impl { + of_trait: Some(ref _trait_ref), + .. + }) => { + println!("trait impl"); + }, + hir::ItemKind::Impl(hir::Impl { of_trait: None, .. }) => { + println!("impl"); + }, + } +} + +#[allow(clippy::similar_names)] +#[allow(clippy::too_many_lines)] +fn print_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, indent: usize) { + let ind = " ".repeat(indent); + println!("{}+", ind); + match pat.kind { + hir::PatKind::Wild => println!("{}Wild", ind), + hir::PatKind::Binding(ref mode, .., ident, ref inner) => { + println!("{}Binding", ind); + println!("{}mode: {:?}", ind, mode); + println!("{}name: {}", ind, ident.name); + if let Some(ref inner) = *inner { + println!("{}inner:", ind); + print_pat(cx, inner, indent + 1); + } + }, + hir::PatKind::Or(fields) => { + println!("{}Or", ind); + for field in fields { + print_pat(cx, field, indent + 1); + } + }, + hir::PatKind::Struct(ref path, fields, ignore) => { + println!("{}Struct", ind); + println!( + "{}name: {}", + ind, + rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false)) + ); + println!("{}ignore leftover fields: {}", ind, ignore); + println!("{}fields:", ind); + for field in fields { + println!("{} field name: {}", ind, field.ident.name); + if field.is_shorthand { + println!("{} in shorthand notation", ind); + } + print_pat(cx, &field.pat, indent + 1); + } + }, + hir::PatKind::TupleStruct(ref path, fields, opt_dots_position) => { + println!("{}TupleStruct", ind); + println!( + "{}path: {}", + ind, + rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false)) + ); + if let Some(dot_position) = opt_dots_position { + println!("{}dot position: {}", ind, dot_position); + } + for field in fields { + print_pat(cx, field, indent + 1); + } + }, + hir::PatKind::Path(hir::QPath::Resolved(ref ty, ref path)) => { + println!("{}Resolved Path, {:?}", ind, ty); + println!("{}path: {:?}", ind, path); + }, + hir::PatKind::Path(hir::QPath::TypeRelative(ref ty, ref seg)) => { + println!("{}Relative Path, {:?}", ind, ty); + println!("{}seg: {:?}", ind, seg); + }, + hir::PatKind::Path(hir::QPath::LangItem(lang_item, ..)) => { + println!("{}Lang Item Path, {:?}", ind, lang_item.name()); + }, + hir::PatKind::Tuple(pats, opt_dots_position) => { + println!("{}Tuple", ind); + if let Some(dot_position) = opt_dots_position { + println!("{}dot position: {}", ind, dot_position); + } + for field in pats { + print_pat(cx, field, indent + 1); + } + }, + hir::PatKind::Box(ref inner) => { + println!("{}Box", ind); + print_pat(cx, inner, indent + 1); + }, + hir::PatKind::Ref(ref inner, ref muta) => { + println!("{}Ref", ind); + println!("{}mutability: {:?}", ind, muta); + print_pat(cx, inner, indent + 1); + }, + hir::PatKind::Lit(ref e) => { + println!("{}Lit", ind); + print_expr(cx, e, indent + 1); + }, + hir::PatKind::Range(ref l, ref r, ref range_end) => { + println!("{}Range", ind); + if let Some(expr) = l { + print_expr(cx, expr, indent + 1); + } + if let Some(expr) = r { + print_expr(cx, expr, indent + 1); + } + match *range_end { + hir::RangeEnd::Included => println!("{} end included", ind), + hir::RangeEnd::Excluded => println!("{} end excluded", ind), + } + }, + hir::PatKind::Slice(first_pats, ref range, last_pats) => { + println!("{}Slice [a, b, ..i, y, z]", ind); + println!("[a, b]:"); + for pat in first_pats { + print_pat(cx, pat, indent + 1); + } + println!("i:"); + if let Some(ref pat) = *range { + print_pat(cx, pat, indent + 1); + } + println!("[y, z]:"); + for pat in last_pats { + print_pat(cx, pat, indent + 1); + } + }, + } +} + +fn print_guard(cx: &LateContext<'_>, guard: &hir::Guard<'_>, indent: usize) { + let ind = " ".repeat(indent); + println!("{}+", ind); + match guard { + hir::Guard::If(expr) => { + println!("{}If", ind); + print_expr(cx, expr, indent + 1); + }, + hir::Guard::IfLet(pat, expr) => { + println!("{}IfLet", ind); + print_pat(cx, pat, indent + 1); + print_expr(cx, expr, indent + 1); + }, + } +} diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs new file mode 100644 index 00000000000..d8c602fab22 --- /dev/null +++ b/clippy_lints/src/utils/internal_lints.rs @@ -0,0 +1,1070 @@ +use crate::consts::{constant_simple, Constant}; +use crate::utils::{ + is_expn_of, match_def_path, match_qpath, match_type, method_calls, path_to_res, paths, run_lints, snippet, + span_lint, span_lint_and_help, span_lint_and_sugg, SpanlessEq, +}; +use if_chain::if_chain; +use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, NodeId}; +use rustc_ast::visit::FnKind; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::DefId; +use rustc_hir::hir_id::CRATE_HIR_ID; +use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; +use rustc_hir::{ + BinOpKind, Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Node, Path, StmtKind, Ty, TyKind, UnOp, +}; +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_middle::mir::interpret::ConstValue; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; +use rustc_span::source_map::{Span, Spanned}; +use rustc_span::symbol::{Symbol, SymbolStr}; +use rustc_typeck::hir_ty_to_ty; + +use std::borrow::{Borrow, Cow}; + +declare_clippy_lint! { + /// **What it does:** Checks for various things we like to keep tidy in clippy. + /// + /// **Why is this bad?** We like to pretend we're an example of tidy code. + /// + /// **Known problems:** None. + /// + /// **Example:** Wrong ordering of the util::paths constants. + pub CLIPPY_LINTS_INTERNAL, + internal, + "various things that will negatively affect your clippy experience" +} + +declare_clippy_lint! { + /// **What it does:** Ensures every lint is associated to a `LintPass`. + /// + /// **Why is this bad?** The compiler only knows lints via a `LintPass`. Without + /// putting a lint to a `LintPass::get_lints()`'s return, the compiler will not + /// know the name of the lint. + /// + /// **Known problems:** Only checks for lints associated using the + /// `declare_lint_pass!`, `impl_lint_pass!`, and `lint_array!` macros. + /// + /// **Example:** + /// ```rust,ignore + /// declare_lint! { pub LINT_1, ... } + /// declare_lint! { pub LINT_2, ... } + /// declare_lint! { pub FORGOTTEN_LINT, ... } + /// // ... + /// declare_lint_pass!(Pass => [LINT_1, LINT_2]); + /// // missing FORGOTTEN_LINT + /// ``` + pub LINT_WITHOUT_LINT_PASS, + internal, + "declaring a lint without associating it in a LintPass" +} + +declare_clippy_lint! { + /// **What it does:** Checks for calls to `cx.span_lint*` and suggests to use the `utils::*` + /// variant of the function. + /// + /// **Why is this bad?** The `utils::*` variants also add a link to the Clippy documentation to the + /// warning/error messages. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// Bad: + /// ```rust,ignore + /// cx.span_lint(LINT_NAME, "message"); + /// ``` + /// + /// Good: + /// ```rust,ignore + /// utils::span_lint(cx, LINT_NAME, "message"); + /// ``` + pub COMPILER_LINT_FUNCTIONS, + internal, + "usage of the lint functions of the compiler instead of the utils::* variant" +} + +declare_clippy_lint! { + /// **What it does:** Checks for calls to `cx.outer().expn_data()` and suggests to use + /// the `cx.outer_expn_data()` + /// + /// **Why is this bad?** `cx.outer_expn_data()` is faster and more concise. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// Bad: + /// ```rust,ignore + /// expr.span.ctxt().outer().expn_data() + /// ``` + /// + /// Good: + /// ```rust,ignore + /// expr.span.ctxt().outer_expn_data() + /// ``` + pub OUTER_EXPN_EXPN_DATA, + internal, + "using `cx.outer_expn().expn_data()` instead of `cx.outer_expn_data()`" +} + +declare_clippy_lint! { + /// **What it does:** Not an actual lint. This lint is only meant for testing our customized internal compiler + /// error message by calling `panic`. + /// + /// **Why is this bad?** ICE in large quantities can damage your teeth + /// + /// **Known problems:** None + /// + /// **Example:** + /// Bad: + /// ```rust,ignore + /// 🍦🍦🍦🍦🍦 + /// ``` + pub PRODUCE_ICE, + internal, + "this message should not appear anywhere as we ICE before and don't emit the lint" +} + +declare_clippy_lint! { + /// **What it does:** Checks for cases of an auto-generated lint without an updated description, + /// i.e. `default lint description`. + /// + /// **Why is this bad?** Indicates that the lint is not finished. + /// + /// **Known problems:** None + /// + /// **Example:** + /// Bad: + /// ```rust,ignore + /// declare_lint! { pub COOL_LINT, nursery, "default lint description" } + /// ``` + /// + /// Good: + /// ```rust,ignore + /// declare_lint! { pub COOL_LINT, nursery, "a great new lint" } + /// ``` + pub DEFAULT_LINT, + internal, + "found 'default lint description' in a lint declaration" +} + +declare_clippy_lint! { + /// **What it does:** Lints `span_lint_and_then` function calls, where the + /// closure argument has only one statement and that statement is a method + /// call to `span_suggestion`, `span_help`, `span_note` (using the same + /// span), `help` or `note`. + /// + /// These usages of `span_lint_and_then` should be replaced with one of the + /// wrapper functions `span_lint_and_sugg`, span_lint_and_help`, or + /// `span_lint_and_note`. + /// + /// **Why is this bad?** Using the wrapper `span_lint_and_*` functions, is more + /// convenient, readable and less error prone. + /// + /// **Known problems:** None + /// + /// *Example:** + /// Bad: + /// ```rust,ignore + /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { + /// diag.span_suggestion( + /// expr.span, + /// help_msg, + /// sugg.to_string(), + /// Applicability::MachineApplicable, + /// ); + /// }); + /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { + /// diag.span_help(expr.span, help_msg); + /// }); + /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { + /// diag.help(help_msg); + /// }); + /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { + /// diag.span_note(expr.span, note_msg); + /// }); + /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { + /// diag.note(note_msg); + /// }); + /// ``` + /// + /// Good: + /// ```rust,ignore + /// span_lint_and_sugg( + /// cx, + /// TEST_LINT, + /// expr.span, + /// lint_msg, + /// help_msg, + /// sugg.to_string(), + /// Applicability::MachineApplicable, + /// ); + /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg); + /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg); + /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg); + /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg); + /// ``` + pub COLLAPSIBLE_SPAN_LINT_CALLS, + internal, + "found collapsible `span_lint_and_then` calls" +} + +declare_clippy_lint! { + /// **What it does:** Checks for calls to `utils::match_type()` on a type diagnostic item + /// and suggests to use `utils::is_type_diagnostic_item()` instead. + /// + /// **Why is this bad?** `utils::is_type_diagnostic_item()` does not require hardcoded paths. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// Bad: + /// ```rust,ignore + /// utils::match_type(cx, ty, &paths::VEC) + /// ``` + /// + /// Good: + /// ```rust,ignore + /// utils::is_type_diagnostic_item(cx, ty, sym::vec_type) + /// ``` + pub MATCH_TYPE_ON_DIAGNOSTIC_ITEM, + internal, + "using `utils::match_type()` instead of `utils::is_type_diagnostic_item()`" +} + +declare_clippy_lint! { + /// **What it does:** + /// Checks the paths module for invalid paths. + /// + /// **Why is this bad?** + /// It indicates a bug in the code. + /// + /// **Known problems:** None. + /// + /// **Example:** None. + pub INVALID_PATHS, + internal, + "invalid path" +} + +declare_clippy_lint! { + /// **What it does:** + /// Checks for interning symbols that have already been pre-interned and defined as constants. + /// + /// **Why is this bad?** + /// It's faster and easier to use the symbol constant. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// Bad: + /// ```rust,ignore + /// let _ = sym!(f32); + /// ``` + /// + /// Good: + /// ```rust,ignore + /// let _ = sym::f32; + /// ``` + pub INTERNING_DEFINED_SYMBOL, + internal, + "interning a symbol that is pre-interned and defined as a constant" +} + +declare_clippy_lint! { + /// **What it does:** Checks for unnecessary conversion from Symbol to a string. + /// + /// **Why is this bad?** It's faster use symbols directly intead of strings. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// Bad: + /// ```rust,ignore + /// symbol.as_str() == "clippy"; + /// ``` + /// + /// Good: + /// ```rust,ignore + /// symbol == sym::clippy; + /// ``` + pub UNNECESSARY_SYMBOL_STR, + internal, + "unnecessary conversion between Symbol and string" +} + +declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]); + +impl EarlyLintPass for ClippyLintsInternal { + fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &AstCrate) { + if let Some(utils) = krate + .module + .items + .iter() + .find(|item| item.ident.name.as_str() == "utils") + { + if let ItemKind::Mod(ref utils_mod) = utils.kind { + if let Some(paths) = utils_mod.items.iter().find(|item| item.ident.name.as_str() == "paths") { + if let ItemKind::Mod(ref paths_mod) = paths.kind { + let mut last_name: Option = None; + for item in &*paths_mod.items { + let name = item.ident.as_str(); + if let Some(ref last_name) = last_name { + if **last_name > *name { + span_lint( + cx, + CLIPPY_LINTS_INTERNAL, + item.span, + "this constant should be before the previous constant due to lexical \ + ordering", + ); + } + } + last_name = Some(name); + } + } + } + } + } + } +} + +#[derive(Clone, Debug, Default)] +pub struct LintWithoutLintPass { + declared_lints: FxHashMap, + registered_lints: FxHashSet, +} + +impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS]); + +impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + if !run_lints(cx, &[DEFAULT_LINT], item.hir_id) { + return; + } + + if let hir::ItemKind::Static(ref ty, Mutability::Not, body_id) = item.kind { + if is_lint_ref_type(cx, ty) { + let expr = &cx.tcx.hir().body(body_id).value; + if_chain! { + if let ExprKind::AddrOf(_, _, ref inner_exp) = expr.kind; + if let ExprKind::Struct(_, ref fields, _) = inner_exp.kind; + let field = fields + .iter() + .find(|f| f.ident.as_str() == "desc") + .expect("lints must have a description field"); + if let ExprKind::Lit(Spanned { + node: LitKind::Str(ref sym, _), + .. + }) = field.expr.kind; + if sym.as_str() == "default lint description"; + + then { + span_lint( + cx, + DEFAULT_LINT, + item.span, + &format!("the lint `{}` has the default lint description", item.ident.name), + ); + } + } + self.declared_lints.insert(item.ident.name, item.span); + } + } else if is_expn_of(item.span, "impl_lint_pass").is_some() + || is_expn_of(item.span, "declare_lint_pass").is_some() + { + if let hir::ItemKind::Impl(hir::Impl { + of_trait: None, + items: ref impl_item_refs, + .. + }) = item.kind + { + let mut collector = LintCollector { + output: &mut self.registered_lints, + cx, + }; + let body_id = cx.tcx.hir().body_owned_by( + impl_item_refs + .iter() + .find(|iiref| iiref.ident.as_str() == "get_lints") + .expect("LintPass needs to implement get_lints") + .id + .hir_id, + ); + collector.visit_expr(&cx.tcx.hir().body(body_id).value); + } + } + } + + fn check_crate_post(&mut self, cx: &LateContext<'tcx>, _: &'tcx Crate<'_>) { + if !run_lints(cx, &[LINT_WITHOUT_LINT_PASS], CRATE_HIR_ID) { + return; + } + + for (lint_name, &lint_span) in &self.declared_lints { + // When using the `declare_tool_lint!` macro, the original `lint_span`'s + // file points to "". + // `compiletest-rs` thinks that's an error in a different file and + // just ignores it. This causes the test in compile-fail/lint_pass + // not able to capture the error. + // Therefore, we need to climb the macro expansion tree and find the + // actual span that invoked `declare_tool_lint!`: + let lint_span = lint_span.ctxt().outer_expn_data().call_site; + + if !self.registered_lints.contains(lint_name) { + span_lint( + cx, + LINT_WITHOUT_LINT_PASS, + lint_span, + &format!("the lint `{}` is not added to any `LintPass`", lint_name), + ); + } + } + } +} + +fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &Ty<'_>) -> bool { + if let TyKind::Rptr( + _, + MutTy { + ty: ref inner, + mutbl: Mutability::Not, + }, + ) = ty.kind + { + if let TyKind::Path(ref path) = inner.kind { + if let Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, inner.hir_id) { + return match_def_path(cx, def_id, &paths::LINT); + } + } + } + + false +} + +struct LintCollector<'a, 'tcx> { + output: &'a mut FxHashSet, + cx: &'a LateContext<'tcx>, +} + +impl<'a, 'tcx> Visitor<'tcx> for LintCollector<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_path(&mut self, path: &'tcx Path<'_>, _: HirId) { + if path.segments.len() == 1 { + self.output.insert(path.segments[0].ident.name); + } + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::All(self.cx.tcx.hir()) + } +} + +#[derive(Clone, Default)] +pub struct CompilerLintFunctions { + map: FxHashMap<&'static str, &'static str>, +} + +impl CompilerLintFunctions { + #[must_use] + pub fn new() -> Self { + let mut map = FxHashMap::default(); + map.insert("span_lint", "utils::span_lint"); + map.insert("struct_span_lint", "utils::span_lint"); + map.insert("lint", "utils::span_lint"); + map.insert("span_lint_note", "utils::span_lint_and_note"); + map.insert("span_lint_help", "utils::span_lint_and_help"); + Self { map } + } +} + +impl_lint_pass!(CompilerLintFunctions => [COMPILER_LINT_FUNCTIONS]); + +impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if !run_lints(cx, &[COMPILER_LINT_FUNCTIONS], expr.hir_id) { + return; + } + + if_chain! { + if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind; + let fn_name = path.ident; + if let Some(sugg) = self.map.get(&*fn_name.as_str()); + let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs(); + if match_type(cx, ty, &paths::EARLY_CONTEXT) + || match_type(cx, ty, &paths::LATE_CONTEXT); + then { + span_lint_and_help( + cx, + COMPILER_LINT_FUNCTIONS, + path.ident.span, + "usage of a compiler lint function", + None, + &format!("please use the Clippy variant of this function: `{}`", sugg), + ); + } + } + } +} + +declare_lint_pass!(OuterExpnDataPass => [OUTER_EXPN_EXPN_DATA]); + +impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if !run_lints(cx, &[OUTER_EXPN_EXPN_DATA], expr.hir_id) { + return; + } + + let (method_names, arg_lists, spans) = method_calls(expr, 2); + let method_names: Vec = method_names.iter().map(|s| s.as_str()).collect(); + let method_names: Vec<&str> = method_names.iter().map(|s| &**s).collect(); + if_chain! { + if let ["expn_data", "outer_expn"] = method_names.as_slice(); + let args = arg_lists[1]; + if args.len() == 1; + let self_arg = &args[0]; + let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); + if match_type(cx, self_ty, &paths::SYNTAX_CONTEXT); + then { + span_lint_and_sugg( + cx, + OUTER_EXPN_EXPN_DATA, + spans[1].with_hi(expr.span.hi()), + "usage of `outer_expn().expn_data()`", + "try", + "outer_expn_data()".to_string(), + Applicability::MachineApplicable, + ); + } + } + } +} + +declare_lint_pass!(ProduceIce => [PRODUCE_ICE]); + +impl EarlyLintPass for ProduceIce { + fn check_fn(&mut self, _: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) { + if is_trigger_fn(fn_kind) { + panic!("Would you like some help with that?"); + } + } +} + +fn is_trigger_fn(fn_kind: FnKind<'_>) -> bool { + match fn_kind { + FnKind::Fn(_, ident, ..) => ident.name.as_str() == "it_looks_like_you_are_trying_to_kill_clippy", + FnKind::Closure(..) => false, + } +} + +declare_lint_pass!(CollapsibleCalls => [COLLAPSIBLE_SPAN_LINT_CALLS]); + +impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if !run_lints(cx, &[COLLAPSIBLE_SPAN_LINT_CALLS], expr.hir_id) { + return; + } + + if_chain! { + if let ExprKind::Call(ref func, ref and_then_args) = expr.kind; + if let ExprKind::Path(ref path) = func.kind; + if match_qpath(path, &["span_lint_and_then"]); + if and_then_args.len() == 5; + if let ExprKind::Closure(_, _, body_id, _, _) = &and_then_args[4].kind; + let body = cx.tcx.hir().body(*body_id); + if let ExprKind::Block(block, _) = &body.value.kind; + let stmts = &block.stmts; + if stmts.len() == 1 && block.expr.is_none(); + if let StmtKind::Semi(only_expr) = &stmts[0].kind; + if let ExprKind::MethodCall(ref ps, _, ref span_call_args, _) = &only_expr.kind; + let and_then_snippets = get_and_then_snippets(cx, and_then_args); + let mut sle = SpanlessEq::new(cx).deny_side_effects(); + then { + match &*ps.ident.as_str() { + "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => { + suggest_suggestion(cx, expr, &and_then_snippets, &span_suggestion_snippets(cx, span_call_args)); + }, + "span_help" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => { + let help_snippet = snippet(cx, span_call_args[2].span, r#""...""#); + suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), true); + }, + "span_note" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => { + let note_snippet = snippet(cx, span_call_args[2].span, r#""...""#); + suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true); + }, + "help" => { + let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#); + suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), false); + } + "note" => { + let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#); + suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), false); + } + _ => (), + } + } + } + } +} + +struct AndThenSnippets<'a> { + cx: Cow<'a, str>, + lint: Cow<'a, str>, + span: Cow<'a, str>, + msg: Cow<'a, str>, +} + +fn get_and_then_snippets<'a, 'hir>(cx: &LateContext<'_>, and_then_snippets: &'hir [Expr<'hir>]) -> AndThenSnippets<'a> { + let cx_snippet = snippet(cx, and_then_snippets[0].span, "cx"); + let lint_snippet = snippet(cx, and_then_snippets[1].span, ".."); + let span_snippet = snippet(cx, and_then_snippets[2].span, "span"); + let msg_snippet = snippet(cx, and_then_snippets[3].span, r#""...""#); + + AndThenSnippets { + cx: cx_snippet, + lint: lint_snippet, + span: span_snippet, + msg: msg_snippet, + } +} + +struct SpanSuggestionSnippets<'a> { + help: Cow<'a, str>, + sugg: Cow<'a, str>, + applicability: Cow<'a, str>, +} + +fn span_suggestion_snippets<'a, 'hir>( + cx: &LateContext<'_>, + span_call_args: &'hir [Expr<'hir>], +) -> SpanSuggestionSnippets<'a> { + let help_snippet = snippet(cx, span_call_args[2].span, r#""...""#); + let sugg_snippet = snippet(cx, span_call_args[3].span, ".."); + let applicability_snippet = snippet(cx, span_call_args[4].span, "Applicability::MachineApplicable"); + + SpanSuggestionSnippets { + help: help_snippet, + sugg: sugg_snippet, + applicability: applicability_snippet, + } +} + +fn suggest_suggestion( + cx: &LateContext<'_>, + expr: &Expr<'_>, + and_then_snippets: &AndThenSnippets<'_>, + span_suggestion_snippets: &SpanSuggestionSnippets<'_>, +) { + span_lint_and_sugg( + cx, + COLLAPSIBLE_SPAN_LINT_CALLS, + expr.span, + "this call is collapsible", + "collapse into", + format!( + "span_lint_and_sugg({}, {}, {}, {}, {}, {}, {})", + and_then_snippets.cx, + and_then_snippets.lint, + and_then_snippets.span, + and_then_snippets.msg, + span_suggestion_snippets.help, + span_suggestion_snippets.sugg, + span_suggestion_snippets.applicability + ), + Applicability::MachineApplicable, + ); +} + +fn suggest_help( + cx: &LateContext<'_>, + expr: &Expr<'_>, + and_then_snippets: &AndThenSnippets<'_>, + help: &str, + with_span: bool, +) { + let option_span = if with_span { + format!("Some({})", and_then_snippets.span) + } else { + "None".to_string() + }; + + span_lint_and_sugg( + cx, + COLLAPSIBLE_SPAN_LINT_CALLS, + expr.span, + "this call is collapsible", + "collapse into", + format!( + "span_lint_and_help({}, {}, {}, {}, {}, {})", + and_then_snippets.cx, + and_then_snippets.lint, + and_then_snippets.span, + and_then_snippets.msg, + &option_span, + help + ), + Applicability::MachineApplicable, + ); +} + +fn suggest_note( + cx: &LateContext<'_>, + expr: &Expr<'_>, + and_then_snippets: &AndThenSnippets<'_>, + note: &str, + with_span: bool, +) { + let note_span = if with_span { + format!("Some({})", and_then_snippets.span) + } else { + "None".to_string() + }; + + span_lint_and_sugg( + cx, + COLLAPSIBLE_SPAN_LINT_CALLS, + expr.span, + "this call is collspible", + "collapse into", + format!( + "span_lint_and_note({}, {}, {}, {}, {}, {})", + and_then_snippets.cx, + and_then_snippets.lint, + and_then_snippets.span, + and_then_snippets.msg, + note_span, + note + ), + Applicability::MachineApplicable, + ); +} + +declare_lint_pass!(MatchTypeOnDiagItem => [MATCH_TYPE_ON_DIAGNOSTIC_ITEM]); + +impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if !run_lints(cx, &[MATCH_TYPE_ON_DIAGNOSTIC_ITEM], expr.hir_id) { + return; + } + + if_chain! { + // Check if this is a call to utils::match_type() + if let ExprKind::Call(fn_path, [context, ty, ty_path]) = expr.kind; + if let ExprKind::Path(fn_qpath) = &fn_path.kind; + if match_qpath(&fn_qpath, &["utils", "match_type"]); + // Extract the path to the matched type + if let Some(segments) = path_to_matched_type(cx, ty_path); + let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect(); + if let Some(ty_did) = path_to_res(cx, &segments[..]).opt_def_id(); + // Check if the matched type is a diagnostic item + let diag_items = cx.tcx.diagnostic_items(ty_did.krate); + if let Some(item_name) = diag_items.iter().find_map(|(k, v)| if *v == ty_did { Some(k) } else { None }); + then { + let cx_snippet = snippet(cx, context.span, "_"); + let ty_snippet = snippet(cx, ty.span, "_"); + + span_lint_and_sugg( + cx, + MATCH_TYPE_ON_DIAGNOSTIC_ITEM, + expr.span, + "usage of `utils::match_type()` on a type diagnostic item", + "try", + format!("utils::is_type_diagnostic_item({}, {}, sym::{})", cx_snippet, ty_snippet, item_name), + Applicability::MaybeIncorrect, + ); + } + } + } +} + +fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option> { + use rustc_hir::ItemKind; + + match &expr.kind { + ExprKind::AddrOf(.., expr) => return path_to_matched_type(cx, expr), + ExprKind::Path(qpath) => match cx.qpath_res(qpath, expr.hir_id) { + Res::Local(hir_id) => { + let parent_id = cx.tcx.hir().get_parent_node(hir_id); + if let Some(Node::Local(local)) = cx.tcx.hir().find(parent_id) { + if let Some(init) = local.init { + return path_to_matched_type(cx, init); + } + } + }, + Res::Def(DefKind::Const | DefKind::Static, def_id) => { + if let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(def_id) { + if let ItemKind::Const(.., body_id) | ItemKind::Static(.., body_id) = item.kind { + let body = cx.tcx.hir().body(body_id); + return path_to_matched_type(cx, &body.value); + } + } + }, + _ => {}, + }, + ExprKind::Array(exprs) => { + let segments: Vec = exprs + .iter() + .filter_map(|expr| { + if let ExprKind::Lit(lit) = &expr.kind { + if let LitKind::Str(sym, _) = lit.node { + return Some(sym.as_str()); + } + } + + None + }) + .collect(); + + if segments.len() == exprs.len() { + return Some(segments); + } + }, + _ => {}, + } + + None +} + +// This is not a complete resolver for paths. It works on all the paths currently used in the paths +// module. That's all it does and all it needs to do. +pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { + if path_to_res(cx, path) != Res::Err { + return true; + } + + // Some implementations can't be found by `path_to_res`, particularly inherent + // implementations of native types. Check lang items. + let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect(); + let lang_items = cx.tcx.lang_items(); + for item_def_id in lang_items.items().iter().flatten() { + let lang_item_path = cx.get_def_path(*item_def_id); + if path_syms.starts_with(&lang_item_path) { + if let [item] = &path_syms[lang_item_path.len()..] { + for child in cx.tcx.item_children(*item_def_id) { + if child.ident.name == *item { + return true; + } + } + } + } + } + + false +} + +declare_lint_pass!(InvalidPaths => [INVALID_PATHS]); + +impl<'tcx> LateLintPass<'tcx> for InvalidPaths { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + let local_def_id = &cx.tcx.parent_module(item.hir_id); + let mod_name = &cx.tcx.item_name(local_def_id.to_def_id()); + if_chain! { + if mod_name.as_str() == "paths"; + if let hir::ItemKind::Const(ty, body_id) = item.kind; + let ty = hir_ty_to_ty(cx.tcx, ty); + if let ty::Array(el_ty, _) = &ty.kind(); + if let ty::Ref(_, el_ty, _) = &el_ty.kind(); + if el_ty.is_str(); + let body = cx.tcx.hir().body(body_id); + let typeck_results = cx.tcx.typeck_body(body_id); + if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, &body.value); + let path: Vec<&str> = path.iter().map(|x| { + if let Constant::Str(s) = x { + s.as_str() + } else { + // We checked the type of the constant above + unreachable!() + } + }).collect(); + if !check_path(cx, &path[..]); + then { + span_lint(cx, CLIPPY_LINTS_INTERNAL, item.span, "invalid path"); + } + } + } +} + +#[derive(Default)] +pub struct InterningDefinedSymbol { + // Maps the symbol value to the constant DefId. + symbol_map: FxHashMap, +} + +impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL, UNNECESSARY_SYMBOL_STR]); + +impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { + fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) { + if !self.symbol_map.is_empty() { + return; + } + + for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] { + if let Some(def_id) = path_to_res(cx, module).opt_def_id() { + for item in cx.tcx.item_children(def_id).iter() { + if_chain! { + if let Res::Def(DefKind::Const, item_def_id) = item.res; + let ty = cx.tcx.type_of(item_def_id); + if match_type(cx, ty, &paths::SYMBOL); + if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id); + if let Ok(value) = value.to_u32(); + then { + self.symbol_map.insert(value, item_def_id); + } + } + } + } + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + if let ExprKind::Call(func, [arg]) = &expr.kind; + if let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind(); + if match_def_path(cx, *def_id, &paths::SYMBOL_INTERN); + if let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg); + let value = Symbol::intern(&arg).as_u32(); + if let Some(&def_id) = self.symbol_map.get(&value); + then { + span_lint_and_sugg( + cx, + INTERNING_DEFINED_SYMBOL, + is_expn_of(expr.span, "sym").unwrap_or(expr.span), + "interning a defined symbol", + "try", + cx.tcx.def_path_str(def_id), + Applicability::MachineApplicable, + ); + } + } + if let ExprKind::Binary(op, left, right) = expr.kind { + if matches!(op.node, BinOpKind::Eq | BinOpKind::Ne) { + let data = [ + (left, self.symbol_str_expr(left, cx)), + (right, self.symbol_str_expr(right, cx)), + ]; + match data { + // both operands are a symbol string + [(_, Some(left)), (_, Some(right))] => { + span_lint_and_sugg( + cx, + UNNECESSARY_SYMBOL_STR, + expr.span, + "unnecessary `Symbol` to string conversion", + "try", + format!( + "{} {} {}", + left.as_symbol_snippet(cx), + op.node.as_str(), + right.as_symbol_snippet(cx), + ), + Applicability::MachineApplicable, + ); + }, + // one of the operands is a symbol string + [(expr, Some(symbol)), _] | [_, (expr, Some(symbol))] => { + // creating an owned string for comparison + if matches!(symbol, SymbolStrExpr::Expr { is_to_owned: true, .. }) { + span_lint_and_sugg( + cx, + UNNECESSARY_SYMBOL_STR, + expr.span, + "unnecessary string allocation", + "try", + format!("{}.as_str()", symbol.as_symbol_snippet(cx)), + Applicability::MachineApplicable, + ); + } + }, + // nothing found + [(_, None), (_, None)] => {}, + } + } + } + } +} + +impl InterningDefinedSymbol { + fn symbol_str_expr<'tcx>(&self, expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> Option> { + static IDENT_STR_PATHS: &[&[&str]] = &[&paths::IDENT_AS_STR, &paths::TO_STRING_METHOD]; + static SYMBOL_STR_PATHS: &[&[&str]] = &[ + &paths::SYMBOL_AS_STR, + &paths::SYMBOL_TO_IDENT_STRING, + &paths::TO_STRING_METHOD, + ]; + // SymbolStr might be de-referenced: `&*symbol.as_str()` + let call = if_chain! { + if let ExprKind::AddrOf(_, _, e) = expr.kind; + if let ExprKind::Unary(UnOp::Deref, e) = e.kind; + then { e } else { expr } + }; + if_chain! { + // is a method call + if let ExprKind::MethodCall(_, _, [item], _) = call.kind; + if let Some(did) = cx.typeck_results().type_dependent_def_id(call.hir_id); + let ty = cx.typeck_results().expr_ty(item); + // ...on either an Ident or a Symbol + if let Some(is_ident) = if match_type(cx, ty, &paths::SYMBOL) { + Some(false) + } else if match_type(cx, ty, &paths::IDENT) { + Some(true) + } else { + None + }; + // ...which converts it to a string + let paths = if is_ident { IDENT_STR_PATHS } else { SYMBOL_STR_PATHS }; + if let Some(path) = paths.iter().find(|path| match_def_path(cx, did, path)); + then { + let is_to_owned = path.last().unwrap().ends_with("string"); + return Some(SymbolStrExpr::Expr { + item, + is_ident, + is_to_owned, + }); + } + } + // is a string constant + if let Some(Constant::Str(s)) = constant_simple(cx, cx.typeck_results(), expr) { + let value = Symbol::intern(&s).as_u32(); + // ...which matches a symbol constant + if let Some(&def_id) = self.symbol_map.get(&value) { + return Some(SymbolStrExpr::Const(def_id)); + } + } + None + } +} + +enum SymbolStrExpr<'tcx> { + /// a string constant with a corresponding symbol constant + Const(DefId), + /// a "symbol to string" expression like `symbol.as_str()` + Expr { + /// part that evaluates to `Symbol` or `Ident` + item: &'tcx Expr<'tcx>, + is_ident: bool, + /// whether an owned `String` is created like `to_ident_string()` + is_to_owned: bool, + }, +} + +impl<'tcx> SymbolStrExpr<'tcx> { + /// Returns a snippet that evaluates to a `Symbol` and is const if possible + fn as_symbol_snippet(&self, cx: &LateContext<'_>) -> Cow<'tcx, str> { + match *self { + Self::Const(def_id) => cx.tcx.def_path_str(def_id).into(), + Self::Expr { item, is_ident, .. } => { + let mut snip = snippet(cx, item.span.source_callsite(), ".."); + if is_ident { + // get `Ident.name` + snip.to_mut().push_str(".name"); + } + snip + }, + } + } +} diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs new file mode 100644 index 00000000000..68ab8161e20 --- /dev/null +++ b/clippy_lints/src/utils/mod.rs @@ -0,0 +1,6 @@ +pub mod author; +pub mod inspector; +#[cfg(feature = "internal-lints")] +pub mod internal_lints; + +pub use clippy_utils::*; diff --git a/clippy_utils/src/author.rs b/clippy_utils/src/author.rs deleted file mode 100644 index ba83566b5e6..00000000000 --- a/clippy_utils/src/author.rs +++ /dev/null @@ -1,779 +0,0 @@ -//! A group of attributes that can be attached to Rust code in order -//! to generate a clippy lint detecting said code automatically. - -use crate::{declare_clippy_lint, get_attr}; -use rustc_ast::ast::{Attribute, LitFloatType, LitKind}; -use rustc_ast::walk_list; -use rustc_data_structures::fx::FxHashMap; -use rustc_hir as hir; -use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; -use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, Pat, PatKind, QPath, Stmt, StmtKind, TyKind}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::hir::map::Map; -use rustc_session::Session; -use rustc_session::{declare_lint_pass, declare_tool_lint}; - -declare_clippy_lint! { - /// **What it does:** Generates clippy code that detects the offending pattern - /// - /// **Example:** - /// ```rust,ignore - /// // ./tests/ui/my_lint.rs - /// fn foo() { - /// // detect the following pattern - /// #[clippy::author] - /// if x == 42 { - /// // but ignore everything from here on - /// #![clippy::author = "ignore"] - /// } - /// () - /// } - /// ``` - /// - /// Running `TESTNAME=ui/my_lint cargo uitest` will produce - /// a `./tests/ui/new_lint.stdout` file with the generated code: - /// - /// ```rust,ignore - /// // ./tests/ui/new_lint.stdout - /// if_chain! { - /// if let ExprKind::If(ref cond, ref then, None) = item.kind, - /// if let ExprKind::Binary(BinOp::Eq, ref left, ref right) = cond.kind, - /// if let ExprKind::Path(ref path) = left.kind, - /// if let ExprKind::Lit(ref lit) = right.kind, - /// if let LitKind::Int(42, _) = lit.node, - /// then { - /// // report your lint here - /// } - /// } - /// ``` - pub LINT_AUTHOR, - internal_warn, - "helper for writing lints" -} - -declare_lint_pass!(Author => [LINT_AUTHOR]); - -fn prelude() { - println!("if_chain! {{"); -} - -fn done() { - println!(" then {{"); - println!(" // report your lint here"); - println!(" }}"); - println!("}}"); -} - -impl<'tcx> LateLintPass<'tcx> for Author { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - if !has_attr(cx.sess(), &item.attrs) { - return; - } - prelude(); - PrintVisitor::new("item").visit_item(item); - done(); - } - - fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { - if !has_attr(cx.sess(), &item.attrs) { - return; - } - prelude(); - PrintVisitor::new("item").visit_impl_item(item); - done(); - } - - fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { - if !has_attr(cx.sess(), &item.attrs) { - return; - } - prelude(); - PrintVisitor::new("item").visit_trait_item(item); - done(); - } - - fn check_variant(&mut self, cx: &LateContext<'tcx>, var: &'tcx hir::Variant<'_>) { - if !has_attr(cx.sess(), &var.attrs) { - return; - } - prelude(); - let parent_hir_id = cx.tcx.hir().get_parent_node(var.id); - PrintVisitor::new("var").visit_variant(var, &hir::Generics::empty(), parent_hir_id); - done(); - } - - fn check_struct_field(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::StructField<'_>) { - if !has_attr(cx.sess(), &field.attrs) { - return; - } - prelude(); - PrintVisitor::new("field").visit_struct_field(field); - done(); - } - - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if !has_attr(cx.sess(), &expr.attrs) { - return; - } - prelude(); - PrintVisitor::new("expr").visit_expr(expr); - done(); - } - - fn check_arm(&mut self, cx: &LateContext<'tcx>, arm: &'tcx hir::Arm<'_>) { - if !has_attr(cx.sess(), &arm.attrs) { - return; - } - prelude(); - PrintVisitor::new("arm").visit_arm(arm); - done(); - } - - fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'_>) { - if !has_attr(cx.sess(), stmt.kind.attrs(|id| cx.tcx.hir().item(id.id))) { - return; - } - prelude(); - PrintVisitor::new("stmt").visit_stmt(stmt); - done(); - } - - fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ForeignItem<'_>) { - if !has_attr(cx.sess(), &item.attrs) { - return; - } - prelude(); - PrintVisitor::new("item").visit_foreign_item(item); - done(); - } -} - -impl PrintVisitor { - #[must_use] - fn new(s: &'static str) -> Self { - Self { - ids: FxHashMap::default(), - current: s.to_owned(), - } - } - - fn next(&mut self, s: &'static str) -> String { - use std::collections::hash_map::Entry::{Occupied, Vacant}; - match self.ids.entry(s) { - // already there: start numbering from `1` - Occupied(mut occ) => { - let val = occ.get_mut(); - *val += 1; - format!("{}{}", s, *val) - }, - // not there: insert and return name as given - Vacant(vac) => { - vac.insert(0); - s.to_owned() - }, - } - } - - fn print_qpath(&mut self, path: &QPath<'_>) { - if let QPath::LangItem(lang_item, _) = *path { - println!( - " if matches!({}, QPath::LangItem(LangItem::{:?}, _));", - self.current, lang_item, - ); - } else { - print!(" if match_qpath({}, &[", self.current); - print_path(path, &mut true); - println!("]);"); - } - } -} - -struct PrintVisitor { - /// Fields are the current index that needs to be appended to pattern - /// binding names - ids: FxHashMap<&'static str, usize>, - /// the name that needs to be destructured - current: String, -} - -impl<'tcx> Visitor<'tcx> for PrintVisitor { - type Map = Map<'tcx>; - - #[allow(clippy::too_many_lines)] - fn visit_expr(&mut self, expr: &Expr<'_>) { - print!(" if let ExprKind::"); - let current = format!("{}.kind", self.current); - match expr.kind { - ExprKind::Box(ref inner) => { - let inner_pat = self.next("inner"); - println!("Box(ref {}) = {};", inner_pat, current); - self.current = inner_pat; - self.visit_expr(inner); - }, - ExprKind::Array(ref elements) => { - let elements_pat = self.next("elements"); - println!("Array(ref {}) = {};", elements_pat, current); - println!(" if {}.len() == {};", elements_pat, elements.len()); - for (i, element) in elements.iter().enumerate() { - self.current = format!("{}[{}]", elements_pat, i); - self.visit_expr(element); - } - }, - ExprKind::Call(ref func, ref args) => { - let func_pat = self.next("func"); - let args_pat = self.next("args"); - println!("Call(ref {}, ref {}) = {};", func_pat, args_pat, current); - self.current = func_pat; - self.visit_expr(func); - println!(" if {}.len() == {};", args_pat, args.len()); - for (i, arg) in args.iter().enumerate() { - self.current = format!("{}[{}]", args_pat, i); - self.visit_expr(arg); - } - }, - ExprKind::MethodCall(ref _method_name, ref _generics, ref _args, ref _fn_span) => { - println!( - "MethodCall(ref method_name, ref generics, ref args, ref fn_span) = {};", - current - ); - println!(" // unimplemented: `ExprKind::MethodCall` is not further destructured at the moment"); - }, - ExprKind::Tup(ref elements) => { - let elements_pat = self.next("elements"); - println!("Tup(ref {}) = {};", elements_pat, current); - println!(" if {}.len() == {};", elements_pat, elements.len()); - for (i, element) in elements.iter().enumerate() { - self.current = format!("{}[{}]", elements_pat, i); - self.visit_expr(element); - } - }, - ExprKind::Binary(ref op, ref left, ref right) => { - let op_pat = self.next("op"); - let left_pat = self.next("left"); - let right_pat = self.next("right"); - println!( - "Binary(ref {}, ref {}, ref {}) = {};", - op_pat, left_pat, right_pat, current - ); - println!(" if BinOpKind::{:?} == {}.node;", op.node, op_pat); - self.current = left_pat; - self.visit_expr(left); - self.current = right_pat; - self.visit_expr(right); - }, - ExprKind::Unary(ref op, ref inner) => { - let inner_pat = self.next("inner"); - println!("Unary(UnOp::{:?}, ref {}) = {};", op, inner_pat, current); - self.current = inner_pat; - self.visit_expr(inner); - }, - ExprKind::Lit(ref lit) => { - let lit_pat = self.next("lit"); - println!("Lit(ref {}) = {};", lit_pat, current); - match lit.node { - LitKind::Bool(val) => println!(" if let LitKind::Bool({:?}) = {}.node;", val, lit_pat), - LitKind::Char(c) => println!(" if let LitKind::Char({:?}) = {}.node;", c, lit_pat), - LitKind::Err(val) => println!(" if let LitKind::Err({}) = {}.node;", val, lit_pat), - LitKind::Byte(b) => println!(" if let LitKind::Byte({}) = {}.node;", b, lit_pat), - // FIXME: also check int type - LitKind::Int(i, _) => println!(" if let LitKind::Int({}, _) = {}.node;", i, lit_pat), - LitKind::Float(_, LitFloatType::Suffixed(_)) => println!( - " if let LitKind::Float(_, LitFloatType::Suffixed(_)) = {}.node;", - lit_pat - ), - LitKind::Float(_, LitFloatType::Unsuffixed) => println!( - " if let LitKind::Float(_, LitFloatType::Unsuffixed) = {}.node;", - lit_pat - ), - LitKind::ByteStr(ref vec) => { - let vec_pat = self.next("vec"); - println!(" if let LitKind::ByteStr(ref {}) = {}.node;", vec_pat, lit_pat); - println!(" if let [{:?}] = **{};", vec, vec_pat); - }, - LitKind::Str(ref text, _) => { - let str_pat = self.next("s"); - println!(" if let LitKind::Str(ref {}, _) = {}.node;", str_pat, lit_pat); - println!(" if {}.as_str() == {:?}", str_pat, &*text.as_str()) - }, - } - }, - ExprKind::Cast(ref expr, ref ty) => { - let cast_pat = self.next("expr"); - let cast_ty = self.next("cast_ty"); - let qp_label = self.next("qp"); - - println!("Cast(ref {}, ref {}) = {};", cast_pat, cast_ty, current); - if let TyKind::Path(ref qp) = ty.kind { - println!(" if let TyKind::Path(ref {}) = {}.kind;", qp_label, cast_ty); - self.current = qp_label; - self.print_qpath(qp); - } - self.current = cast_pat; - self.visit_expr(expr); - }, - ExprKind::Type(ref expr, ref _ty) => { - let cast_pat = self.next("expr"); - println!("Type(ref {}, _) = {};", cast_pat, current); - self.current = cast_pat; - self.visit_expr(expr); - }, - ExprKind::Loop(ref body, _, desugaring, _) => { - let body_pat = self.next("body"); - let des = loop_desugaring_name(desugaring); - let label_pat = self.next("label"); - println!("Loop(ref {}, ref {}, {}) = {};", body_pat, label_pat, des, current); - self.current = body_pat; - self.visit_block(body); - }, - ExprKind::If(ref cond, ref then, ref opt_else) => { - let cond_pat = self.next("cond"); - let then_pat = self.next("then"); - if let Some(ref else_) = *opt_else { - let else_pat = self.next("else_"); - println!( - "If(ref {}, ref {}, Some(ref {})) = {};", - cond_pat, then_pat, else_pat, current - ); - self.current = else_pat; - self.visit_expr(else_); - } else { - println!("If(ref {}, ref {}, None) = {};", cond_pat, then_pat, current); - } - self.current = cond_pat; - self.visit_expr(cond); - self.current = then_pat; - self.visit_expr(then); - }, - ExprKind::Match(ref expr, ref arms, desugaring) => { - let des = desugaring_name(desugaring); - let expr_pat = self.next("expr"); - let arms_pat = self.next("arms"); - println!("Match(ref {}, ref {}, {}) = {};", expr_pat, arms_pat, des, current); - self.current = expr_pat; - self.visit_expr(expr); - println!(" if {}.len() == {};", arms_pat, arms.len()); - for (i, arm) in arms.iter().enumerate() { - self.current = format!("{}[{}].body", arms_pat, i); - self.visit_expr(&arm.body); - if let Some(ref guard) = arm.guard { - let guard_pat = self.next("guard"); - println!(" if let Some(ref {}) = {}[{}].guard;", guard_pat, arms_pat, i); - match guard { - hir::Guard::If(ref if_expr) => { - let if_expr_pat = self.next("expr"); - println!(" if let Guard::If(ref {}) = {};", if_expr_pat, guard_pat); - self.current = if_expr_pat; - self.visit_expr(if_expr); - }, - hir::Guard::IfLet(ref if_let_pat, ref if_let_expr) => { - let if_let_pat_pat = self.next("pat"); - let if_let_expr_pat = self.next("expr"); - println!( - " if let Guard::IfLet(ref {}, ref {}) = {};", - if_let_pat_pat, if_let_expr_pat, guard_pat - ); - self.current = if_let_expr_pat; - self.visit_expr(if_let_expr); - self.current = if_let_pat_pat; - self.visit_pat(if_let_pat); - }, - } - } - self.current = format!("{}[{}].pat", arms_pat, i); - self.visit_pat(&arm.pat); - } - }, - ExprKind::Closure(ref _capture_clause, ref _func, _, _, _) => { - println!("Closure(ref capture_clause, ref func, _, _, _) = {};", current); - println!(" // unimplemented: `ExprKind::Closure` is not further destructured at the moment"); - }, - ExprKind::Yield(ref sub, _) => { - let sub_pat = self.next("sub"); - println!("Yield(ref sub) = {};", current); - self.current = sub_pat; - self.visit_expr(sub); - }, - ExprKind::Block(ref block, _) => { - let block_pat = self.next("block"); - println!("Block(ref {}) = {};", block_pat, current); - self.current = block_pat; - self.visit_block(block); - }, - ExprKind::Assign(ref target, ref value, _) => { - let target_pat = self.next("target"); - let value_pat = self.next("value"); - println!( - "Assign(ref {}, ref {}, ref _span) = {};", - target_pat, value_pat, current - ); - self.current = target_pat; - self.visit_expr(target); - self.current = value_pat; - self.visit_expr(value); - }, - ExprKind::AssignOp(ref op, ref target, ref value) => { - let op_pat = self.next("op"); - let target_pat = self.next("target"); - let value_pat = self.next("value"); - println!( - "AssignOp(ref {}, ref {}, ref {}) = {};", - op_pat, target_pat, value_pat, current - ); - println!(" if BinOpKind::{:?} == {}.node;", op.node, op_pat); - self.current = target_pat; - self.visit_expr(target); - self.current = value_pat; - self.visit_expr(value); - }, - ExprKind::Field(ref object, ref field_ident) => { - let obj_pat = self.next("object"); - let field_name_pat = self.next("field_name"); - println!("Field(ref {}, ref {}) = {};", obj_pat, field_name_pat, current); - println!(" if {}.as_str() == {:?}", field_name_pat, field_ident.as_str()); - self.current = obj_pat; - self.visit_expr(object); - }, - ExprKind::Index(ref object, ref index) => { - let object_pat = self.next("object"); - let index_pat = self.next("index"); - println!("Index(ref {}, ref {}) = {};", object_pat, index_pat, current); - self.current = object_pat; - self.visit_expr(object); - self.current = index_pat; - self.visit_expr(index); - }, - ExprKind::Path(ref path) => { - let path_pat = self.next("path"); - println!("Path(ref {}) = {};", path_pat, current); - self.current = path_pat; - self.print_qpath(path); - }, - ExprKind::AddrOf(kind, mutability, ref inner) => { - let inner_pat = self.next("inner"); - println!( - "AddrOf(BorrowKind::{:?}, Mutability::{:?}, ref {}) = {};", - kind, mutability, inner_pat, current - ); - self.current = inner_pat; - self.visit_expr(inner); - }, - ExprKind::Break(ref _destination, ref opt_value) => { - let destination_pat = self.next("destination"); - if let Some(ref value) = *opt_value { - let value_pat = self.next("value"); - println!("Break(ref {}, Some(ref {})) = {};", destination_pat, value_pat, current); - self.current = value_pat; - self.visit_expr(value); - } else { - println!("Break(ref {}, None) = {};", destination_pat, current); - } - // FIXME: implement label printing - }, - ExprKind::Continue(ref _destination) => { - let destination_pat = self.next("destination"); - println!("Again(ref {}) = {};", destination_pat, current); - // FIXME: implement label printing - }, - ExprKind::Ret(ref opt_value) => { - if let Some(ref value) = *opt_value { - let value_pat = self.next("value"); - println!("Ret(Some(ref {})) = {};", value_pat, current); - self.current = value_pat; - self.visit_expr(value); - } else { - println!("Ret(None) = {};", current); - } - }, - ExprKind::InlineAsm(_) => { - println!("InlineAsm(_) = {};", current); - println!(" // unimplemented: `ExprKind::InlineAsm` is not further destructured at the moment"); - }, - ExprKind::LlvmInlineAsm(_) => { - println!("LlvmInlineAsm(_) = {};", current); - println!(" // unimplemented: `ExprKind::LlvmInlineAsm` is not further destructured at the moment"); - }, - ExprKind::Struct(ref path, ref fields, ref opt_base) => { - let path_pat = self.next("path"); - let fields_pat = self.next("fields"); - if let Some(ref base) = *opt_base { - let base_pat = self.next("base"); - println!( - "Struct(ref {}, ref {}, Some(ref {})) = {};", - path_pat, fields_pat, base_pat, current - ); - self.current = base_pat; - self.visit_expr(base); - } else { - println!("Struct(ref {}, ref {}, None) = {};", path_pat, fields_pat, current); - } - self.current = path_pat; - self.print_qpath(path); - println!(" if {}.len() == {};", fields_pat, fields.len()); - println!(" // unimplemented: field checks"); - }, - ExprKind::ConstBlock(_) => { - let value_pat = self.next("value"); - println!("Const({})", value_pat); - self.current = value_pat; - }, - // FIXME: compute length (needs type info) - ExprKind::Repeat(ref value, _) => { - let value_pat = self.next("value"); - println!("Repeat(ref {}, _) = {};", value_pat, current); - println!("// unimplemented: repeat count check"); - self.current = value_pat; - self.visit_expr(value); - }, - ExprKind::Err => { - println!("Err = {}", current); - }, - ExprKind::DropTemps(ref expr) => { - let expr_pat = self.next("expr"); - println!("DropTemps(ref {}) = {};", expr_pat, current); - self.current = expr_pat; - self.visit_expr(expr); - }, - } - } - - fn visit_block(&mut self, block: &Block<'_>) { - let trailing_pat = self.next("trailing_expr"); - println!(" if let Some({}) = &{}.expr;", trailing_pat, self.current); - println!(" if {}.stmts.len() == {};", self.current, block.stmts.len()); - let current = self.current.clone(); - for (i, stmt) in block.stmts.iter().enumerate() { - self.current = format!("{}.stmts[{}]", current, i); - self.visit_stmt(stmt); - } - } - - #[allow(clippy::too_many_lines)] - fn visit_pat(&mut self, pat: &Pat<'_>) { - print!(" if let PatKind::"); - let current = format!("{}.kind", self.current); - match pat.kind { - PatKind::Wild => println!("Wild = {};", current), - PatKind::Binding(anno, .., ident, ref sub) => { - let anno_pat = match anno { - BindingAnnotation::Unannotated => "BindingAnnotation::Unannotated", - BindingAnnotation::Mutable => "BindingAnnotation::Mutable", - BindingAnnotation::Ref => "BindingAnnotation::Ref", - BindingAnnotation::RefMut => "BindingAnnotation::RefMut", - }; - let name_pat = self.next("name"); - if let Some(ref sub) = *sub { - let sub_pat = self.next("sub"); - println!( - "Binding({}, _, {}, Some(ref {})) = {};", - anno_pat, name_pat, sub_pat, current - ); - self.current = sub_pat; - self.visit_pat(sub); - } else { - println!("Binding({}, _, {}, None) = {};", anno_pat, name_pat, current); - } - println!(" if {}.as_str() == \"{}\";", name_pat, ident.as_str()); - }, - PatKind::Struct(ref path, ref fields, ignore) => { - let path_pat = self.next("path"); - let fields_pat = self.next("fields"); - println!( - "Struct(ref {}, ref {}, {}) = {};", - path_pat, fields_pat, ignore, current - ); - self.current = path_pat; - self.print_qpath(path); - println!(" if {}.len() == {};", fields_pat, fields.len()); - println!(" // unimplemented: field checks"); - }, - PatKind::Or(ref fields) => { - let fields_pat = self.next("fields"); - println!("Or(ref {}) = {};", fields_pat, current); - println!(" if {}.len() == {};", fields_pat, fields.len()); - println!(" // unimplemented: field checks"); - }, - PatKind::TupleStruct(ref path, ref fields, skip_pos) => { - let path_pat = self.next("path"); - let fields_pat = self.next("fields"); - println!( - "TupleStruct(ref {}, ref {}, {:?}) = {};", - path_pat, fields_pat, skip_pos, current - ); - self.current = path_pat; - self.print_qpath(path); - println!(" if {}.len() == {};", fields_pat, fields.len()); - println!(" // unimplemented: field checks"); - }, - PatKind::Path(ref path) => { - let path_pat = self.next("path"); - println!("Path(ref {}) = {};", path_pat, current); - self.current = path_pat; - self.print_qpath(path); - }, - PatKind::Tuple(ref fields, skip_pos) => { - let fields_pat = self.next("fields"); - println!("Tuple(ref {}, {:?}) = {};", fields_pat, skip_pos, current); - println!(" if {}.len() == {};", fields_pat, fields.len()); - println!(" // unimplemented: field checks"); - }, - PatKind::Box(ref pat) => { - let pat_pat = self.next("pat"); - println!("Box(ref {}) = {};", pat_pat, current); - self.current = pat_pat; - self.visit_pat(pat); - }, - PatKind::Ref(ref pat, muta) => { - let pat_pat = self.next("pat"); - println!("Ref(ref {}, Mutability::{:?}) = {};", pat_pat, muta, current); - self.current = pat_pat; - self.visit_pat(pat); - }, - PatKind::Lit(ref lit_expr) => { - let lit_expr_pat = self.next("lit_expr"); - println!("Lit(ref {}) = {}", lit_expr_pat, current); - self.current = lit_expr_pat; - self.visit_expr(lit_expr); - }, - PatKind::Range(ref start, ref end, end_kind) => { - let start_pat = self.next("start"); - let end_pat = self.next("end"); - println!( - "Range(ref {}, ref {}, RangeEnd::{:?}) = {};", - start_pat, end_pat, end_kind, current - ); - self.current = start_pat; - walk_list!(self, visit_expr, start); - self.current = end_pat; - walk_list!(self, visit_expr, end); - }, - PatKind::Slice(ref start, ref middle, ref end) => { - let start_pat = self.next("start"); - let end_pat = self.next("end"); - if let Some(ref middle) = middle { - let middle_pat = self.next("middle"); - println!( - "Slice(ref {}, Some(ref {}), ref {}) = {};", - start_pat, middle_pat, end_pat, current - ); - self.current = middle_pat; - self.visit_pat(middle); - } else { - println!("Slice(ref {}, None, ref {}) = {};", start_pat, end_pat, current); - } - println!(" if {}.len() == {};", start_pat, start.len()); - for (i, pat) in start.iter().enumerate() { - self.current = format!("{}[{}]", start_pat, i); - self.visit_pat(pat); - } - println!(" if {}.len() == {};", end_pat, end.len()); - for (i, pat) in end.iter().enumerate() { - self.current = format!("{}[{}]", end_pat, i); - self.visit_pat(pat); - } - }, - } - } - - fn visit_stmt(&mut self, s: &Stmt<'_>) { - print!(" if let StmtKind::"); - let current = format!("{}.kind", self.current); - match s.kind { - // A local (let) binding: - StmtKind::Local(ref local) => { - let local_pat = self.next("local"); - println!("Local(ref {}) = {};", local_pat, current); - if let Some(ref init) = local.init { - let init_pat = self.next("init"); - println!(" if let Some(ref {}) = {}.init;", init_pat, local_pat); - self.current = init_pat; - self.visit_expr(init); - } - self.current = format!("{}.pat", local_pat); - self.visit_pat(&local.pat); - }, - // An item binding: - StmtKind::Item(_) => { - println!("Item(item_id) = {};", current); - }, - - // Expr without trailing semi-colon (must have unit type): - StmtKind::Expr(ref e) => { - let e_pat = self.next("e"); - println!("Expr(ref {}, _) = {}", e_pat, current); - self.current = e_pat; - self.visit_expr(e); - }, - - // Expr with trailing semi-colon (may have any type): - StmtKind::Semi(ref e) => { - let e_pat = self.next("e"); - println!("Semi(ref {}, _) = {}", e_pat, current); - self.current = e_pat; - self.visit_expr(e); - }, - } - } - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } -} - -fn has_attr(sess: &Session, attrs: &[Attribute]) -> bool { - get_attr(sess, attrs, "author").count() > 0 -} - -#[must_use] -fn desugaring_name(des: hir::MatchSource) -> String { - match des { - hir::MatchSource::ForLoopDesugar => "MatchSource::ForLoopDesugar".to_string(), - hir::MatchSource::TryDesugar => "MatchSource::TryDesugar".to_string(), - hir::MatchSource::WhileDesugar => "MatchSource::WhileDesugar".to_string(), - hir::MatchSource::WhileLetDesugar => "MatchSource::WhileLetDesugar".to_string(), - hir::MatchSource::Normal => "MatchSource::Normal".to_string(), - hir::MatchSource::IfLetDesugar { contains_else_clause } => format!( - "MatchSource::IfLetDesugar {{ contains_else_clause: {} }}", - contains_else_clause - ), - hir::MatchSource::IfLetGuardDesugar => "MatchSource::IfLetGuardDesugar".to_string(), - hir::MatchSource::AwaitDesugar => "MatchSource::AwaitDesugar".to_string(), - } -} - -#[must_use] -fn loop_desugaring_name(des: hir::LoopSource) -> &'static str { - match des { - hir::LoopSource::ForLoop => "LoopSource::ForLoop", - hir::LoopSource::Loop => "LoopSource::Loop", - hir::LoopSource::While => "LoopSource::While", - hir::LoopSource::WhileLet => "LoopSource::WhileLet", - } -} - -fn print_path(path: &QPath<'_>, first: &mut bool) { - match *path { - QPath::Resolved(_, ref path) => { - for segment in path.segments { - if *first { - *first = false; - } else { - print!(", "); - } - print!("{:?}", segment.ident.as_str()); - } - }, - QPath::TypeRelative(ref ty, ref segment) => match ty.kind { - hir::TyKind::Path(ref inner_path) => { - print_path(inner_path, first); - if *first { - *first = false; - } else { - print!(", "); - } - print!("{:?}", segment.ident.as_str()); - }, - ref other => print!("/* unimplemented: {:?}*/", other), - }, - QPath::LangItem(..) => panic!("print_path: called for lang item qpath"), - } -} diff --git a/clippy_utils/src/inspector.rs b/clippy_utils/src/inspector.rs deleted file mode 100644 index b082bb6cfda..00000000000 --- a/clippy_utils/src/inspector.rs +++ /dev/null @@ -1,578 +0,0 @@ -//! checks for attributes - -use crate::{declare_clippy_lint, get_attr}; -use rustc_ast::ast::{Attribute, InlineAsmTemplatePiece}; -use rustc_hir as hir; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_session::Session; -use rustc_session::{declare_lint_pass, declare_tool_lint}; - -declare_clippy_lint! { - /// **What it does:** Dumps every ast/hir node which has the `#[clippy::dump]` - /// attribute - /// - /// **Example:** - /// ```rust,ignore - /// #[clippy::dump] - /// extern crate foo; - /// ``` - /// - /// prints - /// - /// ```text - /// item `foo` - /// visibility inherited from outer item - /// extern crate dylib source: "/path/to/foo.so" - /// ``` - pub DEEP_CODE_INSPECTION, - internal_warn, - "helper to dump info about code" -} - -declare_lint_pass!(DeepCodeInspector => [DEEP_CODE_INSPECTION]); - -impl<'tcx> LateLintPass<'tcx> for DeepCodeInspector { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - if !has_attr(cx.sess(), &item.attrs) { - return; - } - print_item(cx, item); - } - - fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { - if !has_attr(cx.sess(), &item.attrs) { - return; - } - println!("impl item `{}`", item.ident.name); - match item.vis.node { - hir::VisibilityKind::Public => println!("public"), - hir::VisibilityKind::Crate(_) => println!("visible crate wide"), - hir::VisibilityKind::Restricted { ref path, .. } => println!( - "visible in module `{}`", - rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(path, false)) - ), - hir::VisibilityKind::Inherited => println!("visibility inherited from outer item"), - } - if item.defaultness.is_default() { - println!("default"); - } - match item.kind { - hir::ImplItemKind::Const(_, body_id) => { - println!("associated constant"); - print_expr(cx, &cx.tcx.hir().body(body_id).value, 1); - }, - hir::ImplItemKind::Fn(..) => println!("method"), - hir::ImplItemKind::TyAlias(_) => println!("associated type"), - } - } - // fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx - // hir::TraitItem) { - // if !has_attr(&item.attrs) { - // return; - // } - // } - // - // fn check_variant(&mut self, cx: &LateContext<'tcx>, var: &'tcx - // hir::Variant, _: - // &hir::Generics) { - // if !has_attr(&var.node.attrs) { - // return; - // } - // } - // - // fn check_struct_field(&mut self, cx: &LateContext<'tcx>, field: &'tcx - // hir::StructField) { - // if !has_attr(&field.attrs) { - // return; - // } - // } - // - - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if !has_attr(cx.sess(), &expr.attrs) { - return; - } - print_expr(cx, expr, 0); - } - - fn check_arm(&mut self, cx: &LateContext<'tcx>, arm: &'tcx hir::Arm<'_>) { - if !has_attr(cx.sess(), &arm.attrs) { - return; - } - print_pat(cx, &arm.pat, 1); - if let Some(ref guard) = arm.guard { - println!("guard:"); - print_guard(cx, guard, 1); - } - println!("body:"); - print_expr(cx, &arm.body, 1); - } - - fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'_>) { - if !has_attr(cx.sess(), stmt.kind.attrs(|id| cx.tcx.hir().item(id.id))) { - return; - } - match stmt.kind { - hir::StmtKind::Local(ref local) => { - println!("local variable of type {}", cx.typeck_results().node_type(local.hir_id)); - println!("pattern:"); - print_pat(cx, &local.pat, 0); - if let Some(ref e) = local.init { - println!("init expression:"); - print_expr(cx, e, 0); - } - }, - hir::StmtKind::Item(_) => println!("item decl"), - hir::StmtKind::Expr(ref e) | hir::StmtKind::Semi(ref e) => print_expr(cx, e, 0), - } - } - // fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx - // hir::ForeignItem) { - // if !has_attr(&item.attrs) { - // return; - // } - // } - // -} - -fn has_attr(sess: &Session, attrs: &[Attribute]) -> bool { - get_attr(sess, attrs, "dump").count() > 0 -} - -#[allow(clippy::similar_names)] -#[allow(clippy::too_many_lines)] -fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { - let ind = " ".repeat(indent); - println!("{}+", ind); - println!("{}ty: {}", ind, cx.typeck_results().expr_ty(expr)); - println!( - "{}adjustments: {:?}", - ind, - cx.typeck_results().adjustments().get(expr.hir_id) - ); - match expr.kind { - hir::ExprKind::Box(ref e) => { - println!("{}Box", ind); - print_expr(cx, e, indent + 1); - }, - hir::ExprKind::Array(v) => { - println!("{}Array", ind); - for e in v { - print_expr(cx, e, indent + 1); - } - }, - hir::ExprKind::Call(ref func, args) => { - println!("{}Call", ind); - println!("{}function:", ind); - print_expr(cx, func, indent + 1); - println!("{}arguments:", ind); - for arg in args { - print_expr(cx, arg, indent + 1); - } - }, - hir::ExprKind::MethodCall(ref path, _, args, _) => { - println!("{}MethodCall", ind); - println!("{}method name: {}", ind, path.ident.name); - for arg in args { - print_expr(cx, arg, indent + 1); - } - }, - hir::ExprKind::Tup(v) => { - println!("{}Tup", ind); - for e in v { - print_expr(cx, e, indent + 1); - } - }, - hir::ExprKind::Binary(op, ref lhs, ref rhs) => { - println!("{}Binary", ind); - println!("{}op: {:?}", ind, op.node); - println!("{}lhs:", ind); - print_expr(cx, lhs, indent + 1); - println!("{}rhs:", ind); - print_expr(cx, rhs, indent + 1); - }, - hir::ExprKind::Unary(op, ref inner) => { - println!("{}Unary", ind); - println!("{}op: {:?}", ind, op); - print_expr(cx, inner, indent + 1); - }, - hir::ExprKind::Lit(ref lit) => { - println!("{}Lit", ind); - println!("{}{:?}", ind, lit); - }, - hir::ExprKind::Cast(ref e, ref target) => { - println!("{}Cast", ind); - print_expr(cx, e, indent + 1); - println!("{}target type: {:?}", ind, target); - }, - hir::ExprKind::Type(ref e, ref target) => { - println!("{}Type", ind); - print_expr(cx, e, indent + 1); - println!("{}target type: {:?}", ind, target); - }, - hir::ExprKind::Loop(..) => { - println!("{}Loop", ind); - }, - hir::ExprKind::If(ref cond, _, ref else_opt) => { - println!("{}If", ind); - println!("{}condition:", ind); - print_expr(cx, cond, indent + 1); - if let Some(ref els) = *else_opt { - println!("{}else:", ind); - print_expr(cx, els, indent + 1); - } - }, - hir::ExprKind::Match(ref cond, _, ref source) => { - println!("{}Match", ind); - println!("{}condition:", ind); - print_expr(cx, cond, indent + 1); - println!("{}source: {:?}", ind, source); - }, - hir::ExprKind::Closure(ref clause, _, _, _, _) => { - println!("{}Closure", ind); - println!("{}clause: {:?}", ind, clause); - }, - hir::ExprKind::Yield(ref sub, _) => { - println!("{}Yield", ind); - print_expr(cx, sub, indent + 1); - }, - hir::ExprKind::Block(_, _) => { - println!("{}Block", ind); - }, - hir::ExprKind::Assign(ref lhs, ref rhs, _) => { - println!("{}Assign", ind); - println!("{}lhs:", ind); - print_expr(cx, lhs, indent + 1); - println!("{}rhs:", ind); - print_expr(cx, rhs, indent + 1); - }, - hir::ExprKind::AssignOp(ref binop, ref lhs, ref rhs) => { - println!("{}AssignOp", ind); - println!("{}op: {:?}", ind, binop.node); - println!("{}lhs:", ind); - print_expr(cx, lhs, indent + 1); - println!("{}rhs:", ind); - print_expr(cx, rhs, indent + 1); - }, - hir::ExprKind::Field(ref e, ident) => { - println!("{}Field", ind); - println!("{}field name: {}", ind, ident.name); - println!("{}struct expr:", ind); - print_expr(cx, e, indent + 1); - }, - hir::ExprKind::Index(ref arr, ref idx) => { - println!("{}Index", ind); - println!("{}array expr:", ind); - print_expr(cx, arr, indent + 1); - println!("{}index expr:", ind); - print_expr(cx, idx, indent + 1); - }, - hir::ExprKind::Path(hir::QPath::Resolved(ref ty, ref path)) => { - println!("{}Resolved Path, {:?}", ind, ty); - println!("{}path: {:?}", ind, path); - }, - hir::ExprKind::Path(hir::QPath::TypeRelative(ref ty, ref seg)) => { - println!("{}Relative Path, {:?}", ind, ty); - println!("{}seg: {:?}", ind, seg); - }, - hir::ExprKind::Path(hir::QPath::LangItem(lang_item, ..)) => { - println!("{}Lang Item Path, {:?}", ind, lang_item.name()); - }, - hir::ExprKind::AddrOf(kind, ref muta, ref e) => { - println!("{}AddrOf", ind); - println!("kind: {:?}", kind); - println!("mutability: {:?}", muta); - print_expr(cx, e, indent + 1); - }, - hir::ExprKind::Break(_, ref e) => { - println!("{}Break", ind); - if let Some(ref e) = *e { - print_expr(cx, e, indent + 1); - } - }, - hir::ExprKind::Continue(_) => println!("{}Again", ind), - hir::ExprKind::Ret(ref e) => { - println!("{}Ret", ind); - if let Some(ref e) = *e { - print_expr(cx, e, indent + 1); - } - }, - hir::ExprKind::InlineAsm(ref asm) => { - println!("{}InlineAsm", ind); - println!("{}template: {}", ind, InlineAsmTemplatePiece::to_string(asm.template)); - println!("{}options: {:?}", ind, asm.options); - println!("{}operands:", ind); - for (op, _op_sp) in asm.operands { - match op { - hir::InlineAsmOperand::In { expr, .. } - | hir::InlineAsmOperand::InOut { expr, .. } - | hir::InlineAsmOperand::Const { expr } - | hir::InlineAsmOperand::Sym { expr } => print_expr(cx, expr, indent + 1), - hir::InlineAsmOperand::Out { expr, .. } => { - if let Some(expr) = expr { - print_expr(cx, expr, indent + 1); - } - }, - hir::InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { - print_expr(cx, in_expr, indent + 1); - if let Some(out_expr) = out_expr { - print_expr(cx, out_expr, indent + 1); - } - }, - } - } - }, - hir::ExprKind::LlvmInlineAsm(ref asm) => { - let inputs = &asm.inputs_exprs; - let outputs = &asm.outputs_exprs; - println!("{}LlvmInlineAsm", ind); - println!("{}inputs:", ind); - for e in inputs.iter() { - print_expr(cx, e, indent + 1); - } - println!("{}outputs:", ind); - for e in outputs.iter() { - print_expr(cx, e, indent + 1); - } - }, - hir::ExprKind::Struct(ref path, fields, ref base) => { - println!("{}Struct", ind); - println!("{}path: {:?}", ind, path); - for field in fields { - println!("{}field \"{}\":", ind, field.ident.name); - print_expr(cx, &field.expr, indent + 1); - } - if let Some(ref base) = *base { - println!("{}base:", ind); - print_expr(cx, base, indent + 1); - } - }, - hir::ExprKind::ConstBlock(ref anon_const) => { - println!("{}ConstBlock", ind); - println!("{}anon_const:", ind); - print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1); - }, - hir::ExprKind::Repeat(ref val, ref anon_const) => { - println!("{}Repeat", ind); - println!("{}value:", ind); - print_expr(cx, val, indent + 1); - println!("{}repeat count:", ind); - print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1); - }, - hir::ExprKind::Err => { - println!("{}Err", ind); - }, - hir::ExprKind::DropTemps(ref e) => { - println!("{}DropTemps", ind); - print_expr(cx, e, indent + 1); - }, - } -} - -fn print_item(cx: &LateContext<'_>, item: &hir::Item<'_>) { - let did = cx.tcx.hir().local_def_id(item.hir_id); - println!("item `{}`", item.ident.name); - match item.vis.node { - hir::VisibilityKind::Public => println!("public"), - hir::VisibilityKind::Crate(_) => println!("visible crate wide"), - hir::VisibilityKind::Restricted { ref path, .. } => println!( - "visible in module `{}`", - rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(path, false)) - ), - hir::VisibilityKind::Inherited => println!("visibility inherited from outer item"), - } - match item.kind { - hir::ItemKind::ExternCrate(ref _renamed_from) => { - let def_id = cx.tcx.hir().local_def_id(item.hir_id); - if let Some(crate_id) = cx.tcx.extern_mod_stmt_cnum(def_id) { - let source = cx.tcx.used_crate_source(crate_id); - if let Some(ref src) = source.dylib { - println!("extern crate dylib source: {:?}", src.0); - } - if let Some(ref src) = source.rlib { - println!("extern crate rlib source: {:?}", src.0); - } - } else { - println!("weird extern crate without a crate id"); - } - }, - hir::ItemKind::Use(ref path, ref kind) => println!("{:?}, {:?}", path, kind), - hir::ItemKind::Static(..) => println!("static item of type {:#?}", cx.tcx.type_of(did)), - hir::ItemKind::Const(..) => println!("const item of type {:#?}", cx.tcx.type_of(did)), - hir::ItemKind::Fn(..) => { - let item_ty = cx.tcx.type_of(did); - println!("function of type {:#?}", item_ty); - }, - hir::ItemKind::Mod(..) => println!("module"), - hir::ItemKind::ForeignMod { abi, .. } => println!("foreign module with abi: {}", abi), - hir::ItemKind::GlobalAsm(ref asm) => println!("global asm: {:?}", asm), - hir::ItemKind::TyAlias(..) => { - println!("type alias for {:?}", cx.tcx.type_of(did)); - }, - hir::ItemKind::OpaqueTy(..) => { - println!("existential type with real type {:?}", cx.tcx.type_of(did)); - }, - hir::ItemKind::Enum(..) => { - println!("enum definition of type {:?}", cx.tcx.type_of(did)); - }, - hir::ItemKind::Struct(..) => { - println!("struct definition of type {:?}", cx.tcx.type_of(did)); - }, - hir::ItemKind::Union(..) => { - println!("union definition of type {:?}", cx.tcx.type_of(did)); - }, - hir::ItemKind::Trait(..) => { - println!("trait decl"); - if cx.tcx.trait_is_auto(did.to_def_id()) { - println!("trait is auto"); - } else { - println!("trait is not auto"); - } - }, - hir::ItemKind::TraitAlias(..) => { - println!("trait alias"); - }, - hir::ItemKind::Impl(hir::Impl { - of_trait: Some(ref _trait_ref), - .. - }) => { - println!("trait impl"); - }, - hir::ItemKind::Impl(hir::Impl { of_trait: None, .. }) => { - println!("impl"); - }, - } -} - -#[allow(clippy::similar_names)] -#[allow(clippy::too_many_lines)] -fn print_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, indent: usize) { - let ind = " ".repeat(indent); - println!("{}+", ind); - match pat.kind { - hir::PatKind::Wild => println!("{}Wild", ind), - hir::PatKind::Binding(ref mode, .., ident, ref inner) => { - println!("{}Binding", ind); - println!("{}mode: {:?}", ind, mode); - println!("{}name: {}", ind, ident.name); - if let Some(ref inner) = *inner { - println!("{}inner:", ind); - print_pat(cx, inner, indent + 1); - } - }, - hir::PatKind::Or(fields) => { - println!("{}Or", ind); - for field in fields { - print_pat(cx, field, indent + 1); - } - }, - hir::PatKind::Struct(ref path, fields, ignore) => { - println!("{}Struct", ind); - println!( - "{}name: {}", - ind, - rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false)) - ); - println!("{}ignore leftover fields: {}", ind, ignore); - println!("{}fields:", ind); - for field in fields { - println!("{} field name: {}", ind, field.ident.name); - if field.is_shorthand { - println!("{} in shorthand notation", ind); - } - print_pat(cx, &field.pat, indent + 1); - } - }, - hir::PatKind::TupleStruct(ref path, fields, opt_dots_position) => { - println!("{}TupleStruct", ind); - println!( - "{}path: {}", - ind, - rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false)) - ); - if let Some(dot_position) = opt_dots_position { - println!("{}dot position: {}", ind, dot_position); - } - for field in fields { - print_pat(cx, field, indent + 1); - } - }, - hir::PatKind::Path(hir::QPath::Resolved(ref ty, ref path)) => { - println!("{}Resolved Path, {:?}", ind, ty); - println!("{}path: {:?}", ind, path); - }, - hir::PatKind::Path(hir::QPath::TypeRelative(ref ty, ref seg)) => { - println!("{}Relative Path, {:?}", ind, ty); - println!("{}seg: {:?}", ind, seg); - }, - hir::PatKind::Path(hir::QPath::LangItem(lang_item, ..)) => { - println!("{}Lang Item Path, {:?}", ind, lang_item.name()); - }, - hir::PatKind::Tuple(pats, opt_dots_position) => { - println!("{}Tuple", ind); - if let Some(dot_position) = opt_dots_position { - println!("{}dot position: {}", ind, dot_position); - } - for field in pats { - print_pat(cx, field, indent + 1); - } - }, - hir::PatKind::Box(ref inner) => { - println!("{}Box", ind); - print_pat(cx, inner, indent + 1); - }, - hir::PatKind::Ref(ref inner, ref muta) => { - println!("{}Ref", ind); - println!("{}mutability: {:?}", ind, muta); - print_pat(cx, inner, indent + 1); - }, - hir::PatKind::Lit(ref e) => { - println!("{}Lit", ind); - print_expr(cx, e, indent + 1); - }, - hir::PatKind::Range(ref l, ref r, ref range_end) => { - println!("{}Range", ind); - if let Some(expr) = l { - print_expr(cx, expr, indent + 1); - } - if let Some(expr) = r { - print_expr(cx, expr, indent + 1); - } - match *range_end { - hir::RangeEnd::Included => println!("{} end included", ind), - hir::RangeEnd::Excluded => println!("{} end excluded", ind), - } - }, - hir::PatKind::Slice(first_pats, ref range, last_pats) => { - println!("{}Slice [a, b, ..i, y, z]", ind); - println!("[a, b]:"); - for pat in first_pats { - print_pat(cx, pat, indent + 1); - } - println!("i:"); - if let Some(ref pat) = *range { - print_pat(cx, pat, indent + 1); - } - println!("[y, z]:"); - for pat in last_pats { - print_pat(cx, pat, indent + 1); - } - }, - } -} - -fn print_guard(cx: &LateContext<'_>, guard: &hir::Guard<'_>, indent: usize) { - let ind = " ".repeat(indent); - println!("{}+", ind); - match guard { - hir::Guard::If(expr) => { - println!("{}If", ind); - print_expr(cx, expr, indent + 1); - }, - hir::Guard::IfLet(pat, expr) => { - println!("{}IfLet", ind); - print_pat(cx, pat, indent + 1); - print_expr(cx, expr, indent + 1); - }, - } -} diff --git a/clippy_utils/src/internal_lints.rs b/clippy_utils/src/internal_lints.rs deleted file mode 100644 index bbfb60f11d3..00000000000 --- a/clippy_utils/src/internal_lints.rs +++ /dev/null @@ -1,1070 +0,0 @@ -use crate::consts::{constant_simple, Constant}; -use crate::{ - declare_clippy_lint, is_expn_of, match_def_path, match_qpath, match_type, method_calls, path_to_res, paths, - run_lints, snippet, span_lint, span_lint_and_help, span_lint_and_sugg, SpanlessEq, -}; -use if_chain::if_chain; -use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, NodeId}; -use rustc_ast::visit::FnKind; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::DefId; -use rustc_hir::hir_id::CRATE_HIR_ID; -use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; -use rustc_hir::{ - BinOpKind, Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Node, Path, StmtKind, Ty, TyKind, UnOp, -}; -use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; -use rustc_middle::hir::map::Map; -use rustc_middle::mir::interpret::ConstValue; -use rustc_middle::ty; -use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; -use rustc_span::source_map::{Span, Spanned}; -use rustc_span::symbol::{Symbol, SymbolStr}; -use rustc_typeck::hir_ty_to_ty; - -use std::borrow::{Borrow, Cow}; - -declare_clippy_lint! { - /// **What it does:** Checks for various things we like to keep tidy in clippy. - /// - /// **Why is this bad?** We like to pretend we're an example of tidy code. - /// - /// **Known problems:** None. - /// - /// **Example:** Wrong ordering of the util::paths constants. - pub CLIPPY_LINTS_INTERNAL, - internal, - "various things that will negatively affect your clippy experience" -} - -declare_clippy_lint! { - /// **What it does:** Ensures every lint is associated to a `LintPass`. - /// - /// **Why is this bad?** The compiler only knows lints via a `LintPass`. Without - /// putting a lint to a `LintPass::get_lints()`'s return, the compiler will not - /// know the name of the lint. - /// - /// **Known problems:** Only checks for lints associated using the - /// `declare_lint_pass!`, `impl_lint_pass!`, and `lint_array!` macros. - /// - /// **Example:** - /// ```rust,ignore - /// declare_lint! { pub LINT_1, ... } - /// declare_lint! { pub LINT_2, ... } - /// declare_lint! { pub FORGOTTEN_LINT, ... } - /// // ... - /// declare_lint_pass!(Pass => [LINT_1, LINT_2]); - /// // missing FORGOTTEN_LINT - /// ``` - pub LINT_WITHOUT_LINT_PASS, - internal, - "declaring a lint without associating it in a LintPass" -} - -declare_clippy_lint! { - /// **What it does:** Checks for calls to `cx.span_lint*` and suggests to use the `utils::*` - /// variant of the function. - /// - /// **Why is this bad?** The `utils::*` variants also add a link to the Clippy documentation to the - /// warning/error messages. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// Bad: - /// ```rust,ignore - /// cx.span_lint(LINT_NAME, "message"); - /// ``` - /// - /// Good: - /// ```rust,ignore - /// utils::span_lint(cx, LINT_NAME, "message"); - /// ``` - pub COMPILER_LINT_FUNCTIONS, - internal, - "usage of the lint functions of the compiler instead of the utils::* variant" -} - -declare_clippy_lint! { - /// **What it does:** Checks for calls to `cx.outer().expn_data()` and suggests to use - /// the `cx.outer_expn_data()` - /// - /// **Why is this bad?** `cx.outer_expn_data()` is faster and more concise. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// Bad: - /// ```rust,ignore - /// expr.span.ctxt().outer().expn_data() - /// ``` - /// - /// Good: - /// ```rust,ignore - /// expr.span.ctxt().outer_expn_data() - /// ``` - pub OUTER_EXPN_EXPN_DATA, - internal, - "using `cx.outer_expn().expn_data()` instead of `cx.outer_expn_data()`" -} - -declare_clippy_lint! { - /// **What it does:** Not an actual lint. This lint is only meant for testing our customized internal compiler - /// error message by calling `panic`. - /// - /// **Why is this bad?** ICE in large quantities can damage your teeth - /// - /// **Known problems:** None - /// - /// **Example:** - /// Bad: - /// ```rust,ignore - /// 🍦🍦🍦🍦🍦 - /// ``` - pub PRODUCE_ICE, - internal, - "this message should not appear anywhere as we ICE before and don't emit the lint" -} - -declare_clippy_lint! { - /// **What it does:** Checks for cases of an auto-generated lint without an updated description, - /// i.e. `default lint description`. - /// - /// **Why is this bad?** Indicates that the lint is not finished. - /// - /// **Known problems:** None - /// - /// **Example:** - /// Bad: - /// ```rust,ignore - /// declare_lint! { pub COOL_LINT, nursery, "default lint description" } - /// ``` - /// - /// Good: - /// ```rust,ignore - /// declare_lint! { pub COOL_LINT, nursery, "a great new lint" } - /// ``` - pub DEFAULT_LINT, - internal, - "found 'default lint description' in a lint declaration" -} - -declare_clippy_lint! { - /// **What it does:** Lints `span_lint_and_then` function calls, where the - /// closure argument has only one statement and that statement is a method - /// call to `span_suggestion`, `span_help`, `span_note` (using the same - /// span), `help` or `note`. - /// - /// These usages of `span_lint_and_then` should be replaced with one of the - /// wrapper functions `span_lint_and_sugg`, span_lint_and_help`, or - /// `span_lint_and_note`. - /// - /// **Why is this bad?** Using the wrapper `span_lint_and_*` functions, is more - /// convenient, readable and less error prone. - /// - /// **Known problems:** None - /// - /// *Example:** - /// Bad: - /// ```rust,ignore - /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { - /// diag.span_suggestion( - /// expr.span, - /// help_msg, - /// sugg.to_string(), - /// Applicability::MachineApplicable, - /// ); - /// }); - /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { - /// diag.span_help(expr.span, help_msg); - /// }); - /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { - /// diag.help(help_msg); - /// }); - /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { - /// diag.span_note(expr.span, note_msg); - /// }); - /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { - /// diag.note(note_msg); - /// }); - /// ``` - /// - /// Good: - /// ```rust,ignore - /// span_lint_and_sugg( - /// cx, - /// TEST_LINT, - /// expr.span, - /// lint_msg, - /// help_msg, - /// sugg.to_string(), - /// Applicability::MachineApplicable, - /// ); - /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg); - /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg); - /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg); - /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg); - /// ``` - pub COLLAPSIBLE_SPAN_LINT_CALLS, - internal, - "found collapsible `span_lint_and_then` calls" -} - -declare_clippy_lint! { - /// **What it does:** Checks for calls to `utils::match_type()` on a type diagnostic item - /// and suggests to use `utils::is_type_diagnostic_item()` instead. - /// - /// **Why is this bad?** `utils::is_type_diagnostic_item()` does not require hardcoded paths. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// Bad: - /// ```rust,ignore - /// utils::match_type(cx, ty, &paths::VEC) - /// ``` - /// - /// Good: - /// ```rust,ignore - /// utils::is_type_diagnostic_item(cx, ty, sym::vec_type) - /// ``` - pub MATCH_TYPE_ON_DIAGNOSTIC_ITEM, - internal, - "using `utils::match_type()` instead of `utils::is_type_diagnostic_item()`" -} - -declare_clippy_lint! { - /// **What it does:** - /// Checks the paths module for invalid paths. - /// - /// **Why is this bad?** - /// It indicates a bug in the code. - /// - /// **Known problems:** None. - /// - /// **Example:** None. - pub INVALID_PATHS, - internal, - "invalid path" -} - -declare_clippy_lint! { - /// **What it does:** - /// Checks for interning symbols that have already been pre-interned and defined as constants. - /// - /// **Why is this bad?** - /// It's faster and easier to use the symbol constant. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// Bad: - /// ```rust,ignore - /// let _ = sym!(f32); - /// ``` - /// - /// Good: - /// ```rust,ignore - /// let _ = sym::f32; - /// ``` - pub INTERNING_DEFINED_SYMBOL, - internal, - "interning a symbol that is pre-interned and defined as a constant" -} - -declare_clippy_lint! { - /// **What it does:** Checks for unnecessary conversion from Symbol to a string. - /// - /// **Why is this bad?** It's faster use symbols directly intead of strings. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// Bad: - /// ```rust,ignore - /// symbol.as_str() == "clippy"; - /// ``` - /// - /// Good: - /// ```rust,ignore - /// symbol == sym::clippy; - /// ``` - pub UNNECESSARY_SYMBOL_STR, - internal, - "unnecessary conversion between Symbol and string" -} - -declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]); - -impl EarlyLintPass for ClippyLintsInternal { - fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &AstCrate) { - if let Some(utils) = krate - .module - .items - .iter() - .find(|item| item.ident.name.as_str() == "utils") - { - if let ItemKind::Mod(ref utils_mod) = utils.kind { - if let Some(paths) = utils_mod.items.iter().find(|item| item.ident.name.as_str() == "paths") { - if let ItemKind::Mod(ref paths_mod) = paths.kind { - let mut last_name: Option = None; - for item in &*paths_mod.items { - let name = item.ident.as_str(); - if let Some(ref last_name) = last_name { - if **last_name > *name { - span_lint( - cx, - CLIPPY_LINTS_INTERNAL, - item.span, - "this constant should be before the previous constant due to lexical \ - ordering", - ); - } - } - last_name = Some(name); - } - } - } - } - } - } -} - -#[derive(Clone, Debug, Default)] -pub struct LintWithoutLintPass { - declared_lints: FxHashMap, - registered_lints: FxHashSet, -} - -impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS]); - -impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if !run_lints(cx, &[DEFAULT_LINT], item.hir_id) { - return; - } - - if let hir::ItemKind::Static(ref ty, Mutability::Not, body_id) = item.kind { - if is_lint_ref_type(cx, ty) { - let expr = &cx.tcx.hir().body(body_id).value; - if_chain! { - if let ExprKind::AddrOf(_, _, ref inner_exp) = expr.kind; - if let ExprKind::Struct(_, ref fields, _) = inner_exp.kind; - let field = fields - .iter() - .find(|f| f.ident.as_str() == "desc") - .expect("lints must have a description field"); - if let ExprKind::Lit(Spanned { - node: LitKind::Str(ref sym, _), - .. - }) = field.expr.kind; - if sym.as_str() == "default lint description"; - - then { - span_lint( - cx, - DEFAULT_LINT, - item.span, - &format!("the lint `{}` has the default lint description", item.ident.name), - ); - } - } - self.declared_lints.insert(item.ident.name, item.span); - } - } else if is_expn_of(item.span, "impl_lint_pass").is_some() - || is_expn_of(item.span, "declare_lint_pass").is_some() - { - if let hir::ItemKind::Impl(hir::Impl { - of_trait: None, - items: ref impl_item_refs, - .. - }) = item.kind - { - let mut collector = LintCollector { - output: &mut self.registered_lints, - cx, - }; - let body_id = cx.tcx.hir().body_owned_by( - impl_item_refs - .iter() - .find(|iiref| iiref.ident.as_str() == "get_lints") - .expect("LintPass needs to implement get_lints") - .id - .hir_id, - ); - collector.visit_expr(&cx.tcx.hir().body(body_id).value); - } - } - } - - fn check_crate_post(&mut self, cx: &LateContext<'tcx>, _: &'tcx Crate<'_>) { - if !run_lints(cx, &[LINT_WITHOUT_LINT_PASS], CRATE_HIR_ID) { - return; - } - - for (lint_name, &lint_span) in &self.declared_lints { - // When using the `declare_tool_lint!` macro, the original `lint_span`'s - // file points to "". - // `compiletest-rs` thinks that's an error in a different file and - // just ignores it. This causes the test in compile-fail/lint_pass - // not able to capture the error. - // Therefore, we need to climb the macro expansion tree and find the - // actual span that invoked `declare_tool_lint!`: - let lint_span = lint_span.ctxt().outer_expn_data().call_site; - - if !self.registered_lints.contains(lint_name) { - span_lint( - cx, - LINT_WITHOUT_LINT_PASS, - lint_span, - &format!("the lint `{}` is not added to any `LintPass`", lint_name), - ); - } - } - } -} - -fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &Ty<'_>) -> bool { - if let TyKind::Rptr( - _, - MutTy { - ty: ref inner, - mutbl: Mutability::Not, - }, - ) = ty.kind - { - if let TyKind::Path(ref path) = inner.kind { - if let Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, inner.hir_id) { - return match_def_path(cx, def_id, &paths::LINT); - } - } - } - - false -} - -struct LintCollector<'a, 'tcx> { - output: &'a mut FxHashSet, - cx: &'a LateContext<'tcx>, -} - -impl<'a, 'tcx> Visitor<'tcx> for LintCollector<'a, 'tcx> { - type Map = Map<'tcx>; - - fn visit_path(&mut self, path: &'tcx Path<'_>, _: HirId) { - if path.segments.len() == 1 { - self.output.insert(path.segments[0].ident.name); - } - } - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::All(self.cx.tcx.hir()) - } -} - -#[derive(Clone, Default)] -pub struct CompilerLintFunctions { - map: FxHashMap<&'static str, &'static str>, -} - -impl CompilerLintFunctions { - #[must_use] - pub fn new() -> Self { - let mut map = FxHashMap::default(); - map.insert("span_lint", "utils::span_lint"); - map.insert("struct_span_lint", "utils::span_lint"); - map.insert("lint", "utils::span_lint"); - map.insert("span_lint_note", "utils::span_lint_and_note"); - map.insert("span_lint_help", "utils::span_lint_and_help"); - Self { map } - } -} - -impl_lint_pass!(CompilerLintFunctions => [COMPILER_LINT_FUNCTIONS]); - -impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !run_lints(cx, &[COMPILER_LINT_FUNCTIONS], expr.hir_id) { - return; - } - - if_chain! { - if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind; - let fn_name = path.ident; - if let Some(sugg) = self.map.get(&*fn_name.as_str()); - let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs(); - if match_type(cx, ty, &paths::EARLY_CONTEXT) - || match_type(cx, ty, &paths::LATE_CONTEXT); - then { - span_lint_and_help( - cx, - COMPILER_LINT_FUNCTIONS, - path.ident.span, - "usage of a compiler lint function", - None, - &format!("please use the Clippy variant of this function: `{}`", sugg), - ); - } - } - } -} - -declare_lint_pass!(OuterExpnDataPass => [OUTER_EXPN_EXPN_DATA]); - -impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if !run_lints(cx, &[OUTER_EXPN_EXPN_DATA], expr.hir_id) { - return; - } - - let (method_names, arg_lists, spans) = method_calls(expr, 2); - let method_names: Vec = method_names.iter().map(|s| s.as_str()).collect(); - let method_names: Vec<&str> = method_names.iter().map(|s| &**s).collect(); - if_chain! { - if let ["expn_data", "outer_expn"] = method_names.as_slice(); - let args = arg_lists[1]; - if args.len() == 1; - let self_arg = &args[0]; - let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); - if match_type(cx, self_ty, &paths::SYNTAX_CONTEXT); - then { - span_lint_and_sugg( - cx, - OUTER_EXPN_EXPN_DATA, - spans[1].with_hi(expr.span.hi()), - "usage of `outer_expn().expn_data()`", - "try", - "outer_expn_data()".to_string(), - Applicability::MachineApplicable, - ); - } - } - } -} - -declare_lint_pass!(ProduceIce => [PRODUCE_ICE]); - -impl EarlyLintPass for ProduceIce { - fn check_fn(&mut self, _: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) { - if is_trigger_fn(fn_kind) { - panic!("Would you like some help with that?"); - } - } -} - -fn is_trigger_fn(fn_kind: FnKind<'_>) -> bool { - match fn_kind { - FnKind::Fn(_, ident, ..) => ident.name.as_str() == "it_looks_like_you_are_trying_to_kill_clippy", - FnKind::Closure(..) => false, - } -} - -declare_lint_pass!(CollapsibleCalls => [COLLAPSIBLE_SPAN_LINT_CALLS]); - -impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if !run_lints(cx, &[COLLAPSIBLE_SPAN_LINT_CALLS], expr.hir_id) { - return; - } - - if_chain! { - if let ExprKind::Call(ref func, ref and_then_args) = expr.kind; - if let ExprKind::Path(ref path) = func.kind; - if match_qpath(path, &["span_lint_and_then"]); - if and_then_args.len() == 5; - if let ExprKind::Closure(_, _, body_id, _, _) = &and_then_args[4].kind; - let body = cx.tcx.hir().body(*body_id); - if let ExprKind::Block(block, _) = &body.value.kind; - let stmts = &block.stmts; - if stmts.len() == 1 && block.expr.is_none(); - if let StmtKind::Semi(only_expr) = &stmts[0].kind; - if let ExprKind::MethodCall(ref ps, _, ref span_call_args, _) = &only_expr.kind; - let and_then_snippets = get_and_then_snippets(cx, and_then_args); - let mut sle = SpanlessEq::new(cx).deny_side_effects(); - then { - match &*ps.ident.as_str() { - "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => { - suggest_suggestion(cx, expr, &and_then_snippets, &span_suggestion_snippets(cx, span_call_args)); - }, - "span_help" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => { - let help_snippet = snippet(cx, span_call_args[2].span, r#""...""#); - suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), true); - }, - "span_note" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => { - let note_snippet = snippet(cx, span_call_args[2].span, r#""...""#); - suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true); - }, - "help" => { - let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#); - suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), false); - } - "note" => { - let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#); - suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), false); - } - _ => (), - } - } - } - } -} - -struct AndThenSnippets<'a> { - cx: Cow<'a, str>, - lint: Cow<'a, str>, - span: Cow<'a, str>, - msg: Cow<'a, str>, -} - -fn get_and_then_snippets<'a, 'hir>(cx: &LateContext<'_>, and_then_snippets: &'hir [Expr<'hir>]) -> AndThenSnippets<'a> { - let cx_snippet = snippet(cx, and_then_snippets[0].span, "cx"); - let lint_snippet = snippet(cx, and_then_snippets[1].span, ".."); - let span_snippet = snippet(cx, and_then_snippets[2].span, "span"); - let msg_snippet = snippet(cx, and_then_snippets[3].span, r#""...""#); - - AndThenSnippets { - cx: cx_snippet, - lint: lint_snippet, - span: span_snippet, - msg: msg_snippet, - } -} - -struct SpanSuggestionSnippets<'a> { - help: Cow<'a, str>, - sugg: Cow<'a, str>, - applicability: Cow<'a, str>, -} - -fn span_suggestion_snippets<'a, 'hir>( - cx: &LateContext<'_>, - span_call_args: &'hir [Expr<'hir>], -) -> SpanSuggestionSnippets<'a> { - let help_snippet = snippet(cx, span_call_args[2].span, r#""...""#); - let sugg_snippet = snippet(cx, span_call_args[3].span, ".."); - let applicability_snippet = snippet(cx, span_call_args[4].span, "Applicability::MachineApplicable"); - - SpanSuggestionSnippets { - help: help_snippet, - sugg: sugg_snippet, - applicability: applicability_snippet, - } -} - -fn suggest_suggestion( - cx: &LateContext<'_>, - expr: &Expr<'_>, - and_then_snippets: &AndThenSnippets<'_>, - span_suggestion_snippets: &SpanSuggestionSnippets<'_>, -) { - span_lint_and_sugg( - cx, - COLLAPSIBLE_SPAN_LINT_CALLS, - expr.span, - "this call is collapsible", - "collapse into", - format!( - "span_lint_and_sugg({}, {}, {}, {}, {}, {}, {})", - and_then_snippets.cx, - and_then_snippets.lint, - and_then_snippets.span, - and_then_snippets.msg, - span_suggestion_snippets.help, - span_suggestion_snippets.sugg, - span_suggestion_snippets.applicability - ), - Applicability::MachineApplicable, - ); -} - -fn suggest_help( - cx: &LateContext<'_>, - expr: &Expr<'_>, - and_then_snippets: &AndThenSnippets<'_>, - help: &str, - with_span: bool, -) { - let option_span = if with_span { - format!("Some({})", and_then_snippets.span) - } else { - "None".to_string() - }; - - span_lint_and_sugg( - cx, - COLLAPSIBLE_SPAN_LINT_CALLS, - expr.span, - "this call is collapsible", - "collapse into", - format!( - "span_lint_and_help({}, {}, {}, {}, {}, {})", - and_then_snippets.cx, - and_then_snippets.lint, - and_then_snippets.span, - and_then_snippets.msg, - &option_span, - help - ), - Applicability::MachineApplicable, - ); -} - -fn suggest_note( - cx: &LateContext<'_>, - expr: &Expr<'_>, - and_then_snippets: &AndThenSnippets<'_>, - note: &str, - with_span: bool, -) { - let note_span = if with_span { - format!("Some({})", and_then_snippets.span) - } else { - "None".to_string() - }; - - span_lint_and_sugg( - cx, - COLLAPSIBLE_SPAN_LINT_CALLS, - expr.span, - "this call is collspible", - "collapse into", - format!( - "span_lint_and_note({}, {}, {}, {}, {}, {})", - and_then_snippets.cx, - and_then_snippets.lint, - and_then_snippets.span, - and_then_snippets.msg, - note_span, - note - ), - Applicability::MachineApplicable, - ); -} - -declare_lint_pass!(MatchTypeOnDiagItem => [MATCH_TYPE_ON_DIAGNOSTIC_ITEM]); - -impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if !run_lints(cx, &[MATCH_TYPE_ON_DIAGNOSTIC_ITEM], expr.hir_id) { - return; - } - - if_chain! { - // Check if this is a call to utils::match_type() - if let ExprKind::Call(fn_path, [context, ty, ty_path]) = expr.kind; - if let ExprKind::Path(fn_qpath) = &fn_path.kind; - if match_qpath(&fn_qpath, &["utils", "match_type"]); - // Extract the path to the matched type - if let Some(segments) = path_to_matched_type(cx, ty_path); - let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect(); - if let Some(ty_did) = path_to_res(cx, &segments[..]).opt_def_id(); - // Check if the matched type is a diagnostic item - let diag_items = cx.tcx.diagnostic_items(ty_did.krate); - if let Some(item_name) = diag_items.iter().find_map(|(k, v)| if *v == ty_did { Some(k) } else { None }); - then { - let cx_snippet = snippet(cx, context.span, "_"); - let ty_snippet = snippet(cx, ty.span, "_"); - - span_lint_and_sugg( - cx, - MATCH_TYPE_ON_DIAGNOSTIC_ITEM, - expr.span, - "usage of `utils::match_type()` on a type diagnostic item", - "try", - format!("utils::is_type_diagnostic_item({}, {}, sym::{})", cx_snippet, ty_snippet, item_name), - Applicability::MaybeIncorrect, - ); - } - } - } -} - -fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option> { - use rustc_hir::ItemKind; - - match &expr.kind { - ExprKind::AddrOf(.., expr) => return path_to_matched_type(cx, expr), - ExprKind::Path(qpath) => match cx.qpath_res(qpath, expr.hir_id) { - Res::Local(hir_id) => { - let parent_id = cx.tcx.hir().get_parent_node(hir_id); - if let Some(Node::Local(local)) = cx.tcx.hir().find(parent_id) { - if let Some(init) = local.init { - return path_to_matched_type(cx, init); - } - } - }, - Res::Def(DefKind::Const | DefKind::Static, def_id) => { - if let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(def_id) { - if let ItemKind::Const(.., body_id) | ItemKind::Static(.., body_id) = item.kind { - let body = cx.tcx.hir().body(body_id); - return path_to_matched_type(cx, &body.value); - } - } - }, - _ => {}, - }, - ExprKind::Array(exprs) => { - let segments: Vec = exprs - .iter() - .filter_map(|expr| { - if let ExprKind::Lit(lit) = &expr.kind { - if let LitKind::Str(sym, _) = lit.node { - return Some(sym.as_str()); - } - } - - None - }) - .collect(); - - if segments.len() == exprs.len() { - return Some(segments); - } - }, - _ => {}, - } - - None -} - -// This is not a complete resolver for paths. It works on all the paths currently used in the paths -// module. That's all it does and all it needs to do. -pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { - if path_to_res(cx, path) != Res::Err { - return true; - } - - // Some implementations can't be found by `path_to_res`, particularly inherent - // implementations of native types. Check lang items. - let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect(); - let lang_items = cx.tcx.lang_items(); - for item_def_id in lang_items.items().iter().flatten() { - let lang_item_path = cx.get_def_path(*item_def_id); - if path_syms.starts_with(&lang_item_path) { - if let [item] = &path_syms[lang_item_path.len()..] { - for child in cx.tcx.item_children(*item_def_id) { - if child.ident.name == *item { - return true; - } - } - } - } - } - - false -} - -declare_lint_pass!(InvalidPaths => [INVALID_PATHS]); - -impl<'tcx> LateLintPass<'tcx> for InvalidPaths { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - let local_def_id = &cx.tcx.parent_module(item.hir_id); - let mod_name = &cx.tcx.item_name(local_def_id.to_def_id()); - if_chain! { - if mod_name.as_str() == "paths"; - if let hir::ItemKind::Const(ty, body_id) = item.kind; - let ty = hir_ty_to_ty(cx.tcx, ty); - if let ty::Array(el_ty, _) = &ty.kind(); - if let ty::Ref(_, el_ty, _) = &el_ty.kind(); - if el_ty.is_str(); - let body = cx.tcx.hir().body(body_id); - let typeck_results = cx.tcx.typeck_body(body_id); - if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, &body.value); - let path: Vec<&str> = path.iter().map(|x| { - if let Constant::Str(s) = x { - s.as_str() - } else { - // We checked the type of the constant above - unreachable!() - } - }).collect(); - if !check_path(cx, &path[..]); - then { - span_lint(cx, CLIPPY_LINTS_INTERNAL, item.span, "invalid path"); - } - } - } -} - -#[derive(Default)] -pub struct InterningDefinedSymbol { - // Maps the symbol value to the constant DefId. - symbol_map: FxHashMap, -} - -impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL, UNNECESSARY_SYMBOL_STR]); - -impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { - fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) { - if !self.symbol_map.is_empty() { - return; - } - - for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] { - if let Some(def_id) = path_to_res(cx, module).opt_def_id() { - for item in cx.tcx.item_children(def_id).iter() { - if_chain! { - if let Res::Def(DefKind::Const, item_def_id) = item.res; - let ty = cx.tcx.type_of(item_def_id); - if match_type(cx, ty, &paths::SYMBOL); - if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id); - if let Ok(value) = value.to_u32(); - then { - self.symbol_map.insert(value, item_def_id); - } - } - } - } - } - } - - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - if let ExprKind::Call(func, [arg]) = &expr.kind; - if let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind(); - if match_def_path(cx, *def_id, &paths::SYMBOL_INTERN); - if let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg); - let value = Symbol::intern(&arg).as_u32(); - if let Some(&def_id) = self.symbol_map.get(&value); - then { - span_lint_and_sugg( - cx, - INTERNING_DEFINED_SYMBOL, - is_expn_of(expr.span, "sym").unwrap_or(expr.span), - "interning a defined symbol", - "try", - cx.tcx.def_path_str(def_id), - Applicability::MachineApplicable, - ); - } - } - if let ExprKind::Binary(op, left, right) = expr.kind { - if matches!(op.node, BinOpKind::Eq | BinOpKind::Ne) { - let data = [ - (left, self.symbol_str_expr(left, cx)), - (right, self.symbol_str_expr(right, cx)), - ]; - match data { - // both operands are a symbol string - [(_, Some(left)), (_, Some(right))] => { - span_lint_and_sugg( - cx, - UNNECESSARY_SYMBOL_STR, - expr.span, - "unnecessary `Symbol` to string conversion", - "try", - format!( - "{} {} {}", - left.as_symbol_snippet(cx), - op.node.as_str(), - right.as_symbol_snippet(cx), - ), - Applicability::MachineApplicable, - ); - }, - // one of the operands is a symbol string - [(expr, Some(symbol)), _] | [_, (expr, Some(symbol))] => { - // creating an owned string for comparison - if matches!(symbol, SymbolStrExpr::Expr { is_to_owned: true, .. }) { - span_lint_and_sugg( - cx, - UNNECESSARY_SYMBOL_STR, - expr.span, - "unnecessary string allocation", - "try", - format!("{}.as_str()", symbol.as_symbol_snippet(cx)), - Applicability::MachineApplicable, - ); - } - }, - // nothing found - [(_, None), (_, None)] => {}, - } - } - } - } -} - -impl InterningDefinedSymbol { - fn symbol_str_expr<'tcx>(&self, expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> Option> { - static IDENT_STR_PATHS: &[&[&str]] = &[&paths::IDENT_AS_STR, &paths::TO_STRING_METHOD]; - static SYMBOL_STR_PATHS: &[&[&str]] = &[ - &paths::SYMBOL_AS_STR, - &paths::SYMBOL_TO_IDENT_STRING, - &paths::TO_STRING_METHOD, - ]; - // SymbolStr might be de-referenced: `&*symbol.as_str()` - let call = if_chain! { - if let ExprKind::AddrOf(_, _, e) = expr.kind; - if let ExprKind::Unary(UnOp::Deref, e) = e.kind; - then { e } else { expr } - }; - if_chain! { - // is a method call - if let ExprKind::MethodCall(_, _, [item], _) = call.kind; - if let Some(did) = cx.typeck_results().type_dependent_def_id(call.hir_id); - let ty = cx.typeck_results().expr_ty(item); - // ...on either an Ident or a Symbol - if let Some(is_ident) = if match_type(cx, ty, &paths::SYMBOL) { - Some(false) - } else if match_type(cx, ty, &paths::IDENT) { - Some(true) - } else { - None - }; - // ...which converts it to a string - let paths = if is_ident { IDENT_STR_PATHS } else { SYMBOL_STR_PATHS }; - if let Some(path) = paths.iter().find(|path| match_def_path(cx, did, path)); - then { - let is_to_owned = path.last().unwrap().ends_with("string"); - return Some(SymbolStrExpr::Expr { - item, - is_ident, - is_to_owned, - }); - } - } - // is a string constant - if let Some(Constant::Str(s)) = constant_simple(cx, cx.typeck_results(), expr) { - let value = Symbol::intern(&s).as_u32(); - // ...which matches a symbol constant - if let Some(&def_id) = self.symbol_map.get(&value) { - return Some(SymbolStrExpr::Const(def_id)); - } - } - None - } -} - -enum SymbolStrExpr<'tcx> { - /// a string constant with a corresponding symbol constant - Const(DefId), - /// a "symbol to string" expression like `symbol.as_str()` - Expr { - /// part that evaluates to `Symbol` or `Ident` - item: &'tcx Expr<'tcx>, - is_ident: bool, - /// whether an owned `String` is created like `to_ident_string()` - is_to_owned: bool, - }, -} - -impl<'tcx> SymbolStrExpr<'tcx> { - /// Returns a snippet that evaluates to a `Symbol` and is const if possible - fn as_symbol_snippet(&self, cx: &LateContext<'_>) -> Cow<'tcx, str> { - match *self { - Self::Const(def_id) => cx.tcx.def_path_str(def_id).into(), - Self::Expr { item, is_ident, .. } => { - let mut snip = snippet(cx, item.span.source_callsite(), ".."); - if is_ident { - // get `Ident.name` - snip.to_mut().push_str(".name"); - } - snip - }, - } - } -} diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 0c955259672..5e0f0f084cb 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -30,7 +30,6 @@ pub mod sym_helper; #[allow(clippy::module_name_repetitions)] pub mod ast_utils; pub mod attrs; -pub mod author; pub mod camel_case; pub mod comparisons; pub mod conf; @@ -39,9 +38,6 @@ mod diagnostics; pub mod eager_or_lazy; pub mod higher; mod hir_utils; -pub mod inspector; -#[cfg(feature = "internal-lints")] -pub mod internal_lints; pub mod numeric_literal; pub mod paths; pub mod ptr; @@ -90,105 +86,6 @@ use smallvec::SmallVec; use crate::consts::{constant, Constant}; -/// Macro used to declare a Clippy lint. -/// -/// Every lint declaration consists of 4 parts: -/// -/// 1. The documentation, which is used for the website -/// 2. The `LINT_NAME`. See [lint naming][lint_naming] on lint naming conventions. -/// 3. The `lint_level`, which is a mapping from *one* of our lint groups to `Allow`, `Warn` or -/// `Deny`. The lint level here has nothing to do with what lint groups the lint is a part of. -/// 4. The `description` that contains a short explanation on what's wrong with code where the -/// lint is triggered. -/// -/// Currently the categories `style`, `correctness`, `complexity` and `perf` are enabled by default. -/// As said in the README.md of this repository, if the lint level mapping changes, please update -/// README.md. -/// -/// # Example -/// -/// ``` -/// #![feature(rustc_private)] -/// extern crate rustc_session; -/// use rustc_session::declare_tool_lint; -/// use clippy_utils::declare_clippy_lint; -/// -/// declare_clippy_lint! { -/// /// **What it does:** Checks for ... (describe what the lint matches). -/// /// -/// /// **Why is this bad?** Supply the reason for linting the code. -/// /// -/// /// **Known problems:** None. (Or describe where it could go wrong.) -/// /// -/// /// **Example:** -/// /// -/// /// ```rust -/// /// // Bad -/// /// Insert a short example of code that triggers the lint -/// /// -/// /// // Good -/// /// Insert a short example of improved code that doesn't trigger the lint -/// /// ``` -/// pub LINT_NAME, -/// pedantic, -/// "description" -/// } -/// ``` -/// [lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints -#[macro_export] -macro_rules! declare_clippy_lint { - { $(#[$attr:meta])* pub $name:tt, style, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, correctness, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Deny, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, complexity, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, perf, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, pedantic, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, restriction, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, cargo, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, nursery, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, internal, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, internal_warn, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true - } - }; -} - pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Option { if let Ok(version) = RustcVersion::parse(msrv) { return Some(version); diff --git a/tests/ui-internal/custom_ice_message.stderr b/tests/ui-internal/custom_ice_message.stderr index 29dee73c946..a1b8e2ee162 100644 --- a/tests/ui-internal/custom_ice_message.stderr +++ b/tests/ui-internal/custom_ice_message.stderr @@ -1,4 +1,4 @@ -thread 'rustc' panicked at 'Would you like some help with that?', clippy_utils/src/internal_lints.rs +thread 'rustc' panicked at 'Would you like some help with that?', clippy_lints/src/utils/internal_lints.rs note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace error: internal compiler error: unexpected panic -- cgit 1.4.1-3-g733a5 From 9fe9d94abda8d2698d1dede6961007e0026760ea Mon Sep 17 00:00:00 2001 From: Marc Dominik Migge Date: Wed, 24 Feb 2021 13:31:04 +0100 Subject: Don't lint unit args if expression kind is path --- clippy_lints/src/types.rs | 14 ++++-------- tests/ui/unit_arg.rs | 5 +++++ tests/ui/unit_arg.stderr | 20 +++++++++--------- tests/ui/unit_arg_expressions.rs | 35 ------------------------------ tests/ui/unit_arg_expressions.stderr | 41 ------------------------------------ 5 files changed, 19 insertions(+), 96 deletions(-) delete mode 100644 tests/ui/unit_arg_expressions.rs delete mode 100644 tests/ui/unit_arg_expressions.stderr (limited to 'tests') diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 7d0eea37bc0..77cde8b60c1 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -955,16 +955,10 @@ impl<'tcx> LateLintPass<'tcx> for UnitArg { .iter() .filter(|arg| { if is_unit(cx.typeck_results().expr_ty(arg)) && !is_unit_literal(arg) { - match &arg.kind { - ExprKind::Block(..) - | ExprKind::Call(..) - | ExprKind::If(..) - | ExprKind::MethodCall(..) => true, - ExprKind::Match(..) => { - !matches!(&arg.kind, ExprKind::Match(.., MatchSource::TryDesugar)) - }, - _ => false, - } + !matches!( + &arg.kind, + ExprKind::Match(.., MatchSource::TryDesugar) | ExprKind::Path(..) + ) } else { false } diff --git a/tests/ui/unit_arg.rs b/tests/ui/unit_arg.rs index cce543006d7..5ea2e5d65c5 100644 --- a/tests/ui/unit_arg.rs +++ b/tests/ui/unit_arg.rs @@ -27,6 +27,10 @@ impl Bar { } } +fn baz(t: T) { + foo(t); +} + fn bad() { foo({ 1; @@ -73,6 +77,7 @@ fn ok() { question_mark(); let named_unit_arg = (); foo(named_unit_arg); + baz(()); } fn question_mark() -> Result<(), ()> { diff --git a/tests/ui/unit_arg.stderr b/tests/ui/unit_arg.stderr index 8e3f1811c65..b3fe9addb62 100644 --- a/tests/ui/unit_arg.stderr +++ b/tests/ui/unit_arg.stderr @@ -1,5 +1,5 @@ error: passing a unit value to a function - --> $DIR/unit_arg.rs:31:5 + --> $DIR/unit_arg.rs:35:5 | LL | / foo({ LL | | 1; @@ -20,7 +20,7 @@ LL | foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:34:5 + --> $DIR/unit_arg.rs:38:5 | LL | foo(foo(1)); | ^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:35:5 + --> $DIR/unit_arg.rs:39:5 | LL | / foo({ LL | | foo(1); @@ -54,7 +54,7 @@ LL | foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:40:5 + --> $DIR/unit_arg.rs:44:5 | LL | / b.bar({ LL | | 1; @@ -74,7 +74,7 @@ LL | b.bar(()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:43:5 + --> $DIR/unit_arg.rs:47:5 | LL | taking_multiple_units(foo(0), foo(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -87,7 +87,7 @@ LL | taking_multiple_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:44:5 + --> $DIR/unit_arg.rs:48:5 | LL | / taking_multiple_units(foo(0), { LL | | foo(1); @@ -110,7 +110,7 @@ LL | taking_multiple_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:48:5 + --> $DIR/unit_arg.rs:52:5 | LL | / taking_multiple_units( LL | | { @@ -140,7 +140,7 @@ LL | foo(2); ... error: passing a unit value to a function - --> $DIR/unit_arg.rs:59:13 + --> $DIR/unit_arg.rs:63:13 | LL | None.or(Some(foo(2))); | ^^^^^^^^^^^^ @@ -154,7 +154,7 @@ LL | }); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:62:5 + --> $DIR/unit_arg.rs:66:5 | LL | foo(foo(())); | ^^^^^^^^^^^^ @@ -166,7 +166,7 @@ LL | foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:97:5 + --> $DIR/unit_arg.rs:102:5 | LL | Some(foo(1)) | ^^^^^^^^^^^^ diff --git a/tests/ui/unit_arg_expressions.rs b/tests/ui/unit_arg_expressions.rs deleted file mode 100644 index a6807cb2e97..00000000000 --- a/tests/ui/unit_arg_expressions.rs +++ /dev/null @@ -1,35 +0,0 @@ -#![warn(clippy::unit_arg)] -#![allow(clippy::no_effect)] - -use std::fmt::Debug; - -fn foo(t: T) { - println!("{:?}", t); -} - -fn bad() { - foo(if true { - 1; - }); - foo(match Some(1) { - Some(_) => { - 1; - }, - None => { - 0; - }, - }); -} - -fn ok() { - foo(if true { 1 } else { 0 }); - foo(match Some(1) { - Some(_) => 1, - None => 0, - }); -} - -fn main() { - bad(); - ok(); -} diff --git a/tests/ui/unit_arg_expressions.stderr b/tests/ui/unit_arg_expressions.stderr deleted file mode 100644 index 9fb08106b72..00000000000 --- a/tests/ui/unit_arg_expressions.stderr +++ /dev/null @@ -1,41 +0,0 @@ -error: passing a unit value to a function - --> $DIR/unit_arg_expressions.rs:11:5 - | -LL | / foo(if true { -LL | | 1; -LL | | }); - | |______^ - | - = note: `-D clippy::unit-arg` implied by `-D warnings` -help: move the expression in front of the call and replace it with the unit literal `()` - | -LL | if true { -LL | 1; -LL | }; -LL | foo(()); - | - -error: passing a unit value to a function - --> $DIR/unit_arg_expressions.rs:14:5 - | -LL | / foo(match Some(1) { -LL | | Some(_) => { -LL | | 1; -LL | | }, -... | -LL | | }, -LL | | }); - | |______^ - | -help: move the expression in front of the call and replace it with the unit literal `()` - | -LL | match Some(1) { -LL | Some(_) => { -LL | 1; -LL | }, -LL | None => { -LL | 0; - ... - -error: aborting due to 2 previous errors - -- cgit 1.4.1-3-g733a5 From b0d18e93d61ae9a065b175433471c507b9f56e35 Mon Sep 17 00:00:00 2001 From: avitex Date: Thu, 25 Feb 2021 09:26:11 +1100 Subject: add test coverage for all `doc-valid-idents` --- tests/ui/doc.rs | 12 ++++++++++++ tests/ui/doc.stderr | 44 ++++++++++++++++++++++---------------------- 2 files changed, 34 insertions(+), 22 deletions(-) (limited to 'tests') diff --git a/tests/ui/doc.rs b/tests/ui/doc.rs index e30970ed952..d2c666bd290 100644 --- a/tests/ui/doc.rs +++ b/tests/ui/doc.rs @@ -50,11 +50,23 @@ fn test_units() { } /// This tests allowed identifiers. +/// KiB MiB GiB TiB PiB EiB /// DirectX /// ECMAScript +/// GPLv2 GPLv3 +/// GitHub GitLab +/// IPv4 IPv6 +/// ClojureScript CoffeeScript JavaScript PureScript TypeScript +/// NaN NaNs /// OAuth GraphQL +/// OCaml +/// OpenGL OpenMP OpenSSH OpenSSL OpenStreetMap OpenDNS /// WebGL +/// TensorFlow +/// TrueType +/// iOS macOS /// TeX LaTeX BibTeX BibLaTeX +/// MinGW /// CamelCase (see also #2395) /// be_sure_we_got_to_the_end_of_it fn test_allowed() { diff --git a/tests/ui/doc.stderr b/tests/ui/doc.stderr index e1c1aa85a60..7eab8a85f09 100644 --- a/tests/ui/doc.stderr +++ b/tests/ui/doc.stderr @@ -55,133 +55,133 @@ LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:59:5 + --> $DIR/doc.rs:71:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `link_with_underscores` between ticks in the documentation - --> $DIR/doc.rs:63:22 + --> $DIR/doc.rs:75:22 | LL | /// This test has [a link_with_underscores][chunked-example] inside it. See #823. | ^^^^^^^^^^^^^^^^^^^^^ error: you should put `inline_link2` between ticks in the documentation - --> $DIR/doc.rs:66:21 + --> $DIR/doc.rs:78:21 | LL | /// It can also be [inline_link2]. | ^^^^^^^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:76:5 + --> $DIR/doc.rs:88:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `CamelCaseThing` between ticks in the documentation - --> $DIR/doc.rs:84:8 + --> $DIR/doc.rs:96:8 | LL | /// ## CamelCaseThing | ^^^^^^^^^^^^^^ error: you should put `CamelCaseThing` between ticks in the documentation - --> $DIR/doc.rs:87:7 + --> $DIR/doc.rs:99:7 | LL | /// # CamelCaseThing | ^^^^^^^^^^^^^^ error: you should put `CamelCaseThing` between ticks in the documentation - --> $DIR/doc.rs:89:22 + --> $DIR/doc.rs:101:22 | LL | /// Not a title #897 CamelCaseThing | ^^^^^^^^^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:90:5 + --> $DIR/doc.rs:102:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:97:5 + --> $DIR/doc.rs:109:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:110:5 + --> $DIR/doc.rs:122:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `FooBar` between ticks in the documentation - --> $DIR/doc.rs:121:43 + --> $DIR/doc.rs:133:43 | LL | /** E.g., serialization of an empty list: FooBar | ^^^^^^ error: you should put `BarQuz` between ticks in the documentation - --> $DIR/doc.rs:126:5 + --> $DIR/doc.rs:138:5 | LL | And BarQuz too. | ^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:127:1 + --> $DIR/doc.rs:139:1 | LL | be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `FooBar` between ticks in the documentation - --> $DIR/doc.rs:132:43 + --> $DIR/doc.rs:144:43 | LL | /** E.g., serialization of an empty list: FooBar | ^^^^^^ error: you should put `BarQuz` between ticks in the documentation - --> $DIR/doc.rs:137:5 + --> $DIR/doc.rs:149:5 | LL | And BarQuz too. | ^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:138:1 + --> $DIR/doc.rs:150:1 | LL | be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:149:5 + --> $DIR/doc.rs:161:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> $DIR/doc.rs:176:13 + --> $DIR/doc.rs:188:13 | LL | /// Not ok: http://www.unicode.org | ^^^^^^^^^^^^^^^^^^^^^^ error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> $DIR/doc.rs:177:13 + --> $DIR/doc.rs:189:13 | LL | /// Not ok: https://www.unicode.org | ^^^^^^^^^^^^^^^^^^^^^^^ error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> $DIR/doc.rs:178:13 + --> $DIR/doc.rs:190:13 | LL | /// Not ok: http://www.unicode.org/ | ^^^^^^^^^^^^^^^^^^^^^^ error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> $DIR/doc.rs:179:13 + --> $DIR/doc.rs:191:13 | LL | /// Not ok: http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: you should put `mycrate::Collection` between ticks in the documentation - --> $DIR/doc.rs:182:22 + --> $DIR/doc.rs:194:22 | LL | /// An iterator over mycrate::Collection's values. | ^^^^^^^^^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From 913c71018ca0adfd8c68677d6c4cf5a8acb44de8 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Wed, 24 Feb 2021 23:41:32 +0100 Subject: upper_case_acronyms: add io-toml tests and bless previous tests --- clippy_lints/src/upper_case_acronyms.rs | 4 +- .../toml_unknown_key/conf_unknown_key.stderr | 2 +- .../upper_case_acronyms_aggressive/clippy.toml | 1 + .../upper_case_acronyms.rs | 22 +++++++ .../upper_case_acronyms.stderr | 70 ++++++++++++++++++++++ tests/ui/upper_case_acronyms.rs | 7 ++- tests/ui/upper_case_acronyms.stderr | 24 +------- 7 files changed, 103 insertions(+), 27 deletions(-) create mode 100644 tests/ui-toml/upper_case_acronyms_aggressive/clippy.toml create mode 100644 tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.rs create mode 100644 tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.stderr (limited to 'tests') diff --git a/clippy_lints/src/upper_case_acronyms.rs b/clippy_lints/src/upper_case_acronyms.rs index 6995fbce72c..0470e1dbbb8 100644 --- a/clippy_lints/src/upper_case_acronyms.rs +++ b/clippy_lints/src/upper_case_acronyms.rs @@ -16,7 +16,7 @@ declare_clippy_lint! { /// for more. /// /// By default, the lint only triggers on fully-capitalized names. - /// You can use the `upper_case_acronyms_aggressive: true` config option to enable linting + /// You can use the `upper-case-acronyms-aggressive: true` config option to enable linting /// on all camel case names /// /// **Known problems:** When two acronyms are contiguous, the lint can't tell where @@ -82,7 +82,7 @@ fn check_ident(cx: &EarlyContext<'_>, ident: &Ident, be_aggressive: bool) { // (and don't warn) if (ident.chars().all(|c| c.is_ascii_uppercase()) && ident.len() > 2) // otherwise, warn if we have SOmeTHING lIKE THIs but only warn with the aggressive - // upper_case_acronyms_aggressive config option enabled + // upper-case-acronyms-aggressive config option enabled || (be_aggressive && ident != &corrected) { span_lint_and_sugg( 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 7ccd0b54845..d83080b69f5 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 `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`, `unreadable-literal-lint-fractions`, `cargo-ignore-publish`, `third-party` at line 5 column 1 +error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `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`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `third-party` at line 5 column 1 error: aborting due to previous error diff --git a/tests/ui-toml/upper_case_acronyms_aggressive/clippy.toml b/tests/ui-toml/upper_case_acronyms_aggressive/clippy.toml new file mode 100644 index 00000000000..cc94ec53e13 --- /dev/null +++ b/tests/ui-toml/upper_case_acronyms_aggressive/clippy.toml @@ -0,0 +1 @@ +upper-case-acronyms-aggressive = true diff --git a/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.rs b/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.rs new file mode 100644 index 00000000000..fdf8905f812 --- /dev/null +++ b/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.rs @@ -0,0 +1,22 @@ +#![warn(clippy::upper_case_acronyms)] + +struct HTTPResponse; // not linted by default, but with cfg option + +struct CString; // not linted + +enum Flags { + NS, // not linted + CWR, + ECE, + URG, + ACK, + PSH, + RST, + SYN, + FIN, +} + +struct GCCLLVMSomething; // linted with cfg option, beware that lint suggests `GccllvmSomething` instead of + // `GccLlvmSomething` + +fn main() {} diff --git a/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.stderr b/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.stderr new file mode 100644 index 00000000000..1cc59dc45f2 --- /dev/null +++ b/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.stderr @@ -0,0 +1,70 @@ +error: name `HTTPResponse` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:3:8 + | +LL | struct HTTPResponse; // not linted by default, but with cfg option + | ^^^^^^^^^^^^ help: consider making the acronym lowercase, except the initial letter: `HttpResponse` + | + = note: `-D clippy::upper-case-acronyms` implied by `-D warnings` + +error: name `NS` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:8:5 + | +LL | NS, // not linted + | ^^ help: consider making the acronym lowercase, except the initial letter (notice the capitalization): `Ns` + +error: name `CWR` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:9:5 + | +LL | CWR, + | ^^^ help: consider making the acronym lowercase, except the initial letter: `Cwr` + +error: name `ECE` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:10:5 + | +LL | ECE, + | ^^^ help: consider making the acronym lowercase, except the initial letter: `Ece` + +error: name `URG` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:11:5 + | +LL | URG, + | ^^^ help: consider making the acronym lowercase, except the initial letter: `Urg` + +error: name `ACK` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:12:5 + | +LL | ACK, + | ^^^ help: consider making the acronym lowercase, except the initial letter (notice the capitalization): `Ack` + +error: name `PSH` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:13:5 + | +LL | PSH, + | ^^^ help: consider making the acronym lowercase, except the initial letter: `Psh` + +error: name `RST` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:14:5 + | +LL | RST, + | ^^^ help: consider making the acronym lowercase, except the initial letter: `Rst` + +error: name `SYN` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:15:5 + | +LL | SYN, + | ^^^ help: consider making the acronym lowercase, except the initial letter: `Syn` + +error: name `FIN` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:16:5 + | +LL | FIN, + | ^^^ help: consider making the acronym lowercase, except the initial letter: `Fin` + +error: name `GCCLLVMSomething` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:19:8 + | +LL | struct GCCLLVMSomething; // linted with cfg option, beware that lint suggests `GccllvmSomething` instead of + | ^^^^^^^^^^^^^^^^ help: consider making the acronym lowercase, except the initial letter: `GccllvmSomething` + +error: aborting due to 11 previous errors + diff --git a/tests/ui/upper_case_acronyms.rs b/tests/ui/upper_case_acronyms.rs index af0b5776348..fdf8905f812 100644 --- a/tests/ui/upper_case_acronyms.rs +++ b/tests/ui/upper_case_acronyms.rs @@ -1,11 +1,11 @@ #![warn(clippy::upper_case_acronyms)] -struct HTTPResponse; // linted +struct HTTPResponse; // not linted by default, but with cfg option struct CString; // not linted enum Flags { - NS, // linted + NS, // not linted CWR, ECE, URG, @@ -16,6 +16,7 @@ enum Flags { FIN, } -struct GCCLLVMSomething; // linted, beware that lint suggests `GccllvmSomething` instead of `GccLlvmSomething` +struct GCCLLVMSomething; // linted with cfg option, beware that lint suggests `GccllvmSomething` instead of + // `GccLlvmSomething` fn main() {} diff --git a/tests/ui/upper_case_acronyms.stderr b/tests/ui/upper_case_acronyms.stderr index 2065fe10bb1..bbe38991e52 100644 --- a/tests/ui/upper_case_acronyms.stderr +++ b/tests/ui/upper_case_acronyms.stderr @@ -1,22 +1,10 @@ -error: name `HTTPResponse` contains a capitalized acronym - --> $DIR/upper_case_acronyms.rs:3:8 - | -LL | struct HTTPResponse; // linted - | ^^^^^^^^^^^^ help: consider making the acronym lowercase, except the initial letter: `HttpResponse` - | - = note: `-D clippy::upper-case-acronyms` implied by `-D warnings` - -error: name `NS` contains a capitalized acronym - --> $DIR/upper_case_acronyms.rs:8:5 - | -LL | NS, // linted - | ^^ help: consider making the acronym lowercase, except the initial letter (notice the capitalization): `Ns` - error: name `CWR` contains a capitalized acronym --> $DIR/upper_case_acronyms.rs:9:5 | LL | CWR, | ^^^ help: consider making the acronym lowercase, except the initial letter: `Cwr` + | + = note: `-D clippy::upper-case-acronyms` implied by `-D warnings` error: name `ECE` contains a capitalized acronym --> $DIR/upper_case_acronyms.rs:10:5 @@ -60,11 +48,5 @@ error: name `FIN` contains a capitalized acronym LL | FIN, | ^^^ help: consider making the acronym lowercase, except the initial letter: `Fin` -error: name `GCCLLVMSomething` contains a capitalized acronym - --> $DIR/upper_case_acronyms.rs:19:8 - | -LL | struct GCCLLVMSomething; // linted, beware that lint suggests `GccllvmSomething` instead of `GccLlvmSomething` - | ^^^^^^^^^^^^^^^^ help: consider making the acronym lowercase, except the initial letter: `GccllvmSomething` - -error: aborting due to 11 previous errors +error: aborting due to 8 previous errors -- cgit 1.4.1-3-g733a5 From 2f0e9f7d3afb3cb169e58d65261f04215b017561 Mon Sep 17 00:00:00 2001 From: Mateusz Gacek <96mateusz.gacek@gmail.com> Date: Wed, 24 Feb 2021 21:06:26 +0100 Subject: or_fun_call: fix suggestion for `or_insert(vec![])` Applies for `std::collections::hash_map::Entry` and `std::collections::btree_map::Entry` Example: Previously, for the following code: `let _ = hash_map.entry("test".to_owned()).or_insert(vec![]);` clippy would suggest to use: `or_insert_with(vec![])`, which causes a compiler error (E0277). Now clippy suggests: `or_insert_with(Vec::new)` --- clippy_lints/src/methods/mod.rs | 16 +++++++++++++++- tests/ui/or_fun_call.fixed | 6 ++++++ tests/ui/or_fun_call.rs | 6 ++++++ tests/ui/or_fun_call.stderr | 26 +++++++++++++++++++------- 4 files changed, 46 insertions(+), 8 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 433f513b1a8..26878d5bba9 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2005,12 +2005,26 @@ fn lint_or_fun_call<'tcx>( if poss.contains(&name); then { + let macro_expanded_snipped; let sugg: Cow<'_, str> = { let (snippet_span, use_lambda) = match (fn_has_arguments, fun_span) { (false, Some(fun_span)) => (fun_span, false), _ => (arg.span, true), }; - let snippet = snippet_with_macro_callsite(cx, snippet_span, ".."); + let snippet = { + let not_macro_argument_snippet = snippet_with_macro_callsite(cx, snippet_span, ".."); + if not_macro_argument_snippet == "vec![]" { + macro_expanded_snipped = snippet(cx, snippet_span, ".."); + match macro_expanded_snipped.strip_prefix("$crate::vec::") { + Some(stripped) => Cow::from(stripped), + None => macro_expanded_snipped + } + } + else { + not_macro_argument_snippet + } + }; + if use_lambda { let l_arg = if fn_has_arguments { "_" } else { "" }; format!("|{}| {}", l_arg, snippet).into() diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 2a63318c8c7..64347cae5da 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -62,9 +62,15 @@ fn or_fun_call() { let mut map = HashMap::::new(); map.entry(42).or_insert_with(String::new); + let mut map_vec = HashMap::>::new(); + map_vec.entry(42).or_insert_with(Vec::new); + let mut btree = BTreeMap::::new(); btree.entry(42).or_insert_with(String::new); + let mut btree_vec = BTreeMap::>::new(); + btree_vec.entry(42).or_insert_with(Vec::new); + let stringy = Some(String::from("")); let _ = stringy.unwrap_or_else(|| "".to_owned()); diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 026ef437caa..7faab0017b2 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -62,9 +62,15 @@ fn or_fun_call() { let mut map = HashMap::::new(); map.entry(42).or_insert(String::new()); + let mut map_vec = HashMap::>::new(); + map_vec.entry(42).or_insert(vec![]); + let mut btree = BTreeMap::::new(); btree.entry(42).or_insert(String::new()); + let mut btree_vec = BTreeMap::>::new(); + btree_vec.entry(42).or_insert(vec![]); + let stringy = Some(String::from("")); let _ = stringy.unwrap_or("".to_owned()); diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index fb8bf339828..1e2bfd490e0 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -67,40 +67,52 @@ LL | map.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)` error: use of `or_insert` followed by a function call - --> $DIR/or_fun_call.rs:66:21 + --> $DIR/or_fun_call.rs:66:23 + | +LL | map_vec.entry(42).or_insert(vec![]); + | ^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(Vec::new)` + +error: use of `or_insert` followed by a function call + --> $DIR/or_fun_call.rs:69:21 | LL | btree.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)` +error: use of `or_insert` followed by a function call + --> $DIR/or_fun_call.rs:72:25 + | +LL | btree_vec.entry(42).or_insert(vec![]); + | ^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(Vec::new)` + error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:69:21 + --> $DIR/or_fun_call.rs:75:21 | LL | let _ = stringy.unwrap_or("".to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "".to_owned())` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:77:21 + --> $DIR/or_fun_call.rs:83:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| map[&1])` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:79:21 + --> $DIR/or_fun_call.rs:85:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| map[&1])` error: use of `or` followed by a function call - --> $DIR/or_fun_call.rs:103:35 + --> $DIR/or_fun_call.rs:109:35 | LL | let _ = Some("a".to_string()).or(Some("b".to_string())); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some("b".to_string()))` error: use of `or` followed by a function call - --> $DIR/or_fun_call.rs:107:10 + --> $DIR/or_fun_call.rs:113:10 | LL | .or(Some(Bar(b, Duration::from_secs(2)))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some(Bar(b, Duration::from_secs(2))))` -error: aborting due to 17 previous errors +error: aborting due to 19 previous errors -- cgit 1.4.1-3-g733a5 From cbe6eec3e67347af130769ad4a3bd2168997b411 Mon Sep 17 00:00:00 2001 From: Marc Dominik Migge Date: Thu, 25 Feb 2021 22:03:11 +0100 Subject: Add original test case from issue --- tests/ui/unit_arg.rs | 21 +++++++++++++++++++++ tests/ui/unit_arg.stderr | 20 ++++++++++---------- 2 files changed, 31 insertions(+), 10 deletions(-) (limited to 'tests') diff --git a/tests/ui/unit_arg.rs b/tests/ui/unit_arg.rs index 5ea2e5d65c5..938cc3c7859 100644 --- a/tests/ui/unit_arg.rs +++ b/tests/ui/unit_arg.rs @@ -31,6 +31,26 @@ fn baz(t: T) { foo(t); } +trait Tr { + type Args; + fn do_it(args: Self::Args); +} + +struct A; +impl Tr for A { + type Args = (); + fn do_it(_: Self::Args) {} +} + +struct B; +impl Tr for B { + type Args = ::Args; + + fn do_it(args: Self::Args) { + A::do_it(args) + } +} + fn bad() { foo({ 1; @@ -78,6 +98,7 @@ fn ok() { let named_unit_arg = (); foo(named_unit_arg); baz(()); + B::do_it(()); } fn question_mark() -> Result<(), ()> { diff --git a/tests/ui/unit_arg.stderr b/tests/ui/unit_arg.stderr index b3fe9addb62..354fd51cd6b 100644 --- a/tests/ui/unit_arg.stderr +++ b/tests/ui/unit_arg.stderr @@ -1,5 +1,5 @@ error: passing a unit value to a function - --> $DIR/unit_arg.rs:35:5 + --> $DIR/unit_arg.rs:55:5 | LL | / foo({ LL | | 1; @@ -20,7 +20,7 @@ LL | foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:38:5 + --> $DIR/unit_arg.rs:58:5 | LL | foo(foo(1)); | ^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:39:5 + --> $DIR/unit_arg.rs:59:5 | LL | / foo({ LL | | foo(1); @@ -54,7 +54,7 @@ LL | foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:44:5 + --> $DIR/unit_arg.rs:64:5 | LL | / b.bar({ LL | | 1; @@ -74,7 +74,7 @@ LL | b.bar(()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:47:5 + --> $DIR/unit_arg.rs:67:5 | LL | taking_multiple_units(foo(0), foo(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -87,7 +87,7 @@ LL | taking_multiple_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:48:5 + --> $DIR/unit_arg.rs:68:5 | LL | / taking_multiple_units(foo(0), { LL | | foo(1); @@ -110,7 +110,7 @@ LL | taking_multiple_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:52:5 + --> $DIR/unit_arg.rs:72:5 | LL | / taking_multiple_units( LL | | { @@ -140,7 +140,7 @@ LL | foo(2); ... error: passing a unit value to a function - --> $DIR/unit_arg.rs:63:13 + --> $DIR/unit_arg.rs:83:13 | LL | None.or(Some(foo(2))); | ^^^^^^^^^^^^ @@ -154,7 +154,7 @@ LL | }); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:66:5 + --> $DIR/unit_arg.rs:86:5 | LL | foo(foo(())); | ^^^^^^^^^^^^ @@ -166,7 +166,7 @@ LL | foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:102:5 + --> $DIR/unit_arg.rs:123:5 | LL | Some(foo(1)) | ^^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From aef6dc23eed7cc20bff2fe7cdea058cd7c1346f7 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 26 Feb 2021 12:12:33 -0600 Subject: Add package arguments to dogfood test This is necessary after migrating to RUSTC_WORKSPACE_WRAPPER. --- tests/dogfood.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'tests') diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 052223d6d6f..8fe48a67beb 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -26,6 +26,7 @@ fn dogfood_clippy() { .arg("clippy-preview") .arg("--all-targets") .arg("--all-features") + .args(&["-p", "clippy_lints", "-p", "clippy_utils", "-p", "rustc_tools_util"]) .arg("--") .args(&["-D", "clippy::all"]) .args(&["-D", "clippy::pedantic"]) -- cgit 1.4.1-3-g733a5 From d71ed26fd24d8121fcb08f7d4a5e00ce62e6e1fd Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 26 Feb 2021 12:30:43 -0600 Subject: Revert "Fix versioncheck test" This reverts commit 1e7b1ccb2a05f80ae0a580401e7565fb1c0a4917. --- tests/versioncheck.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/tests/versioncheck.rs b/tests/versioncheck.rs index 1c954c57a85..aadd2c1fb7f 100644 --- a/tests/versioncheck.rs +++ b/tests/versioncheck.rs @@ -9,15 +9,15 @@ fn check_that_clippy_lints_and_clippy_utils_have_the_same_version_as_clippy() { .expect("could not obtain cargo metadata"); for krate in &["clippy_lints", "clippy_utils"] { - let krate_meta = clippy_meta - .packages - .iter() - .find(|package| package.name == *krate) + let krate_meta = cargo_metadata::MetadataCommand::new() + .current_dir(std::env::current_dir().unwrap().join(krate)) + .no_deps() + .exec() .expect("could not obtain cargo metadata"); - assert_eq!(krate_meta.version, clippy_meta.packages[0].version); + assert_eq!(krate_meta.packages[0].version, clippy_meta.packages[0].version); for package in &clippy_meta.packages[0].dependencies { if package.name == *krate { - assert!(package.req.matches(&krate_meta.version)); + assert!(package.req.matches(&krate_meta.packages[0].version)); break; } } -- cgit 1.4.1-3-g733a5 From 9dba6a9fdeefba74167953fc14610faec387a9c9 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Fri, 26 Feb 2021 21:22:30 +0100 Subject: upper_case_acronyms: don't warn on public items Fixes #6803 changelog: upper_case_acronyms: ignore public items --- clippy_lints/src/upper_case_acronyms.rs | 4 +++- tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.rs | 5 +++++ tests/ui/upper_case_acronyms.rs | 4 ++++ 3 files changed, 12 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/clippy_lints/src/upper_case_acronyms.rs b/clippy_lints/src/upper_case_acronyms.rs index 0470e1dbbb8..fea3abaec93 100644 --- a/clippy_lints/src/upper_case_acronyms.rs +++ b/clippy_lints/src/upper_case_acronyms.rs @@ -1,7 +1,7 @@ use crate::utils::span_lint_and_sugg; use if_chain::if_chain; use itertools::Itertools; -use rustc_ast::ast::{Item, ItemKind, Variant}; +use rustc_ast::ast::{Item, ItemKind, Variant, VisibilityKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -105,6 +105,8 @@ impl EarlyLintPass for UpperCaseAcronyms { it.kind, ItemKind::TyAlias(..) | ItemKind::Enum(..) | ItemKind::Struct(..) | ItemKind::Trait(..) ); + // do not lint public items + if !matches!(it.vis.kind, VisibilityKind::Public); then { check_ident(cx, &it.ident, self.upper_case_acronyms_aggressive); } diff --git a/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.rs b/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.rs index fdf8905f812..5d54e083ef0 100644 --- a/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.rs +++ b/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.rs @@ -19,4 +19,9 @@ enum Flags { struct GCCLLVMSomething; // linted with cfg option, beware that lint suggests `GccllvmSomething` instead of // `GccLlvmSomething` +// don't warn on public items +pub struct MIXEDCapital; + +pub struct FULLCAPITAL; + fn main() {} diff --git a/tests/ui/upper_case_acronyms.rs b/tests/ui/upper_case_acronyms.rs index fdf8905f812..eea125500a2 100644 --- a/tests/ui/upper_case_acronyms.rs +++ b/tests/ui/upper_case_acronyms.rs @@ -19,4 +19,8 @@ enum Flags { struct GCCLLVMSomething; // linted with cfg option, beware that lint suggests `GccllvmSomething` instead of // `GccLlvmSomething` +// public items must not be linted +pub struct NOWARNINGHERE; +pub struct ALSONoWarningHERE; + fn main() {} -- cgit 1.4.1-3-g733a5 From ef87e58993ace9e607a43b5581d40b4c0a2e71f6 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 26 Feb 2021 11:50:12 -0500 Subject: Fix `manual_map`: don't lint when partially moved values are used. Fix `manual_map`: don't lint when `return`, `break`, and `continue` are used. --- clippy_lints/src/manual_map.rs | 54 ++++++++++++++++++++++++++++++++++++--- clippy_utils/src/lib.rs | 12 +++++++++ tests/ui/manual_map_option.fixed | 45 +++++++++++++++++++++++++++++++- tests/ui/manual_map_option.rs | 45 +++++++++++++++++++++++++++++++- tests/ui/manual_map_option.stderr | 34 ++++++++++++------------ 5 files changed, 168 insertions(+), 22 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs index 78dc0d54f28..bc6544ee79e 100644 --- a/clippy_lints/src/manual_map.rs +++ b/clippy_lints/src/manual_map.rs @@ -2,13 +2,17 @@ use crate::{ map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF, utils::{ - is_allowed, is_type_diagnostic_item, match_def_path, match_var, paths, peel_hir_expr_refs, - peel_mid_ty_refs_is_mutable, snippet_with_applicability, span_lint_and_sugg, + can_partially_move_ty, is_allowed, is_type_diagnostic_item, match_def_path, match_var, paths, + peel_hir_expr_refs, peel_mid_ty_refs_is_mutable, snippet_with_applicability, span_lint_and_sugg, }, }; use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::Applicability; -use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, Mutability, Pat, PatKind, QPath}; +use rustc_hir::{ + def::Res, + intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor}, + Arm, BindingAnnotation, Block, Expr, ExprKind, Mutability, Pat, PatKind, Path, QPath, +}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -99,6 +103,10 @@ impl LateLintPass<'_> for ManualMap { return; } + if !can_move_expr_to_closure(cx, some_expr) { + return; + } + // Determine which binding mode to use. let explicit_ref = some_pat.contains_explicit_ref_binding(); let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then(|| ty_mutability)); @@ -171,6 +179,46 @@ impl LateLintPass<'_> for ManualMap { } } +// Checks if the expression can be moved into a closure as is. +fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { + struct V<'cx, 'tcx> { + cx: &'cx LateContext<'tcx>, + make_closure: bool, + } + impl Visitor<'tcx> for V<'_, 'tcx> { + type Map = ErasedMap<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + + fn visit_expr(&mut self, e: &'tcx Expr<'_>) { + match e.kind { + ExprKind::Break(..) | ExprKind::Continue(_) | ExprKind::Ret(_) => { + self.make_closure = false; + }, + // Accessing a field of a local value can only be done if the type isn't + // partially moved. + ExprKind::Field(base_expr, _) + if matches!( + base_expr.kind, + ExprKind::Path(QPath::Resolved(_, Path { res: Res::Local(_), .. })) + ) && can_partially_move_ty(self.cx, self.cx.typeck_results().expr_ty(base_expr)) => + { + // TODO: check if the local has been partially moved. Assume it has for now. + self.make_closure = false; + return; + } + _ => (), + }; + walk_expr(self, e); + } + } + + let mut v = V { cx, make_closure: true }; + v.visit_expr(expr); + v.make_closure +} + // Checks whether the expression could be passed as a function, or whether a closure is needed. // Returns the function to be passed to `map` if it exists. fn can_pass_as_func(cx: &LateContext<'tcx>, binding: Ident, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 8e5cf5dc654..05523943511 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -456,6 +456,18 @@ pub fn has_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { } } +/// Checks whether a type can be partially moved. +pub fn can_partially_move_ty(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + if has_drop(cx, ty) || is_copy(cx, ty) { + return false; + } + match ty.kind() { + ty::Param(_) => false, + ty::Adt(def, subs) => def.all_fields().any(|f| !is_copy(cx, f.ty(cx.tcx, subs))), + _ => true, + } +} + /// Returns the method names and argument list of nested method call expressions that make up /// `expr`. method/span lists are sorted with the most recent call first. pub fn method_calls<'tcx>( diff --git a/tests/ui/manual_map_option.fixed b/tests/ui/manual_map_option.fixed index 19350906758..428aac43940 100644 --- a/tests/ui/manual_map_option.fixed +++ b/tests/ui/manual_map_option.fixed @@ -1,7 +1,13 @@ // run-rustfix #![warn(clippy::manual_map)] -#![allow(clippy::no_effect, clippy::map_identity, clippy::unit_arg, clippy::match_ref_pats)] +#![allow( + clippy::no_effect, + clippy::map_identity, + clippy::unit_arg, + clippy::match_ref_pats, + dead_code +)] fn main() { Some(0).map(|_| 2); @@ -67,4 +73,41 @@ fn main() { Some(Some((x, 1))) => Some(x), _ => None, }; + + // #6795 + fn f1() -> Result<(), ()> { + let _ = match Some(Ok(())) { + Some(x) => Some(x?), + None => None, + }; + Ok(()) + } + + for &x in Some(Some(true)).iter() { + let _ = match x { + Some(x) => Some(if x { continue } else { x }), + None => None, + }; + } + + // #6797 + let x1 = (Some(String::new()), 0); + let x2 = x1.0; + match x2 { + Some(x) => Some((x, x1.1)), + None => None, + }; + + struct S1 { + x: Option, + y: u32, + } + impl S1 { + fn f(self) -> Option<(String, u32)> { + match self.x { + Some(x) => Some((x, self.y)), + None => None, + } + } + } } diff --git a/tests/ui/manual_map_option.rs b/tests/ui/manual_map_option.rs index 8b8187db0a9..0f4a5bb2eb7 100644 --- a/tests/ui/manual_map_option.rs +++ b/tests/ui/manual_map_option.rs @@ -1,7 +1,13 @@ // run-rustfix #![warn(clippy::manual_map)] -#![allow(clippy::no_effect, clippy::map_identity, clippy::unit_arg, clippy::match_ref_pats)] +#![allow( + clippy::no_effect, + clippy::map_identity, + clippy::unit_arg, + clippy::match_ref_pats, + dead_code +)] fn main() { match Some(0) { @@ -119,4 +125,41 @@ fn main() { Some(Some((x, 1))) => Some(x), _ => None, }; + + // #6795 + fn f1() -> Result<(), ()> { + let _ = match Some(Ok(())) { + Some(x) => Some(x?), + None => None, + }; + Ok(()) + } + + for &x in Some(Some(true)).iter() { + let _ = match x { + Some(x) => Some(if x { continue } else { x }), + None => None, + }; + } + + // #6797 + let x1 = (Some(String::new()), 0); + let x2 = x1.0; + match x2 { + Some(x) => Some((x, x1.1)), + None => None, + }; + + struct S1 { + x: Option, + y: u32, + } + impl S1 { + fn f(self) -> Option<(String, u32)> { + match self.x { + Some(x) => Some((x, self.y)), + None => None, + } + } + } } diff --git a/tests/ui/manual_map_option.stderr b/tests/ui/manual_map_option.stderr index 210a30d05d4..49a51737784 100644 --- a/tests/ui/manual_map_option.stderr +++ b/tests/ui/manual_map_option.stderr @@ -1,5 +1,5 @@ error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:7:5 + --> $DIR/manual_map_option.rs:13:5 | LL | / match Some(0) { LL | | Some(_) => Some(2), @@ -10,7 +10,7 @@ LL | | }; = note: `-D clippy::manual-map` implied by `-D warnings` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:12:5 + --> $DIR/manual_map_option.rs:18:5 | LL | / match Some(0) { LL | | Some(x) => Some(x + 1), @@ -19,7 +19,7 @@ LL | | }; | |_____^ help: try this: `Some(0).map(|x| x + 1)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:17:5 + --> $DIR/manual_map_option.rs:23:5 | LL | / match Some("") { LL | | Some(x) => Some(x.is_empty()), @@ -28,7 +28,7 @@ LL | | }; | |_____^ help: try this: `Some("").map(|x| x.is_empty())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:22:5 + --> $DIR/manual_map_option.rs:28:5 | LL | / if let Some(x) = Some(0) { LL | | Some(!x) @@ -38,7 +38,7 @@ LL | | }; | |_____^ help: try this: `Some(0).map(|x| !x)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:29:5 + --> $DIR/manual_map_option.rs:35:5 | LL | / match Some(0) { LL | | Some(x) => { Some(std::convert::identity(x)) } @@ -47,7 +47,7 @@ LL | | }; | |_____^ help: try this: `Some(0).map(std::convert::identity)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:34:5 + --> $DIR/manual_map_option.rs:40:5 | LL | / match Some(&String::new()) { LL | | Some(x) => Some(str::len(x)), @@ -56,7 +56,7 @@ LL | | }; | |_____^ help: try this: `Some(&String::new()).map(|x| str::len(x))` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:44:5 + --> $DIR/manual_map_option.rs:50:5 | LL | / match &Some([0, 1]) { LL | | Some(x) => Some(x[0]), @@ -65,7 +65,7 @@ LL | | }; | |_____^ help: try this: `Some([0, 1]).as_ref().map(|x| x[0])` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:49:5 + --> $DIR/manual_map_option.rs:55:5 | LL | / match &Some(0) { LL | | &Some(x) => Some(x * 2), @@ -74,7 +74,7 @@ LL | | }; | |_____^ help: try this: `Some(0).map(|x| x * 2)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:54:5 + --> $DIR/manual_map_option.rs:60:5 | LL | / match Some(String::new()) { LL | | Some(ref x) => Some(x.is_empty()), @@ -83,7 +83,7 @@ LL | | }; | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.is_empty())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:59:5 + --> $DIR/manual_map_option.rs:65:5 | LL | / match &&Some(String::new()) { LL | | Some(x) => Some(x.len()), @@ -92,7 +92,7 @@ LL | | }; | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.len())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:64:5 + --> $DIR/manual_map_option.rs:70:5 | LL | / match &&Some(0) { LL | | &&Some(x) => Some(x + x), @@ -101,7 +101,7 @@ LL | | }; | |_____^ help: try this: `Some(0).map(|x| x + x)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:77:9 + --> $DIR/manual_map_option.rs:83:9 | LL | / match &mut Some(String::new()) { LL | | Some(x) => Some(x.push_str("")), @@ -110,7 +110,7 @@ LL | | }; | |_________^ help: try this: `Some(String::new()).as_mut().map(|x| x.push_str(""))` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:83:5 + --> $DIR/manual_map_option.rs:89:5 | LL | / match &mut Some(String::new()) { LL | | Some(ref x) => Some(x.len()), @@ -119,7 +119,7 @@ LL | | }; | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.len())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:88:5 + --> $DIR/manual_map_option.rs:94:5 | LL | / match &mut &Some(String::new()) { LL | | Some(x) => Some(x.is_empty()), @@ -128,7 +128,7 @@ LL | | }; | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.is_empty())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:93:5 + --> $DIR/manual_map_option.rs:99:5 | LL | / match Some((0, 1, 2)) { LL | | Some((x, y, z)) => Some(x + y + z), @@ -137,7 +137,7 @@ LL | | }; | |_____^ help: try this: `Some((0, 1, 2)).map(|(x, y, z)| x + y + z)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:98:5 + --> $DIR/manual_map_option.rs:104:5 | LL | / match Some([1, 2, 3]) { LL | | Some([first, ..]) => Some(first), @@ -146,7 +146,7 @@ LL | | }; | |_____^ help: try this: `Some([1, 2, 3]).map(|[first, ..]| first)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:103:5 + --> $DIR/manual_map_option.rs:109:5 | LL | / match &Some((String::new(), "test")) { LL | | Some((x, y)) => Some((y, x)), -- cgit 1.4.1-3-g733a5 From 3d3cfd3754d616db959015eb7bd3c6238961320a Mon Sep 17 00:00:00 2001 From: Andrea Nall Date: Fri, 12 Feb 2021 04:27:04 +0000 Subject: added new lint `implicit_clone` --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/methods/implicit_clone.rs | 32 ++++++++ clippy_lints/src/methods/mod.rs | 32 ++++++++ clippy_utils/src/lib.rs | 16 ++++ tests/ui/cmp_owned/without_suggestion.rs | 1 + tests/ui/cmp_owned/without_suggestion.stderr | 6 +- tests/ui/implicit_clone.rs | 108 +++++++++++++++++++++++++++ tests/ui/implicit_clone.stderr | 64 ++++++++++++++++ tests/ui/redundant_clone.fixed | 1 + tests/ui/redundant_clone.rs | 1 + tests/ui/redundant_clone.stderr | 56 +++++++------- 12 files changed, 289 insertions(+), 31 deletions(-) create mode 100644 clippy_lints/src/methods/implicit_clone.rs create mode 100644 tests/ui/implicit_clone.rs create mode 100644 tests/ui/implicit_clone.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index d96c74b6e41..687f7447202 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2104,6 +2104,7 @@ Released 2018-09-13 [`if_not_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_not_else [`if_same_then_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_same_then_else [`ifs_same_cond`]: https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond +[`implicit_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_clone [`implicit_hasher`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_hasher [`implicit_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_return [`implicit_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_sub diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index faec9ec31f3..fa8f03eb445 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -769,6 +769,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::FLAT_MAP_IDENTITY, &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, @@ -1380,6 +1381,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&matches::SINGLE_MATCH_ELSE), LintId::of(&methods::FILTER_MAP), LintId::of(&methods::FILTER_MAP_NEXT), + LintId::of(&methods::IMPLICIT_CLONE), LintId::of(&methods::INEFFICIENT_TO_STRING), LintId::of(&methods::MAP_FLATTEN), LintId::of(&methods::MAP_UNWRAP_OR), diff --git a/clippy_lints/src/methods/implicit_clone.rs b/clippy_lints/src/methods/implicit_clone.rs new file mode 100644 index 00000000000..a769493d11d --- /dev/null +++ b/clippy_lints/src/methods/implicit_clone.rs @@ -0,0 +1,32 @@ +use crate::utils::span_lint_and_sugg; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::ExprKind; +use rustc_lint::LateContext; +use rustc_middle::ty::TyS; +use rustc_span::symbol::Symbol; + +use super::IMPLICIT_CLONE; +use clippy_utils::is_diagnostic_assoc_item; + +pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, trait_diagnostic: Symbol) { + if_chain! { + if let ExprKind::MethodCall(method_path, _, [arg], _) = &expr.kind; + let return_type = cx.typeck_results().expr_ty(&expr); + let input_type = cx.typeck_results().expr_ty(arg).peel_refs(); + if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); + if let Some(ty_name) = input_type.ty_adt_def().map(|adt_def| cx.tcx.item_name(adt_def.did)); + if TyS::same_type(return_type, input_type); + if is_diagnostic_assoc_item(cx, expr_def_id, trait_diagnostic); + then { + span_lint_and_sugg( + cx,IMPLICIT_CLONE,method_path.ident.span, + &format!("implicitly cloning a `{}` by calling `{}` on its dereferenced type", ty_name, method_path.ident.name), + "consider using", + "clone".to_string(), + Applicability::MachineApplicable + ); + } + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 5fb3ae1890d..6f491144435 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1,6 +1,7 @@ mod bind_instead_of_map; mod bytes_nth; mod filter_map_identity; +mod implicit_clone; mod inefficient_to_string; mod inspect_for_each; mod manual_saturating_arithmetic; @@ -1513,6 +1514,32 @@ declare_clippy_lint! { "replace `.bytes().nth()` with `.as_bytes().get()`" } +declare_clippy_lint! { + /// **What it does:** Checks for the usage of `_.to_owned()`, `vec.to_vec()`, or similar when calling `_.clone()` would be clearer. + /// + /// **Why is this bad?** These methods do the same thing as `_.clone()` but may be confusing as + /// to why we are calling `to_vec` on something that is already a `Vec` or calling `to_owned` on something that is already owned. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let a = vec![1, 2, 3]; + /// let b = a.to_vec(); + /// let c = a.to_owned(); + /// ``` + /// Use instead: + /// ```rust + /// let a = vec![1, 2, 3]; + /// let b = a.clone(); + /// let c = a.clone(); + /// ``` + pub IMPLICIT_CLONE, + pedantic, + "implicitly cloning a value by invoking a function on its dereferenced type" +} + pub struct Methods { msrv: Option, } @@ -1579,6 +1606,7 @@ impl_lint_pass!(Methods => [ MAP_COLLECT_RESULT_UNIT, FROM_ITER_INSTEAD_OF_COLLECT, INSPECT_FOR_EACH, + IMPLICIT_CLONE ]); impl<'tcx> LateLintPass<'tcx> for Methods { @@ -1670,6 +1698,10 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["ok_or_else", ..] => unnecessary_lazy_eval::lint(cx, expr, arg_lists[0], "ok_or"), ["collect", "map"] => lint_map_collect(cx, expr, arg_lists[1], arg_lists[0]), ["for_each", "inspect"] => inspect_for_each::lint(cx, expr, method_spans[1]), + ["to_owned", ..] => implicit_clone::check(cx, expr, sym::ToOwned), + ["to_os_string", ..] => implicit_clone::check(cx, expr, sym::OsStr), + ["to_path_buf", ..] => implicit_clone::check(cx, expr, sym::Path), + ["to_vec", ..] => implicit_clone::check(cx, expr, sym::slice), _ => {}, } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 8e5cf5dc654..2380ea4c7bf 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -237,6 +237,22 @@ pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) trt_id.map_or(false, |trt_id| match_def_path(cx, trt_id, path)) } +/// Checks if the method call given in `expr` belongs to a trait or other container with a given +/// diagnostic item +pub fn is_diagnostic_assoc_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool { + cx.tcx + .opt_associated_item(def_id) + .and_then(|associated_item| match associated_item.container { + ty::TraitContainer(assoc_def_id) => Some(assoc_def_id), + ty::ImplContainer(assoc_def_id) => match cx.tcx.type_of(assoc_def_id).kind() { + ty::Adt(adt, _) => Some(adt.did), + ty::Slice(_) => cx.tcx.get_diagnostic_item(sym::slice), // this isn't perfect but it works + _ => None, + }, + }) + .map_or(false, |assoc_def_id| cx.tcx.is_diagnostic_item(diag_item, assoc_def_id)) +} + /// Checks if an expression references a variable of the given name. pub fn match_var(expr: &Expr<'_>, var: Symbol) -> bool { if let ExprKind::Path(QPath::Resolved(None, ref path)) = expr.kind { diff --git a/tests/ui/cmp_owned/without_suggestion.rs b/tests/ui/cmp_owned/without_suggestion.rs index 9ab8795474c..f44a3901fb4 100644 --- a/tests/ui/cmp_owned/without_suggestion.rs +++ b/tests/ui/cmp_owned/without_suggestion.rs @@ -1,4 +1,5 @@ #[allow(clippy::unnecessary_operation)] +#[allow(clippy::implicit_clone)] fn main() { let x = &Baz; diff --git a/tests/ui/cmp_owned/without_suggestion.stderr b/tests/ui/cmp_owned/without_suggestion.stderr index 6e8a5ad2a17..2ea3d8fac0d 100644 --- a/tests/ui/cmp_owned/without_suggestion.stderr +++ b/tests/ui/cmp_owned/without_suggestion.stderr @@ -1,5 +1,5 @@ error: this creates an owned instance just for comparison - --> $DIR/without_suggestion.rs:6:5 + --> $DIR/without_suggestion.rs:7:5 | LL | y.to_owned() == *x; | ^^^^^^^^^^^^^^^^^^ try implementing the comparison without allocating @@ -7,13 +7,13 @@ LL | y.to_owned() == *x; = note: `-D clippy::cmp-owned` implied by `-D warnings` error: this creates an owned instance just for comparison - --> $DIR/without_suggestion.rs:10:5 + --> $DIR/without_suggestion.rs:11:5 | LL | y.to_owned() == **x; | ^^^^^^^^^^^^^^^^^^^ try implementing the comparison without allocating error: this creates an owned instance just for comparison - --> $DIR/without_suggestion.rs:17:9 + --> $DIR/without_suggestion.rs:18:9 | LL | self.to_owned() == *other | ^^^^^^^^^^^^^^^^^^^^^^^^^ try implementing the comparison without allocating diff --git a/tests/ui/implicit_clone.rs b/tests/ui/implicit_clone.rs new file mode 100644 index 00000000000..19101522163 --- /dev/null +++ b/tests/ui/implicit_clone.rs @@ -0,0 +1,108 @@ +#![warn(clippy::implicit_clone)] +#![allow(clippy::redundant_clone)] +use std::borrow::Borrow; +use std::ffi::{OsStr, OsString}; +use std::path::PathBuf; + +fn return_owned_from_slice(slice: &[u32]) -> Vec { + slice.to_owned() +} + +pub fn own_same(v: T) -> T +where + T: ToOwned, +{ + v.to_owned() +} + +pub fn own_same_from_ref(v: &T) -> T +where + T: ToOwned, +{ + v.to_owned() +} + +pub fn own_different(v: T) -> U +where + T: ToOwned, +{ + v.to_owned() +} + +#[derive(Copy, Clone)] +struct Kitten {} +impl Kitten { + // badly named method + fn to_vec(self) -> Kitten { + Kitten {} + } +} +impl Borrow for Kitten { + fn borrow(&self) -> &BorrowedKitten { + static VALUE: BorrowedKitten = BorrowedKitten {}; + &VALUE + } +} + +struct BorrowedKitten {} +impl ToOwned for BorrowedKitten { + type Owned = Kitten; + fn to_owned(&self) -> Kitten { + Kitten {} + } +} + +mod weird { + #[allow(clippy::ptr_arg)] + pub fn to_vec(v: &Vec) -> Vec { + v.clone() + } +} + +fn main() { + let vec = vec![5]; + let _ = return_owned_from_slice(&vec); + let _ = vec.to_owned(); + let _ = vec.to_vec(); + + let vec_ref = &vec; + let _ = return_owned_from_slice(&vec_ref); + let _ = vec_ref.to_owned(); + let _ = vec_ref.to_vec(); + + // we expect no lint for this + let _ = weird::to_vec(&vec); + + // we expect no lints for this + let slice: &[u32] = &[1, 2, 3, 4, 5]; + let _ = return_owned_from_slice(slice); + let _ = slice.to_owned(); + let _ = slice.to_vec(); + + let str = "hello world".to_string(); + let _ = str.to_owned(); + + // testing w/ an arbitrary type + let kitten = Kitten {}; + let _ = kitten.to_owned(); + let _ = own_same_from_ref(&kitten); + // this shouln't lint + let _ = kitten.to_vec(); + + // we expect no lints for this + let borrowed = BorrowedKitten {}; + let _ = borrowed.to_owned(); + + let pathbuf = PathBuf::new(); + let _ = pathbuf.to_owned(); + let _ = pathbuf.to_path_buf(); + + let os_string = OsString::from("foo"); + let _ = os_string.to_owned(); + let _ = os_string.to_os_string(); + + // we expect no lints for this + let os_str = OsStr::new("foo"); + let _ = os_str.to_owned(); + let _ = os_str.to_os_string(); +} diff --git a/tests/ui/implicit_clone.stderr b/tests/ui/implicit_clone.stderr new file mode 100644 index 00000000000..e6f7527b672 --- /dev/null +++ b/tests/ui/implicit_clone.stderr @@ -0,0 +1,64 @@ +error: implicitly cloning a `Vec` by calling `to_owned` on its dereferenced type + --> $DIR/implicit_clone.rs:65:17 + | +LL | let _ = vec.to_owned(); + | ^^^^^^^^ help: consider using: `clone` + | + = note: `-D clippy::implicit-clone` implied by `-D warnings` + +error: implicitly cloning a `Vec` by calling `to_vec` on its dereferenced type + --> $DIR/implicit_clone.rs:66:17 + | +LL | let _ = vec.to_vec(); + | ^^^^^^ help: consider using: `clone` + +error: implicitly cloning a `Vec` by calling `to_owned` on its dereferenced type + --> $DIR/implicit_clone.rs:70:21 + | +LL | let _ = vec_ref.to_owned(); + | ^^^^^^^^ help: consider using: `clone` + +error: implicitly cloning a `Vec` by calling `to_vec` on its dereferenced type + --> $DIR/implicit_clone.rs:71:21 + | +LL | let _ = vec_ref.to_vec(); + | ^^^^^^ help: consider using: `clone` + +error: implicitly cloning a `String` by calling `to_owned` on its dereferenced type + --> $DIR/implicit_clone.rs:83:17 + | +LL | let _ = str.to_owned(); + | ^^^^^^^^ help: consider using: `clone` + +error: implicitly cloning a `Kitten` by calling `to_owned` on its dereferenced type + --> $DIR/implicit_clone.rs:87:20 + | +LL | let _ = kitten.to_owned(); + | ^^^^^^^^ help: consider using: `clone` + +error: implicitly cloning a `PathBuf` by calling `to_owned` on its dereferenced type + --> $DIR/implicit_clone.rs:97:21 + | +LL | let _ = pathbuf.to_owned(); + | ^^^^^^^^ help: consider using: `clone` + +error: implicitly cloning a `PathBuf` by calling `to_path_buf` on its dereferenced type + --> $DIR/implicit_clone.rs:98:21 + | +LL | let _ = pathbuf.to_path_buf(); + | ^^^^^^^^^^^ help: consider using: `clone` + +error: implicitly cloning a `OsString` by calling `to_owned` on its dereferenced type + --> $DIR/implicit_clone.rs:101:23 + | +LL | let _ = os_string.to_owned(); + | ^^^^^^^^ help: consider using: `clone` + +error: implicitly cloning a `OsString` by calling `to_os_string` on its dereferenced type + --> $DIR/implicit_clone.rs:102:23 + | +LL | let _ = os_string.to_os_string(); + | ^^^^^^^^^^^^ help: consider using: `clone` + +error: aborting due to 10 previous errors + diff --git a/tests/ui/redundant_clone.fixed b/tests/ui/redundant_clone.fixed index cdeefda4c23..a5847e37c97 100644 --- a/tests/ui/redundant_clone.fixed +++ b/tests/ui/redundant_clone.fixed @@ -1,6 +1,7 @@ // run-rustfix // rustfix-only-machine-applicable +#![allow(clippy::implicit_clone)] use std::ffi::OsString; use std::path::Path; diff --git a/tests/ui/redundant_clone.rs b/tests/ui/redundant_clone.rs index acb7ffb305f..dab8d7fb1c7 100644 --- a/tests/ui/redundant_clone.rs +++ b/tests/ui/redundant_clone.rs @@ -1,6 +1,7 @@ // run-rustfix // rustfix-only-machine-applicable +#![allow(clippy::implicit_clone)] use std::ffi::OsString; use std::path::Path; diff --git a/tests/ui/redundant_clone.stderr b/tests/ui/redundant_clone.stderr index 89b39254299..87c219316ce 100644 --- a/tests/ui/redundant_clone.stderr +++ b/tests/ui/redundant_clone.stderr @@ -1,168 +1,168 @@ error: redundant clone - --> $DIR/redundant_clone.rs:8:42 + --> $DIR/redundant_clone.rs:9:42 | LL | let _s = ["lorem", "ipsum"].join(" ").to_string(); | ^^^^^^^^^^^^ help: remove this | = note: `-D clippy::redundant-clone` implied by `-D warnings` note: this value is dropped without further use - --> $DIR/redundant_clone.rs:8:14 + --> $DIR/redundant_clone.rs:9:14 | LL | let _s = ["lorem", "ipsum"].join(" ").to_string(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> $DIR/redundant_clone.rs:11:15 + --> $DIR/redundant_clone.rs:12:15 | LL | let _s = s.clone(); | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:11:14 + --> $DIR/redundant_clone.rs:12:14 | LL | let _s = s.clone(); | ^ error: redundant clone - --> $DIR/redundant_clone.rs:14:15 + --> $DIR/redundant_clone.rs:15:15 | LL | let _s = s.to_string(); | ^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:14:14 + --> $DIR/redundant_clone.rs:15:14 | LL | let _s = s.to_string(); | ^ error: redundant clone - --> $DIR/redundant_clone.rs:17:15 + --> $DIR/redundant_clone.rs:18:15 | LL | let _s = s.to_owned(); | ^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:17:14 + --> $DIR/redundant_clone.rs:18:14 | LL | let _s = s.to_owned(); | ^ error: redundant clone - --> $DIR/redundant_clone.rs:19:42 + --> $DIR/redundant_clone.rs:20:42 | LL | let _s = Path::new("/a/b/").join("c").to_owned(); | ^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:19:14 + --> $DIR/redundant_clone.rs:20:14 | LL | let _s = Path::new("/a/b/").join("c").to_owned(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> $DIR/redundant_clone.rs:21:42 + --> $DIR/redundant_clone.rs:22:42 | LL | let _s = Path::new("/a/b/").join("c").to_path_buf(); | ^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:21:14 + --> $DIR/redundant_clone.rs:22:14 | LL | let _s = Path::new("/a/b/").join("c").to_path_buf(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> $DIR/redundant_clone.rs:23:29 + --> $DIR/redundant_clone.rs:24:29 | LL | let _s = OsString::new().to_owned(); | ^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:23:14 + --> $DIR/redundant_clone.rs:24:14 | LL | let _s = OsString::new().to_owned(); | ^^^^^^^^^^^^^^^ error: redundant clone - --> $DIR/redundant_clone.rs:25:29 + --> $DIR/redundant_clone.rs:26:29 | LL | let _s = OsString::new().to_os_string(); | ^^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:25:14 + --> $DIR/redundant_clone.rs:26:14 | LL | let _s = OsString::new().to_os_string(); | ^^^^^^^^^^^^^^^ error: redundant clone - --> $DIR/redundant_clone.rs:32:19 + --> $DIR/redundant_clone.rs:33:19 | LL | let _t = tup.0.clone(); | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:32:14 + --> $DIR/redundant_clone.rs:33:14 | LL | let _t = tup.0.clone(); | ^^^^^ error: redundant clone - --> $DIR/redundant_clone.rs:62:22 + --> $DIR/redundant_clone.rs:63:22 | LL | (a.clone(), a.clone()) | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:62:21 + --> $DIR/redundant_clone.rs:63:21 | LL | (a.clone(), a.clone()) | ^ error: redundant clone - --> $DIR/redundant_clone.rs:122:15 + --> $DIR/redundant_clone.rs:123:15 | LL | let _s = s.clone(); | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:122:14 + --> $DIR/redundant_clone.rs:123:14 | LL | let _s = s.clone(); | ^ error: redundant clone - --> $DIR/redundant_clone.rs:123:15 + --> $DIR/redundant_clone.rs:124:15 | LL | let _t = t.clone(); | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:123:14 + --> $DIR/redundant_clone.rs:124:14 | LL | let _t = t.clone(); | ^ error: redundant clone - --> $DIR/redundant_clone.rs:133:19 + --> $DIR/redundant_clone.rs:134:19 | LL | let _f = f.clone(); | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:133:18 + --> $DIR/redundant_clone.rs:134:18 | LL | let _f = f.clone(); | ^ error: redundant clone - --> $DIR/redundant_clone.rs:145:14 + --> $DIR/redundant_clone.rs:146:14 | LL | let y = x.clone().join("matthias"); | ^^^^^^^^ help: remove this | note: cloned value is neither consumed nor mutated - --> $DIR/redundant_clone.rs:145:13 + --> $DIR/redundant_clone.rs:146:13 | LL | let y = x.clone().join("matthias"); | ^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From 51617b83a18b2a0b1f0f2cae8c43e42043a86b76 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Thu, 25 Feb 2021 23:06:50 +0900 Subject: new lint: iter_count --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 3 ++ clippy_lints/src/methods/mod.rs | 62 ++++++++++++++++++++++++++++++++++++ tests/ui/auxiliary/option_helpers.rs | 4 +++ tests/ui/iter_count.fixed | 58 +++++++++++++++++++++++++++++++++ tests/ui/iter_count.rs | 58 +++++++++++++++++++++++++++++++++ tests/ui/iter_count.stderr | 52 ++++++++++++++++++++++++++++++ 7 files changed, 238 insertions(+) create mode 100644 tests/ui/iter_count.fixed create mode 100644 tests/ui/iter_count.rs create mode 100644 tests/ui/iter_count.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 687f7447202..41c334c6816 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2135,6 +2135,7 @@ Released 2018-09-13 [`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters [`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements [`iter_cloned_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_cloned_collect +[`iter_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_count [`iter_next_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_loop [`iter_next_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_slice [`iter_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index fa8f03eb445..1ace4c8a10c 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -775,6 +775,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &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, @@ -1577,6 +1578,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), @@ -1881,6 +1883,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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::OPTION_AS_REF_DEREF), diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 6f491144435..17e7f6f662d 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1540,6 +1540,32 @@ declare_clippy_lint! { "implicitly cloning a value by invoking a function on its dereferenced type" } +declare_clippy_lint! { + /// **What it does:** Checks for the use of `.iter().count()`. + /// + /// **Why is this bad?** `.len()` is more efficient and more + /// readable. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // Bad + /// let some_vec = vec![0, 1, 2, 3]; + /// let _ = some_vec.iter().count(); + /// let _ = &some_vec[..].iter().count(); + /// + /// // Good + /// let some_vec = vec![0, 1, 2, 3]; + /// let _ = some_vec.len(); + /// let _ = &some_vec[..].len(); + /// ``` + pub ITER_COUNT, + complexity, + "replace `.iter().count()` with `.len()`" +} + pub struct Methods { msrv: Option, } @@ -1585,6 +1611,7 @@ impl_lint_pass!(Methods => [ MAP_FLATTEN, ITERATOR_STEP_BY_ZERO, ITER_NEXT_SLICE, + ITER_COUNT, ITER_NTH, ITER_NTH_ZERO, BYTES_NTH, @@ -1664,6 +1691,8 @@ impl<'tcx> LateLintPass<'tcx> for Methods { lint_search_is_some(cx, expr, "rposition", arg_lists[1], arg_lists[0], method_spans[1]) }, ["extend", ..] => lint_extend(cx, expr, arg_lists[0]), + ["count", "iter"] => lint_iter_count(cx, expr, &arg_lists[1], false), + ["count", "iter_mut"] => lint_iter_count(cx, expr, &arg_lists[1], true), ["nth", "iter"] => lint_iter_nth(cx, expr, &arg_lists, false), ["nth", "iter_mut"] => lint_iter_nth(cx, expr, &arg_lists, true), ["nth", "bytes"] => bytes_nth::lints(cx, expr, &arg_lists[1]), @@ -2632,6 +2661,39 @@ fn lint_iter_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, iter_ } } +fn lint_iter_count<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, iter_args: &'tcx [Expr<'tcx>], is_mut: bool) { + let mut_str = if is_mut { "_mut" } else { "" }; + if_chain! { + let caller_type = if derefs_to_slice(cx, &iter_args[0], cx.typeck_results().expr_ty(&iter_args[0])).is_some() { + Some("slice") + } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&iter_args[0]), sym::vec_type) { + Some("Vec") + } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&iter_args[0]), sym!(vecdeque_type)) { + Some("VecDeque") + } else if match_trait_method(cx, expr, &paths::ITERATOR) { + Some("std::iter::Iterator") + } else { + None + }; + if let Some(caller_type) = caller_type; + then { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + ITER_COUNT, + expr.span, + &format!("called `.iter{}().count()` on a `{}`", mut_str, caller_type), + "try", + format!( + "{}.len()", + snippet_with_applicability(cx, iter_args[0].span, "..", &mut applicability), + ), + applicability, + ); + } + } +} + fn lint_iter_nth<'tcx>( cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, diff --git a/tests/ui/auxiliary/option_helpers.rs b/tests/ui/auxiliary/option_helpers.rs index ed11c41e21c..7dc3f4ebd4d 100644 --- a/tests/ui/auxiliary/option_helpers.rs +++ b/tests/ui/auxiliary/option_helpers.rs @@ -48,4 +48,8 @@ impl IteratorFalsePositives { pub fn skip_while(self) -> IteratorFalsePositives { self } + + pub fn count(self) -> usize { + self.foo as usize + } } diff --git a/tests/ui/iter_count.fixed b/tests/ui/iter_count.fixed new file mode 100644 index 00000000000..14cae1cd73e --- /dev/null +++ b/tests/ui/iter_count.fixed @@ -0,0 +1,58 @@ +// run-rustfix +// aux-build:option_helpers.rs + +#![warn(clippy::iter_count)] +#![allow(unused_variables)] +#![allow(unused_mut)] + +extern crate option_helpers; + +use option_helpers::IteratorFalsePositives; +use std::collections::{HashSet, VecDeque}; + +/// Struct to generate false positives for things with `.iter()`. +#[derive(Copy, Clone)] +struct HasIter; + +impl HasIter { + fn iter(self) -> IteratorFalsePositives { + IteratorFalsePositives { foo: 0 } + } + + fn iter_mut(self) -> IteratorFalsePositives { + IteratorFalsePositives { foo: 0 } + } +} + +fn main() { + let mut some_vec = vec![0, 1, 2, 3]; + let mut boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]); + let mut some_vec_deque: VecDeque<_> = some_vec.iter().cloned().collect(); + let mut some_hash_set = HashSet::new(); + some_hash_set.insert(1); + + { + // Make sure we lint `.iter()` for relevant types. + let bad_vec = some_vec.len(); + let bad_slice = &some_vec[..].len(); + let bad_boxed_slice = boxed_slice.len(); + let bad_vec_deque = some_vec_deque.len(); + let bad_hash_set = some_hash_set.len(); + } + + { + // Make sure we lint `.iter_mut()` for relevant types. + let bad_vec = some_vec.len(); + } + { + let bad_slice = &some_vec[..].len(); + } + { + let bad_vec_deque = some_vec_deque.len(); + } + + // Make sure we don't lint for non-relevant types. + let false_positive = HasIter; + let ok = false_positive.iter().count(); + let ok_mut = false_positive.iter_mut().count(); +} diff --git a/tests/ui/iter_count.rs b/tests/ui/iter_count.rs new file mode 100644 index 00000000000..dfe02e5d53e --- /dev/null +++ b/tests/ui/iter_count.rs @@ -0,0 +1,58 @@ +// run-rustfix +// aux-build:option_helpers.rs + +#![warn(clippy::iter_count)] +#![allow(unused_variables)] +#![allow(unused_mut)] + +extern crate option_helpers; + +use option_helpers::IteratorFalsePositives; +use std::collections::{HashSet, VecDeque}; + +/// Struct to generate false positives for things with `.iter()`. +#[derive(Copy, Clone)] +struct HasIter; + +impl HasIter { + fn iter(self) -> IteratorFalsePositives { + IteratorFalsePositives { foo: 0 } + } + + fn iter_mut(self) -> IteratorFalsePositives { + IteratorFalsePositives { foo: 0 } + } +} + +fn main() { + let mut some_vec = vec![0, 1, 2, 3]; + let mut boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]); + let mut some_vec_deque: VecDeque<_> = some_vec.iter().cloned().collect(); + let mut some_hash_set = HashSet::new(); + some_hash_set.insert(1); + + { + // Make sure we lint `.iter()` for relevant types. + let bad_vec = some_vec.iter().count(); + let bad_slice = &some_vec[..].iter().count(); + let bad_boxed_slice = boxed_slice.iter().count(); + let bad_vec_deque = some_vec_deque.iter().count(); + let bad_hash_set = some_hash_set.iter().count(); + } + + { + // Make sure we lint `.iter_mut()` for relevant types. + let bad_vec = some_vec.iter_mut().count(); + } + { + let bad_slice = &some_vec[..].iter_mut().count(); + } + { + let bad_vec_deque = some_vec_deque.iter_mut().count(); + } + + // Make sure we don't lint for non-relevant types. + let false_positive = HasIter; + let ok = false_positive.iter().count(); + let ok_mut = false_positive.iter_mut().count(); +} diff --git a/tests/ui/iter_count.stderr b/tests/ui/iter_count.stderr new file mode 100644 index 00000000000..a465d221994 --- /dev/null +++ b/tests/ui/iter_count.stderr @@ -0,0 +1,52 @@ +error: called `.iter().count()` on a `Vec` + --> $DIR/iter_count.rs:36:23 + | +LL | let bad_vec = some_vec.iter().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vec.len()` + | + = note: `-D clippy::iter-count` implied by `-D warnings` + +error: called `.iter().count()` on a `slice` + --> $DIR/iter_count.rs:37:26 + | +LL | let bad_slice = &some_vec[..].iter().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vec[..].len()` + +error: called `.iter().count()` on a `slice` + --> $DIR/iter_count.rs:38:31 + | +LL | let bad_boxed_slice = boxed_slice.iter().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `boxed_slice.len()` + +error: called `.iter().count()` on a `VecDeque` + --> $DIR/iter_count.rs:39:29 + | +LL | let bad_vec_deque = some_vec_deque.iter().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vec_deque.len()` + +error: called `.iter().count()` on a `std::iter::Iterator` + --> $DIR/iter_count.rs:40:28 + | +LL | let bad_hash_set = some_hash_set.iter().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_hash_set.len()` + +error: called `.iter_mut().count()` on a `Vec` + --> $DIR/iter_count.rs:45:23 + | +LL | let bad_vec = some_vec.iter_mut().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vec.len()` + +error: called `.iter_mut().count()` on a `slice` + --> $DIR/iter_count.rs:48:26 + | +LL | let bad_slice = &some_vec[..].iter_mut().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vec[..].len()` + +error: called `.iter_mut().count()` on a `VecDeque` + --> $DIR/iter_count.rs:51:29 + | +LL | let bad_vec_deque = some_vec_deque.iter_mut().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vec_deque.len()` + +error: aborting due to 8 previous errors + -- cgit 1.4.1-3-g733a5 From 7223ee6590bb74e2a7ad02361b51cfa58178c740 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Thu, 25 Feb 2021 23:07:15 +0900 Subject: allow clippy::iter_count --- tests/ui/needless_collect.fixed | 2 +- tests/ui/needless_collect.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/ui/needless_collect.fixed b/tests/ui/needless_collect.fixed index 7f2fcf02f6b..af6c7bf15ea 100644 --- a/tests/ui/needless_collect.fixed +++ b/tests/ui/needless_collect.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![allow(unused, clippy::suspicious_map)] +#![allow(unused, clippy::suspicious_map, clippy::iter_count)] use std::collections::{BTreeSet, HashMap, HashSet}; diff --git a/tests/ui/needless_collect.rs b/tests/ui/needless_collect.rs index 788a9eb3264..6ae14f370b1 100644 --- a/tests/ui/needless_collect.rs +++ b/tests/ui/needless_collect.rs @@ -1,6 +1,6 @@ // run-rustfix -#![allow(unused, clippy::suspicious_map)] +#![allow(unused, clippy::suspicious_map, clippy::iter_count)] use std::collections::{BTreeSet, HashMap, HashSet}; -- cgit 1.4.1-3-g733a5 From 204b27937c488eb7c1b81c31d56779861ff488c3 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Fri, 26 Feb 2021 01:54:51 +0900 Subject: lint for `into_iter().count()` --- clippy_lints/src/methods/mod.rs | 11 +++++-- tests/ui/iter_count.fixed | 58 +++++++++++++++++---------------- tests/ui/iter_count.rs | 58 +++++++++++++++++---------------- tests/ui/iter_count.stderr | 72 +++++++++++++++++++++++++---------------- 4 files changed, 116 insertions(+), 83 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 17e7f6f662d..e5509ee2c4e 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1691,7 +1691,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { lint_search_is_some(cx, expr, "rposition", arg_lists[1], arg_lists[0], method_spans[1]) }, ["extend", ..] => lint_extend(cx, expr, arg_lists[0]), - ["count", "iter"] => lint_iter_count(cx, expr, &arg_lists[1], false), + ["count", "into_iter" | "iter"] => lint_iter_count(cx, expr, &arg_lists[1], false), ["count", "iter_mut"] => lint_iter_count(cx, expr, &arg_lists[1], true), ["nth", "iter"] => lint_iter_nth(cx, expr, &arg_lists, false), ["nth", "iter_mut"] => lint_iter_nth(cx, expr, &arg_lists, true), @@ -2663,6 +2663,13 @@ fn lint_iter_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, iter_ fn lint_iter_count<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, iter_args: &'tcx [Expr<'tcx>], is_mut: bool) { let mut_str = if is_mut { "_mut" } else { "" }; + let iter_method = if method_chain_args(expr, &[format!("iter{}", mut_str).as_str(), "count"]).is_some() { + "iter" + } else if method_chain_args(expr, &["into_iter", "count"]).is_some() { + "into_iter" + } else { + return; + }; if_chain! { let caller_type = if derefs_to_slice(cx, &iter_args[0], cx.typeck_results().expr_ty(&iter_args[0])).is_some() { Some("slice") @@ -2682,7 +2689,7 @@ fn lint_iter_count<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, iter_args: &'t cx, ITER_COUNT, expr.span, - &format!("called `.iter{}().count()` on a `{}`", mut_str, caller_type), + &format!("called `.{}{}().count()` on a `{}`", iter_method, mut_str, caller_type), "try", format!( "{}.len()", diff --git a/tests/ui/iter_count.fixed b/tests/ui/iter_count.fixed index 14cae1cd73e..c8f89640847 100644 --- a/tests/ui/iter_count.fixed +++ b/tests/ui/iter_count.fixed @@ -2,8 +2,13 @@ // aux-build:option_helpers.rs #![warn(clippy::iter_count)] -#![allow(unused_variables)] -#![allow(unused_mut)] +#![allow( + unused_variables, + array_into_iter, + unused_mut, + clippy::into_iter_on_ref, + clippy::unnecessary_operation +)] extern crate option_helpers; @@ -22,37 +27,36 @@ impl HasIter { fn iter_mut(self) -> IteratorFalsePositives { IteratorFalsePositives { foo: 0 } } + + fn into_iter(self) -> IteratorFalsePositives { + IteratorFalsePositives { foo: 0 } + } } fn main() { - let mut some_vec = vec![0, 1, 2, 3]; + let mut vec = vec![0, 1, 2, 3]; let mut boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]); - let mut some_vec_deque: VecDeque<_> = some_vec.iter().cloned().collect(); - let mut some_hash_set = HashSet::new(); - some_hash_set.insert(1); - - { - // Make sure we lint `.iter()` for relevant types. - let bad_vec = some_vec.len(); - let bad_slice = &some_vec[..].len(); - let bad_boxed_slice = boxed_slice.len(); - let bad_vec_deque = some_vec_deque.len(); - let bad_hash_set = some_hash_set.len(); - } + let mut vec_deque: VecDeque<_> = vec.iter().cloned().collect(); + let mut hash_set = HashSet::new(); + hash_set.insert(1); - { - // Make sure we lint `.iter_mut()` for relevant types. - let bad_vec = some_vec.len(); - } - { - let bad_slice = &some_vec[..].len(); - } - { - let bad_vec_deque = some_vec_deque.len(); - } + &vec[..].len(); + vec.len(); + boxed_slice.len(); + vec_deque.len(); + hash_set.len(); + + vec.len(); + &vec[..].len(); + vec_deque.len(); + + &vec[..].len(); + vec.len(); + vec_deque.len(); // Make sure we don't lint for non-relevant types. let false_positive = HasIter; - let ok = false_positive.iter().count(); - let ok_mut = false_positive.iter_mut().count(); + false_positive.iter().count(); + false_positive.iter_mut().count(); + false_positive.into_iter().count(); } diff --git a/tests/ui/iter_count.rs b/tests/ui/iter_count.rs index dfe02e5d53e..8ea17ef34fa 100644 --- a/tests/ui/iter_count.rs +++ b/tests/ui/iter_count.rs @@ -2,8 +2,13 @@ // aux-build:option_helpers.rs #![warn(clippy::iter_count)] -#![allow(unused_variables)] -#![allow(unused_mut)] +#![allow( + unused_variables, + array_into_iter, + unused_mut, + clippy::into_iter_on_ref, + clippy::unnecessary_operation +)] extern crate option_helpers; @@ -22,37 +27,36 @@ impl HasIter { fn iter_mut(self) -> IteratorFalsePositives { IteratorFalsePositives { foo: 0 } } + + fn into_iter(self) -> IteratorFalsePositives { + IteratorFalsePositives { foo: 0 } + } } fn main() { - let mut some_vec = vec![0, 1, 2, 3]; + let mut vec = vec![0, 1, 2, 3]; let mut boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]); - let mut some_vec_deque: VecDeque<_> = some_vec.iter().cloned().collect(); - let mut some_hash_set = HashSet::new(); - some_hash_set.insert(1); - - { - // Make sure we lint `.iter()` for relevant types. - let bad_vec = some_vec.iter().count(); - let bad_slice = &some_vec[..].iter().count(); - let bad_boxed_slice = boxed_slice.iter().count(); - let bad_vec_deque = some_vec_deque.iter().count(); - let bad_hash_set = some_hash_set.iter().count(); - } + let mut vec_deque: VecDeque<_> = vec.iter().cloned().collect(); + let mut hash_set = HashSet::new(); + hash_set.insert(1); - { - // Make sure we lint `.iter_mut()` for relevant types. - let bad_vec = some_vec.iter_mut().count(); - } - { - let bad_slice = &some_vec[..].iter_mut().count(); - } - { - let bad_vec_deque = some_vec_deque.iter_mut().count(); - } + &vec[..].iter().count(); + vec.iter().count(); + boxed_slice.iter().count(); + vec_deque.iter().count(); + hash_set.iter().count(); + + vec.iter_mut().count(); + &vec[..].iter_mut().count(); + vec_deque.iter_mut().count(); + + &vec[..].into_iter().count(); + vec.into_iter().count(); + vec_deque.into_iter().count(); // Make sure we don't lint for non-relevant types. let false_positive = HasIter; - let ok = false_positive.iter().count(); - let ok_mut = false_positive.iter_mut().count(); + false_positive.iter().count(); + false_positive.iter_mut().count(); + false_positive.into_iter().count(); } diff --git a/tests/ui/iter_count.stderr b/tests/ui/iter_count.stderr index a465d221994..0820c001443 100644 --- a/tests/ui/iter_count.stderr +++ b/tests/ui/iter_count.stderr @@ -1,52 +1,70 @@ -error: called `.iter().count()` on a `Vec` - --> $DIR/iter_count.rs:36:23 +error: called `.iter().count()` on a `slice` + --> $DIR/iter_count.rs:43:6 | -LL | let bad_vec = some_vec.iter().count(); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vec.len()` +LL | &vec[..].iter().count(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec[..].len()` | = note: `-D clippy::iter-count` implied by `-D warnings` -error: called `.iter().count()` on a `slice` - --> $DIR/iter_count.rs:37:26 +error: called `.iter().count()` on a `Vec` + --> $DIR/iter_count.rs:44:5 | -LL | let bad_slice = &some_vec[..].iter().count(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vec[..].len()` +LL | vec.iter().count(); + | ^^^^^^^^^^^^^^^^^^ help: try: `vec.len()` error: called `.iter().count()` on a `slice` - --> $DIR/iter_count.rs:38:31 + --> $DIR/iter_count.rs:45:5 | -LL | let bad_boxed_slice = boxed_slice.iter().count(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `boxed_slice.len()` +LL | boxed_slice.iter().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `boxed_slice.len()` error: called `.iter().count()` on a `VecDeque` - --> $DIR/iter_count.rs:39:29 + --> $DIR/iter_count.rs:46:5 | -LL | let bad_vec_deque = some_vec_deque.iter().count(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vec_deque.len()` +LL | vec_deque.iter().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec_deque.len()` error: called `.iter().count()` on a `std::iter::Iterator` - --> $DIR/iter_count.rs:40:28 + --> $DIR/iter_count.rs:47:5 | -LL | let bad_hash_set = some_hash_set.iter().count(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_hash_set.len()` +LL | hash_set.iter().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hash_set.len()` error: called `.iter_mut().count()` on a `Vec` - --> $DIR/iter_count.rs:45:23 + --> $DIR/iter_count.rs:49:5 | -LL | let bad_vec = some_vec.iter_mut().count(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vec.len()` +LL | vec.iter_mut().count(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.len()` error: called `.iter_mut().count()` on a `slice` - --> $DIR/iter_count.rs:48:26 + --> $DIR/iter_count.rs:50:6 | -LL | let bad_slice = &some_vec[..].iter_mut().count(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vec[..].len()` +LL | &vec[..].iter_mut().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec[..].len()` error: called `.iter_mut().count()` on a `VecDeque` - --> $DIR/iter_count.rs:51:29 + --> $DIR/iter_count.rs:51:5 + | +LL | vec_deque.iter_mut().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec_deque.len()` + +error: called `.into_iter().count()` on a `slice` + --> $DIR/iter_count.rs:53:6 + | +LL | &vec[..].into_iter().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec[..].len()` + +error: called `.into_iter().count()` on a `Vec` + --> $DIR/iter_count.rs:54:5 + | +LL | vec.into_iter().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.len()` + +error: called `.into_iter().count()` on a `VecDeque` + --> $DIR/iter_count.rs:55:5 | -LL | let bad_vec_deque = some_vec_deque.iter_mut().count(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vec_deque.len()` +LL | vec_deque.into_iter().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec_deque.len()` -error: aborting due to 8 previous errors +error: aborting due to 11 previous errors -- cgit 1.4.1-3-g733a5 From cc2b00055ccc18f4b345a1f6d50865ada9093e88 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Fri, 26 Feb 2021 04:11:01 +0900 Subject: return when the ty doesn't have `len()` --- clippy_lints/src/methods/iter_count.rs | 33 ++++++---- tests/ui/iter_count.fixed | 26 +++++++- tests/ui/iter_count.rs | 26 +++++++- tests/ui/iter_count.stderr | 110 +++++++++++++++++++++++++++++---- 4 files changed, 169 insertions(+), 26 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/iter_count.rs b/clippy_lints/src/methods/iter_count.rs index ca8723cec94..1bcdb57ad29 100644 --- a/clippy_lints/src/methods/iter_count.rs +++ b/clippy_lints/src/methods/iter_count.rs @@ -1,7 +1,8 @@ use crate::utils::{ - derefs_to_slice, is_type_diagnostic_item, match_trait_method, method_chain_args, paths, snippet_with_applicability, + derefs_to_slice, is_type_diagnostic_item, match_type, method_chain_args, paths, snippet_with_applicability, span_lint_and_sugg, }; + use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::Expr; @@ -19,19 +20,29 @@ pub(crate) fn lints<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, iter_args: &' } else { return; }; + let ty = cx.typeck_results().expr_ty(&iter_args[0]); if_chain! { - let caller_type = if derefs_to_slice(cx, &iter_args[0], cx.typeck_results().expr_ty(&iter_args[0])).is_some() { - Some("slice") - } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&iter_args[0]), sym::vec_type) { - Some("Vec") - } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&iter_args[0]), sym!(vecdeque_type)) { - Some("VecDeque") - } else if match_trait_method(cx, expr, &paths::ITERATOR) { - Some("std::iter::Iterator") + let caller_type = if derefs_to_slice(cx, &iter_args[0], ty).is_some() { + "slice" + } else if is_type_diagnostic_item(cx, ty, sym::vec_type) { + "Vec" + } else if is_type_diagnostic_item(cx, ty, sym!(vecdeque_type)) { + "VecDeque" + } else if is_type_diagnostic_item(cx, ty, sym!(hashset_type)) { + "HashSet" + } else if is_type_diagnostic_item(cx, ty, sym!(hashmap_type)) { + "HashMap" + } else if match_type(cx, ty, &paths::BTREEMAP) { + "BTreeMap" + } else if match_type(cx, ty, &paths::BTREESET) { + "BTreeSet" + } else if match_type(cx, ty, &paths::LINKED_LIST) { + "LinkedList" + } else if match_type(cx, ty, &paths::BINARY_HEAP) { + "BinaryHeap" } else { - None + return }; - if let Some(caller_type) = caller_type; then { let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( diff --git a/tests/ui/iter_count.fixed b/tests/ui/iter_count.fixed index c8f89640847..b11dadda6c2 100644 --- a/tests/ui/iter_count.fixed +++ b/tests/ui/iter_count.fixed @@ -13,7 +13,7 @@ extern crate option_helpers; use option_helpers::IteratorFalsePositives; -use std::collections::{HashSet, VecDeque}; +use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque}; /// Struct to generate false positives for things with `.iter()`. #[derive(Copy, Clone)] @@ -38,21 +38,45 @@ fn main() { let mut boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]); let mut vec_deque: VecDeque<_> = vec.iter().cloned().collect(); let mut hash_set = HashSet::new(); + let mut hash_map = HashMap::new(); + let mut b_tree_map = BTreeMap::new(); + let mut b_tree_set = BTreeSet::new(); + let mut linked_list = LinkedList::new(); + let mut binary_heap = BinaryHeap::new(); hash_set.insert(1); + hash_map.insert(1, 2); + b_tree_map.insert(1, 2); + b_tree_set.insert(1); + linked_list.push_back(1); + binary_heap.push(1); &vec[..].len(); vec.len(); boxed_slice.len(); vec_deque.len(); hash_set.len(); + hash_map.len(); + b_tree_map.len(); + b_tree_set.len(); + linked_list.len(); + binary_heap.len(); vec.len(); &vec[..].len(); vec_deque.len(); + hash_map.len(); + b_tree_map.len(); + linked_list.len(); &vec[..].len(); vec.len(); vec_deque.len(); + hash_set.len(); + hash_map.len(); + b_tree_map.len(); + b_tree_set.len(); + linked_list.len(); + binary_heap.len(); // Make sure we don't lint for non-relevant types. let false_positive = HasIter; diff --git a/tests/ui/iter_count.rs b/tests/ui/iter_count.rs index 8ea17ef34fa..7d49c6a3dbb 100644 --- a/tests/ui/iter_count.rs +++ b/tests/ui/iter_count.rs @@ -13,7 +13,7 @@ extern crate option_helpers; use option_helpers::IteratorFalsePositives; -use std::collections::{HashSet, VecDeque}; +use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque}; /// Struct to generate false positives for things with `.iter()`. #[derive(Copy, Clone)] @@ -38,21 +38,45 @@ fn main() { let mut boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]); let mut vec_deque: VecDeque<_> = vec.iter().cloned().collect(); let mut hash_set = HashSet::new(); + let mut hash_map = HashMap::new(); + let mut b_tree_map = BTreeMap::new(); + let mut b_tree_set = BTreeSet::new(); + let mut linked_list = LinkedList::new(); + let mut binary_heap = BinaryHeap::new(); hash_set.insert(1); + hash_map.insert(1, 2); + b_tree_map.insert(1, 2); + b_tree_set.insert(1); + linked_list.push_back(1); + binary_heap.push(1); &vec[..].iter().count(); vec.iter().count(); boxed_slice.iter().count(); vec_deque.iter().count(); hash_set.iter().count(); + hash_map.iter().count(); + b_tree_map.iter().count(); + b_tree_set.iter().count(); + linked_list.iter().count(); + binary_heap.iter().count(); vec.iter_mut().count(); &vec[..].iter_mut().count(); vec_deque.iter_mut().count(); + hash_map.iter_mut().count(); + b_tree_map.iter_mut().count(); + linked_list.iter_mut().count(); &vec[..].into_iter().count(); vec.into_iter().count(); vec_deque.into_iter().count(); + hash_set.into_iter().count(); + hash_map.into_iter().count(); + b_tree_map.into_iter().count(); + b_tree_set.into_iter().count(); + linked_list.into_iter().count(); + binary_heap.into_iter().count(); // Make sure we don't lint for non-relevant types. let false_positive = HasIter; diff --git a/tests/ui/iter_count.stderr b/tests/ui/iter_count.stderr index 0820c001443..f3fb98e65b9 100644 --- a/tests/ui/iter_count.stderr +++ b/tests/ui/iter_count.stderr @@ -1,5 +1,5 @@ error: called `.iter().count()` on a `slice` - --> $DIR/iter_count.rs:43:6 + --> $DIR/iter_count.rs:53:6 | LL | &vec[..].iter().count(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec[..].len()` @@ -7,64 +7,148 @@ LL | &vec[..].iter().count(); = note: `-D clippy::iter-count` implied by `-D warnings` error: called `.iter().count()` on a `Vec` - --> $DIR/iter_count.rs:44:5 + --> $DIR/iter_count.rs:54:5 | LL | vec.iter().count(); | ^^^^^^^^^^^^^^^^^^ help: try: `vec.len()` error: called `.iter().count()` on a `slice` - --> $DIR/iter_count.rs:45:5 + --> $DIR/iter_count.rs:55:5 | LL | boxed_slice.iter().count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `boxed_slice.len()` error: called `.iter().count()` on a `VecDeque` - --> $DIR/iter_count.rs:46:5 + --> $DIR/iter_count.rs:56:5 | LL | vec_deque.iter().count(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec_deque.len()` -error: called `.iter().count()` on a `std::iter::Iterator` - --> $DIR/iter_count.rs:47:5 +error: called `.iter().count()` on a `HashSet` + --> $DIR/iter_count.rs:57:5 | LL | hash_set.iter().count(); | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hash_set.len()` +error: called `.iter().count()` on a `HashMap` + --> $DIR/iter_count.rs:58:5 + | +LL | hash_map.iter().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hash_map.len()` + +error: called `.iter().count()` on a `BTreeMap` + --> $DIR/iter_count.rs:59:5 + | +LL | b_tree_map.iter().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b_tree_map.len()` + +error: called `.iter().count()` on a `BTreeSet` + --> $DIR/iter_count.rs:60:5 + | +LL | b_tree_set.iter().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b_tree_set.len()` + +error: called `.iter().count()` on a `LinkedList` + --> $DIR/iter_count.rs:61:5 + | +LL | linked_list.iter().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `linked_list.len()` + +error: called `.iter().count()` on a `BinaryHeap` + --> $DIR/iter_count.rs:62:5 + | +LL | binary_heap.iter().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `binary_heap.len()` + error: called `.iter_mut().count()` on a `Vec` - --> $DIR/iter_count.rs:49:5 + --> $DIR/iter_count.rs:64:5 | LL | vec.iter_mut().count(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.len()` error: called `.iter_mut().count()` on a `slice` - --> $DIR/iter_count.rs:50:6 + --> $DIR/iter_count.rs:65:6 | LL | &vec[..].iter_mut().count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec[..].len()` error: called `.iter_mut().count()` on a `VecDeque` - --> $DIR/iter_count.rs:51:5 + --> $DIR/iter_count.rs:66:5 | LL | vec_deque.iter_mut().count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec_deque.len()` +error: called `.iter_mut().count()` on a `HashMap` + --> $DIR/iter_count.rs:67:5 + | +LL | hash_map.iter_mut().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hash_map.len()` + +error: called `.iter_mut().count()` on a `BTreeMap` + --> $DIR/iter_count.rs:68:5 + | +LL | b_tree_map.iter_mut().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b_tree_map.len()` + +error: called `.iter_mut().count()` on a `LinkedList` + --> $DIR/iter_count.rs:69:5 + | +LL | linked_list.iter_mut().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `linked_list.len()` + error: called `.into_iter().count()` on a `slice` - --> $DIR/iter_count.rs:53:6 + --> $DIR/iter_count.rs:71:6 | LL | &vec[..].into_iter().count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec[..].len()` error: called `.into_iter().count()` on a `Vec` - --> $DIR/iter_count.rs:54:5 + --> $DIR/iter_count.rs:72:5 | LL | vec.into_iter().count(); | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.len()` error: called `.into_iter().count()` on a `VecDeque` - --> $DIR/iter_count.rs:55:5 + --> $DIR/iter_count.rs:73:5 | LL | vec_deque.into_iter().count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec_deque.len()` -error: aborting due to 11 previous errors +error: called `.into_iter().count()` on a `HashSet` + --> $DIR/iter_count.rs:74:5 + | +LL | hash_set.into_iter().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hash_set.len()` + +error: called `.into_iter().count()` on a `HashMap` + --> $DIR/iter_count.rs:75:5 + | +LL | hash_map.into_iter().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hash_map.len()` + +error: called `.into_iter().count()` on a `BTreeMap` + --> $DIR/iter_count.rs:76:5 + | +LL | b_tree_map.into_iter().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b_tree_map.len()` + +error: called `.into_iter().count()` on a `BTreeSet` + --> $DIR/iter_count.rs:77:5 + | +LL | b_tree_set.into_iter().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b_tree_set.len()` + +error: called `.into_iter().count()` on a `LinkedList` + --> $DIR/iter_count.rs:78:5 + | +LL | linked_list.into_iter().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `linked_list.len()` + +error: called `.into_iter().count()` on a `BinaryHeap` + --> $DIR/iter_count.rs:79:5 + | +LL | binary_heap.into_iter().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `binary_heap.len()` + +error: aborting due to 25 previous errors -- cgit 1.4.1-3-g733a5 From bdeec5dbd6e484cec26039cb795193ab044cf4d9 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Sat, 27 Feb 2021 21:52:15 +0900 Subject: Use TypeckResults::expr_ty instead of TyCtxt::type_of to fix "Not a type" ICE --- clippy_lints/src/default_numeric_fallback.rs | 5 ++--- clippy_lints/src/inconsistent_struct_constructor.rs | 3 +-- tests/ui/crashes/ice-6792.rs | 20 ++++++++++++++++++++ 3 files changed, 23 insertions(+), 5 deletions(-) create mode 100644 tests/ui/crashes/ice-6792.rs (limited to 'tests') diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index 6ace9aa6bdf..369efacc9bc 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -130,10 +130,9 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { } }, - ExprKind::Struct(qpath, fields, base) => { + ExprKind::Struct(_, fields, base) => { if_chain! { - if let Some(def_id) = self.cx.qpath_res(qpath, expr.hir_id).opt_def_id(); - let ty = self.cx.tcx.type_of(def_id); + let ty = self.cx.typeck_results().expr_ty(expr); if let Some(adt_def) = ty.ty_adt_def(); if adt_def.is_struct(); if let Some(variant) = adt_def.variants.iter().next(); diff --git a/clippy_lints/src/inconsistent_struct_constructor.rs b/clippy_lints/src/inconsistent_struct_constructor.rs index c5afdf530eb..4f35e13c85a 100644 --- a/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/clippy_lints/src/inconsistent_struct_constructor.rs @@ -66,8 +66,7 @@ impl LateLintPass<'_> for InconsistentStructConstructor { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if_chain! { if let ExprKind::Struct(qpath, fields, base) = expr.kind; - if let Some(def_id) = cx.qpath_res(qpath, expr.hir_id).opt_def_id(); - let ty = cx.tcx.type_of(def_id); + let ty = cx.typeck_results().expr_ty(expr); if let Some(adt_def) = ty.ty_adt_def(); if adt_def.is_struct(); if let Some(variant) = adt_def.variants.iter().next(); diff --git a/tests/ui/crashes/ice-6792.rs b/tests/ui/crashes/ice-6792.rs new file mode 100644 index 00000000000..0e2ab1a39b8 --- /dev/null +++ b/tests/ui/crashes/ice-6792.rs @@ -0,0 +1,20 @@ +//! This is a reproducer for the ICE 6792: https://github.com/rust-lang/rust-clippy/issues/6792. +//! The ICE is caused by using `TyCtxt::type_of(assoc_type_id)`. + +trait Trait { + type Ty; + + fn broken() -> Self::Ty; +} + +struct Foo {} + +impl Trait for Foo { + type Ty = Foo; + + fn broken() -> Self::Ty { + Self::Ty {} + } +} + +fn main() {} -- cgit 1.4.1-3-g733a5 From e51bb0ee26413c8db8264abe413cc06c95bd9a13 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Sat, 27 Feb 2021 22:46:10 +0900 Subject: Add test for ICE 6793 --- tests/ui/crashes/ice-6793.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 tests/ui/crashes/ice-6793.rs (limited to 'tests') diff --git a/tests/ui/crashes/ice-6793.rs b/tests/ui/crashes/ice-6793.rs new file mode 100644 index 00000000000..12a4a0d25ef --- /dev/null +++ b/tests/ui/crashes/ice-6793.rs @@ -0,0 +1,23 @@ +//! This is a reproducer for the ICE 6793: https://github.com/rust-lang/rust-clippy/issues/6793. +//! The ICE is caused by using `TyCtxt::type_of(assoc_type_id)`, which is the same as the ICE 6792. + +trait Trait { + type Ty: 'static + Clone; + + fn broken() -> Self::Ty; +} + +#[derive(Clone)] +struct MyType { + x: i32, +} + +impl Trait for MyType { + type Ty = MyType; + + fn broken() -> Self::Ty { + Self::Ty { x: 1 } + } +} + +fn main() {} -- cgit 1.4.1-3-g733a5 From b119b6585959b77017d75149075b22927c452207 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Wed, 24 Feb 2021 13:50:11 +0100 Subject: tests: add test that roughly ensures that our lint messages conform with the diagnostics convention of the rustc dev guide lint message should not start with uppercase letters lint messages should not have punctuation at the end of the last line https://rustc-dev-guide.rust-lang.org/diagnostics.html#diagnostic-structure The test reads through all the .stderr files in the testsuit and checks lint messages that start with "help: ", "error: " etc. There is also an exception list for special messages that are deemed acceptable. changelog: make sure lint messages conform with the rustc dev guide and add test --- Cargo.toml | 1 + tests/lint_message_convention.rs | 102 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 tests/lint_message_convention.rs (limited to 'tests') diff --git a/Cargo.toml b/Cargo.toml index ea32a8edd1f..98a5be2898d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,6 +42,7 @@ tester = "0.9" clippy-mini-macro-test = { version = "0.2", path = "mini-macro" } serde = { version = "1.0", features = ["derive"] } derive-new = "0.5" +regex = "1.4" # A noop dependency that changes in the Rust repository, it's a bit of a hack. # See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust` diff --git a/tests/lint_message_convention.rs b/tests/lint_message_convention.rs new file mode 100644 index 00000000000..45e0d7336c1 --- /dev/null +++ b/tests/lint_message_convention.rs @@ -0,0 +1,102 @@ +use std::path::PathBuf; + +use regex::RegexSet; + +#[derive(Debug)] +struct Message { + path: PathBuf, + bad_lines: Vec, +} + +impl Message { + fn new(path: PathBuf) -> Self { + let content: String = std::fs::read_to_string(&path).unwrap(); + // we don't want the first letter after "error: ", "help: " ... to be capitalized + // also no puncutation (except for "?" ?) at the end of a line + let regex_set: RegexSet = RegexSet::new(&[ + r"error: [A-Z]", + r"help: [A-Z]", + r"warning: [A-Z]", + r"note: [A-Z]", + r"try this: [A-Z]", + r"error: .*[.!]$", + r"help: .*[.!]$", + r"warning: .*[.!]$", + r"note: .*[.!]$", + r"try this: .*[.!]$", + ]) + .unwrap(); + + // sometimes the first character is capitalized and it is legal (like in "Iterator...") or + // we want to ask a question ending in "?" + let exceptions_set: RegexSet = RegexSet::new(&[ + r".*error: I see you're using a LinkedList! Perhaps you meant some other data structure?", + r".*C-like enum variant discriminant is not portable to 32-bit targets", + r".*Iterator::step_by(0) will panic at runtime", + r".*did you mean `unix`?", + r".*the arguments may be inverted...", + r".*Intel x86 assembly syntax used", + r".*AT&T x86 assembly syntax used", + r".*remove .* the return type...", + r"note: Clippy version: .*", + ]) + .unwrap(); + + let bad_lines = content + .lines() + .filter(|line| regex_set.matches(line).matched_any()) + // ignore exceptions + .filter(|line| !exceptions_set.matches(line).matched_any()) + .map(|s| s.to_owned()) + .collect::>(); + + Message { path, bad_lines } + } +} + +#[test] +fn lint_message_convention() { + // make sure that lint messages: + // * are not capitalized + // * don't have puncuation at the end of the last sentence + + // these directories have interesting tests + let test_dirs = ["ui", "ui-cargo", "ui-internal", "ui-toml"] + .iter() + .map(PathBuf::from) + .map(|p| { + let base = PathBuf::from("tests"); + base.join(p) + }); + + // gather all .stderr files + let tests = test_dirs + .map(|dir| { + std::fs::read_dir(dir) + .expect("failed to read dir") + .map(|direntry| direntry.unwrap().path()) + }) + .flatten() + .filter(|file| matches!(file.extension().map(|s| s.to_str()), Some(Some("stderr")))); + + // get all files that have any "bad lines" in them + let bad_tests: Vec = tests + .map(|path| Message::new(path)) + .filter(|message| !message.bad_lines.is_empty()) + .collect(); + + bad_tests.iter().for_each(|message| { + eprintln!( + "error: the test '{}' contained the following nonconforming lines :", + message.path.display() + ); + message.bad_lines.iter().for_each(|line| eprintln!("{}", line)); + eprintln!("\n\n"); + }); + + eprintln!("\n\n\nLint message should not start with a capital letter and should not have punctuation at the end of the message unless multiple sentences are needed."); + eprintln!("Check out the rustc-dev-guide for more information:"); + eprintln!("https://rustc-dev-guide.rust-lang.org/diagnostics.html#diagnostic-structure"); + + assert!(bad_tests.is_empty()); +} -- cgit 1.4.1-3-g733a5 From 53705768824aca634ea6366acd666344611d37e3 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Wed, 24 Feb 2021 13:56:04 +0100 Subject: fix clippy lint warnings --- tests/lint_message_convention.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/tests/lint_message_convention.rs b/tests/lint_message_convention.rs index 45e0d7336c1..493bd3bf56b 100644 --- a/tests/lint_message_convention.rs +++ b/tests/lint_message_convention.rs @@ -1,3 +1,4 @@ +use std::ffi::OsStr; use std::path::PathBuf; use regex::RegexSet; @@ -47,7 +48,7 @@ impl Message { .filter(|line| regex_set.matches(line).matched_any()) // ignore exceptions .filter(|line| !exceptions_set.matches(line).matched_any()) - .map(|s| s.to_owned()) + .map(ToOwned::to_owned) .collect::>(); Message { path, bad_lines } @@ -71,17 +72,16 @@ fn lint_message_convention() { // gather all .stderr files let tests = test_dirs - .map(|dir| { + .flat_map(|dir| { std::fs::read_dir(dir) .expect("failed to read dir") .map(|direntry| direntry.unwrap().path()) }) - .flatten() - .filter(|file| matches!(file.extension().map(|s| s.to_str()), Some(Some("stderr")))); + .filter(|file| matches!(file.extension().map(OsStr::to_str), Some(Some("stderr")))); // get all files that have any "bad lines" in them let bad_tests: Vec = tests - .map(|path| Message::new(path)) + .map(Message::new) .filter(|message| !message.bad_lines.is_empty()) .collect(); -- cgit 1.4.1-3-g733a5 From 8eb2bd13d0dd70131be1e21a0a0261c9dc69937a Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Wed, 24 Feb 2021 14:02:51 +0100 Subject: update the lint messages and tests --- clippy_lints/src/assign_ops.rs | 2 +- clippy_lints/src/attrs.rs | 2 +- clippy_lints/src/await_holding_invalid.rs | 4 +- clippy_lints/src/comparison_chain.rs | 2 +- clippy_lints/src/drop_forget_ref.rs | 8 +-- clippy_lints/src/fallible_impl_from.rs | 2 +- clippy_lints/src/indexing_slicing.rs | 12 ++-- clippy_lints/src/integer_division.rs | 2 +- clippy_lints/src/loops.rs | 19 +++---- clippy_lints/src/matches.rs | 4 +- clippy_lints/src/methods/mod.rs | 4 +- clippy_lints/src/methods/unnecessary_lazy_eval.rs | 2 +- clippy_lints/src/misc.rs | 4 +- clippy_lints/src/needless_question_mark.rs | 2 +- clippy_lints/src/ptr.rs | 8 +-- clippy_lints/src/suspicious_operation_groupings.rs | 2 +- clippy_lints/src/transmuting_null.rs | 2 +- clippy_lints/src/types.rs | 4 +- clippy_lints/src/zero_div_zero.rs | 2 +- tests/lint_message_convention.rs | 7 ++- tests/ui-toml/vec_box_sized/test.stderr | 6 +- tests/ui/assign_ops2.stderr | 18 +++--- tests/ui/await_holding_lock.stderr | 8 +-- tests/ui/await_holding_refcell_ref.stderr | 12 ++-- tests/ui/box_vec.stderr | 2 +- tests/ui/comparison_chain.stderr | 14 ++--- tests/ui/drop_forget_copy.stderr | 12 ++-- tests/ui/drop_ref.stderr | 18 +++--- tests/ui/explicit_counter_loop.stderr | 14 ++--- tests/ui/fallible_impl_from.stderr | 8 +-- tests/ui/filter_map_next.stderr | 2 +- tests/ui/filter_map_next_fixable.stderr | 2 +- tests/ui/for_loops_over_fallibles.stderr | 10 ++-- tests/ui/forget_ref.stderr | 18 +++--- tests/ui/indexing_slicing_index.stderr | 24 ++++---- tests/ui/indexing_slicing_slice.stderr | 48 ++++++++-------- tests/ui/integer_division.stderr | 6 +- tests/ui/methods.stderr | 2 +- tests/ui/methods_fixable.stderr | 2 +- tests/ui/mismatched_target_os_unix.stderr | 34 ++++++------ tests/ui/needless_collect_indirect.stderr | 10 ++-- tests/ui/needless_question_mark.stderr | 28 +++++----- tests/ui/needless_range_loop.stderr | 20 +++---- tests/ui/needless_range_loop2.stderr | 16 +++--- tests/ui/ptr_arg.stderr | 24 ++++---- tests/ui/suspicious_operation_groupings.stderr | 54 +++++++++--------- tests/ui/toplevel_ref_arg_non_rustfix.stderr | 4 +- tests/ui/transmuting_null.stderr | 6 +- tests/ui/unnecessary_lazy_eval.stderr | 64 +++++++++++----------- tests/ui/unnecessary_lazy_eval_unfixable.stderr | 6 +- tests/ui/used_underscore_binding.stderr | 12 ++-- tests/ui/vec_box_sized.stderr | 8 +-- tests/ui/wild_in_or_pats.stderr | 16 +++--- tests/ui/zero_div_zero.stderr | 8 +-- 54 files changed, 315 insertions(+), 315 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/assign_ops.rs b/clippy_lints/src/assign_ops.rs index b3185b88840..e13f62d0428 100644 --- a/clippy_lints/src/assign_ops.rs +++ b/clippy_lints/src/assign_ops.rs @@ -209,7 +209,7 @@ fn lint_misrefactored_assign_op( diag.span_suggestion( expr.span, &format!( - "Did you mean `{} = {} {} {}` or `{}`? Consider replacing it with", + "did you mean `{} = {} {} {}` or `{}`? Consider replacing it with", snip_a, snip_a, op.node.as_str(), diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 652d1fa16b6..bb7be3d4724 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -639,7 +639,7 @@ fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) { diag.span_suggestion(span, "try", sugg, Applicability::MaybeIncorrect); if !unix_suggested && is_unix(os) { - diag.help("Did you mean `unix`?"); + diag.help("did you mean `unix`?"); unix_suggested = true; } } diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index ae64c688744..fad3aff96cc 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -116,7 +116,7 @@ fn check_interior_types(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorType cx, AWAIT_HOLDING_LOCK, ty_cause.span, - "this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await.", + "this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await", ty_cause.scope_span.or(Some(span)), "these are all the await points this lock is held through", ); @@ -126,7 +126,7 @@ fn check_interior_types(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorType cx, AWAIT_HOLDING_REFCELL_REF, ty_cause.span, - "this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await.", + "this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await", ty_cause.scope_span.or(Some(span)), "these are all the await points this ref is held through", ); diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs index 90d31dece13..e309db25995 100644 --- a/clippy_lints/src/comparison_chain.rs +++ b/clippy_lints/src/comparison_chain.rs @@ -117,7 +117,7 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain { expr.span, "`if` chain can be rewritten with `match`", None, - "Consider rewriting the `if` chain to use `cmp` and `match`.", + "consider rewriting the `if` chain to use `cmp` and `match`", ) } } diff --git a/clippy_lints/src/drop_forget_ref.rs b/clippy_lints/src/drop_forget_ref.rs index a84f9c46287..2aea00d883c 100644 --- a/clippy_lints/src/drop_forget_ref.rs +++ b/clippy_lints/src/drop_forget_ref.rs @@ -98,13 +98,13 @@ declare_clippy_lint! { } const DROP_REF_SUMMARY: &str = "calls to `std::mem::drop` with a reference instead of an owned value. \ - Dropping a reference does nothing."; + Dropping a reference does nothing"; const FORGET_REF_SUMMARY: &str = "calls to `std::mem::forget` with a reference instead of an owned value. \ - Forgetting a reference does nothing."; + Forgetting a reference does nothing"; const DROP_COPY_SUMMARY: &str = "calls to `std::mem::drop` with a value that implements `Copy`. \ - Dropping a copy leaves the original intact."; + Dropping a copy leaves the original intact"; const FORGET_COPY_SUMMARY: &str = "calls to `std::mem::forget` with a value that implements `Copy`. \ - Forgetting a copy leaves the original intact."; + Forgetting a copy leaves the original intact"; declare_lint_pass!(DropForgetRef => [DROP_REF, FORGET_REF, DROP_COPY, FORGET_COPY]); diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs index 6d522c7ef33..f466dddc13c 100644 --- a/clippy_lints/src/fallible_impl_from.rs +++ b/clippy_lints/src/fallible_impl_from.rs @@ -133,7 +133,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[h move |diag| { diag.help( "`From` is intended for infallible conversions only. \ - Use `TryFrom` if there's a possibility for the conversion to fail."); + Use `TryFrom` if there's a possibility for the conversion to fail"); diag.span_note(fpu.result, "potential failure(s)"); }); } diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs index 741195f3b10..c919ec097a2 100644 --- a/clippy_lints/src/indexing_slicing.rs +++ b/clippy_lints/src/indexing_slicing.rs @@ -132,13 +132,13 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { } let help_msg = match (range.start, range.end) { - (None, Some(_)) => "Consider using `.get(..n)`or `.get_mut(..n)` instead", - (Some(_), None) => "Consider using `.get(n..)` or .get_mut(n..)` instead", - (Some(_), Some(_)) => "Consider using `.get(n..m)` or `.get_mut(n..m)` instead", + (None, Some(_)) => "consider using `.get(..n)`or `.get_mut(..n)` instead", + (Some(_), None) => "consider using `.get(n..)` or .get_mut(n..)` instead", + (Some(_), Some(_)) => "consider using `.get(n..m)` or `.get_mut(n..m)` instead", (None, None) => return, // [..] is ok. }; - span_lint_and_help(cx, INDEXING_SLICING, expr.span, "slicing may panic.", None, help_msg); + span_lint_and_help(cx, INDEXING_SLICING, expr.span, "slicing may panic", None, help_msg); } else { // Catchall non-range index, i.e., [n] or [n << m] if let ty::Array(..) = ty.kind() { @@ -153,9 +153,9 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { cx, INDEXING_SLICING, expr.span, - "indexing may panic.", + "indexing may panic", None, - "Consider using `.get(n)` or `.get_mut(n)` instead", + "consider using `.get(n)` or `.get_mut(n)` instead", ); } } diff --git a/clippy_lints/src/integer_division.rs b/clippy_lints/src/integer_division.rs index 31181c10d23..39b4605e72f 100644 --- a/clippy_lints/src/integer_division.rs +++ b/clippy_lints/src/integer_division.rs @@ -39,7 +39,7 @@ impl<'tcx> LateLintPass<'tcx> for IntegerDivision { expr.span, "integer division", None, - "division of integers may cause loss of precision. consider using floats.", + "division of integers may cause loss of precision. consider using floats", ); } } diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 1c9373a756c..3ff9e182121 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1625,10 +1625,7 @@ fn check_for_loop_range<'tcx>( cx, NEEDLESS_RANGE_LOOP, expr.span, - &format!( - "the loop variable `{}` is only used to index `{}`.", - ident.name, indexed - ), + &format!("the loop variable `{}` is only used to index `{}`", ident.name, indexed), |diag| { multispan_sugg( diag, @@ -1763,7 +1760,7 @@ fn check_arg_type(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) { arg.span, &format!( "for loop over `{0}`, which is an `Option`. This is more readably written as an \ - `if let` statement.", + `if let` statement", snippet(cx, arg.span, "_") ), None, @@ -1780,7 +1777,7 @@ fn check_arg_type(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) { arg.span, &format!( "for loop over `{0}`, which is a `Result`. This is more readably written as an \ - `if let` statement.", + `if let` statement", snippet(cx, arg.span, "_") ), None, @@ -1826,7 +1823,7 @@ fn check_for_loop_explicit_counter<'tcx>( cx, EXPLICIT_COUNTER_LOOP, for_span.with_hi(arg.span.hi()), - &format!("the variable `{}` is used as a loop counter.", name), + &format!("the variable `{}` is used as a loop counter", name), "consider using", format!( "for ({}, {}) in {}.enumerate()", @@ -3055,16 +3052,16 @@ impl IterFunction { fn get_suggestion_text(&self) -> &'static str { match &self.func { IterFunctionKind::IntoIter => { - "Use the original Iterator instead of collecting it and then producing a new one" + "use the original Iterator instead of collecting it and then producing a new one" }, IterFunctionKind::Len => { - "Take the original Iterator's count instead of collecting it and finding the length" + "take the original Iterator's count instead of collecting it and finding the length" }, IterFunctionKind::IsEmpty => { - "Check if the original Iterator has anything instead of collecting it and seeing if it's empty" + "check if the original Iterator has anything instead of collecting it and seeing if it's empty" }, IterFunctionKind::Contains(_) => { - "Check if the original Iterator contains an element instead of collecting then checking" + "check if the original Iterator contains an element instead of collecting then checking" }, } } diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index efc8b139425..0d79ffbe944 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -1173,9 +1173,9 @@ fn check_wild_in_or_pats(cx: &LateContext<'_>, arms: &[Arm<'_>]) { cx, WILDCARD_IN_OR_PATTERNS, arm.pat.span, - "wildcard pattern covers any other pattern as it will match anyway.", + "wildcard pattern covers any other pattern as it will match anyway", None, - "Consider handling `_` separately.", + "consider handling `_` separately", ); } } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 6f491144435..f5c01ac7729 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3081,7 +3081,7 @@ fn lint_filter_next<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, fil // lint if caller of `.filter().next()` is an Iterator if match_trait_method(cx, expr, &paths::ITERATOR) { let msg = "called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling \ - `.find(..)` instead."; + `.find(..)` instead"; let filter_snippet = snippet(cx, filter_args[1].span, ".."); if filter_snippet.lines().count() <= 1 { let iter_snippet = snippet(cx, filter_args[0].span, ".."); @@ -3209,7 +3209,7 @@ fn lint_filter_map_next<'tcx>( } let msg = "called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling \ - `.find_map(..)` instead."; + `.find_map(..)` instead"; let filter_snippet = snippet(cx, filter_args[1].span, ".."); if filter_snippet.lines().count() <= 1 { let iter_snippet = snippet(cx, filter_args[0].span, ".."); diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs index a867bdb326d..40ccb8c80b3 100644 --- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -50,7 +50,7 @@ pub(super) fn lint<'tcx>( UNNECESSARY_LAZY_EVALUATIONS, expr.span, msg, - &format!("Use `{}` instead", simplify_using), + &format!("use `{}` instead", simplify_using), format!( "{0}.{1}({2})", snippet(cx, args[0].span, ".."), diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 2ef5c6aa2a4..12f91d7bf63 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -292,7 +292,7 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { TOPLEVEL_REF_ARG, arg.pat.span, "`ref` directly on a function argument is ignored. \ - Consider using a reference type instead.", + Consider using a reference type instead", ); } } @@ -422,7 +422,7 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { expr.span, &format!( "used binding `{}` which is prefixed with an underscore. A leading \ - underscore signals that a binding will not be used.", + underscore signals that a binding will not be used", binding ), ); diff --git a/clippy_lints/src/needless_question_mark.rs b/clippy_lints/src/needless_question_mark.rs index fe8d4d07abc..a3293f1b361 100644 --- a/clippy_lints/src/needless_question_mark.rs +++ b/clippy_lints/src/needless_question_mark.rs @@ -142,7 +142,7 @@ fn emit_lint(cx: &LateContext<'_>, expr: &SomeOkCall<'_>) { cx, NEEDLESS_QUESTION_MARK, entire_expr.span, - "Question mark operator is useless here", + "question mark operator is useless here", "try", format!("{}", utils::snippet(cx, inner_expr.span, r#""...""#)), Applicability::MachineApplicable, diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index de2fb8decb7..5474fdf30bf 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -188,7 +188,7 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: PTR_ARG, arg.span, "writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used \ - with non-Vec-based slices.", + with non-Vec-based slices", |diag| { if let Some(ref snippet) = get_only_generic_arg_snippet(cx, arg) { diag.span_suggestion( @@ -217,7 +217,7 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: cx, PTR_ARG, arg.span, - "writing `&String` instead of `&str` involves a new object where a slice will do.", + "writing `&String` instead of `&str` involves a new object where a slice will do", |diag| { diag.span_suggestion(arg.span, "change this to", "&str".into(), Applicability::Unspecified); for (clonespan, suggestion) in spans { @@ -239,7 +239,7 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: cx, PTR_ARG, arg.span, - "writing `&PathBuf` instead of `&Path` involves a new object where a slice will do.", + "writing `&PathBuf` instead of `&Path` involves a new object where a slice will do", |diag| { diag.span_suggestion( arg.span, @@ -278,7 +278,7 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: cx, PTR_ARG, arg.span, - "using a reference to `Cow` is not recommended.", + "using a reference to `Cow` is not recommended", "change this to", "&".to_owned() + &r, Applicability::Unspecified, diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs index cccd24ccf94..2ff30698043 100644 --- a/clippy_lints/src/suspicious_operation_groupings.rs +++ b/clippy_lints/src/suspicious_operation_groupings.rs @@ -261,7 +261,7 @@ fn emit_suggestion(cx: &EarlyContext<'_>, span: Span, sugg: String, applicabilit cx, SUSPICIOUS_OPERATION_GROUPINGS, span, - "This sequence of operators looks suspiciously like a bug.", + "this sequence of operators looks suspiciously like a bug", "I think you meant", sugg, applicability, diff --git a/clippy_lints/src/transmuting_null.rs b/clippy_lints/src/transmuting_null.rs index 6b171a0fa1a..2ba2b646f00 100644 --- a/clippy_lints/src/transmuting_null.rs +++ b/clippy_lints/src/transmuting_null.rs @@ -27,7 +27,7 @@ declare_clippy_lint! { declare_lint_pass!(TransmutingNull => [TRANSMUTING_NULL]); -const LINT_MSG: &str = "transmuting a known null pointer into a reference."; +const LINT_MSG: &str = "transmuting a known null pointer into a reference"; impl<'tcx> LateLintPass<'tcx> for TransmutingNull { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index af8d6865f93..a18cba6fb44 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -388,7 +388,7 @@ impl Types { hir_ty.span, "you seem to be trying to use `Box>`. Consider using just `Vec`", None, - "`Vec` is already on the heap, `Box>` makes an extra allocation.", + "`Vec` is already on the heap, `Box>` makes an extra allocation", ); return; // don't recurse into the type } @@ -554,7 +554,7 @@ impl Types { cx, VEC_BOX, hir_ty.span, - "`Vec` is already on the heap, the boxing is unnecessary.", + "`Vec` is already on the heap, the boxing is unnecessary", "try", format!("Vec<{}>", snippet(cx, boxed_ty.span, "..")), Applicability::MachineApplicable, diff --git a/clippy_lints/src/zero_div_zero.rs b/clippy_lints/src/zero_div_zero.rs index 4b81a27632d..11d96e15ff1 100644 --- a/clippy_lints/src/zero_div_zero.rs +++ b/clippy_lints/src/zero_div_zero.rs @@ -55,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for ZeroDiv { "constant division of `0.0` with `0.0` will always result in NaN", None, &format!( - "Consider using `{}::NAN` if you would like a constant representing NaN", + "consider using `{}::NAN` if you would like a constant representing NaN", float_type, ), ); diff --git a/tests/lint_message_convention.rs b/tests/lint_message_convention.rs index 493bd3bf56b..e316a884996 100644 --- a/tests/lint_message_convention.rs +++ b/tests/lint_message_convention.rs @@ -38,8 +38,11 @@ impl Message { r".*the arguments may be inverted...", r".*Intel x86 assembly syntax used", r".*AT&T x86 assembly syntax used", - r".*remove .* the return type...", + r".*remove .*the return type...", r"note: Clippy version: .*", + r"the compiler unexpectedly panicked. this is a bug.", + r".*help: I think you meant: .*", + r"Iterator.* will panic at runtime", ]) .unwrap(); @@ -96,7 +99,7 @@ fn lint_message_convention() { eprintln!("\n\n\nLint message should not start with a capital letter and should not have punctuation at the end of the message unless multiple sentences are needed."); eprintln!("Check out the rustc-dev-guide for more information:"); - eprintln!("https://rustc-dev-guide.rust-lang.org/diagnostics.html#diagnostic-structure"); + eprintln!("https://rustc-dev-guide.rust-lang.org/diagnostics.html#diagnostic-structure\n\n\n"); assert!(bad_tests.is_empty()); } diff --git a/tests/ui-toml/vec_box_sized/test.stderr b/tests/ui-toml/vec_box_sized/test.stderr index 3bdeca0bc87..cf194de3c55 100644 --- a/tests/ui-toml/vec_box_sized/test.stderr +++ b/tests/ui-toml/vec_box_sized/test.stderr @@ -1,4 +1,4 @@ -error: `Vec` is already on the heap, the boxing is unnecessary. +error: `Vec` is already on the heap, the boxing is unnecessary --> $DIR/test.rs:9:12 | LL | struct Foo(Vec>); @@ -6,13 +6,13 @@ LL | struct Foo(Vec>); | = note: `-D clippy::vec-box` implied by `-D warnings` -error: `Vec` is already on the heap, the boxing is unnecessary. +error: `Vec` is already on the heap, the boxing is unnecessary --> $DIR/test.rs:10:12 | LL | struct Bar(Vec>); | ^^^^^^^^^^^^^ help: try: `Vec` -error: `Vec` is already on the heap, the boxing is unnecessary. +error: `Vec` is already on the heap, the boxing is unnecessary --> $DIR/test.rs:13:18 | LL | struct FooBarBaz(Vec>); diff --git a/tests/ui/assign_ops2.stderr b/tests/ui/assign_ops2.stderr index 70b15d18a56..e40668ed339 100644 --- a/tests/ui/assign_ops2.stderr +++ b/tests/ui/assign_ops2.stderr @@ -5,7 +5,7 @@ LL | a += a + 1; | ^^^^^^^^^^ | = note: `-D clippy::misrefactored-assign-op` implied by `-D warnings` -help: Did you mean `a = a + 1` or `a = a + a + 1`? Consider replacing it with +help: did you mean `a = a + 1` or `a = a + a + 1`? Consider replacing it with | LL | a += 1; | ^^^^^^ @@ -20,7 +20,7 @@ error: variable appears on both sides of an assignment operation LL | a += 1 + a; | ^^^^^^^^^^ | -help: Did you mean `a = a + 1` or `a = a + 1 + a`? Consider replacing it with +help: did you mean `a = a + 1` or `a = a + 1 + a`? Consider replacing it with | LL | a += 1; | ^^^^^^ @@ -35,7 +35,7 @@ error: variable appears on both sides of an assignment operation LL | a -= a - 1; | ^^^^^^^^^^ | -help: Did you mean `a = a - 1` or `a = a - (a - 1)`? Consider replacing it with +help: did you mean `a = a - 1` or `a = a - (a - 1)`? Consider replacing it with | LL | a -= 1; | ^^^^^^ @@ -50,7 +50,7 @@ error: variable appears on both sides of an assignment operation LL | a *= a * 99; | ^^^^^^^^^^^ | -help: Did you mean `a = a * 99` or `a = a * a * 99`? Consider replacing it with +help: did you mean `a = a * 99` or `a = a * a * 99`? Consider replacing it with | LL | a *= 99; | ^^^^^^^ @@ -65,7 +65,7 @@ error: variable appears on both sides of an assignment operation LL | a *= 42 * a; | ^^^^^^^^^^^ | -help: Did you mean `a = a * 42` or `a = a * 42 * a`? Consider replacing it with +help: did you mean `a = a * 42` or `a = a * 42 * a`? Consider replacing it with | LL | a *= 42; | ^^^^^^^ @@ -80,7 +80,7 @@ error: variable appears on both sides of an assignment operation LL | a /= a / 2; | ^^^^^^^^^^ | -help: Did you mean `a = a / 2` or `a = a / (a / 2)`? Consider replacing it with +help: did you mean `a = a / 2` or `a = a / (a / 2)`? Consider replacing it with | LL | a /= 2; | ^^^^^^ @@ -95,7 +95,7 @@ error: variable appears on both sides of an assignment operation LL | a %= a % 5; | ^^^^^^^^^^ | -help: Did you mean `a = a % 5` or `a = a % (a % 5)`? Consider replacing it with +help: did you mean `a = a % 5` or `a = a % (a % 5)`? Consider replacing it with | LL | a %= 5; | ^^^^^^ @@ -110,7 +110,7 @@ error: variable appears on both sides of an assignment operation LL | a &= a & 1; | ^^^^^^^^^^ | -help: Did you mean `a = a & 1` or `a = a & a & 1`? Consider replacing it with +help: did you mean `a = a & 1` or `a = a & a & 1`? Consider replacing it with | LL | a &= 1; | ^^^^^^ @@ -125,7 +125,7 @@ error: variable appears on both sides of an assignment operation LL | a *= a * a; | ^^^^^^^^^^ | -help: Did you mean `a = a * a` or `a = a * a * a`? Consider replacing it with +help: did you mean `a = a * a` or `a = a * a * a`? Consider replacing it with | LL | a *= a; | ^^^^^^ diff --git a/tests/ui/await_holding_lock.stderr b/tests/ui/await_holding_lock.stderr index 21bf49d16f0..a5fcff7e0e4 100644 --- a/tests/ui/await_holding_lock.stderr +++ b/tests/ui/await_holding_lock.stderr @@ -1,4 +1,4 @@ -error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. +error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await --> $DIR/await_holding_lock.rs:7:9 | LL | let guard = x.lock().unwrap(); @@ -13,7 +13,7 @@ LL | | baz().await LL | | } | |_^ -error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. +error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await --> $DIR/await_holding_lock.rs:28:9 | LL | let guard = x.lock().unwrap(); @@ -31,7 +31,7 @@ LL | | first + second + third LL | | } | |_^ -error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. +error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await --> $DIR/await_holding_lock.rs:41:13 | LL | let guard = x.lock().unwrap(); @@ -45,7 +45,7 @@ LL | | baz().await LL | | }; | |_____^ -error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await. +error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await --> $DIR/await_holding_lock.rs:53:13 | LL | let guard = x.lock().unwrap(); diff --git a/tests/ui/await_holding_refcell_ref.stderr b/tests/ui/await_holding_refcell_ref.stderr index b504f045491..55e41dbca96 100644 --- a/tests/ui/await_holding_refcell_ref.stderr +++ b/tests/ui/await_holding_refcell_ref.stderr @@ -1,4 +1,4 @@ -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await --> $DIR/await_holding_refcell_ref.rs:7:9 | LL | let b = x.borrow(); @@ -13,7 +13,7 @@ LL | | baz().await LL | | } | |_^ -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await --> $DIR/await_holding_refcell_ref.rs:12:9 | LL | let b = x.borrow_mut(); @@ -27,7 +27,7 @@ LL | | baz().await LL | | } | |_^ -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await --> $DIR/await_holding_refcell_ref.rs:33:9 | LL | let b = x.borrow_mut(); @@ -45,7 +45,7 @@ LL | | first + second + third LL | | } | |_^ -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await --> $DIR/await_holding_refcell_ref.rs:45:9 | LL | let b = x.borrow_mut(); @@ -63,7 +63,7 @@ LL | | first + second + third LL | | } | |_^ -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await --> $DIR/await_holding_refcell_ref.rs:60:13 | LL | let b = x.borrow_mut(); @@ -77,7 +77,7 @@ LL | | baz().await LL | | }; | |_____^ -error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await. +error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await --> $DIR/await_holding_refcell_ref.rs:72:13 | LL | let b = x.borrow_mut(); diff --git a/tests/ui/box_vec.stderr b/tests/ui/box_vec.stderr index fca12eddd57..9b789334bae 100644 --- a/tests/ui/box_vec.stderr +++ b/tests/ui/box_vec.stderr @@ -5,7 +5,7 @@ LL | pub fn test(foo: Box>) { | ^^^^^^^^^^^^^^ | = note: `-D clippy::box-vec` implied by `-D warnings` - = help: `Vec` is already on the heap, `Box>` makes an extra allocation. + = help: `Vec` is already on the heap, `Box>` makes an extra allocation error: aborting due to previous error diff --git a/tests/ui/comparison_chain.stderr b/tests/ui/comparison_chain.stderr index 69db88b03b5..be25a80dde0 100644 --- a/tests/ui/comparison_chain.stderr +++ b/tests/ui/comparison_chain.stderr @@ -9,7 +9,7 @@ LL | | } | |_____^ | = note: `-D clippy::comparison-chain` implied by `-D warnings` - = help: Consider rewriting the `if` chain to use `cmp` and `match`. + = help: consider rewriting the `if` chain to use `cmp` and `match` error: `if` chain can be rewritten with `match` --> $DIR/comparison_chain.rs:27:5 @@ -23,7 +23,7 @@ LL | | c() LL | | } | |_____^ | - = help: Consider rewriting the `if` chain to use `cmp` and `match`. + = help: consider rewriting the `if` chain to use `cmp` and `match` error: `if` chain can be rewritten with `match` --> $DIR/comparison_chain.rs:35:5 @@ -37,7 +37,7 @@ LL | | c() LL | | } | |_____^ | - = help: Consider rewriting the `if` chain to use `cmp` and `match`. + = help: consider rewriting the `if` chain to use `cmp` and `match` error: `if` chain can be rewritten with `match` --> $DIR/comparison_chain.rs:43:5 @@ -51,7 +51,7 @@ LL | | c() LL | | } | |_____^ | - = help: Consider rewriting the `if` chain to use `cmp` and `match`. + = help: consider rewriting the `if` chain to use `cmp` and `match` error: `if` chain can be rewritten with `match` --> $DIR/comparison_chain.rs:117:5 @@ -63,7 +63,7 @@ LL | | b() LL | | } | |_____^ | - = help: Consider rewriting the `if` chain to use `cmp` and `match`. + = help: consider rewriting the `if` chain to use `cmp` and `match` error: `if` chain can be rewritten with `match` --> $DIR/comparison_chain.rs:123:5 @@ -77,7 +77,7 @@ LL | | c() LL | | } | |_____^ | - = help: Consider rewriting the `if` chain to use `cmp` and `match`. + = help: consider rewriting the `if` chain to use `cmp` and `match` error: `if` chain can be rewritten with `match` --> $DIR/comparison_chain.rs:131:5 @@ -91,7 +91,7 @@ LL | | c() LL | | } | |_____^ | - = help: Consider rewriting the `if` chain to use `cmp` and `match`. + = help: consider rewriting the `if` chain to use `cmp` and `match` error: aborting due to 7 previous errors diff --git a/tests/ui/drop_forget_copy.stderr b/tests/ui/drop_forget_copy.stderr index 82a4f047ba8..01de0be7cae 100644 --- a/tests/ui/drop_forget_copy.stderr +++ b/tests/ui/drop_forget_copy.stderr @@ -1,4 +1,4 @@ -error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a copy leaves the original intact. +error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a copy leaves the original intact --> $DIR/drop_forget_copy.rs:33:5 | LL | drop(s1); @@ -11,7 +11,7 @@ note: argument has type SomeStruct LL | drop(s1); | ^^ -error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a copy leaves the original intact. +error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a copy leaves the original intact --> $DIR/drop_forget_copy.rs:34:5 | LL | drop(s2); @@ -23,7 +23,7 @@ note: argument has type SomeStruct LL | drop(s2); | ^^ -error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a copy leaves the original intact. +error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a copy leaves the original intact --> $DIR/drop_forget_copy.rs:36:5 | LL | drop(s4); @@ -35,7 +35,7 @@ note: argument has type SomeStruct LL | drop(s4); | ^^ -error: calls to `std::mem::forget` with a value that implements `Copy`. Forgetting a copy leaves the original intact. +error: calls to `std::mem::forget` with a value that implements `Copy`. Forgetting a copy leaves the original intact --> $DIR/drop_forget_copy.rs:39:5 | LL | forget(s1); @@ -48,7 +48,7 @@ note: argument has type SomeStruct LL | forget(s1); | ^^ -error: calls to `std::mem::forget` with a value that implements `Copy`. Forgetting a copy leaves the original intact. +error: calls to `std::mem::forget` with a value that implements `Copy`. Forgetting a copy leaves the original intact --> $DIR/drop_forget_copy.rs:40:5 | LL | forget(s2); @@ -60,7 +60,7 @@ note: argument has type SomeStruct LL | forget(s2); | ^^ -error: calls to `std::mem::forget` with a value that implements `Copy`. Forgetting a copy leaves the original intact. +error: calls to `std::mem::forget` with a value that implements `Copy`. Forgetting a copy leaves the original intact --> $DIR/drop_forget_copy.rs:42:5 | LL | forget(s4); diff --git a/tests/ui/drop_ref.stderr b/tests/ui/drop_ref.stderr index 10087cb4820..531849f0680 100644 --- a/tests/ui/drop_ref.stderr +++ b/tests/ui/drop_ref.stderr @@ -1,4 +1,4 @@ -error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing --> $DIR/drop_ref.rs:11:5 | LL | drop(&SomeStruct); @@ -11,7 +11,7 @@ note: argument has type `&SomeStruct` LL | drop(&SomeStruct); | ^^^^^^^^^^^ -error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing --> $DIR/drop_ref.rs:14:5 | LL | drop(&owned1); @@ -23,7 +23,7 @@ note: argument has type `&SomeStruct` LL | drop(&owned1); | ^^^^^^^ -error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing --> $DIR/drop_ref.rs:15:5 | LL | drop(&&owned1); @@ -35,7 +35,7 @@ note: argument has type `&&SomeStruct` LL | drop(&&owned1); | ^^^^^^^^ -error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing --> $DIR/drop_ref.rs:16:5 | LL | drop(&mut owned1); @@ -47,7 +47,7 @@ note: argument has type `&mut SomeStruct` LL | drop(&mut owned1); | ^^^^^^^^^^^ -error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing --> $DIR/drop_ref.rs:20:5 | LL | drop(reference1); @@ -59,7 +59,7 @@ note: argument has type `&SomeStruct` LL | drop(reference1); | ^^^^^^^^^^ -error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing --> $DIR/drop_ref.rs:23:5 | LL | drop(reference2); @@ -71,7 +71,7 @@ note: argument has type `&mut SomeStruct` LL | drop(reference2); | ^^^^^^^^^^ -error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing --> $DIR/drop_ref.rs:26:5 | LL | drop(reference3); @@ -83,7 +83,7 @@ note: argument has type `&SomeStruct` LL | drop(reference3); | ^^^^^^^^^^ -error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing --> $DIR/drop_ref.rs:31:5 | LL | drop(&val); @@ -95,7 +95,7 @@ note: argument has type `&T` LL | drop(&val); | ^^^^ -error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing. +error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing --> $DIR/drop_ref.rs:39:5 | LL | std::mem::drop(&SomeStruct); diff --git a/tests/ui/explicit_counter_loop.stderr b/tests/ui/explicit_counter_loop.stderr index 931af46efe6..4cbacffe87b 100644 --- a/tests/ui/explicit_counter_loop.stderr +++ b/tests/ui/explicit_counter_loop.stderr @@ -1,4 +1,4 @@ -error: the variable `_index` is used as a loop counter. +error: the variable `_index` is used as a loop counter --> $DIR/explicit_counter_loop.rs:6:5 | LL | for _v in &vec { @@ -6,37 +6,37 @@ LL | for _v in &vec { | = note: `-D clippy::explicit-counter-loop` implied by `-D warnings` -error: the variable `_index` is used as a loop counter. +error: the variable `_index` is used as a loop counter --> $DIR/explicit_counter_loop.rs:12:5 | LL | for _v in &vec { | ^^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in vec.iter().enumerate()` -error: the variable `_index` is used as a loop counter. +error: the variable `_index` is used as a loop counter --> $DIR/explicit_counter_loop.rs:17:5 | LL | for _v in &mut vec { | ^^^^^^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in vec.iter_mut().enumerate()` -error: the variable `_index` is used as a loop counter. +error: the variable `_index` is used as a loop counter --> $DIR/explicit_counter_loop.rs:22:5 | LL | for _v in vec { | ^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in vec.into_iter().enumerate()` -error: the variable `count` is used as a loop counter. +error: the variable `count` is used as a loop counter --> $DIR/explicit_counter_loop.rs:61:9 | LL | for ch in text.chars() { | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `for (count, ch) in text.chars().enumerate()` -error: the variable `count` is used as a loop counter. +error: the variable `count` is used as a loop counter --> $DIR/explicit_counter_loop.rs:72:9 | LL | for ch in text.chars() { | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `for (count, ch) in text.chars().enumerate()` -error: the variable `count` is used as a loop counter. +error: the variable `count` is used as a loop counter --> $DIR/explicit_counter_loop.rs:130:9 | LL | for _i in 3..10 { diff --git a/tests/ui/fallible_impl_from.stderr b/tests/ui/fallible_impl_from.stderr index f787b30bdab..a938d234fa0 100644 --- a/tests/ui/fallible_impl_from.stderr +++ b/tests/ui/fallible_impl_from.stderr @@ -13,7 +13,7 @@ note: the lint level is defined here | LL | #![deny(clippy::fallible_impl_from)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail. + = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail note: potential failure(s) --> $DIR/fallible_impl_from.rs:7:13 | @@ -32,7 +32,7 @@ LL | | } LL | | } | |_^ | - = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail. + = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail note: potential failure(s) --> $DIR/fallible_impl_from.rs:29:13 | @@ -52,7 +52,7 @@ LL | | } LL | | } | |_^ | - = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail. + = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail note: potential failure(s) --> $DIR/fallible_impl_from.rs:37:17 | @@ -79,7 +79,7 @@ LL | | } LL | | } | |_^ | - = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail. + = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail note: potential failure(s) --> $DIR/fallible_impl_from.rs:55:12 | diff --git a/tests/ui/filter_map_next.stderr b/tests/ui/filter_map_next.stderr index 45427684d96..ddc982c93fe 100644 --- a/tests/ui/filter_map_next.stderr +++ b/tests/ui/filter_map_next.stderr @@ -1,4 +1,4 @@ -error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead. +error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead --> $DIR/filter_map_next.rs:7:26 | LL | let _: Option = vec![1, 2, 3, 4, 5, 6] diff --git a/tests/ui/filter_map_next_fixable.stderr b/tests/ui/filter_map_next_fixable.stderr index 6c2530e0379..3bb062ffd7a 100644 --- a/tests/ui/filter_map_next_fixable.stderr +++ b/tests/ui/filter_map_next_fixable.stderr @@ -1,4 +1,4 @@ -error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead. +error: called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find_map(..)` instead --> $DIR/filter_map_next_fixable.rs:8:32 | LL | let element: Option = a.iter().filter_map(|s| s.parse().ok()).next(); diff --git a/tests/ui/for_loops_over_fallibles.stderr b/tests/ui/for_loops_over_fallibles.stderr index bef228d4b93..52b94875aec 100644 --- a/tests/ui/for_loops_over_fallibles.stderr +++ b/tests/ui/for_loops_over_fallibles.stderr @@ -1,4 +1,4 @@ -error: for loop over `option`, which is an `Option`. This is more readably written as an `if let` statement. +error: for loop over `option`, which is an `Option`. This is more readably written as an `if let` statement --> $DIR/for_loops_over_fallibles.rs:9:14 | LL | for x in option { @@ -7,7 +7,7 @@ LL | for x in option { = note: `-D clippy::for-loops-over-fallibles` implied by `-D warnings` = help: consider replacing `for x in option` with `if let Some(x) = option` -error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement. +error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement --> $DIR/for_loops_over_fallibles.rs:14:14 | LL | for x in result { @@ -15,7 +15,7 @@ LL | for x in result { | = help: consider replacing `for x in result` with `if let Ok(x) = result` -error: for loop over `option.ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement. +error: for loop over `option.ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement --> $DIR/for_loops_over_fallibles.rs:18:14 | LL | for x in option.ok_or("x not found") { @@ -31,7 +31,7 @@ LL | for x in v.iter().next() { | = note: `#[deny(clippy::iter_next_loop)]` on by default -error: for loop over `v.iter().next().and(Some(0))`, which is an `Option`. This is more readably written as an `if let` statement. +error: for loop over `v.iter().next().and(Some(0))`, which is an `Option`. This is more readably written as an `if let` statement --> $DIR/for_loops_over_fallibles.rs:29:14 | LL | for x in v.iter().next().and(Some(0)) { @@ -39,7 +39,7 @@ LL | for x in v.iter().next().and(Some(0)) { | = help: consider replacing `for x in v.iter().next().and(Some(0))` with `if let Some(x) = v.iter().next().and(Some(0))` -error: for loop over `v.iter().next().ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement. +error: for loop over `v.iter().next().ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement --> $DIR/for_loops_over_fallibles.rs:33:14 | LL | for x in v.iter().next().ok_or("x not found") { diff --git a/tests/ui/forget_ref.stderr b/tests/ui/forget_ref.stderr index b2c7f2023bf..73409388ed1 100644 --- a/tests/ui/forget_ref.stderr +++ b/tests/ui/forget_ref.stderr @@ -1,4 +1,4 @@ -error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing --> $DIR/forget_ref.rs:10:5 | LL | forget(&SomeStruct); @@ -11,7 +11,7 @@ note: argument has type `&SomeStruct` LL | forget(&SomeStruct); | ^^^^^^^^^^^ -error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing --> $DIR/forget_ref.rs:13:5 | LL | forget(&owned); @@ -23,7 +23,7 @@ note: argument has type `&SomeStruct` LL | forget(&owned); | ^^^^^^ -error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing --> $DIR/forget_ref.rs:14:5 | LL | forget(&&owned); @@ -35,7 +35,7 @@ note: argument has type `&&SomeStruct` LL | forget(&&owned); | ^^^^^^^ -error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing --> $DIR/forget_ref.rs:15:5 | LL | forget(&mut owned); @@ -47,7 +47,7 @@ note: argument has type `&mut SomeStruct` LL | forget(&mut owned); | ^^^^^^^^^^ -error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing --> $DIR/forget_ref.rs:19:5 | LL | forget(&*reference1); @@ -59,7 +59,7 @@ note: argument has type `&SomeStruct` LL | forget(&*reference1); | ^^^^^^^^^^^^ -error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing --> $DIR/forget_ref.rs:22:5 | LL | forget(reference2); @@ -71,7 +71,7 @@ note: argument has type `&mut SomeStruct` LL | forget(reference2); | ^^^^^^^^^^ -error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing --> $DIR/forget_ref.rs:25:5 | LL | forget(reference3); @@ -83,7 +83,7 @@ note: argument has type `&SomeStruct` LL | forget(reference3); | ^^^^^^^^^^ -error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing --> $DIR/forget_ref.rs:30:5 | LL | forget(&val); @@ -95,7 +95,7 @@ note: argument has type `&T` LL | forget(&val); | ^^^^ -error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing. +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing --> $DIR/forget_ref.rs:38:5 | LL | std::mem::forget(&SomeStruct); diff --git a/tests/ui/indexing_slicing_index.stderr b/tests/ui/indexing_slicing_index.stderr index 2f6c9e2f4e5..76ecec33484 100644 --- a/tests/ui/indexing_slicing_index.stderr +++ b/tests/ui/indexing_slicing_index.stderr @@ -1,51 +1,51 @@ -error: indexing may panic. +error: indexing may panic --> $DIR/indexing_slicing_index.rs:10:5 | LL | x[index]; | ^^^^^^^^ | = note: `-D clippy::indexing-slicing` implied by `-D warnings` - = help: Consider using `.get(n)` or `.get_mut(n)` instead + = help: consider using `.get(n)` or `.get_mut(n)` instead -error: indexing may panic. +error: indexing may panic --> $DIR/indexing_slicing_index.rs:22:5 | LL | v[0]; | ^^^^ | - = help: Consider using `.get(n)` or `.get_mut(n)` instead + = help: consider using `.get(n)` or `.get_mut(n)` instead -error: indexing may panic. +error: indexing may panic --> $DIR/indexing_slicing_index.rs:23:5 | LL | v[10]; | ^^^^^ | - = help: Consider using `.get(n)` or `.get_mut(n)` instead + = help: consider using `.get(n)` or `.get_mut(n)` instead -error: indexing may panic. +error: indexing may panic --> $DIR/indexing_slicing_index.rs:24:5 | LL | v[1 << 3]; | ^^^^^^^^^ | - = help: Consider using `.get(n)` or `.get_mut(n)` instead + = help: consider using `.get(n)` or `.get_mut(n)` instead -error: indexing may panic. +error: indexing may panic --> $DIR/indexing_slicing_index.rs:30:5 | LL | v[N]; | ^^^^ | - = help: Consider using `.get(n)` or `.get_mut(n)` instead + = help: consider using `.get(n)` or `.get_mut(n)` instead -error: indexing may panic. +error: indexing may panic --> $DIR/indexing_slicing_index.rs:31:5 | LL | v[M]; | ^^^^ | - = help: Consider using `.get(n)` or `.get_mut(n)` instead + = help: consider using `.get(n)` or `.get_mut(n)` instead error: aborting due to 6 previous errors diff --git a/tests/ui/indexing_slicing_slice.stderr b/tests/ui/indexing_slicing_slice.stderr index 2231deee833..f70722b92a5 100644 --- a/tests/ui/indexing_slicing_slice.stderr +++ b/tests/ui/indexing_slicing_slice.stderr @@ -1,51 +1,51 @@ -error: slicing may panic. +error: slicing may panic --> $DIR/indexing_slicing_slice.rs:12:6 | LL | &x[index..]; | ^^^^^^^^^^ | = note: `-D clippy::indexing-slicing` implied by `-D warnings` - = help: Consider using `.get(n..)` or .get_mut(n..)` instead + = help: consider using `.get(n..)` or .get_mut(n..)` instead -error: slicing may panic. +error: slicing may panic --> $DIR/indexing_slicing_slice.rs:13:6 | LL | &x[..index]; | ^^^^^^^^^^ | - = help: Consider using `.get(..n)`or `.get_mut(..n)` instead + = help: consider using `.get(..n)`or `.get_mut(..n)` instead -error: slicing may panic. +error: slicing may panic --> $DIR/indexing_slicing_slice.rs:14:6 | LL | &x[index_from..index_to]; | ^^^^^^^^^^^^^^^^^^^^^^^ | - = help: Consider using `.get(n..m)` or `.get_mut(n..m)` instead + = help: consider using `.get(n..m)` or `.get_mut(n..m)` instead -error: slicing may panic. +error: slicing may panic --> $DIR/indexing_slicing_slice.rs:15:6 | LL | &x[index_from..][..index_to]; // Two lint reports, one for [index_from..] and another for [..index_to]. | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: Consider using `.get(..n)`or `.get_mut(..n)` instead + = help: consider using `.get(..n)`or `.get_mut(..n)` instead -error: slicing may panic. +error: slicing may panic --> $DIR/indexing_slicing_slice.rs:15:6 | LL | &x[index_from..][..index_to]; // Two lint reports, one for [index_from..] and another for [..index_to]. | ^^^^^^^^^^^^^^^ | - = help: Consider using `.get(n..)` or .get_mut(n..)` instead + = help: consider using `.get(n..)` or .get_mut(n..)` instead -error: slicing may panic. +error: slicing may panic --> $DIR/indexing_slicing_slice.rs:16:6 | LL | &x[5..][..10]; // Two lint reports, one for out of bounds [5..] and another for slicing [..10]. | ^^^^^^^^^^^^ | - = help: Consider using `.get(..n)`or `.get_mut(..n)` instead + = help: consider using `.get(..n)`or `.get_mut(..n)` instead error: range is out of bounds --> $DIR/indexing_slicing_slice.rs:16:8 @@ -55,21 +55,21 @@ LL | &x[5..][..10]; // Two lint reports, one for out of bounds [5..] and ano | = note: `-D clippy::out-of-bounds-indexing` implied by `-D warnings` -error: slicing may panic. +error: slicing may panic --> $DIR/indexing_slicing_slice.rs:17:6 | LL | &x[0..][..3]; | ^^^^^^^^^^^ | - = help: Consider using `.get(..n)`or `.get_mut(..n)` instead + = help: consider using `.get(..n)`or `.get_mut(..n)` instead -error: slicing may panic. +error: slicing may panic --> $DIR/indexing_slicing_slice.rs:18:6 | LL | &x[1..][..5]; | ^^^^^^^^^^^ | - = help: Consider using `.get(..n)`or `.get_mut(..n)` instead + = help: consider using `.get(..n)`or `.get_mut(..n)` instead error: range is out of bounds --> $DIR/indexing_slicing_slice.rs:25:12 @@ -83,21 +83,21 @@ error: range is out of bounds LL | &y[..=4]; | ^ -error: slicing may panic. +error: slicing may panic --> $DIR/indexing_slicing_slice.rs:31:6 | LL | &v[10..100]; | ^^^^^^^^^^ | - = help: Consider using `.get(n..m)` or `.get_mut(n..m)` instead + = help: consider using `.get(n..m)` or `.get_mut(n..m)` instead -error: slicing may panic. +error: slicing may panic --> $DIR/indexing_slicing_slice.rs:32:6 | LL | &x[10..][..100]; // Two lint reports, one for [10..] and another for [..100]. | ^^^^^^^^^^^^^^ | - = help: Consider using `.get(..n)`or `.get_mut(..n)` instead + = help: consider using `.get(..n)`or `.get_mut(..n)` instead error: range is out of bounds --> $DIR/indexing_slicing_slice.rs:32:8 @@ -105,21 +105,21 @@ error: range is out of bounds LL | &x[10..][..100]; // Two lint reports, one for [10..] and another for [..100]. | ^^ -error: slicing may panic. +error: slicing may panic --> $DIR/indexing_slicing_slice.rs:33:6 | LL | &v[10..]; | ^^^^^^^ | - = help: Consider using `.get(n..)` or .get_mut(n..)` instead + = help: consider using `.get(n..)` or .get_mut(n..)` instead -error: slicing may panic. +error: slicing may panic --> $DIR/indexing_slicing_slice.rs:34:6 | LL | &v[..100]; | ^^^^^^^^ | - = help: Consider using `.get(..n)`or `.get_mut(..n)` instead + = help: consider using `.get(..n)`or `.get_mut(..n)` instead error: aborting due to 16 previous errors diff --git a/tests/ui/integer_division.stderr b/tests/ui/integer_division.stderr index 72a232ef3d7..cbb7f881424 100644 --- a/tests/ui/integer_division.stderr +++ b/tests/ui/integer_division.stderr @@ -5,7 +5,7 @@ LL | let n = 1 / 2; | ^^^^^ | = note: `-D clippy::integer-division` implied by `-D warnings` - = help: division of integers may cause loss of precision. consider using floats. + = help: division of integers may cause loss of precision. consider using floats error: integer division --> $DIR/integer_division.rs:6:13 @@ -13,7 +13,7 @@ error: integer division LL | let o = 1 / two; | ^^^^^^^ | - = help: division of integers may cause loss of precision. consider using floats. + = help: division of integers may cause loss of precision. consider using floats error: integer division --> $DIR/integer_division.rs:7:13 @@ -21,7 +21,7 @@ error: integer division LL | let p = two / 4; | ^^^^^^^ | - = help: division of integers may cause loss of precision. consider using floats. + = help: division of integers may cause loss of precision. consider using floats error: aborting due to 3 previous errors diff --git a/tests/ui/methods.stderr b/tests/ui/methods.stderr index 33aba630a53..4643e09e270 100644 --- a/tests/ui/methods.stderr +++ b/tests/ui/methods.stderr @@ -8,7 +8,7 @@ LL | | } | = note: `-D clippy::new-ret-no-self` implied by `-D warnings` -error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead. +error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead --> $DIR/methods.rs:126:13 | LL | let _ = v.iter().filter(|&x| { diff --git a/tests/ui/methods_fixable.stderr b/tests/ui/methods_fixable.stderr index 70e7c3dea54..852f48e32d6 100644 --- a/tests/ui/methods_fixable.stderr +++ b/tests/ui/methods_fixable.stderr @@ -1,4 +1,4 @@ -error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead. +error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead --> $DIR/methods_fixable.rs:10:13 | LL | let _ = v.iter().filter(|&x| *x < 0).next(); diff --git a/tests/ui/mismatched_target_os_unix.stderr b/tests/ui/mismatched_target_os_unix.stderr index fe9aeedb59c..ea39f5b5577 100644 --- a/tests/ui/mismatched_target_os_unix.stderr +++ b/tests/ui/mismatched_target_os_unix.stderr @@ -7,7 +7,7 @@ LL | #[cfg(linux)] | help: try: `target_os = "linux"` | = note: `-D clippy::mismatched-target-os` implied by `-D warnings` - = help: Did you mean `unix`? + = help: did you mean `unix`? error: operating system used in target family position --> $DIR/mismatched_target_os_unix.rs:9:1 @@ -17,7 +17,7 @@ LL | #[cfg(freebsd)] | | | help: try: `target_os = "freebsd"` | - = help: Did you mean `unix`? + = help: did you mean `unix`? error: operating system used in target family position --> $DIR/mismatched_target_os_unix.rs:12:1 @@ -27,7 +27,7 @@ LL | #[cfg(dragonfly)] | | | help: try: `target_os = "dragonfly"` | - = help: Did you mean `unix`? + = help: did you mean `unix`? error: operating system used in target family position --> $DIR/mismatched_target_os_unix.rs:15:1 @@ -37,7 +37,7 @@ LL | #[cfg(openbsd)] | | | help: try: `target_os = "openbsd"` | - = help: Did you mean `unix`? + = help: did you mean `unix`? error: operating system used in target family position --> $DIR/mismatched_target_os_unix.rs:18:1 @@ -47,7 +47,7 @@ LL | #[cfg(netbsd)] | | | help: try: `target_os = "netbsd"` | - = help: Did you mean `unix`? + = help: did you mean `unix`? error: operating system used in target family position --> $DIR/mismatched_target_os_unix.rs:21:1 @@ -57,7 +57,7 @@ LL | #[cfg(macos)] | | | help: try: `target_os = "macos"` | - = help: Did you mean `unix`? + = help: did you mean `unix`? error: operating system used in target family position --> $DIR/mismatched_target_os_unix.rs:24:1 @@ -67,7 +67,7 @@ LL | #[cfg(ios)] | | | help: try: `target_os = "ios"` | - = help: Did you mean `unix`? + = help: did you mean `unix`? error: operating system used in target family position --> $DIR/mismatched_target_os_unix.rs:27:1 @@ -77,7 +77,7 @@ LL | #[cfg(android)] | | | help: try: `target_os = "android"` | - = help: Did you mean `unix`? + = help: did you mean `unix`? error: operating system used in target family position --> $DIR/mismatched_target_os_unix.rs:30:1 @@ -87,7 +87,7 @@ LL | #[cfg(emscripten)] | | | help: try: `target_os = "emscripten"` | - = help: Did you mean `unix`? + = help: did you mean `unix`? error: operating system used in target family position --> $DIR/mismatched_target_os_unix.rs:33:1 @@ -97,7 +97,7 @@ LL | #[cfg(fuchsia)] | | | help: try: `target_os = "fuchsia"` | - = help: Did you mean `unix`? + = help: did you mean `unix`? error: operating system used in target family position --> $DIR/mismatched_target_os_unix.rs:36:1 @@ -107,7 +107,7 @@ LL | #[cfg(haiku)] | | | help: try: `target_os = "haiku"` | - = help: Did you mean `unix`? + = help: did you mean `unix`? error: operating system used in target family position --> $DIR/mismatched_target_os_unix.rs:39:1 @@ -117,7 +117,7 @@ LL | #[cfg(illumos)] | | | help: try: `target_os = "illumos"` | - = help: Did you mean `unix`? + = help: did you mean `unix`? error: operating system used in target family position --> $DIR/mismatched_target_os_unix.rs:42:1 @@ -127,7 +127,7 @@ LL | #[cfg(l4re)] | | | help: try: `target_os = "l4re"` | - = help: Did you mean `unix`? + = help: did you mean `unix`? error: operating system used in target family position --> $DIR/mismatched_target_os_unix.rs:45:1 @@ -137,7 +137,7 @@ LL | #[cfg(redox)] | | | help: try: `target_os = "redox"` | - = help: Did you mean `unix`? + = help: did you mean `unix`? error: operating system used in target family position --> $DIR/mismatched_target_os_unix.rs:48:1 @@ -147,7 +147,7 @@ LL | #[cfg(solaris)] | | | help: try: `target_os = "solaris"` | - = help: Did you mean `unix`? + = help: did you mean `unix`? error: operating system used in target family position --> $DIR/mismatched_target_os_unix.rs:51:1 @@ -157,7 +157,7 @@ LL | #[cfg(vxworks)] | | | help: try: `target_os = "vxworks"` | - = help: Did you mean `unix`? + = help: did you mean `unix`? error: operating system used in target family position --> $DIR/mismatched_target_os_unix.rs:55:1 @@ -165,7 +165,7 @@ error: operating system used in target family position LL | #[cfg(all(not(any(solaris, linux)), freebsd))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: Did you mean `unix`? + = help: did you mean `unix`? help: try | LL | #[cfg(all(not(any(target_os = "solaris", linux)), freebsd))] diff --git a/tests/ui/needless_collect_indirect.stderr b/tests/ui/needless_collect_indirect.stderr index fb807da5f8a..76e789d9052 100644 --- a/tests/ui/needless_collect_indirect.stderr +++ b/tests/ui/needless_collect_indirect.stderr @@ -6,7 +6,7 @@ LL | | indirect_iter.into_iter().map(|x| (x, x + 1)).collect:: | |____^ | = note: `-D clippy::needless-collect` implied by `-D warnings` -help: Use the original Iterator instead of collecting it and then producing a new one +help: use the original Iterator instead of collecting it and then producing a new one | LL | LL | sample.iter().map(|x| (x, x + 1)).collect::>(); @@ -19,7 +19,7 @@ LL | / let indirect_len = sample.iter().collect::>(); LL | | indirect_len.len(); | |____^ | -help: Take the original Iterator's count instead of collecting it and finding the length +help: take the original Iterator's count instead of collecting it and finding the length | LL | LL | sample.iter().count(); @@ -32,7 +32,7 @@ LL | / let indirect_empty = sample.iter().collect::>(); LL | | indirect_empty.is_empty(); | |____^ | -help: Check if the original Iterator has anything instead of collecting it and seeing if it's empty +help: check if the original Iterator has anything instead of collecting it and seeing if it's empty | LL | LL | sample.iter().next().is_none(); @@ -45,7 +45,7 @@ LL | / let indirect_contains = sample.iter().collect::>(); LL | | indirect_contains.contains(&&5); | |____^ | -help: Check if the original Iterator contains an element instead of collecting then checking +help: check if the original Iterator contains an element instead of collecting then checking | LL | LL | sample.iter().any(|x| x == &5); @@ -58,7 +58,7 @@ LL | / let non_copy_contains = sample.into_iter().collect::>(); LL | | non_copy_contains.contains(&a); | |____^ | -help: Check if the original Iterator contains an element instead of collecting then checking +help: check if the original Iterator contains an element instead of collecting then checking | LL | LL | sample.into_iter().any(|x| x == a); diff --git a/tests/ui/needless_question_mark.stderr b/tests/ui/needless_question_mark.stderr index 567bc518a3f..983c56031d8 100644 --- a/tests/ui/needless_question_mark.stderr +++ b/tests/ui/needless_question_mark.stderr @@ -1,4 +1,4 @@ -error: Question mark operator is useless here +error: question mark operator is useless here --> $DIR/needless_question_mark.rs:23:12 | LL | return Some(to.magic?); @@ -6,79 +6,79 @@ LL | return Some(to.magic?); | = note: `-D clippy::needless-question-mark` implied by `-D warnings` -error: Question mark operator is useless here +error: question mark operator is useless here --> $DIR/needless_question_mark.rs:31:12 | LL | return Some(to.magic?) | ^^^^^^^^^^^^^^^ help: try: `to.magic` -error: Question mark operator is useless here +error: question mark operator is useless here --> $DIR/needless_question_mark.rs:36:5 | LL | Some(to.magic?) | ^^^^^^^^^^^^^^^ help: try: `to.magic` -error: Question mark operator is useless here +error: question mark operator is useless here --> $DIR/needless_question_mark.rs:41:21 | LL | to.and_then(|t| Some(t.magic?)) | ^^^^^^^^^^^^^^ help: try: `t.magic` -error: Question mark operator is useless here +error: question mark operator is useless here --> $DIR/needless_question_mark.rs:50:9 | LL | Some(t.magic?) | ^^^^^^^^^^^^^^ help: try: `t.magic` -error: Question mark operator is useless here +error: question mark operator is useless here --> $DIR/needless_question_mark.rs:55:12 | LL | return Ok(tr.magic?); | ^^^^^^^^^^^^^ help: try: `tr.magic` -error: Question mark operator is useless here +error: question mark operator is useless here --> $DIR/needless_question_mark.rs:62:12 | LL | return Ok(tr.magic?) | ^^^^^^^^^^^^^ help: try: `tr.magic` -error: Question mark operator is useless here +error: question mark operator is useless here --> $DIR/needless_question_mark.rs:66:5 | LL | Ok(tr.magic?) | ^^^^^^^^^^^^^ help: try: `tr.magic` -error: Question mark operator is useless here +error: question mark operator is useless here --> $DIR/needless_question_mark.rs:70:21 | LL | tr.and_then(|t| Ok(t.magic?)) | ^^^^^^^^^^^^ help: try: `t.magic` -error: Question mark operator is useless here +error: question mark operator is useless here --> $DIR/needless_question_mark.rs:78:9 | LL | Ok(t.magic?) | ^^^^^^^^^^^^ help: try: `t.magic` -error: Question mark operator is useless here +error: question mark operator is useless here --> $DIR/needless_question_mark.rs:85:16 | LL | return Ok(t.magic?); | ^^^^^^^^^^^^ help: try: `t.magic` -error: Question mark operator is useless here +error: question mark operator is useless here --> $DIR/needless_question_mark.rs:138:9 | LL | Ok(to.magic?) // should be triggered | ^^^^^^^^^^^^^ help: try: `to.magic` -error: Question mark operator is useless here +error: question mark operator is useless here --> $DIR/needless_question_mark.rs:154:9 | LL | Some(to.magic?) // should be triggered | ^^^^^^^^^^^^^^^ help: try: `to.magic` -error: Question mark operator is useless here +error: question mark operator is useless here --> $DIR/needless_question_mark.rs:162:9 | LL | Ok(to.magic?) // should be triggered diff --git a/tests/ui/needless_range_loop.stderr b/tests/ui/needless_range_loop.stderr index c50c4931fb4..c898cd64a93 100644 --- a/tests/ui/needless_range_loop.stderr +++ b/tests/ui/needless_range_loop.stderr @@ -1,4 +1,4 @@ -error: the loop variable `i` is only used to index `vec`. +error: the loop variable `i` is only used to index `vec` --> $DIR/needless_range_loop.rs:10:14 | LL | for i in 0..vec.len() { @@ -10,7 +10,7 @@ help: consider using an iterator LL | for in &vec { | ^^^^^^ ^^^^ -error: the loop variable `i` is only used to index `vec`. +error: the loop variable `i` is only used to index `vec` --> $DIR/needless_range_loop.rs:19:14 | LL | for i in 0..vec.len() { @@ -21,7 +21,7 @@ help: consider using an iterator LL | for in &vec { | ^^^^^^ ^^^^ -error: the loop variable `j` is only used to index `STATIC`. +error: the loop variable `j` is only used to index `STATIC` --> $DIR/needless_range_loop.rs:24:14 | LL | for j in 0..4 { @@ -32,7 +32,7 @@ help: consider using an iterator LL | for in &STATIC { | ^^^^^^ ^^^^^^^ -error: the loop variable `j` is only used to index `CONST`. +error: the loop variable `j` is only used to index `CONST` --> $DIR/needless_range_loop.rs:28:14 | LL | for j in 0..4 { @@ -54,7 +54,7 @@ help: consider using an iterator LL | for (i, ) in vec.iter().enumerate() { | ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^ -error: the loop variable `i` is only used to index `vec2`. +error: the loop variable `i` is only used to index `vec2` --> $DIR/needless_range_loop.rs:40:14 | LL | for i in 0..vec.len() { @@ -65,7 +65,7 @@ help: consider using an iterator LL | for in vec2.iter().take(vec.len()) { | ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: the loop variable `i` is only used to index `vec`. +error: the loop variable `i` is only used to index `vec` --> $DIR/needless_range_loop.rs:44:14 | LL | for i in 5..vec.len() { @@ -76,7 +76,7 @@ help: consider using an iterator LL | for in vec.iter().skip(5) { | ^^^^^^ ^^^^^^^^^^^^^^^^^^ -error: the loop variable `i` is only used to index `vec`. +error: the loop variable `i` is only used to index `vec` --> $DIR/needless_range_loop.rs:48:14 | LL | for i in 0..MAX_LEN { @@ -87,7 +87,7 @@ help: consider using an iterator LL | for in vec.iter().take(MAX_LEN) { | ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^ -error: the loop variable `i` is only used to index `vec`. +error: the loop variable `i` is only used to index `vec` --> $DIR/needless_range_loop.rs:52:14 | LL | for i in 0..=MAX_LEN { @@ -98,7 +98,7 @@ help: consider using an iterator LL | for in vec.iter().take(MAX_LEN + 1) { | ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: the loop variable `i` is only used to index `vec`. +error: the loop variable `i` is only used to index `vec` --> $DIR/needless_range_loop.rs:56:14 | LL | for i in 5..10 { @@ -109,7 +109,7 @@ help: consider using an iterator LL | for in vec.iter().take(10).skip(5) { | ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: the loop variable `i` is only used to index `vec`. +error: the loop variable `i` is only used to index `vec` --> $DIR/needless_range_loop.rs:60:14 | LL | for i in 5..=10 { diff --git a/tests/ui/needless_range_loop2.stderr b/tests/ui/needless_range_loop2.stderr index c54ab5ec980..2e1f0fd0299 100644 --- a/tests/ui/needless_range_loop2.stderr +++ b/tests/ui/needless_range_loop2.stderr @@ -1,4 +1,4 @@ -error: the loop variable `i` is only used to index `ns`. +error: the loop variable `i` is only used to index `ns` --> $DIR/needless_range_loop2.rs:10:14 | LL | for i in 3..10 { @@ -10,7 +10,7 @@ help: consider using an iterator LL | for in ns.iter().take(10).skip(3) { | ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: the loop variable `i` is only used to index `ms`. +error: the loop variable `i` is only used to index `ms` --> $DIR/needless_range_loop2.rs:31:14 | LL | for i in 0..ms.len() { @@ -21,7 +21,7 @@ help: consider using an iterator LL | for in &mut ms { | ^^^^^^ ^^^^^^^ -error: the loop variable `i` is only used to index `ms`. +error: the loop variable `i` is only used to index `ms` --> $DIR/needless_range_loop2.rs:37:14 | LL | for i in 0..ms.len() { @@ -32,7 +32,7 @@ help: consider using an iterator LL | for in &mut ms { | ^^^^^^ ^^^^^^^ -error: the loop variable `i` is only used to index `vec`. +error: the loop variable `i` is only used to index `vec` --> $DIR/needless_range_loop2.rs:61:14 | LL | for i in x..x + 4 { @@ -43,7 +43,7 @@ help: consider using an iterator LL | for in vec.iter_mut().skip(x).take(4) { | ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: the loop variable `i` is only used to index `vec`. +error: the loop variable `i` is only used to index `vec` --> $DIR/needless_range_loop2.rs:68:14 | LL | for i in x..=x + 4 { @@ -54,7 +54,7 @@ help: consider using an iterator LL | for in vec.iter_mut().skip(x).take(4 + 1) { | ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: the loop variable `i` is only used to index `arr`. +error: the loop variable `i` is only used to index `arr` --> $DIR/needless_range_loop2.rs:74:14 | LL | for i in 0..3 { @@ -65,7 +65,7 @@ help: consider using an iterator LL | for in &arr { | ^^^^^^ ^^^^ -error: the loop variable `i` is only used to index `arr`. +error: the loop variable `i` is only used to index `arr` --> $DIR/needless_range_loop2.rs:78:14 | LL | for i in 0..2 { @@ -76,7 +76,7 @@ help: consider using an iterator LL | for in arr.iter().take(2) { | ^^^^^^ ^^^^^^^^^^^^^^^^^^ -error: the loop variable `i` is only used to index `arr`. +error: the loop variable `i` is only used to index `arr` --> $DIR/needless_range_loop2.rs:82:14 | LL | for i in 1..3 { diff --git a/tests/ui/ptr_arg.stderr b/tests/ui/ptr_arg.stderr index 708318bbe29..d302b16d4b7 100644 --- a/tests/ui/ptr_arg.stderr +++ b/tests/ui/ptr_arg.stderr @@ -1,4 +1,4 @@ -error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices. +error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices --> $DIR/ptr_arg.rs:7:14 | LL | fn do_vec(x: &Vec) { @@ -6,25 +6,25 @@ LL | fn do_vec(x: &Vec) { | = note: `-D clippy::ptr-arg` implied by `-D warnings` -error: writing `&String` instead of `&str` involves a new object where a slice will do. +error: writing `&String` instead of `&str` involves a new object where a slice will do --> $DIR/ptr_arg.rs:16:14 | LL | fn do_str(x: &String) { | ^^^^^^^ help: change this to: `&str` -error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do. +error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do --> $DIR/ptr_arg.rs:25:15 | LL | fn do_path(x: &PathBuf) { | ^^^^^^^^ help: change this to: `&Path` -error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices. +error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices --> $DIR/ptr_arg.rs:38:18 | LL | fn do_vec(x: &Vec); | ^^^^^^^^^ help: change this to: `&[i64]` -error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices. +error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices --> $DIR/ptr_arg.rs:51:14 | LL | fn cloned(x: &Vec) -> Vec { @@ -43,7 +43,7 @@ help: change `x.clone()` to LL | x.to_owned() | -error: writing `&String` instead of `&str` involves a new object where a slice will do. +error: writing `&String` instead of `&str` involves a new object where a slice will do --> $DIR/ptr_arg.rs:60:18 | LL | fn str_cloned(x: &String) -> String { @@ -66,7 +66,7 @@ help: change `x.clone()` to LL | x.to_string() | -error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do. +error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do --> $DIR/ptr_arg.rs:68:19 | LL | fn path_cloned(x: &PathBuf) -> PathBuf { @@ -89,7 +89,7 @@ help: change `x.clone()` to LL | x.to_path_buf() | -error: writing `&String` instead of `&str` involves a new object where a slice will do. +error: writing `&String` instead of `&str` involves a new object where a slice will do --> $DIR/ptr_arg.rs:76:44 | LL | fn false_positive_capacity(x: &Vec, y: &String) { @@ -108,13 +108,13 @@ help: change `y.as_str()` to LL | let c = y; | ^ -error: using a reference to `Cow` is not recommended. +error: using a reference to `Cow` is not recommended --> $DIR/ptr_arg.rs:90:25 | LL | fn test_cow_with_ref(c: &Cow<[i32]>) {} | ^^^^^^^^^^^ help: change this to: `&[i32]` -error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices. +error: writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used with non-Vec-based slices --> $DIR/ptr_arg.rs:143:21 | LL | fn foo_vec(vec: &Vec) { @@ -133,7 +133,7 @@ help: change `vec.clone()` to LL | let _ = vec.to_owned().clone(); | ^^^^^^^^^^^^^^ -error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do. +error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do --> $DIR/ptr_arg.rs:148:23 | LL | fn foo_path(path: &PathBuf) { @@ -152,7 +152,7 @@ help: change `path.clone()` to LL | let _ = path.to_path_buf().clone(); | ^^^^^^^^^^^^^^^^^^ -error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do. +error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do --> $DIR/ptr_arg.rs:153:21 | LL | fn foo_str(str: &PathBuf) { diff --git a/tests/ui/suspicious_operation_groupings.stderr b/tests/ui/suspicious_operation_groupings.stderr index ce7108217f1..2da05399575 100644 --- a/tests/ui/suspicious_operation_groupings.stderr +++ b/tests/ui/suspicious_operation_groupings.stderr @@ -1,4 +1,4 @@ -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:14:9 | LL | self.x == other.y && self.y == other.y && self.z == other.z @@ -6,157 +6,157 @@ LL | self.x == other.y && self.y == other.y && self.z == other.z | = note: `-D clippy::suspicious-operation-groupings` implied by `-D warnings` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:14:9 | LL | self.x == other.y && self.y == other.y && self.z == other.z | ^^^^^^^^^^^^^^^^^ help: I think you meant: `self.x == other.x` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:27:20 | LL | s1.a < s2.a && s1.a < s2.b | ^^^^^^^^^^^ help: I think you meant: `s1.b < s2.b` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:75:33 | LL | s1.a * s2.a + s1.b * s2.b + s1.c * s2.b + s1.d * s2.d | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:80:19 | LL | s1.a * s2.a + s1.b * s2.c + s1.c * s2.c | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:80:19 | LL | s1.a * s2.a + s1.b * s2.c + s1.c * s2.c | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:85:19 | LL | s1.a * s2.a + s2.b * s2.b + s1.c * s2.c | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:90:19 | LL | s1.a * s2.a + s1.b * s1.b + s1.c * s2.c | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:95:5 | LL | s1.a * s1.a + s1.b * s2.b + s1.c * s2.c | ^^^^^^^^^^^ help: I think you meant: `s1.a * s2.a` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:100:33 | LL | s1.a * s2.a + s1.b * s2.b + s1.c * s1.c | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:113:20 | LL | (s1.a * s2.a + s1.b * s1.b) | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:118:34 | LL | (s1.a * s2.a + s1.b * s2.b + s1.c * s2.b + s1.d * s2.d) | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:123:38 | LL | (s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d) | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:128:39 | LL | ((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d)) | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:133:42 | LL | (((s1.a * s2.a) + (s1.b * s2.b)) + ((s1.c * s2.b) + (s1.d * s2.d))) | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:133:42 | LL | (((s1.a * s2.a) + (s1.b * s2.b)) + ((s1.c * s2.b) + (s1.d * s2.d))) | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:138:40 | LL | (((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b)) + (s1.d * s2.d)) | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:143:40 | LL | ((s1.a * s2.a) + ((s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d))) | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:148:20 | LL | (s1.a * s2.a + s2.b * s2.b) / 2 | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:153:35 | LL | i32::swap_bytes(s1.a * s2.a + s2.b * s2.b) | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:158:29 | LL | s1.a > 0 && s1.b > 0 && s1.d == s2.c && s1.d == s2.d | ^^^^^^^^^^^^ help: I think you meant: `s1.c == s2.c` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:163:17 | LL | s1.a > 0 && s1.d == s2.c && s1.b > 0 && s1.d == s2.d | ^^^^^^^^^^^^ help: I think you meant: `s1.c == s2.c` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:172:77 | LL | (n1.inner.0).0 == (n2.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.1).0 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: I think you meant: `(n1.inner.2).0 == (n2.inner.2).0` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:186:25 | LL | s1.a <= s2.a && s1.a <= s2.b | ^^^^^^^^^^^^ help: I think you meant: `s1.b <= s2.b` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:192:23 | LL | if s1.a < s2.a && s1.a < s2.b { | ^^^^^^^^^^^ help: I think you meant: `s1.b < s2.b` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:199:48 | LL | -(-(-s1.a * -s2.a) + (-(-s1.b * -s2.b) + -(-s1.c * -s2.b) + -(-s1.d * -s2.d))) | ^^^^^^^^^^^^^ help: I think you meant: `-s1.c * -s2.c` -error: This sequence of operators looks suspiciously like a bug. +error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:204:27 | LL | -(if -s1.a < -s2.a && -s1.a < -s2.b { s1.c } else { s2.a }) diff --git a/tests/ui/toplevel_ref_arg_non_rustfix.stderr b/tests/ui/toplevel_ref_arg_non_rustfix.stderr index 6c36141a58c..b8cfd987394 100644 --- a/tests/ui/toplevel_ref_arg_non_rustfix.stderr +++ b/tests/ui/toplevel_ref_arg_non_rustfix.stderr @@ -1,4 +1,4 @@ -error: `ref` directly on a function argument is ignored. Consider using a reference type instead. +error: `ref` directly on a function argument is ignored. Consider using a reference type instead --> $DIR/toplevel_ref_arg_non_rustfix.rs:9:15 | LL | fn the_answer(ref mut x: u8) { @@ -6,7 +6,7 @@ LL | fn the_answer(ref mut x: u8) { | = note: `-D clippy::toplevel-ref-arg` implied by `-D warnings` -error: `ref` directly on a function argument is ignored. Consider using a reference type instead. +error: `ref` directly on a function argument is ignored. Consider using a reference type instead --> $DIR/toplevel_ref_arg_non_rustfix.rs:15:24 | LL | fn fun_example(ref _x: usize) {} diff --git a/tests/ui/transmuting_null.stderr b/tests/ui/transmuting_null.stderr index 05f91ee2ada..1848fc2490a 100644 --- a/tests/ui/transmuting_null.stderr +++ b/tests/ui/transmuting_null.stderr @@ -1,4 +1,4 @@ -error: transmuting a known null pointer into a reference. +error: transmuting a known null pointer into a reference --> $DIR/transmuting_null.rs:10:23 | LL | let _: &u64 = std::mem::transmute(0 as *const u64); @@ -6,13 +6,13 @@ LL | let _: &u64 = std::mem::transmute(0 as *const u64); | = note: `-D clippy::transmuting-null` implied by `-D warnings` -error: transmuting a known null pointer into a reference. +error: transmuting a known null pointer into a reference --> $DIR/transmuting_null.rs:11:23 | LL | let _: &u64 = std::mem::transmute(std::ptr::null::()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: transmuting a known null pointer into a reference. +error: transmuting a known null pointer into a reference --> $DIR/transmuting_null.rs:21:23 | LL | let _: &u64 = std::mem::transmute(ZPTR); diff --git a/tests/ui/unnecessary_lazy_eval.stderr b/tests/ui/unnecessary_lazy_eval.stderr index 44dcd0cafbb..cc94bd5cd9e 100644 --- a/tests/ui/unnecessary_lazy_eval.stderr +++ b/tests/ui/unnecessary_lazy_eval.stderr @@ -2,7 +2,7 @@ error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:35:13 | LL | let _ = opt.unwrap_or_else(|| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `opt.unwrap_or(2)` | = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings` @@ -10,187 +10,187 @@ error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:36:13 | LL | let _ = opt.unwrap_or_else(|| astronomers_pi); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(astronomers_pi)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `opt.unwrap_or(astronomers_pi)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:37:13 | LL | let _ = opt.unwrap_or_else(|| ext_str.some_field); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `opt.unwrap_or(ext_str.some_field)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `opt.unwrap_or(ext_str.some_field)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:39:13 | LL | let _ = opt.and_then(|_| ext_opt); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `opt.and(ext_opt)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `and` instead: `opt.and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:40:13 | LL | let _ = opt.or_else(|| ext_opt); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(ext_opt)` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `opt.or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:41:13 | LL | let _ = opt.or_else(|| None); - | ^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(None)` + | ^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `opt.or(None)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:42:13 | LL | let _ = opt.get_or_insert_with(|| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `opt.get_or_insert(2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `get_or_insert` instead: `opt.get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:43:13 | LL | let _ = opt.ok_or_else(|| 2); - | ^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `opt.ok_or(2)` + | ^^^^^^^^^^^^^^^^^^^^ help: use `ok_or` instead: `opt.ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:44:13 | LL | let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `nested_tuple_opt.unwrap_or(Some((1, 2)))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `nested_tuple_opt.unwrap_or(Some((1, 2)))` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:47:13 | LL | let _ = Some(10).unwrap_or_else(|| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `Some(10).unwrap_or(2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `Some(10).unwrap_or(2)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:48:13 | LL | let _ = Some(10).and_then(|_| ext_opt); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `Some(10).and(ext_opt)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `and` instead: `Some(10).and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:49:28 | LL | let _: Option = None.or_else(|| ext_opt); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(ext_opt)` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `None.or(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:50:13 | LL | let _ = None.get_or_insert_with(|| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `None.get_or_insert(2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `get_or_insert` instead: `None.get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:51:35 | LL | let _: Result = None.ok_or_else(|| 2); - | ^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `None.ok_or(2)` + | ^^^^^^^^^^^^^^^^^^^^^ help: use `ok_or` instead: `None.ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:52:28 | LL | let _: Option = None.or_else(|| None); - | ^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(None)` + | ^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `None.or(None)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:55:13 | LL | let _ = deep.0.unwrap_or_else(|| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `deep.0.unwrap_or(2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `deep.0.unwrap_or(2)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:56:13 | LL | let _ = deep.0.and_then(|_| ext_opt); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `deep.0.and(ext_opt)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `and` instead: `deep.0.and(ext_opt)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:57:13 | LL | let _ = deep.0.or_else(|| None); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `deep.0.or(None)` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `deep.0.or(None)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:58:13 | LL | let _ = deep.0.get_or_insert_with(|| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `get_or_insert` instead: `deep.0.get_or_insert(2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `get_or_insert` instead: `deep.0.get_or_insert(2)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:59:13 | LL | let _ = deep.0.ok_or_else(|| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `ok_or` instead: `deep.0.ok_or(2)` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `ok_or` instead: `deep.0.ok_or(2)` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:79:28 | LL | let _: Option = None.or_else(|| Some(3)); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `None.or(Some(3))` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `None.or(Some(3))` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:80:13 | LL | let _ = deep.0.or_else(|| Some(3)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `deep.0.or(Some(3))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `deep.0.or(Some(3))` error: unnecessary closure used to substitute value for `Option::None` --> $DIR/unnecessary_lazy_eval.rs:81:13 | LL | let _ = opt.or_else(|| Some(3)); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `opt.or(Some(3))` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `opt.or(Some(3))` error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval.rs:87:13 | LL | let _ = res2.unwrap_or_else(|_| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `res2.unwrap_or(2)` error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval.rs:88:13 | LL | let _ = res2.unwrap_or_else(|_| astronomers_pi); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(astronomers_pi)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `res2.unwrap_or(astronomers_pi)` error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval.rs:89:13 | LL | let _ = res2.unwrap_or_else(|_| ext_str.some_field); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `res2.unwrap_or(ext_str.some_field)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `res2.unwrap_or(ext_str.some_field)` error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval.rs:111:35 | LL | let _: Result = res.and_then(|_| Err(2)); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `res.and(Err(2))` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `and` instead: `res.and(Err(2))` error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval.rs:112:35 | LL | let _: Result = res.and_then(|_| Err(astronomers_pi)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `res.and(Err(astronomers_pi))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `and` instead: `res.and(Err(astronomers_pi))` error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval.rs:113:35 | LL | let _: Result = res.and_then(|_| Err(ext_str.some_field)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `and` instead: `res.and(Err(ext_str.some_field))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `and` instead: `res.and(Err(ext_str.some_field))` error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval.rs:115:35 | LL | let _: Result = res.or_else(|_| Ok(2)); - | ^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `res.or(Ok(2))` + | ^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `res.or(Ok(2))` error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval.rs:116:35 | LL | let _: Result = res.or_else(|_| Ok(astronomers_pi)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `res.or(Ok(astronomers_pi))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `res.or(Ok(astronomers_pi))` error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval.rs:117:35 | LL | let _: Result = res.or_else(|_| Ok(ext_str.some_field)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `or` instead: `res.or(Ok(ext_str.some_field))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `or` instead: `res.or(Ok(ext_str.some_field))` error: aborting due to 32 previous errors diff --git a/tests/ui/unnecessary_lazy_eval_unfixable.stderr b/tests/ui/unnecessary_lazy_eval_unfixable.stderr index 581d641cbf5..75674b0a9d2 100644 --- a/tests/ui/unnecessary_lazy_eval_unfixable.stderr +++ b/tests/ui/unnecessary_lazy_eval_unfixable.stderr @@ -2,7 +2,7 @@ error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval_unfixable.rs:12:13 | LL | let _ = Ok(1).unwrap_or_else(|()| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `Ok(1).unwrap_or(2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `Ok(1).unwrap_or(2)` | = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings` @@ -10,13 +10,13 @@ error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval_unfixable.rs:16:13 | LL | let _ = Ok(1).unwrap_or_else(|e::E| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `Ok(1).unwrap_or(2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `Ok(1).unwrap_or(2)` error: unnecessary closure used to substitute value for `Result::Err` --> $DIR/unnecessary_lazy_eval_unfixable.rs:17:13 | LL | let _ = Ok(1).unwrap_or_else(|SomeStruct { .. }| 2); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Use `unwrap_or` instead: `Ok(1).unwrap_or(2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `unwrap_or` instead: `Ok(1).unwrap_or(2)` error: aborting due to 3 previous errors diff --git a/tests/ui/used_underscore_binding.stderr b/tests/ui/used_underscore_binding.stderr index 68e96148093..2cbfc5ca2e2 100644 --- a/tests/ui/used_underscore_binding.stderr +++ b/tests/ui/used_underscore_binding.stderr @@ -1,4 +1,4 @@ -error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used. +error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used --> $DIR/used_underscore_binding.rs:26:5 | LL | _foo + 1 @@ -6,31 +6,31 @@ LL | _foo + 1 | = note: `-D clippy::used-underscore-binding` implied by `-D warnings` -error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used. +error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used --> $DIR/used_underscore_binding.rs:31:20 | LL | println!("{}", _foo); | ^^^^ -error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used. +error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used --> $DIR/used_underscore_binding.rs:32:16 | LL | assert_eq!(_foo, _foo); | ^^^^ -error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used. +error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used --> $DIR/used_underscore_binding.rs:32:22 | LL | assert_eq!(_foo, _foo); | ^^^^ -error: used binding `_underscore_field` which is prefixed with an underscore. A leading underscore signals that a binding will not be used. +error: used binding `_underscore_field` which is prefixed with an underscore. A leading underscore signals that a binding will not be used --> $DIR/used_underscore_binding.rs:45:5 | LL | s._underscore_field += 1; | ^^^^^^^^^^^^^^^^^^^ -error: used binding `_i` which is prefixed with an underscore. A leading underscore signals that a binding will not be used. +error: used binding `_i` which is prefixed with an underscore. A leading underscore signals that a binding will not be used --> $DIR/used_underscore_binding.rs:100:16 | LL | uses_i(_i); diff --git a/tests/ui/vec_box_sized.stderr b/tests/ui/vec_box_sized.stderr index 57e2f1fdf9a..83435a40aa1 100644 --- a/tests/ui/vec_box_sized.stderr +++ b/tests/ui/vec_box_sized.stderr @@ -1,4 +1,4 @@ -error: `Vec` is already on the heap, the boxing is unnecessary. +error: `Vec` is already on the heap, the boxing is unnecessary --> $DIR/vec_box_sized.rs:14:21 | LL | sized_type: Vec>, @@ -6,19 +6,19 @@ LL | sized_type: Vec>, | = note: `-D clippy::vec-box` implied by `-D warnings` -error: `Vec` is already on the heap, the boxing is unnecessary. +error: `Vec` is already on the heap, the boxing is unnecessary --> $DIR/vec_box_sized.rs:17:14 | LL | struct A(Vec>); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Vec` -error: `Vec` is already on the heap, the boxing is unnecessary. +error: `Vec` is already on the heap, the boxing is unnecessary --> $DIR/vec_box_sized.rs:18:18 | LL | struct B(Vec>>); | ^^^^^^^^^^^^^^^ help: try: `Vec` -error: `Vec` is already on the heap, the boxing is unnecessary. +error: `Vec` is already on the heap, the boxing is unnecessary --> $DIR/vec_box_sized.rs:46:23 | LL | pub fn f() -> Vec> { diff --git a/tests/ui/wild_in_or_pats.stderr b/tests/ui/wild_in_or_pats.stderr index 33c34cbbd40..45b87aa0f20 100644 --- a/tests/ui/wild_in_or_pats.stderr +++ b/tests/ui/wild_in_or_pats.stderr @@ -1,35 +1,35 @@ -error: wildcard pattern covers any other pattern as it will match anyway. +error: wildcard pattern covers any other pattern as it will match anyway --> $DIR/wild_in_or_pats.rs:8:9 | LL | "bar" | _ => { | ^^^^^^^^^ | = note: `-D clippy::wildcard-in-or-patterns` implied by `-D warnings` - = help: Consider handling `_` separately. + = help: consider handling `_` separately -error: wildcard pattern covers any other pattern as it will match anyway. +error: wildcard pattern covers any other pattern as it will match anyway --> $DIR/wild_in_or_pats.rs:16:9 | LL | "bar" | "bar2" | _ => { | ^^^^^^^^^^^^^^^^^^ | - = help: Consider handling `_` separately. + = help: consider handling `_` separately -error: wildcard pattern covers any other pattern as it will match anyway. +error: wildcard pattern covers any other pattern as it will match anyway --> $DIR/wild_in_or_pats.rs:24:9 | LL | _ | "bar" | _ => { | ^^^^^^^^^^^^^ | - = help: Consider handling `_` separately. + = help: consider handling `_` separately -error: wildcard pattern covers any other pattern as it will match anyway. +error: wildcard pattern covers any other pattern as it will match anyway --> $DIR/wild_in_or_pats.rs:32:9 | LL | _ | "bar" => { | ^^^^^^^^^ | - = help: Consider handling `_` separately. + = help: consider handling `_` separately error: aborting due to 4 previous errors diff --git a/tests/ui/zero_div_zero.stderr b/tests/ui/zero_div_zero.stderr index d0e88f3c5a5..0931dd32e7a 100644 --- a/tests/ui/zero_div_zero.stderr +++ b/tests/ui/zero_div_zero.stderr @@ -13,7 +13,7 @@ LL | let nan = 0.0 / 0.0; | ^^^^^^^^^ | = note: `-D clippy::zero-divided-by-zero` implied by `-D warnings` - = help: Consider using `f64::NAN` if you would like a constant representing NaN + = help: consider using `f64::NAN` if you would like a constant representing NaN error: equal expressions as operands to `/` --> $DIR/zero_div_zero.rs:5:19 @@ -27,7 +27,7 @@ error: constant division of `0.0` with `0.0` will always result in NaN LL | let f64_nan = 0.0 / 0.0f64; | ^^^^^^^^^^^^ | - = help: Consider using `f64::NAN` if you would like a constant representing NaN + = help: consider using `f64::NAN` if you would like a constant representing NaN error: equal expressions as operands to `/` --> $DIR/zero_div_zero.rs:6:25 @@ -41,7 +41,7 @@ error: constant division of `0.0` with `0.0` will always result in NaN LL | let other_f64_nan = 0.0f64 / 0.0; | ^^^^^^^^^^^^ | - = help: Consider using `f64::NAN` if you would like a constant representing NaN + = help: consider using `f64::NAN` if you would like a constant representing NaN error: equal expressions as operands to `/` --> $DIR/zero_div_zero.rs:7:28 @@ -55,7 +55,7 @@ error: constant division of `0.0` with `0.0` will always result in NaN LL | let one_more_f64_nan = 0.0f64 / 0.0f64; | ^^^^^^^^^^^^^^^ | - = help: Consider using `f64::NAN` if you would like a constant representing NaN + = help: consider using `f64::NAN` if you would like a constant representing NaN error: aborting due to 8 previous errors -- cgit 1.4.1-3-g733a5 From e107b65b5a025d6c3a7109f5f3dfa9f6efdf0852 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 25 Feb 2021 12:30:51 +0100 Subject: disable lint_message_convention test inside the rustc test suite --- tests/lint_message_convention.rs | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'tests') diff --git a/tests/lint_message_convention.rs b/tests/lint_message_convention.rs index e316a884996..1606a7881c7 100644 --- a/tests/lint_message_convention.rs +++ b/tests/lint_message_convention.rs @@ -60,6 +60,11 @@ impl Message { #[test] fn lint_message_convention() { + // disable the test inside the rustc test suite + if option_env!("RUSTC_TEST_SUITE").is_some() { + return; + } + // make sure that lint messages: // * are not capitalized // * don't have puncuation at the end of the last sentence -- cgit 1.4.1-3-g733a5 From e00b1cc73ada33c7ef72702810a74f5abd38b112 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Fri, 26 Feb 2021 15:49:18 +0100 Subject: change some lint messages and remove old entries from the ignorelist --- clippy_lints/src/methods/mod.rs | 2 +- clippy_lints/src/suspicious_operation_groupings.rs | 2 +- clippy_lints/src/types.rs | 2 +- tests/lint_message_convention.rs | 4 -- tests/ui/dlist.stderr | 12 ++--- tests/ui/iterator_step_by_zero.stderr | 14 +++--- tests/ui/suspicious_operation_groupings.stderr | 54 +++++++++++----------- 7 files changed, 43 insertions(+), 47 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index f5c01ac7729..b0f8185e331 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2569,7 +2569,7 @@ fn lint_step_by<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, args: &'tcx cx, ITERATOR_STEP_BY_ZERO, expr.span, - "Iterator::step_by(0) will panic at runtime", + "`Iterator::step_by(0)` will panic at runtime", ); } } diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs index 2ff30698043..44521885d20 100644 --- a/clippy_lints/src/suspicious_operation_groupings.rs +++ b/clippy_lints/src/suspicious_operation_groupings.rs @@ -262,7 +262,7 @@ fn emit_suggestion(cx: &EarlyContext<'_>, span: Span, sugg: String, applicabilit SUSPICIOUS_OPERATION_GROUPINGS, span, "this sequence of operators looks suspiciously like a bug", - "I think you meant", + "did you mean", sugg, applicability, ) diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index a18cba6fb44..f71b1651bfe 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -578,7 +578,7 @@ impl Types { cx, LINKEDLIST, hir_ty.span, - "I see you're using a LinkedList! Perhaps you meant some other data structure?", + "you seem to be using a `LinkedList`! Perhaps you meant some other data structure?", None, "a `VecDeque` might work", ); diff --git a/tests/lint_message_convention.rs b/tests/lint_message_convention.rs index 1606a7881c7..d45d93f8184 100644 --- a/tests/lint_message_convention.rs +++ b/tests/lint_message_convention.rs @@ -31,9 +31,7 @@ impl Message { // sometimes the first character is capitalized and it is legal (like in "Iterator...") or // we want to ask a question ending in "?" let exceptions_set: RegexSet = RegexSet::new(&[ - r".*error: I see you're using a LinkedList! Perhaps you meant some other data structure?", r".*C-like enum variant discriminant is not portable to 32-bit targets", - r".*Iterator::step_by(0) will panic at runtime", r".*did you mean `unix`?", r".*the arguments may be inverted...", r".*Intel x86 assembly syntax used", @@ -41,8 +39,6 @@ impl Message { r".*remove .*the return type...", r"note: Clippy version: .*", r"the compiler unexpectedly panicked. this is a bug.", - r".*help: I think you meant: .*", - r"Iterator.* will panic at runtime", ]) .unwrap(); diff --git a/tests/ui/dlist.stderr b/tests/ui/dlist.stderr index 64fde33c64f..234db33ba12 100644 --- a/tests/ui/dlist.stderr +++ b/tests/ui/dlist.stderr @@ -1,4 +1,4 @@ -error: I see you're using a LinkedList! Perhaps you meant some other data structure? +error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? --> $DIR/dlist.rs:9:16 | LL | type Baz = LinkedList; @@ -7,7 +7,7 @@ LL | type Baz = LinkedList; = note: `-D clippy::linkedlist` implied by `-D warnings` = help: a `VecDeque` might work -error: I see you're using a LinkedList! Perhaps you meant some other data structure? +error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? --> $DIR/dlist.rs:10:15 | LL | fn foo(_: LinkedList); @@ -15,7 +15,7 @@ LL | fn foo(_: LinkedList); | = help: a `VecDeque` might work -error: I see you're using a LinkedList! Perhaps you meant some other data structure? +error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? --> $DIR/dlist.rs:11:23 | LL | const BAR: Option>; @@ -23,7 +23,7 @@ LL | const BAR: Option>; | = help: a `VecDeque` might work -error: I see you're using a LinkedList! Perhaps you meant some other data structure? +error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? --> $DIR/dlist.rs:22:15 | LL | fn foo(_: LinkedList) {} @@ -31,7 +31,7 @@ LL | fn foo(_: LinkedList) {} | = help: a `VecDeque` might work -error: I see you're using a LinkedList! Perhaps you meant some other data structure? +error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? --> $DIR/dlist.rs:25:39 | LL | pub fn test(my_favourite_linked_list: LinkedList) { @@ -39,7 +39,7 @@ LL | pub fn test(my_favourite_linked_list: LinkedList) { | = help: a `VecDeque` might work -error: I see you're using a LinkedList! Perhaps you meant some other data structure? +error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? --> $DIR/dlist.rs:29:29 | LL | pub fn test_ret() -> Option> { diff --git a/tests/ui/iterator_step_by_zero.stderr b/tests/ui/iterator_step_by_zero.stderr index c2c6803b3e6..d792aea11df 100644 --- a/tests/ui/iterator_step_by_zero.stderr +++ b/tests/ui/iterator_step_by_zero.stderr @@ -1,4 +1,4 @@ -error: Iterator::step_by(0) will panic at runtime +error: `Iterator::step_by(0)` will panic at runtime --> $DIR/iterator_step_by_zero.rs:3:13 | LL | let _ = vec!["A", "B", "B"].iter().step_by(0); @@ -6,37 +6,37 @@ LL | let _ = vec!["A", "B", "B"].iter().step_by(0); | = note: `-D clippy::iterator-step-by-zero` implied by `-D warnings` -error: Iterator::step_by(0) will panic at runtime +error: `Iterator::step_by(0)` will panic at runtime --> $DIR/iterator_step_by_zero.rs:4:13 | LL | let _ = "XXX".chars().step_by(0); | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: Iterator::step_by(0) will panic at runtime +error: `Iterator::step_by(0)` will panic at runtime --> $DIR/iterator_step_by_zero.rs:5:13 | LL | let _ = (0..1).step_by(0); | ^^^^^^^^^^^^^^^^^ -error: Iterator::step_by(0) will panic at runtime +error: `Iterator::step_by(0)` will panic at runtime --> $DIR/iterator_step_by_zero.rs:14:13 | LL | let _ = (1..).step_by(0); | ^^^^^^^^^^^^^^^^ -error: Iterator::step_by(0) will panic at runtime +error: `Iterator::step_by(0)` will panic at runtime --> $DIR/iterator_step_by_zero.rs:15:13 | LL | let _ = (1..=2).step_by(0); | ^^^^^^^^^^^^^^^^^^ -error: Iterator::step_by(0) will panic at runtime +error: `Iterator::step_by(0)` will panic at runtime --> $DIR/iterator_step_by_zero.rs:18:13 | LL | let _ = x.step_by(0); | ^^^^^^^^^^^^ -error: Iterator::step_by(0) will panic at runtime +error: `Iterator::step_by(0)` will panic at runtime --> $DIR/iterator_step_by_zero.rs:22:13 | LL | let _ = v1.iter().step_by(2 / 3); diff --git a/tests/ui/suspicious_operation_groupings.stderr b/tests/ui/suspicious_operation_groupings.stderr index 2da05399575..96065699d32 100644 --- a/tests/ui/suspicious_operation_groupings.stderr +++ b/tests/ui/suspicious_operation_groupings.stderr @@ -2,7 +2,7 @@ error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:14:9 | LL | self.x == other.y && self.y == other.y && self.z == other.z - | ^^^^^^^^^^^^^^^^^ help: I think you meant: `self.x == other.x` + | ^^^^^^^^^^^^^^^^^ help: did you mean: `self.x == other.x` | = note: `-D clippy::suspicious-operation-groupings` implied by `-D warnings` @@ -10,157 +10,157 @@ error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:14:9 | LL | self.x == other.y && self.y == other.y && self.z == other.z - | ^^^^^^^^^^^^^^^^^ help: I think you meant: `self.x == other.x` + | ^^^^^^^^^^^^^^^^^ help: did you mean: `self.x == other.x` error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:27:20 | LL | s1.a < s2.a && s1.a < s2.b - | ^^^^^^^^^^^ help: I think you meant: `s1.b < s2.b` + | ^^^^^^^^^^^ help: did you mean: `s1.b < s2.b` error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:75:33 | LL | s1.a * s2.a + s1.b * s2.b + s1.c * s2.b + s1.d * s2.d - | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:80:19 | LL | s1.a * s2.a + s1.b * s2.c + s1.c * s2.c - | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` + | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:80:19 | LL | s1.a * s2.a + s1.b * s2.c + s1.c * s2.c - | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` + | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:85:19 | LL | s1.a * s2.a + s2.b * s2.b + s1.c * s2.c - | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` + | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:90:19 | LL | s1.a * s2.a + s1.b * s1.b + s1.c * s2.c - | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` + | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:95:5 | LL | s1.a * s1.a + s1.b * s2.b + s1.c * s2.c - | ^^^^^^^^^^^ help: I think you meant: `s1.a * s2.a` + | ^^^^^^^^^^^ help: did you mean: `s1.a * s2.a` error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:100:33 | LL | s1.a * s2.a + s1.b * s2.b + s1.c * s1.c - | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:113:20 | LL | (s1.a * s2.a + s1.b * s1.b) - | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` + | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:118:34 | LL | (s1.a * s2.a + s1.b * s2.b + s1.c * s2.b + s1.d * s2.d) - | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:123:38 | LL | (s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d) - | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:128:39 | LL | ((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d)) - | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:133:42 | LL | (((s1.a * s2.a) + (s1.b * s2.b)) + ((s1.c * s2.b) + (s1.d * s2.d))) - | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:133:42 | LL | (((s1.a * s2.a) + (s1.b * s2.b)) + ((s1.c * s2.b) + (s1.d * s2.d))) - | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:138:40 | LL | (((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b)) + (s1.d * s2.d)) - | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:143:40 | LL | ((s1.a * s2.a) + ((s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d))) - | ^^^^^^^^^^^ help: I think you meant: `s1.c * s2.c` + | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:148:20 | LL | (s1.a * s2.a + s2.b * s2.b) / 2 - | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` + | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:153:35 | LL | i32::swap_bytes(s1.a * s2.a + s2.b * s2.b) - | ^^^^^^^^^^^ help: I think you meant: `s1.b * s2.b` + | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:158:29 | LL | s1.a > 0 && s1.b > 0 && s1.d == s2.c && s1.d == s2.d - | ^^^^^^^^^^^^ help: I think you meant: `s1.c == s2.c` + | ^^^^^^^^^^^^ help: did you mean: `s1.c == s2.c` error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:163:17 | LL | s1.a > 0 && s1.d == s2.c && s1.b > 0 && s1.d == s2.d - | ^^^^^^^^^^^^ help: I think you meant: `s1.c == s2.c` + | ^^^^^^^^^^^^ help: did you mean: `s1.c == s2.c` error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:172:77 | LL | (n1.inner.0).0 == (n2.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.1).0 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: I think you meant: `(n1.inner.2).0 == (n2.inner.2).0` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `(n1.inner.2).0 == (n2.inner.2).0` error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:186:25 | LL | s1.a <= s2.a && s1.a <= s2.b - | ^^^^^^^^^^^^ help: I think you meant: `s1.b <= s2.b` + | ^^^^^^^^^^^^ help: did you mean: `s1.b <= s2.b` error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:192:23 | LL | if s1.a < s2.a && s1.a < s2.b { - | ^^^^^^^^^^^ help: I think you meant: `s1.b < s2.b` + | ^^^^^^^^^^^ help: did you mean: `s1.b < s2.b` error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:199:48 | LL | -(-(-s1.a * -s2.a) + (-(-s1.b * -s2.b) + -(-s1.c * -s2.b) + -(-s1.d * -s2.d))) - | ^^^^^^^^^^^^^ help: I think you meant: `-s1.c * -s2.c` + | ^^^^^^^^^^^^^ help: did you mean: `-s1.c * -s2.c` error: this sequence of operators looks suspiciously like a bug --> $DIR/suspicious_operation_groupings.rs:204:27 | LL | -(if -s1.a < -s2.a && -s1.a < -s2.b { s1.c } else { s2.a }) - | ^^^^^^^^^^^^^ help: I think you meant: `-s1.b < -s2.b` + | ^^^^^^^^^^^^^ help: did you mean: `-s1.b < -s2.b` error: aborting due to 27 previous errors -- cgit 1.4.1-3-g733a5 From ebc5c8f271cdb9e64f56b19273ff87745639433c Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Sun, 28 Feb 2021 14:01:03 +0100 Subject: use different example (C-like) for valid capitalized start of lint message --- tests/lint_message_convention.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/lint_message_convention.rs b/tests/lint_message_convention.rs index d45d93f8184..8c07c5b242f 100644 --- a/tests/lint_message_convention.rs +++ b/tests/lint_message_convention.rs @@ -28,7 +28,7 @@ impl Message { ]) .unwrap(); - // sometimes the first character is capitalized and it is legal (like in "Iterator...") or + // sometimes the first character is capitalized and it is legal (like in "C-like enum variants") or // we want to ask a question ending in "?" let exceptions_set: RegexSet = RegexSet::new(&[ r".*C-like enum variant discriminant is not portable to 32-bit targets", -- cgit 1.4.1-3-g733a5 From da3a57377ea34c1ddb0c0c41defb456b8dceed53 Mon Sep 17 00:00:00 2001 From: hyd-dev Date: Sun, 28 Feb 2021 20:44:07 +0800 Subject: Fix false positives on procedural macros of `missing_inline_in_public_items` lint --- clippy_lints/src/missing_inline.rs | 8 ++++---- tests/ui/missing_inline_executable.rs | 5 +++++ tests/ui/missing_inline_proc_macro.rs | 23 +++++++++++++++++++++++ 3 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 tests/ui/missing_inline_executable.rs create mode 100644 tests/ui/missing_inline_proc_macro.rs (limited to 'tests') diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index 47d7c5306c4..2448325e899 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -69,21 +69,21 @@ fn check_missing_inline_attrs(cx: &LateContext<'_>, attrs: &[ast::Attribute], sp } } -fn is_executable(cx: &LateContext<'_>) -> bool { +fn is_executable_or_proc_macro(cx: &LateContext<'_>) -> bool { use rustc_session::config::CrateType; cx.tcx .sess .crate_types() .iter() - .any(|t: &CrateType| matches!(t, CrateType::Executable)) + .any(|t: &CrateType| matches!(t, CrateType::Executable | CrateType::ProcMacro)) } declare_lint_pass!(MissingInline => [MISSING_INLINE_IN_PUBLIC_ITEMS]); impl<'tcx> LateLintPass<'tcx> for MissingInline { fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'_>) { - if rustc_middle::lint::in_external_macro(cx.sess(), it.span) || is_executable(cx) { + if rustc_middle::lint::in_external_macro(cx.sess(), it.span) || is_executable_or_proc_macro(cx) { return; } @@ -133,7 +133,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { use rustc_middle::ty::{ImplContainer, TraitContainer}; - if rustc_middle::lint::in_external_macro(cx.sess(), impl_item.span) || is_executable(cx) { + if rustc_middle::lint::in_external_macro(cx.sess(), impl_item.span) || is_executable_or_proc_macro(cx) { return; } diff --git a/tests/ui/missing_inline_executable.rs b/tests/ui/missing_inline_executable.rs new file mode 100644 index 00000000000..6e0400ac935 --- /dev/null +++ b/tests/ui/missing_inline_executable.rs @@ -0,0 +1,5 @@ +#![warn(clippy::missing_inline_in_public_items)] + +pub fn foo() {} + +fn main() {} diff --git a/tests/ui/missing_inline_proc_macro.rs b/tests/ui/missing_inline_proc_macro.rs new file mode 100644 index 00000000000..3c68fb905f1 --- /dev/null +++ b/tests/ui/missing_inline_proc_macro.rs @@ -0,0 +1,23 @@ +#![warn(clippy::missing_inline_in_public_items)] +#![crate_type = "proc-macro"] + +extern crate proc_macro; + +use proc_macro::TokenStream; + +fn _foo() {} + +#[proc_macro] +pub fn function_like(_: TokenStream) -> TokenStream { + TokenStream::new() +} + +#[proc_macro_attribute] +pub fn attribute(_: TokenStream, _: TokenStream) -> TokenStream { + TokenStream::new() +} + +#[proc_macro_derive(Derive)] +pub fn derive(_: TokenStream) -> TokenStream { + TokenStream::new() +} -- cgit 1.4.1-3-g733a5 From a3278a16d31198d45c710c3c4dde433fc77d8d90 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 28 Feb 2021 09:03:21 -0500 Subject: Fix `manual_map`: do not expand macros in suggestions --- clippy_lints/src/manual_map.rs | 149 ++++++++++++++++++++------------------ clippy_utils/src/lib.rs | 33 ++++++++- tests/ui/manual_map_option.fixed | 5 ++ tests/ui/manual_map_option.rs | 11 +++ tests/ui/manual_map_option.stderr | 20 ++++- 5 files changed, 146 insertions(+), 72 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs index e6e70004527..983a10e8eaa 100644 --- a/clippy_lints/src/manual_map.rs +++ b/clippy_lints/src/manual_map.rs @@ -3,7 +3,8 @@ use crate::{ matches::MATCH_AS_REF, utils::{ can_partially_move_ty, is_allowed, is_type_diagnostic_item, match_def_path, match_var, paths, - peel_hir_expr_refs, peel_mid_ty_refs_is_mutable, snippet_with_applicability, span_lint_and_sugg, + peel_hir_expr_refs, peel_mid_ty_refs_is_mutable, snippet_with_applicability, snippet_with_context, + span_lint_and_sugg, }, }; use rustc_ast::util::parser::PREC_POSTFIX; @@ -16,7 +17,10 @@ use rustc_hir::{ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::symbol::{sym, Ident}; +use rustc_span::{ + symbol::{sym, Ident}, + SyntaxContext, +}; declare_clippy_lint! { /// **What it does:** Checks for usages of `match` which could be implemented using `map` @@ -56,43 +60,46 @@ impl LateLintPass<'_> for ManualMap { { let (scrutinee_ty, ty_ref_count, ty_mutability) = peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee)); - if !is_type_diagnostic_item(cx, scrutinee_ty, sym::option_type) - || !is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::option_type) + if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::option_type) + && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::option_type)) { return; } - let (some_expr, some_pat, pat_ref_count, is_wild_none) = - match (try_parse_pattern(cx, arm1.pat), try_parse_pattern(cx, arm2.pat)) { - (Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) - if is_none_expr(cx, arm1.body) => - { - (arm2.body, pattern, ref_count, true) - }, - (Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) - if is_none_expr(cx, arm1.body) => - { - (arm2.body, pattern, ref_count, false) - }, - (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) - if is_none_expr(cx, arm2.body) => - { - (arm1.body, pattern, ref_count, true) - }, - (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) - if is_none_expr(cx, arm2.body) => - { - (arm1.body, pattern, ref_count, false) - }, - _ => return, - }; + let expr_ctxt = expr.span.ctxt(); + let (some_expr, some_pat, pat_ref_count, is_wild_none) = match ( + try_parse_pattern(cx, arm1.pat, expr_ctxt), + try_parse_pattern(cx, arm2.pat, expr_ctxt), + ) { + (Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) + if is_none_expr(cx, arm1.body) => + { + (arm2.body, pattern, ref_count, true) + }, + (Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) + if is_none_expr(cx, arm1.body) => + { + (arm2.body, pattern, ref_count, false) + }, + (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) + if is_none_expr(cx, arm2.body) => + { + (arm1.body, pattern, ref_count, true) + }, + (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) + if is_none_expr(cx, arm2.body) => + { + (arm1.body, pattern, ref_count, false) + }, + _ => return, + }; // Top level or patterns aren't allowed in closures. if matches!(some_pat.kind, PatKind::Or(_)) { return; } - let some_expr = match get_some_expr(cx, some_expr) { + let some_expr = match get_some_expr(cx, some_expr, expr_ctxt) { Some(expr) => expr, None => return, }; @@ -119,47 +126,50 @@ impl LateLintPass<'_> for ManualMap { let mut app = Applicability::MachineApplicable; - // Remove address-of expressions from the scrutinee. `as_ref` will be called, - // the type is copyable, or the option is being passed by value. + // Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or + // it's being passed by value. let scrutinee = peel_hir_expr_refs(scrutinee).0; - let scrutinee_str = snippet_with_applicability(cx, scrutinee.span, "_", &mut app); - let scrutinee_str = if expr.precedence().order() < PREC_POSTFIX { - // Parens are needed to chain method calls. - format!("({})", scrutinee_str) - } else { - scrutinee_str.into() - }; + let scrutinee_str = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app); + let scrutinee_str = + if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX { + format!("({})", scrutinee_str) + } else { + scrutinee_str.into() + }; let body_str = if let PatKind::Binding(annotation, _, some_binding, None) = some_pat.kind { - if let Some(func) = can_pass_as_func(cx, some_binding, some_expr) { - snippet_with_applicability(cx, func.span, "..", &mut app).into_owned() - } else { - if match_var(some_expr, some_binding.name) - && !is_allowed(cx, MATCH_AS_REF, expr.hir_id) - && binding_ref.is_some() - { - return; - } + match can_pass_as_func(cx, some_binding, some_expr) { + Some(func) if func.span.ctxt() == some_expr.span.ctxt() => { + snippet_with_applicability(cx, func.span, "..", &mut app).into_owned() + }, + _ => { + if match_var(some_expr, some_binding.name) + && !is_allowed(cx, MATCH_AS_REF, expr.hir_id) + && binding_ref.is_some() + { + return; + } - // `ref` and `ref mut` annotations were handled earlier. - let annotation = if matches!(annotation, BindingAnnotation::Mutable) { - "mut " - } else { - "" - }; - format!( - "|{}{}| {}", - annotation, - some_binding, - snippet_with_applicability(cx, some_expr.span, "..", &mut app) - ) + // `ref` and `ref mut` annotations were handled earlier. + let annotation = if matches!(annotation, BindingAnnotation::Mutable) { + "mut " + } else { + "" + }; + format!( + "|{}{}| {}", + annotation, + some_binding, + snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app) + ) + }, } } else if !is_wild_none && explicit_ref.is_none() { // TODO: handle explicit reference annotations. format!( "|{}| {}", - snippet_with_applicability(cx, some_pat.span, "..", &mut app), - snippet_with_applicability(cx, some_expr.span, "..", &mut app) + snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app), + snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app) ) } else { // Refutable bindings and mixed reference annotations can't be handled by `map`. @@ -246,11 +256,11 @@ enum OptionPat<'a> { // Try to parse into a recognized `Option` pattern. // i.e. `_`, `None`, `Some(..)`, or a reference to any of those. -fn try_parse_pattern(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) -> Option> { - fn f(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ref_count: usize) -> Option> { +fn try_parse_pattern(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxContext) -> Option> { + fn f(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ref_count: usize, ctxt: SyntaxContext) -> Option> { match pat.kind { PatKind::Wild => Some(OptionPat::Wild), - PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1), + PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt), PatKind::Path(QPath::Resolved(None, path)) if path .res @@ -263,18 +273,19 @@ fn try_parse_pattern(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) -> Option + .map_or(false, |id| match_def_path(cx, id, &paths::OPTION_SOME)) + && pat.span.ctxt() == ctxt => { Some(OptionPat::Some { pattern, ref_count }) }, _ => None, } } - f(cx, pat, 0) + f(cx, pat, 0, ctxt) } // Checks for an expression wrapped by the `Some` constructor. Returns the contained expression. -fn get_some_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { +fn get_some_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ctxt: SyntaxContext) -> Option<&'tcx Expr<'tcx>> { // TODO: Allow more complex expressions. match expr.kind { ExprKind::Call( @@ -283,7 +294,7 @@ fn get_some_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx E .. }, [arg], - ) => { + ) if ctxt == expr.span.ctxt() => { if match_def_path(cx, path.res.opt_def_id()?, &paths::OPTION_SOME) { Some(arg) } else { @@ -297,7 +308,7 @@ fn get_some_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx E .. }, _, - ) => get_some_expr(cx, expr), + ) => get_some_expr(cx, expr, ctxt), _ => None, } } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 05523943511..5d1093ea040 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -73,11 +73,11 @@ use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; use rustc_middle::ty::{self, layout::IntegerExt, DefIdTree, Ty, TyCtxt, TypeFoldable}; use rustc_semver::RustcVersion; use rustc_session::Session; -use rustc_span::hygiene::{ExpnKind, MacroKind}; +use rustc_span::hygiene::{self, ExpnKind, MacroKind}; use rustc_span::source_map::original_sp; use rustc_span::sym; use rustc_span::symbol::{kw, Symbol}; -use rustc_span::{BytePos, Pos, Span, DUMMY_SP}; +use rustc_span::{BytePos, Pos, Span, SyntaxContext, DUMMY_SP}; use rustc_target::abi::Integer; use rustc_trait_selection::traits::query::normalize::AtExt; use smallvec::SmallVec; @@ -758,6 +758,35 @@ pub fn snippet_block_with_applicability<'a, T: LintContext>( reindent_multiline(snip, true, indent) } +/// Same as `snippet_with_applicability`, but first walks the span up to the given context. This +/// will result in the macro call, rather then the expansion, if the span is from a child context. +/// If the span is not from a child context, it will be used directly instead. +/// +/// e.g. Given the expression `&vec![]`, getting a snippet from the span for `vec![]` as a HIR node +/// would result in `box []`. If given the context of the address of expression, this function will +/// correctly get a snippet of `vec![]`. +pub fn snippet_with_context( + cx: &LateContext<'_>, + span: Span, + outer: SyntaxContext, + default: &'a str, + applicability: &mut Applicability, +) -> Cow<'a, str> { + let outer_span = hygiene::walk_chain(span, outer); + let span = if outer_span.ctxt() == outer { + outer_span + } else { + // The span is from a macro argument, and the outer context is the macro using the argument + if *applicability != Applicability::Unspecified { + *applicability = Applicability::MaybeIncorrect; + } + // TODO: get the argument span. + span + }; + + snippet_with_applicability(cx, span, default, applicability) +} + /// Returns a new Span that extends the original Span to the first non-whitespace char of the first /// line. /// diff --git a/tests/ui/manual_map_option.fixed b/tests/ui/manual_map_option.fixed index 428aac43940..e6fa10d22e1 100644 --- a/tests/ui/manual_map_option.fixed +++ b/tests/ui/manual_map_option.fixed @@ -110,4 +110,9 @@ fn main() { } } } + + // #6811 + Some(0).map(|x| vec![x]); + + option_env!("").map(String::from); } diff --git a/tests/ui/manual_map_option.rs b/tests/ui/manual_map_option.rs index 0f4a5bb2eb7..7c2100299a7 100644 --- a/tests/ui/manual_map_option.rs +++ b/tests/ui/manual_map_option.rs @@ -162,4 +162,15 @@ fn main() { } } } + + // #6811 + match Some(0) { + Some(x) => Some(vec![x]), + None => None, + }; + + match option_env!("") { + Some(x) => Some(String::from(x)), + None => None, + }; } diff --git a/tests/ui/manual_map_option.stderr b/tests/ui/manual_map_option.stderr index 49a51737784..2d13213cf67 100644 --- a/tests/ui/manual_map_option.stderr +++ b/tests/ui/manual_map_option.stderr @@ -154,5 +154,23 @@ LL | | None => None, LL | | }; | |_____^ help: try this: `Some((String::new(), "test")).as_ref().map(|(x, y)| (y, x))` -error: aborting due to 17 previous errors +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:167:5 + | +LL | / match Some(0) { +LL | | Some(x) => Some(vec![x]), +LL | | None => None, +LL | | }; + | |_____^ help: try this: `Some(0).map(|x| vec![x])` + +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:172:5 + | +LL | / match option_env!("") { +LL | | Some(x) => Some(String::from(x)), +LL | | None => None, +LL | | }; + | |_____^ help: try this: `option_env!("").map(String::from)` + +error: aborting due to 19 previous errors -- cgit 1.4.1-3-g733a5 From ada8c72f3f21013f789f774d4c0219c58264e663 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 1 Mar 2021 11:53:33 -0600 Subject: Add version = "Two" to rustfmt.toml Ignore UI tests since this change makes rustfmt less friendly with UI test comments. --- clippy_lints/src/await_holding_invalid.rs | 14 ++--- clippy_lints/src/float_literal.rs | 6 +- clippy_lints/src/infinite_iter.rs | 6 +- clippy_lints/src/inherent_to_string.rs | 2 +- clippy_lints/src/loops.rs | 6 +- clippy_lints/src/mutable_debug_assertion.rs | 6 +- clippy_lints/src/needless_continue.rs | 6 +- clippy_lints/src/open_options.rs | 12 ++-- clippy_utils/src/camel_case.rs | 6 +- clippy_utils/src/lib.rs | 12 ++-- clippy_utils/src/ptr.rs | 6 +- rustfmt.toml | 1 + tests/lint_message_convention.rs | 4 +- .../upper_case_acronyms.rs | 5 +- .../upper_case_acronyms.stderr | 4 +- tests/ui/auxiliary/macro_rules.rs | 6 +- tests/ui/blocks_in_if_conditions.fixed | 33 ++++------ tests/ui/blocks_in_if_conditions.rs | 33 ++++------ tests/ui/blocks_in_if_conditions.stderr | 10 +-- tests/ui/checked_unwrap/simple_conditionals.rs | 14 +++-- tests/ui/crashes/ice-6256.rs | 1 + tests/ui/crashes/ice-6256.stderr | 6 +- tests/ui/dbg_macro.rs | 6 +- tests/ui/dbg_macro.stderr | 16 ++--- tests/ui/default_trait_access.fixed | 20 +----- tests/ui/default_trait_access.rs | 20 +----- tests/ui/doc_panics.rs | 12 +--- tests/ui/doc_panics.stderr | 12 ++-- tests/ui/float_cmp.rs | 12 +--- tests/ui/float_cmp.stderr | 12 ++-- tests/ui/float_cmp_const.rs | 6 +- tests/ui/float_cmp_const.stderr | 16 ++--- tests/ui/floating_point_abs.fixed | 30 ++------- tests/ui/floating_point_abs.rs | 72 ++++------------------ tests/ui/floating_point_abs.stderr | 70 +++++++-------------- tests/ui/if_let_some_result.fixed | 12 +--- tests/ui/if_let_some_result.rs | 12 +--- tests/ui/if_let_some_result.stderr | 6 +- tests/ui/implicit_return.fixed | 6 +- tests/ui/implicit_return.rs | 6 +- tests/ui/implicit_return.stderr | 28 ++++----- tests/ui/needless_lifetimes.rs | 6 +- tests/ui/needless_lifetimes.stderr | 36 +++++------ tests/ui/redundant_clone.fixed | 6 +- tests/ui/redundant_clone.rs | 6 +- tests/ui/redundant_clone.stderr | 28 ++++----- tests/ui/unnecessary_wraps.rs | 36 ++--------- tests/ui/unnecessary_wraps.stderr | 18 +++--- tests/ui/upper_case_acronyms.rs | 5 +- tests/ui/use_self.fixed | 6 +- tests/ui/use_self.rs | 6 +- tests/ui/use_self.stderr | 2 +- 52 files changed, 225 insertions(+), 503 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index fad3aff96cc..112c5bb14e3 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -123,13 +123,13 @@ fn check_interior_types(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorType } if is_refcell_ref(cx, adt.did) { span_lint_and_note( - cx, - AWAIT_HOLDING_REFCELL_REF, - ty_cause.span, - "this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await", - ty_cause.scope_span.or(Some(span)), - "these are all the await points this ref is held through", - ); + cx, + AWAIT_HOLDING_REFCELL_REF, + ty_cause.span, + "this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await", + ty_cause.scope_span.or(Some(span)), + "these are all the await points this ref is held through", + ); } } } diff --git a/clippy_lints/src/float_literal.rs b/clippy_lints/src/float_literal.rs index be646cbe4d0..8e256f34684 100644 --- a/clippy_lints/src/float_literal.rs +++ b/clippy_lints/src/float_literal.rs @@ -145,11 +145,7 @@ fn count_digits(s: &str) -> usize { .take_while(|c| *c != 'e' && *c != 'E') .fold(0, |count, c| { // leading zeros - if c == '0' && count == 0 { - count - } else { - count + 1 - } + if c == '0' && count == 0 { count } else { count + 1 } }) } diff --git a/clippy_lints/src/infinite_iter.rs b/clippy_lints/src/infinite_iter.rs index 129abd7d897..7040ac3191f 100644 --- a/clippy_lints/src/infinite_iter.rs +++ b/clippy_lints/src/infinite_iter.rs @@ -89,11 +89,7 @@ impl Finiteness { impl From for Finiteness { #[must_use] fn from(b: bool) -> Self { - if b { - Infinite - } else { - Finite - } + if b { Infinite } else { Finite } } } diff --git a/clippy_lints/src/inherent_to_string.rs b/clippy_lints/src/inherent_to_string.rs index a95321ea7e2..c1f3e1d9d68 100644 --- a/clippy_lints/src/inherent_to_string.rs +++ b/clippy_lints/src/inherent_to_string.rs @@ -139,7 +139,7 @@ fn show_lint(cx: &LateContext<'_>, item: &ImplItem<'_>) { self_type.to_string() ), None, - &format!("remove the inherent method from type `{}`", self_type.to_string()) + &format!("remove the inherent method from type `{}`", self_type.to_string()), ); } else { span_lint_and_help( diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 3ff9e182121..711cd5b3b15 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -3158,11 +3158,7 @@ fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, identifier: Ident) seen_other: false, }; visitor.visit_block(block); - if visitor.seen_other { - None - } else { - Some(visitor.uses) - } + if visitor.seen_other { None } else { Some(visitor.uses) } } fn shorten_needless_collect_span(expr: &Expr<'_>) -> Span { diff --git a/clippy_lints/src/mutable_debug_assertion.rs b/clippy_lints/src/mutable_debug_assertion.rs index 76417aa7ed0..9ac127abe0a 100644 --- a/clippy_lints/src/mutable_debug_assertion.rs +++ b/clippy_lints/src/mutable_debug_assertion.rs @@ -73,11 +73,7 @@ impl<'a, 'tcx> MutArgVisitor<'a, 'tcx> { } fn expr_span(&self) -> Option { - if self.found { - self.expr_span - } else { - None - } + if self.found { self.expr_span } else { None } } } diff --git a/clippy_lints/src/needless_continue.rs b/clippy_lints/src/needless_continue.rs index 603071a5f4a..30fe2d6225c 100644 --- a/clippy_lints/src/needless_continue.rs +++ b/clippy_lints/src/needless_continue.rs @@ -416,11 +416,7 @@ fn erode_from_back(s: &str) -> String { break; } } - if ret.is_empty() { - s.to_string() - } else { - ret - } + if ret.is_empty() { s.to_string() } else { ret } } fn span_of_first_expr_in_block(block: &ast::Block) -> Option { diff --git a/clippy_lints/src/open_options.rs b/clippy_lints/src/open_options.rs index 73a99a3a2f8..07ca196990d 100644 --- a/clippy_lints/src/open_options.rs +++ b/clippy_lints/src/open_options.rs @@ -69,15 +69,11 @@ fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec .. } = *span { - if lit { - Argument::True - } else { - Argument::False - } + if lit { Argument::True } else { Argument::False } } else { - return; // The function is called with a literal - // which is not a boolean literal. This is theoretically - // possible, but not very likely. + // The function is called with a literal which is not a boolean literal. + // This is theoretically possible, but not very likely. + return; } }, _ => Argument::Unknown, diff --git a/clippy_utils/src/camel_case.rs b/clippy_utils/src/camel_case.rs index ba1c01ebc9f..a6636e39137 100644 --- a/clippy_utils/src/camel_case.rs +++ b/clippy_utils/src/camel_case.rs @@ -25,11 +25,7 @@ pub fn until(s: &str) -> usize { return i; } } - if up { - last_i - } else { - s.len() - } + if up { last_i } else { s.len() } } /// Returns index of the last camel-case component of `s`. diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 2380ea4c7bf..fbcc6d8dc4f 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1563,12 +1563,12 @@ pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool { /// ``` pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool { use rustc_trait_selection::traits; - let predicates = - cx.tcx - .predicates_of(did) - .predicates - .iter() - .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None }); + let predicates = cx + .tcx + .predicates_of(did) + .predicates + .iter() + .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None }); traits::impossible_predicates( cx.tcx, traits::elaborate_predicates(cx.tcx, predicates) diff --git a/clippy_utils/src/ptr.rs b/clippy_utils/src/ptr.rs index baeff08e02c..df6143edbca 100644 --- a/clippy_utils/src/ptr.rs +++ b/clippy_utils/src/ptr.rs @@ -36,11 +36,7 @@ fn extract_clone_suggestions<'tcx>( abort: false, }; visitor.visit_body(body); - if visitor.abort { - None - } else { - Some(visitor.spans) - } + if visitor.abort { None } else { Some(visitor.spans) } } struct PtrCloneVisitor<'a, 'tcx> { diff --git a/rustfmt.toml b/rustfmt.toml index f1241e74b0a..4b415a31b27 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -4,3 +4,4 @@ match_block_trailing_comma = true wrap_comments = true edition = "2018" error_on_line_overflow = true +version = "Two" diff --git a/tests/lint_message_convention.rs b/tests/lint_message_convention.rs index 8c07c5b242f..3f754c255b7 100644 --- a/tests/lint_message_convention.rs +++ b/tests/lint_message_convention.rs @@ -98,7 +98,9 @@ fn lint_message_convention() { eprintln!("\n\n"); }); - eprintln!("\n\n\nLint message should not start with a capital letter and should not have punctuation at the end of the message unless multiple sentences are needed."); + eprintln!( + "\n\n\nLint message should not start with a capital letter and should not have punctuation at the end of the message unless multiple sentences are needed." + ); eprintln!("Check out the rustc-dev-guide for more information:"); eprintln!("https://rustc-dev-guide.rust-lang.org/diagnostics.html#diagnostic-structure\n\n\n"); diff --git a/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.rs b/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.rs index fdf8905f812..735909887ac 100644 --- a/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.rs +++ b/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.rs @@ -16,7 +16,8 @@ enum Flags { FIN, } -struct GCCLLVMSomething; // linted with cfg option, beware that lint suggests `GccllvmSomething` instead of - // `GccLlvmSomething` +// linted with cfg option, beware that lint suggests `GccllvmSomething` instead of +// `GccLlvmSomething` +struct GCCLLVMSomething; fn main() {} diff --git a/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.stderr b/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.stderr index 1cc59dc45f2..38e30683d57 100644 --- a/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.stderr +++ b/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.stderr @@ -61,9 +61,9 @@ LL | FIN, | ^^^ help: consider making the acronym lowercase, except the initial letter: `Fin` error: name `GCCLLVMSomething` contains a capitalized acronym - --> $DIR/upper_case_acronyms.rs:19:8 + --> $DIR/upper_case_acronyms.rs:21:8 | -LL | struct GCCLLVMSomething; // linted with cfg option, beware that lint suggests `GccllvmSomething` instead of +LL | struct GCCLLVMSomething; | ^^^^^^^^^^^^^^^^ help: consider making the acronym lowercase, except the initial letter: `GccllvmSomething` error: aborting due to 11 previous errors diff --git a/tests/ui/auxiliary/macro_rules.rs b/tests/ui/auxiliary/macro_rules.rs index d6ecd8568ce..d4470d3f407 100644 --- a/tests/ui/auxiliary/macro_rules.rs +++ b/tests/ui/auxiliary/macro_rules.rs @@ -23,11 +23,7 @@ macro_rules! try_err { pub fn try_err_fn() -> Result { let err: i32 = 1; // To avoid warnings during rustfix - if true { - Err(err)? - } else { - Ok(2) - } + if true { Err(err)? } else { Ok(2) } } }; } diff --git a/tests/ui/blocks_in_if_conditions.fixed b/tests/ui/blocks_in_if_conditions.fixed index 14562c4d32c..e6e40a9948c 100644 --- a/tests/ui/blocks_in_if_conditions.fixed +++ b/tests/ui/blocks_in_if_conditions.fixed @@ -4,9 +4,7 @@ #![warn(clippy::nonminimal_bool)] macro_rules! blocky { - () => {{ - true - }}; + () => {{ true }}; } macro_rules! blocky_too { @@ -34,20 +32,12 @@ fn condition_has_block() -> i32 { } fn condition_has_block_with_single_expression() -> i32 { - if true { - 6 - } else { - 10 - } + if true { 6 } else { 10 } } fn condition_is_normal() -> i32 { let x = 3; - if x == 3 { - 6 - } else { - 10 - } + if x == 3 { 6 } else { 10 } } fn condition_is_unsafe_block() { @@ -61,14 +51,15 @@ fn condition_is_unsafe_block() { fn block_in_assert() { let opt = Some(42); - assert!(opt - .as_ref() - .map(|val| { - let mut v = val * 2; - v -= 1; - v * 3 - }) - .is_some()); + assert!( + opt.as_ref() + .map(|val| { + let mut v = val * 2; + v -= 1; + v * 3 + }) + .is_some() + ); } fn main() {} diff --git a/tests/ui/blocks_in_if_conditions.rs b/tests/ui/blocks_in_if_conditions.rs index bda87650f6d..69387ff5782 100644 --- a/tests/ui/blocks_in_if_conditions.rs +++ b/tests/ui/blocks_in_if_conditions.rs @@ -4,9 +4,7 @@ #![warn(clippy::nonminimal_bool)] macro_rules! blocky { - () => {{ - true - }}; + () => {{ true }}; } macro_rules! blocky_too { @@ -34,20 +32,12 @@ fn condition_has_block() -> i32 { } fn condition_has_block_with_single_expression() -> i32 { - if { true } { - 6 - } else { - 10 - } + if { true } { 6 } else { 10 } } fn condition_is_normal() -> i32 { let x = 3; - if true && x == 3 { - 6 - } else { - 10 - } + if true && x == 3 { 6 } else { 10 } } fn condition_is_unsafe_block() { @@ -61,14 +51,15 @@ fn condition_is_unsafe_block() { fn block_in_assert() { let opt = Some(42); - assert!(opt - .as_ref() - .map(|val| { - let mut v = val * 2; - v -= 1; - v * 3 - }) - .is_some()); + assert!( + opt.as_ref() + .map(|val| { + let mut v = val * 2; + v -= 1; + v * 3 + }) + .is_some() + ); } fn main() {} diff --git a/tests/ui/blocks_in_if_conditions.stderr b/tests/ui/blocks_in_if_conditions.stderr index 9bdddc8e152..9328492733f 100644 --- a/tests/ui/blocks_in_if_conditions.stderr +++ b/tests/ui/blocks_in_if_conditions.stderr @@ -1,5 +1,5 @@ error: in an `if` condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a `let` - --> $DIR/blocks_in_if_conditions.rs:26:5 + --> $DIR/blocks_in_if_conditions.rs:24:5 | LL | / if { LL | | let x = 3; @@ -17,15 +17,15 @@ LL | }; if res { | error: omit braces around single expression condition - --> $DIR/blocks_in_if_conditions.rs:37:8 + --> $DIR/blocks_in_if_conditions.rs:35:8 | -LL | if { true } { +LL | if { true } { 6 } else { 10 } | ^^^^^^^^ help: try: `true` error: this boolean expression can be simplified - --> $DIR/blocks_in_if_conditions.rs:46:8 + --> $DIR/blocks_in_if_conditions.rs:40:8 | -LL | if true && x == 3 { +LL | if true && x == 3 { 6 } else { 10 } | ^^^^^^^^^^^^^^ help: try: `x == 3` | = note: `-D clippy::nonminimal-bool` implied by `-D warnings` diff --git a/tests/ui/checked_unwrap/simple_conditionals.rs b/tests/ui/checked_unwrap/simple_conditionals.rs index 3e7b4b390ba..36967630834 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.rs +++ b/tests/ui/checked_unwrap/simple_conditionals.rs @@ -66,14 +66,16 @@ fn main() { } if x.is_ok() { x = Err(()); - x.unwrap(); // not unnecessary because of mutation of x - // it will always panic but the lint is not smart enough to see this (it only - // checks if conditions). + // not unnecessary because of mutation of x + // it will always panic but the lint is not smart enough to see this (it only + // checks if conditions). + x.unwrap(); } else { x = Ok(()); - x.unwrap_err(); // not unnecessary because of mutation of x - // it will always panic but the lint is not smart enough to see this (it - // only checks if conditions). + // not unnecessary because of mutation of x + // it will always panic but the lint is not smart enough to see this (it + // only checks if conditions). + x.unwrap_err(); } assert!(x.is_ok(), "{:?}", x.unwrap_err()); // ok, it's a common test pattern diff --git a/tests/ui/crashes/ice-6256.rs b/tests/ui/crashes/ice-6256.rs index 5409f36b3f1..67308263dad 100644 --- a/tests/ui/crashes/ice-6256.rs +++ b/tests/ui/crashes/ice-6256.rs @@ -8,6 +8,7 @@ impl dyn TT { fn func(&self) {} } +#[rustfmt::skip] fn main() { let f = |x: &dyn TT| x.func(); //[default]~ ERROR: mismatched types //[nll]~^ ERROR: borrowed data escapes outside of closure diff --git a/tests/ui/crashes/ice-6256.stderr b/tests/ui/crashes/ice-6256.stderr index d1a8bdc3c8d..d35d459168f 100644 --- a/tests/ui/crashes/ice-6256.stderr +++ b/tests/ui/crashes/ice-6256.stderr @@ -1,13 +1,13 @@ error[E0308]: mismatched types - --> $DIR/ice-6256.rs:12:28 + --> $DIR/ice-6256.rs:13:28 | LL | let f = |x: &dyn TT| x.func(); //[default]~ ERROR: mismatched types | ^^^^ lifetime mismatch | = note: expected reference `&(dyn TT + 'static)` found reference `&dyn TT` -note: the anonymous lifetime #1 defined on the body at 12:13... - --> $DIR/ice-6256.rs:12:13 +note: the anonymous lifetime #1 defined on the body at 13:13... + --> $DIR/ice-6256.rs:13:13 | LL | let f = |x: &dyn TT| x.func(); //[default]~ ERROR: mismatched types | ^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/dbg_macro.rs b/tests/ui/dbg_macro.rs index d2df7fbd3e8..d74e2611ee1 100644 --- a/tests/ui/dbg_macro.rs +++ b/tests/ui/dbg_macro.rs @@ -1,11 +1,7 @@ #![warn(clippy::dbg_macro)] fn foo(n: u32) -> u32 { - if let Some(n) = dbg!(n.checked_sub(4)) { - n - } else { - n - } + if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n } } fn factorial(n: u32) -> u32 { diff --git a/tests/ui/dbg_macro.stderr b/tests/ui/dbg_macro.stderr index b8aafe96678..bdf372af290 100644 --- a/tests/ui/dbg_macro.stderr +++ b/tests/ui/dbg_macro.stderr @@ -1,17 +1,17 @@ error: `dbg!` macro is intended as a debugging tool --> $DIR/dbg_macro.rs:4:22 | -LL | if let Some(n) = dbg!(n.checked_sub(4)) { +LL | if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n } | ^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::dbg-macro` implied by `-D warnings` help: ensure to avoid having uses of it in version control | -LL | if let Some(n) = n.checked_sub(4) { +LL | if let Some(n) = n.checked_sub(4) { n } else { n } | ^^^^^^^^^^^^^^^^ error: `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:12:8 + --> $DIR/dbg_macro.rs:8:8 | LL | if dbg!(n <= 1) { | ^^^^^^^^^^^^ @@ -22,7 +22,7 @@ LL | if n <= 1 { | ^^^^^^ error: `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:13:9 + --> $DIR/dbg_macro.rs:9:9 | LL | dbg!(1) | ^^^^^^^ @@ -33,7 +33,7 @@ LL | 1 | error: `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:15:9 + --> $DIR/dbg_macro.rs:11:9 | LL | dbg!(n * factorial(n - 1)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -44,7 +44,7 @@ LL | n * factorial(n - 1) | error: `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:20:5 + --> $DIR/dbg_macro.rs:16:5 | LL | dbg!(42); | ^^^^^^^^ @@ -55,7 +55,7 @@ LL | 42; | ^^ error: `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:21:5 + --> $DIR/dbg_macro.rs:17:5 | LL | dbg!(dbg!(dbg!(42))); | ^^^^^^^^^^^^^^^^^^^^ @@ -66,7 +66,7 @@ LL | dbg!(dbg!(42)); | ^^^^^^^^^^^^^^ error: `dbg!` macro is intended as a debugging tool - --> $DIR/dbg_macro.rs:22:14 + --> $DIR/dbg_macro.rs:18:14 | LL | foo(3) + dbg!(factorial(4)); | ^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/default_trait_access.fixed b/tests/ui/default_trait_access.fixed index d05567a3f82..4c80cabc723 100644 --- a/tests/ui/default_trait_access.fixed +++ b/tests/ui/default_trait_access.fixed @@ -48,25 +48,7 @@ fn main() { println!( "[{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}], [{:?}]", - s1, - s2, - s3, - s4, - s5, - s6, - s7, - s8, - s9, - s10, - s11, - s12, - s13, - s14, - s15, - s16, - s17, - s18, - s19, + s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17, s18, s19, ); } diff --git a/tests/ui/default_trait_access.rs b/tests/ui/default_trait_access.rs index 447e70c0bbb..a68b6455c04 100644 --- a/tests/ui/default_trait_access.rs +++ b/tests/ui/default_trait_access.rs @@ -48,25 +48,7 @@ fn main() { println!( "[{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}], [{:?}]", - s1, - s2, - s3, - s4, - s5, - s6, - s7, - s8, - s9, - s10, - s11, - s12, - s13, - s14, - s15, - s16, - s17, - s18, - s19, + s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17, s18, s19, ); } diff --git a/tests/ui/doc_panics.rs b/tests/ui/doc_panics.rs index 3008c2d5b85..17e72353f80 100644 --- a/tests/ui/doc_panics.rs +++ b/tests/ui/doc_panics.rs @@ -30,11 +30,7 @@ pub fn inner_body(opt: Option) { /// This needs to be documented pub fn unreachable_and_panic() { - if true { - unreachable!() - } else { - panic!() - } + if true { unreachable!() } else { panic!() } } /// This is documented @@ -84,11 +80,7 @@ pub fn todo_documented() { /// /// We still need to do this part pub fn unreachable_amd_panic_documented() { - if true { - unreachable!() - } else { - panic!() - } + if true { unreachable!() } else { panic!() } } /// This is okay because it is private diff --git a/tests/ui/doc_panics.stderr b/tests/ui/doc_panics.stderr index 287148690d2..2fa88a2f6ec 100644 --- a/tests/ui/doc_panics.stderr +++ b/tests/ui/doc_panics.stderr @@ -67,19 +67,15 @@ error: docs for function which may panic missing `# Panics` section --> $DIR/doc_panics.rs:32:1 | LL | / pub fn unreachable_and_panic() { -LL | | if true { -LL | | unreachable!() -LL | | } else { -LL | | panic!() -LL | | } +LL | | if true { unreachable!() } else { panic!() } LL | | } | |_^ | note: first possible panic found here - --> $DIR/doc_panics.rs:36:9 + --> $DIR/doc_panics.rs:33:39 | -LL | panic!() - | ^^^^^^^^ +LL | if true { unreachable!() } else { panic!() } + | ^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 5 previous errors diff --git a/tests/ui/float_cmp.rs b/tests/ui/float_cmp.rs index 586784b73e6..ad5d1a09c03 100644 --- a/tests/ui/float_cmp.rs +++ b/tests/ui/float_cmp.rs @@ -21,19 +21,11 @@ where } fn eq_fl(x: f32, y: f32) -> bool { - if x.is_nan() { - y.is_nan() - } else { - x == y - } // no error, inside "eq" fn + if x.is_nan() { y.is_nan() } else { x == y } // no error, inside "eq" fn } fn fl_eq(x: f32, y: f32) -> bool { - if x.is_nan() { - y.is_nan() - } else { - x == y - } // no error, inside "eq" fn + if x.is_nan() { y.is_nan() } else { x == y } // no error, inside "eq" fn } struct X { diff --git a/tests/ui/float_cmp.stderr b/tests/ui/float_cmp.stderr index bb4051c4662..cb5b68b2e95 100644 --- a/tests/ui/float_cmp.stderr +++ b/tests/ui/float_cmp.stderr @@ -1,5 +1,5 @@ error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:66:5 + --> $DIR/float_cmp.rs:58:5 | LL | ONE as f64 != 2.0; | ^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(ONE as f64 - 2.0).abs() > error_margin` @@ -8,7 +8,7 @@ LL | ONE as f64 != 2.0; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:71:5 + --> $DIR/float_cmp.rs:63:5 | LL | x == 1.0; | ^^^^^^^^ help: consider comparing them within some margin of error: `(x - 1.0).abs() < error_margin` @@ -16,7 +16,7 @@ LL | x == 1.0; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:74:5 + --> $DIR/float_cmp.rs:66:5 | LL | twice(x) != twice(ONE as f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(twice(x) - twice(ONE as f64)).abs() > error_margin` @@ -24,7 +24,7 @@ LL | twice(x) != twice(ONE as f64); = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:94:5 + --> $DIR/float_cmp.rs:86:5 | LL | NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(NON_ZERO_ARRAY[i] - NON_ZERO_ARRAY[j]).abs() < error_margin` @@ -32,7 +32,7 @@ LL | NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j]; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` arrays - --> $DIR/float_cmp.rs:99:5 + --> $DIR/float_cmp.rs:91:5 | LL | a1 == a2; | ^^^^^^^^ @@ -40,7 +40,7 @@ LL | a1 == a2; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:100:5 + --> $DIR/float_cmp.rs:92:5 | LL | a1[0] == a2[0]; | ^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(a1[0] - a2[0]).abs() < error_margin` diff --git a/tests/ui/float_cmp_const.rs b/tests/ui/float_cmp_const.rs index 263d9a7b92d..86ce3bf3bd9 100644 --- a/tests/ui/float_cmp_const.rs +++ b/tests/ui/float_cmp_const.rs @@ -8,11 +8,7 @@ const ONE: f32 = 1.0; const TWO: f32 = 2.0; fn eq_one(x: f32) -> bool { - if x.is_nan() { - false - } else { - x == ONE - } // no error, inside "eq" fn + if x.is_nan() { false } else { x == ONE } // no error, inside "eq" fn } fn main() { diff --git a/tests/ui/float_cmp_const.stderr b/tests/ui/float_cmp_const.stderr index 5d0455363e8..d8182cf855b 100644 --- a/tests/ui/float_cmp_const.stderr +++ b/tests/ui/float_cmp_const.stderr @@ -1,5 +1,5 @@ error: strict comparison of `f32` or `f64` constant - --> $DIR/float_cmp_const.rs:20:5 + --> $DIR/float_cmp_const.rs:16:5 | LL | 1f32 == ONE; | ^^^^^^^^^^^ help: consider comparing them within some margin of error: `(1f32 - ONE).abs() < error_margin` @@ -8,7 +8,7 @@ LL | 1f32 == ONE; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` constant - --> $DIR/float_cmp_const.rs:21:5 + --> $DIR/float_cmp_const.rs:17:5 | LL | TWO == ONE; | ^^^^^^^^^^ help: consider comparing them within some margin of error: `(TWO - ONE).abs() < error_margin` @@ -16,7 +16,7 @@ LL | TWO == ONE; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` constant - --> $DIR/float_cmp_const.rs:22:5 + --> $DIR/float_cmp_const.rs:18:5 | LL | TWO != ONE; | ^^^^^^^^^^ help: consider comparing them within some margin of error: `(TWO - ONE).abs() > error_margin` @@ -24,7 +24,7 @@ LL | TWO != ONE; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` constant - --> $DIR/float_cmp_const.rs:23:5 + --> $DIR/float_cmp_const.rs:19:5 | LL | ONE + ONE == TWO; | ^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(ONE + ONE - TWO).abs() < error_margin` @@ -32,7 +32,7 @@ LL | ONE + ONE == TWO; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` constant - --> $DIR/float_cmp_const.rs:25:5 + --> $DIR/float_cmp_const.rs:21:5 | LL | x as f32 == ONE; | ^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(x as f32 - ONE).abs() < error_margin` @@ -40,7 +40,7 @@ LL | x as f32 == ONE; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` constant - --> $DIR/float_cmp_const.rs:28:5 + --> $DIR/float_cmp_const.rs:24:5 | LL | v == ONE; | ^^^^^^^^ help: consider comparing them within some margin of error: `(v - ONE).abs() < error_margin` @@ -48,7 +48,7 @@ LL | v == ONE; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` constant - --> $DIR/float_cmp_const.rs:29:5 + --> $DIR/float_cmp_const.rs:25:5 | LL | v != ONE; | ^^^^^^^^ help: consider comparing them within some margin of error: `(v - ONE).abs() > error_margin` @@ -56,7 +56,7 @@ LL | v != ONE; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` constant arrays - --> $DIR/float_cmp_const.rs:61:5 + --> $DIR/float_cmp_const.rs:57:5 | LL | NON_ZERO_ARRAY == NON_ZERO_ARRAY2; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/floating_point_abs.fixed b/tests/ui/floating_point_abs.fixed index b623e4988e7..cea727257c4 100644 --- a/tests/ui/floating_point_abs.fixed +++ b/tests/ui/floating_point_abs.fixed @@ -42,43 +42,23 @@ fn fake_nabs3(a: A) -> A { } fn not_fake_abs1(num: f64) -> f64 { - if num > 0.0 { - num - } else { - -num - 1f64 - } + if num > 0.0 { num } else { -num - 1f64 } } fn not_fake_abs2(num: f64) -> f64 { - if num > 0.0 { - num + 1.0 - } else { - -(num + 1.0) - } + if num > 0.0 { num + 1.0 } else { -(num + 1.0) } } fn not_fake_abs3(num1: f64, num2: f64) -> f64 { - if num1 > 0.0 { - num2 - } else { - -num2 - } + if num1 > 0.0 { num2 } else { -num2 } } fn not_fake_abs4(a: A) -> f64 { - if a.a > 0.0 { - a.b - } else { - -a.b - } + if a.a > 0.0 { a.b } else { -a.b } } fn not_fake_abs5(a: A) -> f64 { - if a.a > 0.0 { - a.a - } else { - -a.b - } + if a.a > 0.0 { a.a } else { -a.b } } fn main() { diff --git a/tests/ui/floating_point_abs.rs b/tests/ui/floating_point_abs.rs index cbf9c94e41e..ba8a8f18fa2 100644 --- a/tests/ui/floating_point_abs.rs +++ b/tests/ui/floating_point_abs.rs @@ -7,59 +7,31 @@ struct A { } fn fake_abs1(num: f64) -> f64 { - if num >= 0.0 { - num - } else { - -num - } + if num >= 0.0 { num } else { -num } } fn fake_abs2(num: f64) -> f64 { - if 0.0 < num { - num - } else { - -num - } + if 0.0 < num { num } else { -num } } fn fake_abs3(a: A) -> f64 { - if a.a > 0.0 { - a.a - } else { - -a.a - } + if a.a > 0.0 { a.a } else { -a.a } } fn fake_abs4(num: f64) -> f64 { - if 0.0 >= num { - -num - } else { - num - } + if 0.0 >= num { -num } else { num } } fn fake_abs5(a: A) -> f64 { - if a.a < 0.0 { - -a.a - } else { - a.a - } + if a.a < 0.0 { -a.a } else { a.a } } fn fake_nabs1(num: f64) -> f64 { - if num < 0.0 { - num - } else { - -num - } + if num < 0.0 { num } else { -num } } fn fake_nabs2(num: f64) -> f64 { - if 0.0 >= num { - num - } else { - -num - } + if 0.0 >= num { num } else { -num } } fn fake_nabs3(a: A) -> A { @@ -70,43 +42,23 @@ fn fake_nabs3(a: A) -> A { } fn not_fake_abs1(num: f64) -> f64 { - if num > 0.0 { - num - } else { - -num - 1f64 - } + if num > 0.0 { num } else { -num - 1f64 } } fn not_fake_abs2(num: f64) -> f64 { - if num > 0.0 { - num + 1.0 - } else { - -(num + 1.0) - } + if num > 0.0 { num + 1.0 } else { -(num + 1.0) } } fn not_fake_abs3(num1: f64, num2: f64) -> f64 { - if num1 > 0.0 { - num2 - } else { - -num2 - } + if num1 > 0.0 { num2 } else { -num2 } } fn not_fake_abs4(a: A) -> f64 { - if a.a > 0.0 { - a.b - } else { - -a.b - } + if a.a > 0.0 { a.b } else { -a.b } } fn not_fake_abs5(a: A) -> f64 { - if a.a > 0.0 { - a.a - } else { - -a.b - } + if a.a > 0.0 { a.a } else { -a.b } } fn main() { diff --git a/tests/ui/floating_point_abs.stderr b/tests/ui/floating_point_abs.stderr index 74a71f2ca7c..35af70201fa 100644 --- a/tests/ui/floating_point_abs.stderr +++ b/tests/ui/floating_point_abs.stderr @@ -1,77 +1,49 @@ error: manual implementation of `abs` method --> $DIR/floating_point_abs.rs:10:5 | -LL | / if num >= 0.0 { -LL | | num -LL | | } else { -LL | | -num -LL | | } - | |_____^ help: try: `num.abs()` +LL | if num >= 0.0 { num } else { -num } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.abs()` | = note: `-D clippy::suboptimal-flops` implied by `-D warnings` error: manual implementation of `abs` method - --> $DIR/floating_point_abs.rs:18:5 + --> $DIR/floating_point_abs.rs:14:5 | -LL | / if 0.0 < num { -LL | | num -LL | | } else { -LL | | -num -LL | | } - | |_____^ help: try: `num.abs()` +LL | if 0.0 < num { num } else { -num } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.abs()` error: manual implementation of `abs` method - --> $DIR/floating_point_abs.rs:26:5 + --> $DIR/floating_point_abs.rs:18:5 | -LL | / if a.a > 0.0 { -LL | | a.a -LL | | } else { -LL | | -a.a -LL | | } - | |_____^ help: try: `a.a.abs()` +LL | if a.a > 0.0 { a.a } else { -a.a } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.a.abs()` error: manual implementation of `abs` method - --> $DIR/floating_point_abs.rs:34:5 + --> $DIR/floating_point_abs.rs:22:5 | -LL | / if 0.0 >= num { -LL | | -num -LL | | } else { -LL | | num -LL | | } - | |_____^ help: try: `num.abs()` +LL | if 0.0 >= num { -num } else { num } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.abs()` error: manual implementation of `abs` method - --> $DIR/floating_point_abs.rs:42:5 + --> $DIR/floating_point_abs.rs:26:5 | -LL | / if a.a < 0.0 { -LL | | -a.a -LL | | } else { -LL | | a.a -LL | | } - | |_____^ help: try: `a.a.abs()` +LL | if a.a < 0.0 { -a.a } else { a.a } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.a.abs()` error: manual implementation of negation of `abs` method - --> $DIR/floating_point_abs.rs:50:5 + --> $DIR/floating_point_abs.rs:30:5 | -LL | / if num < 0.0 { -LL | | num -LL | | } else { -LL | | -num -LL | | } - | |_____^ help: try: `-num.abs()` +LL | if num < 0.0 { num } else { -num } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-num.abs()` error: manual implementation of negation of `abs` method - --> $DIR/floating_point_abs.rs:58:5 + --> $DIR/floating_point_abs.rs:34:5 | -LL | / if 0.0 >= num { -LL | | num -LL | | } else { -LL | | -num -LL | | } - | |_____^ help: try: `-num.abs()` +LL | if 0.0 >= num { num } else { -num } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-num.abs()` error: manual implementation of negation of `abs` method - --> $DIR/floating_point_abs.rs:67:12 + --> $DIR/floating_point_abs.rs:39:12 | LL | a: if a.a >= 0.0 { -a.a } else { a.a }, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-a.a.abs()` diff --git a/tests/ui/if_let_some_result.fixed b/tests/ui/if_let_some_result.fixed index 80505fd997f..62a25ce2d12 100644 --- a/tests/ui/if_let_some_result.fixed +++ b/tests/ui/if_let_some_result.fixed @@ -3,19 +3,11 @@ #![warn(clippy::if_let_some_result)] fn str_to_int(x: &str) -> i32 { - if let Ok(y) = x.parse() { - y - } else { - 0 - } + if let Ok(y) = x.parse() { y } else { 0 } } fn str_to_int_ok(x: &str) -> i32 { - if let Ok(y) = x.parse() { - y - } else { - 0 - } + if let Ok(y) = x.parse() { y } else { 0 } } #[rustfmt::skip] diff --git a/tests/ui/if_let_some_result.rs b/tests/ui/if_let_some_result.rs index ecac1357445..234ff5e9e80 100644 --- a/tests/ui/if_let_some_result.rs +++ b/tests/ui/if_let_some_result.rs @@ -3,19 +3,11 @@ #![warn(clippy::if_let_some_result)] fn str_to_int(x: &str) -> i32 { - if let Some(y) = x.parse().ok() { - y - } else { - 0 - } + if let Some(y) = x.parse().ok() { y } else { 0 } } fn str_to_int_ok(x: &str) -> i32 { - if let Ok(y) = x.parse() { - y - } else { - 0 - } + if let Ok(y) = x.parse() { y } else { 0 } } #[rustfmt::skip] diff --git a/tests/ui/if_let_some_result.stderr b/tests/ui/if_let_some_result.stderr index 6afee0f36b9..0646dd27f35 100644 --- a/tests/ui/if_let_some_result.stderr +++ b/tests/ui/if_let_some_result.stderr @@ -1,17 +1,17 @@ error: matching on `Some` with `ok()` is redundant --> $DIR/if_let_some_result.rs:6:5 | -LL | if let Some(y) = x.parse().ok() { +LL | if let Some(y) = x.parse().ok() { y } else { 0 } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::if-let-some-result` implied by `-D warnings` help: consider matching on `Ok(y)` and removing the call to `ok` instead | -LL | if let Ok(y) = x.parse() { +LL | if let Ok(y) = x.parse() { y } else { 0 } | ^^^^^^^^^^^^^^^^^^^^^^^^ error: matching on `Some` with `ok()` is redundant - --> $DIR/if_let_some_result.rs:24:9 + --> $DIR/if_let_some_result.rs:16:9 | LL | if let Some(y) = x . parse() . ok () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/implicit_return.fixed b/tests/ui/implicit_return.fixed index 9066dc3fedf..59f7ad9c106 100644 --- a/tests/ui/implicit_return.fixed +++ b/tests/ui/implicit_return.fixed @@ -14,11 +14,7 @@ fn test_end_of_fn() -> bool { #[allow(clippy::needless_bool)] fn test_if_block() -> bool { - if true { - return true - } else { - return false - } + if true { return true } else { return false } } #[rustfmt::skip] diff --git a/tests/ui/implicit_return.rs b/tests/ui/implicit_return.rs index c0d70ecf502..2c1bc046515 100644 --- a/tests/ui/implicit_return.rs +++ b/tests/ui/implicit_return.rs @@ -14,11 +14,7 @@ fn test_end_of_fn() -> bool { #[allow(clippy::needless_bool)] fn test_if_block() -> bool { - if true { - true - } else { - false - } + if true { true } else { false } } #[rustfmt::skip] diff --git a/tests/ui/implicit_return.stderr b/tests/ui/implicit_return.stderr index fb2ec902764..3608319e5bd 100644 --- a/tests/ui/implicit_return.stderr +++ b/tests/ui/implicit_return.stderr @@ -7,61 +7,61 @@ LL | true = note: `-D clippy::implicit-return` implied by `-D warnings` error: missing `return` statement - --> $DIR/implicit_return.rs:18:9 + --> $DIR/implicit_return.rs:17:15 | -LL | true - | ^^^^ help: add `return` as shown: `return true` +LL | if true { true } else { false } + | ^^^^ help: add `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:20:9 + --> $DIR/implicit_return.rs:17:29 | -LL | false - | ^^^^^ help: add `return` as shown: `return false` +LL | if true { true } else { false } + | ^^^^^ help: add `return` as shown: `return false` error: missing `return` statement - --> $DIR/implicit_return.rs:27:17 + --> $DIR/implicit_return.rs:23:17 | LL | true => false, | ^^^^^ help: add `return` as shown: `return false` error: missing `return` statement - --> $DIR/implicit_return.rs:28:20 + --> $DIR/implicit_return.rs:24:20 | LL | false => { true }, | ^^^^ help: add `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:43:9 + --> $DIR/implicit_return.rs:39:9 | LL | break true; | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:51:13 + --> $DIR/implicit_return.rs:47:13 | LL | break true; | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:60:13 + --> $DIR/implicit_return.rs:56:13 | LL | break true; | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:78:18 + --> $DIR/implicit_return.rs:74:18 | LL | let _ = || { true }; | ^^^^ help: add `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:79:16 + --> $DIR/implicit_return.rs:75:16 | LL | let _ = || true; | ^^^^ help: add `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:87:5 + --> $DIR/implicit_return.rs:83:5 | LL | format!("test {}", "test") | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `return` as shown: `return format!("test {}", "test")` diff --git a/tests/ui/needless_lifetimes.rs b/tests/ui/needless_lifetimes.rs index 44972c8c639..bda0801e51c 100644 --- a/tests/ui/needless_lifetimes.rs +++ b/tests/ui/needless_lifetimes.rs @@ -105,11 +105,7 @@ fn fn_bound_3_cannot_elide() { // No error; multiple input refs. fn fn_bound_4<'a, F: FnOnce() -> &'a ()>(cond: bool, x: &'a (), f: F) -> &'a () { - if cond { - x - } else { - f() - } + if cond { x } else { f() } } struct X { diff --git a/tests/ui/needless_lifetimes.stderr b/tests/ui/needless_lifetimes.stderr index c8a2e8b81c0..33a6de1618d 100644 --- a/tests/ui/needless_lifetimes.stderr +++ b/tests/ui/needless_lifetimes.stderr @@ -43,109 +43,109 @@ LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:120:5 + --> $DIR/needless_lifetimes.rs:116:5 | LL | fn self_and_out<'s>(&'s self) -> &'s u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:129:5 + --> $DIR/needless_lifetimes.rs:125:5 | LL | fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:148:1 + --> $DIR/needless_lifetimes.rs:144:1 | LL | fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:178:1 + --> $DIR/needless_lifetimes.rs:174:1 | LL | fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:184:1 + --> $DIR/needless_lifetimes.rs:180:1 | LL | fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:203:1 + --> $DIR/needless_lifetimes.rs:199:1 | LL | fn named_input_elided_output<'a>(_arg: &'a str) -> &str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:211:1 + --> $DIR/needless_lifetimes.rs:207:1 | LL | fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:247:1 + --> $DIR/needless_lifetimes.rs:243:1 | LL | fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:254:9 + --> $DIR/needless_lifetimes.rs:250:9 | LL | fn needless_lt<'a>(x: &'a u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:258:9 + --> $DIR/needless_lifetimes.rs:254:9 | LL | fn needless_lt<'a>(_x: &'a u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:271:9 + --> $DIR/needless_lifetimes.rs:267:9 | LL | fn baz<'a>(&'a self) -> impl Foo + 'a { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:300:5 + --> $DIR/needless_lifetimes.rs:296:5 | LL | fn impl_trait_elidable_nested_named_lifetimes<'a>(i: &'a i32, f: impl for<'b> Fn(&'b i32) -> &'b i32) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:303:5 + --> $DIR/needless_lifetimes.rs:299:5 | LL | fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:312:5 + --> $DIR/needless_lifetimes.rs:308:5 | LL | fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:324:5 + --> $DIR/needless_lifetimes.rs:320:5 | LL | fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:339:5 + --> $DIR/needless_lifetimes.rs:335:5 | LL | fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:352:5 + --> $DIR/needless_lifetimes.rs:348:5 | LL | fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:355:5 + --> $DIR/needless_lifetimes.rs:351:5 | LL | fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/redundant_clone.fixed b/tests/ui/redundant_clone.fixed index a5847e37c97..ec309109ed5 100644 --- a/tests/ui/redundant_clone.fixed +++ b/tests/ui/redundant_clone.fixed @@ -59,11 +59,7 @@ fn main() { #[derive(Clone)] struct Alpha; fn with_branch(a: Alpha, b: bool) -> (Alpha, Alpha) { - if b { - (a.clone(), a) - } else { - (Alpha, a) - } + if b { (a.clone(), a) } else { (Alpha, a) } } fn cannot_double_move(a: Alpha) -> (Alpha, Alpha) { diff --git a/tests/ui/redundant_clone.rs b/tests/ui/redundant_clone.rs index dab8d7fb1c7..b57027456e0 100644 --- a/tests/ui/redundant_clone.rs +++ b/tests/ui/redundant_clone.rs @@ -59,11 +59,7 @@ fn main() { #[derive(Clone)] struct Alpha; fn with_branch(a: Alpha, b: bool) -> (Alpha, Alpha) { - if b { - (a.clone(), a.clone()) - } else { - (Alpha, a) - } + if b { (a.clone(), a.clone()) } else { (Alpha, a) } } fn cannot_double_move(a: Alpha) -> (Alpha, Alpha) { diff --git a/tests/ui/redundant_clone.stderr b/tests/ui/redundant_clone.stderr index 87c219316ce..821e7934be8 100644 --- a/tests/ui/redundant_clone.stderr +++ b/tests/ui/redundant_clone.stderr @@ -108,61 +108,61 @@ LL | let _t = tup.0.clone(); | ^^^^^ error: redundant clone - --> $DIR/redundant_clone.rs:63:22 + --> $DIR/redundant_clone.rs:62:25 | -LL | (a.clone(), a.clone()) - | ^^^^^^^^ help: remove this +LL | if b { (a.clone(), a.clone()) } else { (Alpha, a) } + | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:63:21 + --> $DIR/redundant_clone.rs:62:24 | -LL | (a.clone(), a.clone()) - | ^ +LL | if b { (a.clone(), a.clone()) } else { (Alpha, a) } + | ^ error: redundant clone - --> $DIR/redundant_clone.rs:123:15 + --> $DIR/redundant_clone.rs:119:15 | LL | let _s = s.clone(); | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:123:14 + --> $DIR/redundant_clone.rs:119:14 | LL | let _s = s.clone(); | ^ error: redundant clone - --> $DIR/redundant_clone.rs:124:15 + --> $DIR/redundant_clone.rs:120:15 | LL | let _t = t.clone(); | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:124:14 + --> $DIR/redundant_clone.rs:120:14 | LL | let _t = t.clone(); | ^ error: redundant clone - --> $DIR/redundant_clone.rs:134:19 + --> $DIR/redundant_clone.rs:130:19 | LL | let _f = f.clone(); | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:134:18 + --> $DIR/redundant_clone.rs:130:18 | LL | let _f = f.clone(); | ^ error: redundant clone - --> $DIR/redundant_clone.rs:146:14 + --> $DIR/redundant_clone.rs:142:14 | LL | let y = x.clone().join("matthias"); | ^^^^^^^^ help: remove this | note: cloned value is neither consumed nor mutated - --> $DIR/redundant_clone.rs:146:13 + --> $DIR/redundant_clone.rs:142:13 | LL | let y = x.clone().join("matthias"); | ^^^^^^^^^ diff --git a/tests/ui/unnecessary_wraps.rs b/tests/ui/unnecessary_wraps.rs index a510263e67d..54f22e3ee6a 100644 --- a/tests/ui/unnecessary_wraps.rs +++ b/tests/ui/unnecessary_wraps.rs @@ -22,29 +22,17 @@ fn func2(a: bool, b: bool) -> Option { if a && b { return Some(10); } - if a { - Some(20) - } else { - Some(30) - } + if a { Some(20) } else { Some(30) } } // public fns should not be linted pub fn func3(a: bool) -> Option { - if a { - Some(1) - } else { - Some(1) - } + if a { Some(1) } else { Some(1) } } // should not be linted fn func4(a: bool) -> Option { - if a { - Some(1) - } else { - None - } + if a { Some(1) } else { None } } // should be linted @@ -64,11 +52,7 @@ fn func7() -> Result { // should not be linted fn func8(a: bool) -> Result { - if a { - Ok(1) - } else { - Err(()) - } + if a { Ok(1) } else { Err(()) } } // should not be linted @@ -143,20 +127,12 @@ fn issue_6640_2(a: bool, b: bool) -> Result<(), i32> { // should not be linted fn issue_6640_3() -> Option<()> { - if true { - Some(()) - } else { - None - } + if true { Some(()) } else { None } } // should not be linted fn issue_6640_4() -> Result<(), ()> { - if true { - Ok(()) - } else { - Err(()) - } + if true { Ok(()) } else { Err(()) } } fn main() { diff --git a/tests/ui/unnecessary_wraps.stderr b/tests/ui/unnecessary_wraps.stderr index 9a861c61a46..0e570397e2a 100644 --- a/tests/ui/unnecessary_wraps.stderr +++ b/tests/ui/unnecessary_wraps.stderr @@ -32,8 +32,7 @@ LL | / fn func2(a: bool, b: bool) -> Option { LL | | if a && b { LL | | return Some(10); LL | | } -... | -LL | | } +LL | | if a { Some(20) } else { Some(30) } LL | | } | |_^ | @@ -45,14 +44,11 @@ help: ...and then change returning expressions | LL | return 10; LL | } -LL | if a { -LL | 20 -LL | } else { -LL | 30 +LL | if a { 20 } else { 30 } | error: this function's return value is unnecessarily wrapped by `Option` - --> $DIR/unnecessary_wraps.rs:51:1 + --> $DIR/unnecessary_wraps.rs:39:1 | LL | / fn func5() -> Option { LL | | Some(1) @@ -69,7 +65,7 @@ LL | 1 | error: this function's return value is unnecessarily wrapped by `Result` - --> $DIR/unnecessary_wraps.rs:61:1 + --> $DIR/unnecessary_wraps.rs:49:1 | LL | / fn func7() -> Result { LL | | Ok(1) @@ -86,7 +82,7 @@ LL | 1 | error: this function's return value is unnecessarily wrapped by `Option` - --> $DIR/unnecessary_wraps.rs:93:5 + --> $DIR/unnecessary_wraps.rs:77:5 | LL | / fn func12() -> Option { LL | | Some(1) @@ -103,7 +99,7 @@ LL | 1 | error: this function's return value is unnecessary - --> $DIR/unnecessary_wraps.rs:120:1 + --> $DIR/unnecessary_wraps.rs:104:1 | LL | / fn issue_6640_1(a: bool, b: bool) -> Option<()> { LL | | if a && b { @@ -129,7 +125,7 @@ LL | } else { ... error: this function's return value is unnecessary - --> $DIR/unnecessary_wraps.rs:133:1 + --> $DIR/unnecessary_wraps.rs:117:1 | LL | / fn issue_6640_2(a: bool, b: bool) -> Result<(), i32> { LL | | if a && b { diff --git a/tests/ui/upper_case_acronyms.rs b/tests/ui/upper_case_acronyms.rs index fdf8905f812..735909887ac 100644 --- a/tests/ui/upper_case_acronyms.rs +++ b/tests/ui/upper_case_acronyms.rs @@ -16,7 +16,8 @@ enum Flags { FIN, } -struct GCCLLVMSomething; // linted with cfg option, beware that lint suggests `GccllvmSomething` instead of - // `GccLlvmSomething` +// linted with cfg option, beware that lint suggests `GccllvmSomething` instead of +// `GccLlvmSomething` +struct GCCLLVMSomething; fn main() {} diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed index 95e7bc75431..2b22a2ed2d5 100644 --- a/tests/ui/use_self.fixed +++ b/tests/ui/use_self.fixed @@ -329,11 +329,7 @@ mod issue4140 { type To = Self; fn from(value: bool) -> Self { - if value { - 100 - } else { - 0 - } + if value { 100 } else { 0 } } } } diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index 75424f34159..609625abdec 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -329,11 +329,7 @@ mod issue4140 { type To = Self; fn from(value: bool) -> Self { - if value { - 100 - } else { - 0 - } + if value { 100 } else { 0 } } } } diff --git a/tests/ui/use_self.stderr b/tests/ui/use_self.stderr index 37dfef7cfe0..e1410d2e652 100644 --- a/tests/ui/use_self.stderr +++ b/tests/ui/use_self.stderr @@ -169,7 +169,7 @@ LL | type To = T::To; | ^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:457:13 + --> $DIR/use_self.rs:453:13 | LL | A::new::(submod::B {}) | ^ help: use the applicable keyword: `Self` -- cgit 1.4.1-3-g733a5 From 2c485e36cdd76bc887278820822ffc053c5b3b83 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 1 Mar 2021 17:25:23 -0500 Subject: Don't move `yield` or inline assembly into closure --- clippy_lints/src/manual_map.rs | 7 ++++++- tests/ui/manual_map_option.fixed | 13 +++++++++++++ tests/ui/manual_map_option.rs | 13 +++++++++++++ tests/ui/manual_map_option.stderr | 38 +++++++++++++++++++------------------- 4 files changed, 51 insertions(+), 20 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs index 983a10e8eaa..ac1d51e1993 100644 --- a/clippy_lints/src/manual_map.rs +++ b/clippy_lints/src/manual_map.rs @@ -203,7 +203,12 @@ fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> boo fn visit_expr(&mut self, e: &'tcx Expr<'_>) { match e.kind { - ExprKind::Break(..) | ExprKind::Continue(_) | ExprKind::Ret(_) => { + ExprKind::Break(..) + | ExprKind::Continue(_) + | ExprKind::Ret(_) + | ExprKind::Yield(..) + | ExprKind::InlineAsm(_) + | ExprKind::LlvmInlineAsm(_) => { self.make_closure = false; }, // Accessing a field of a local value can only be done if the type isn't diff --git a/tests/ui/manual_map_option.fixed b/tests/ui/manual_map_option.fixed index e6fa10d22e1..9222aaf6c78 100644 --- a/tests/ui/manual_map_option.fixed +++ b/tests/ui/manual_map_option.fixed @@ -1,3 +1,4 @@ +// edition:2018 // run-rustfix #![warn(clippy::manual_map)] @@ -115,4 +116,16 @@ fn main() { Some(0).map(|x| vec![x]); option_env!("").map(String::from); + + // #6819 + async fn f2(x: u32) -> u32 { + x + } + + async fn f3() { + match Some(0) { + Some(x) => Some(f2(x).await), + None => None, + }; + } } diff --git a/tests/ui/manual_map_option.rs b/tests/ui/manual_map_option.rs index 7c2100299a7..1ccb450619c 100644 --- a/tests/ui/manual_map_option.rs +++ b/tests/ui/manual_map_option.rs @@ -1,3 +1,4 @@ +// edition:2018 // run-rustfix #![warn(clippy::manual_map)] @@ -173,4 +174,16 @@ fn main() { Some(x) => Some(String::from(x)), None => None, }; + + // #6819 + async fn f2(x: u32) -> u32 { + x + } + + async fn f3() { + match Some(0) { + Some(x) => Some(f2(x).await), + None => None, + }; + } } diff --git a/tests/ui/manual_map_option.stderr b/tests/ui/manual_map_option.stderr index 2d13213cf67..d9f86eecd93 100644 --- a/tests/ui/manual_map_option.stderr +++ b/tests/ui/manual_map_option.stderr @@ -1,5 +1,5 @@ error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:13:5 + --> $DIR/manual_map_option.rs:14:5 | LL | / match Some(0) { LL | | Some(_) => Some(2), @@ -10,7 +10,7 @@ LL | | }; = note: `-D clippy::manual-map` implied by `-D warnings` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:18:5 + --> $DIR/manual_map_option.rs:19:5 | LL | / match Some(0) { LL | | Some(x) => Some(x + 1), @@ -19,7 +19,7 @@ LL | | }; | |_____^ help: try this: `Some(0).map(|x| x + 1)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:23:5 + --> $DIR/manual_map_option.rs:24:5 | LL | / match Some("") { LL | | Some(x) => Some(x.is_empty()), @@ -28,7 +28,7 @@ LL | | }; | |_____^ help: try this: `Some("").map(|x| x.is_empty())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:28:5 + --> $DIR/manual_map_option.rs:29:5 | LL | / if let Some(x) = Some(0) { LL | | Some(!x) @@ -38,7 +38,7 @@ LL | | }; | |_____^ help: try this: `Some(0).map(|x| !x)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:35:5 + --> $DIR/manual_map_option.rs:36:5 | LL | / match Some(0) { LL | | Some(x) => { Some(std::convert::identity(x)) } @@ -47,7 +47,7 @@ LL | | }; | |_____^ help: try this: `Some(0).map(std::convert::identity)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:40:5 + --> $DIR/manual_map_option.rs:41:5 | LL | / match Some(&String::new()) { LL | | Some(x) => Some(str::len(x)), @@ -56,7 +56,7 @@ LL | | }; | |_____^ help: try this: `Some(&String::new()).map(|x| str::len(x))` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:50:5 + --> $DIR/manual_map_option.rs:51:5 | LL | / match &Some([0, 1]) { LL | | Some(x) => Some(x[0]), @@ -65,7 +65,7 @@ LL | | }; | |_____^ help: try this: `Some([0, 1]).as_ref().map(|x| x[0])` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:55:5 + --> $DIR/manual_map_option.rs:56:5 | LL | / match &Some(0) { LL | | &Some(x) => Some(x * 2), @@ -74,7 +74,7 @@ LL | | }; | |_____^ help: try this: `Some(0).map(|x| x * 2)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:60:5 + --> $DIR/manual_map_option.rs:61:5 | LL | / match Some(String::new()) { LL | | Some(ref x) => Some(x.is_empty()), @@ -83,7 +83,7 @@ LL | | }; | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.is_empty())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:65:5 + --> $DIR/manual_map_option.rs:66:5 | LL | / match &&Some(String::new()) { LL | | Some(x) => Some(x.len()), @@ -92,7 +92,7 @@ LL | | }; | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.len())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:70:5 + --> $DIR/manual_map_option.rs:71:5 | LL | / match &&Some(0) { LL | | &&Some(x) => Some(x + x), @@ -101,7 +101,7 @@ LL | | }; | |_____^ help: try this: `Some(0).map(|x| x + x)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:83:9 + --> $DIR/manual_map_option.rs:84:9 | LL | / match &mut Some(String::new()) { LL | | Some(x) => Some(x.push_str("")), @@ -110,7 +110,7 @@ LL | | }; | |_________^ help: try this: `Some(String::new()).as_mut().map(|x| x.push_str(""))` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:89:5 + --> $DIR/manual_map_option.rs:90:5 | LL | / match &mut Some(String::new()) { LL | | Some(ref x) => Some(x.len()), @@ -119,7 +119,7 @@ LL | | }; | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.len())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:94:5 + --> $DIR/manual_map_option.rs:95:5 | LL | / match &mut &Some(String::new()) { LL | | Some(x) => Some(x.is_empty()), @@ -128,7 +128,7 @@ LL | | }; | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.is_empty())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:99:5 + --> $DIR/manual_map_option.rs:100:5 | LL | / match Some((0, 1, 2)) { LL | | Some((x, y, z)) => Some(x + y + z), @@ -137,7 +137,7 @@ LL | | }; | |_____^ help: try this: `Some((0, 1, 2)).map(|(x, y, z)| x + y + z)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:104:5 + --> $DIR/manual_map_option.rs:105:5 | LL | / match Some([1, 2, 3]) { LL | | Some([first, ..]) => Some(first), @@ -146,7 +146,7 @@ LL | | }; | |_____^ help: try this: `Some([1, 2, 3]).map(|[first, ..]| first)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:109:5 + --> $DIR/manual_map_option.rs:110:5 | LL | / match &Some((String::new(), "test")) { LL | | Some((x, y)) => Some((y, x)), @@ -155,7 +155,7 @@ LL | | }; | |_____^ help: try this: `Some((String::new(), "test")).as_ref().map(|(x, y)| (y, x))` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:167:5 + --> $DIR/manual_map_option.rs:168:5 | LL | / match Some(0) { LL | | Some(x) => Some(vec![x]), @@ -164,7 +164,7 @@ LL | | }; | |_____^ help: try this: `Some(0).map(|x| vec![x])` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:172:5 + --> $DIR/manual_map_option.rs:173:5 | LL | / match option_env!("") { LL | | Some(x) => Some(String::from(x)), -- cgit 1.4.1-3-g733a5 From 5656510eed663b5d5be6b6df3d033b836da5f759 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Wed, 3 Mar 2021 17:32:49 +0100 Subject: Fix false-positive in `use_self` --- clippy_lints/src/use_self.rs | 17 +++++++++++------ tests/ui/use_self.fixed | 7 +++++++ tests/ui/use_self.rs | 7 +++++++ 3 files changed, 25 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index be7b9e9ff2d..c262ec993b1 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -262,12 +262,17 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { // FIXME: this span manipulation should not be necessary // @flip1995 found an ast lowering issue in // https://github.com/rust-lang/rust/blob/master/src/librustc_ast_lowering/path.rs#l142-l162 - match cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_ty.hir_id)) { - Some(Node::Expr(Expr { - kind: ExprKind::Path(QPath::TypeRelative(_, segment)), - .. - })) => span_lint_until_last_segment(cx, hir_ty.span, segment), - _ => span_lint(cx, hir_ty.span), + let hir = cx.tcx.hir(); + let id = hir.get_parent_node(hir_ty.hir_id); + + if !hir.opt_span(id).map(in_macro).unwrap_or(false) { + match hir.find(id) { + Some(Node::Expr(Expr { + kind: ExprKind::Path(QPath::TypeRelative(_, segment)), + .. + })) => span_lint_until_last_segment(cx, hir_ty.span, segment), + _ => span_lint(cx, hir_ty.span), + } } } } diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed index 2b22a2ed2d5..a630936e3b1 100644 --- a/tests/ui/use_self.fixed +++ b/tests/ui/use_self.fixed @@ -454,3 +454,10 @@ mod nested_paths { } } } + +mod issue6818 { + #[derive(serde::Deserialize)] + struct A { + a: i32, + } +} diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index 609625abdec..f3e081dd203 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -454,3 +454,10 @@ mod nested_paths { } } } + +mod issue6818 { + #[derive(serde::Deserialize)] + struct A { + a: i32, + } +} -- cgit 1.4.1-3-g733a5 From 03e72d5446bd104ac4480032ea44bd7419ce5694 Mon Sep 17 00:00:00 2001 From: hyd-dev Date: Wed, 3 Mar 2021 22:06:36 +0800 Subject: Let Cargo track `CLIPPY_ARGS` --- README.md | 1 - src/driver.rs | 60 +++++++++++++++++++++++++++++------ tests/dogfood.rs | 97 ++++++++++++++++++++++++++++++++++++++------------------ 3 files changed, 117 insertions(+), 41 deletions(-) (limited to 'tests') diff --git a/README.md b/README.md index 3cc03cf3603..63057609bb6 100644 --- a/README.md +++ b/README.md @@ -202,7 +202,6 @@ the lint(s) you are interested in: ```terminal cargo clippy -- -A clippy::all -W clippy::useless_format -W clippy::... ``` -Note that if you've run clippy before, this may only take effect after you've modified a file or ran `cargo clean`. ### Specifying the minimum supported Rust version diff --git a/src/driver.rs b/src/driver.rs index d5143e1438e..081a2ddeb16 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -11,12 +11,16 @@ extern crate rustc_driver; extern crate rustc_errors; extern crate rustc_interface; +extern crate rustc_session; +extern crate rustc_span; use rustc_interface::interface; +use rustc_session::Session; +use rustc_span::symbol::Symbol; use rustc_tools_util::VersionInfo; use std::borrow::Cow; -use std::env; +use std::env::{self, VarError}; use std::lazy::SyncLazy; use std::ops::Deref; use std::panic; @@ -59,13 +63,42 @@ fn test_arg_value() { assert_eq!(arg_value(args, "--foo", |_| true), None); } +fn track_clippy_args(sess: &Session, args_env_var: &Option) { + sess.parse_sess.env_depinfo.borrow_mut().insert(( + Symbol::intern("CLIPPY_ARGS"), + args_env_var.as_deref().map(Symbol::intern), + )); +} + struct DefaultCallbacks; impl rustc_driver::Callbacks for DefaultCallbacks {} -struct ClippyCallbacks; +struct ClippyArgsCallbacks { + clippy_args_var: Option, +} + +impl rustc_driver::Callbacks for ClippyArgsCallbacks { + fn config(&mut self, config: &mut interface::Config) { + let previous = config.register_lints.take(); + let clippy_args_var = self.clippy_args_var.take(); + config.register_lints = Some(Box::new(move |sess, lint_store| { + if let Some(ref previous) = previous { + (previous)(sess, lint_store); + } + + track_clippy_args(sess, &clippy_args_var); + })); + } +} + +struct ClippyCallbacks { + clippy_args_var: Option, +} + impl rustc_driver::Callbacks for ClippyCallbacks { fn config(&mut self, config: &mut interface::Config) { let previous = config.register_lints.take(); + let clippy_args_var = self.clippy_args_var.take(); config.register_lints = Some(Box::new(move |sess, mut lint_store| { // technically we're ~guaranteed that this is none but might as well call anything that // is there already. Certainly it can't hurt. @@ -73,6 +106,8 @@ impl rustc_driver::Callbacks for ClippyCallbacks { (previous)(sess, lint_store); } + track_clippy_args(sess, &clippy_args_var); + let conf = clippy_lints::read_conf(&[], &sess); clippy_lints::register_plugins(&mut lint_store, &sess, &conf); clippy_lints::register_pre_expansion_lints(&mut lint_store); @@ -277,7 +312,15 @@ pub fn main() { }; let mut no_deps = false; - let clippy_args = env::var("CLIPPY_ARGS") + let clippy_args_var = env::var("CLIPPY_ARGS").map_or_else( + |e| match e { + VarError::NotPresent => None, + VarError::NotUnicode(s) => panic!("CLIPPY_ARGS is not valid Unicode: {:?}", s), + }, + Some, + ); + let clippy_args = clippy_args_var + .as_deref() .unwrap_or_default() .split("__CLIPPY_HACKERY__") .filter_map(|s| match s { @@ -305,11 +348,10 @@ pub fn main() { args.extend(clippy_args); } - let mut clippy = ClippyCallbacks; - let mut default = DefaultCallbacks; - let callbacks: &mut (dyn rustc_driver::Callbacks + Send) = - if clippy_enabled { &mut clippy } else { &mut default }; - - rustc_driver::RunCompiler::new(&args, callbacks).run() + if clippy_enabled { + rustc_driver::RunCompiler::new(&args, &mut ClippyCallbacks { clippy_args_var }).run() + } else { + rustc_driver::RunCompiler::new(&args, &mut ClippyArgsCallbacks { clippy_args_var }).run() + } })) } diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 8fe48a67beb..2505836a5ed 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -3,7 +3,7 @@ #![feature(once_cell)] use std::lazy::SyncLazy; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::process::Command; mod cargo; @@ -49,17 +49,6 @@ fn dogfood_clippy() { #[test] fn dogfood_subprojects() { fn test_no_deps_ignores_path_deps_in_workspaces() { - fn clean(cwd: &Path, target_dir: &Path) { - Command::new("cargo") - .current_dir(cwd) - .env("CARGO_TARGET_DIR", target_dir) - .arg("clean") - .args(&["-p", "subcrate"]) - .args(&["-p", "path_dep"]) - .output() - .unwrap(); - } - if cargo::is_rustc_test_suite() { return; } @@ -68,7 +57,14 @@ fn dogfood_subprojects() { let cwd = root.join("clippy_workspace_tests"); // Make sure we start with a clean state - clean(&cwd, &target_dir); + Command::new("cargo") + .current_dir(&cwd) + .env("CARGO_TARGET_DIR", &target_dir) + .arg("clean") + .args(&["-p", "subcrate"]) + .args(&["-p", "path_dep"]) + .output() + .unwrap(); // `path_dep` is a path dependency of `subcrate` that would trigger a denied lint. // Make sure that with the `--no-deps` argument Clippy does not run on `path_dep`. @@ -90,26 +86,65 @@ fn dogfood_subprojects() { assert!(output.status.success()); - // Make sure we start with a clean state - clean(&cwd, &target_dir); + let lint_path_dep = || { + // Test that without the `--no-deps` argument, `path_dep` is linted. + let output = Command::new(&*CLIPPY_PATH) + .current_dir(&cwd) + .env("CLIPPY_DOGFOOD", "1") + .env("CARGO_INCREMENTAL", "0") + .arg("clippy") + .args(&["-p", "subcrate"]) + .arg("--") + .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir + .args(&["--cfg", r#"feature="primary_package_test""#]) + .output() + .unwrap(); + println!("status: {}", output.status); + println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); + println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); + + assert!(!output.status.success()); + assert!( + String::from_utf8(output.stderr) + .unwrap() + .contains("error: empty `loop {}` wastes CPU cycles") + ); + }; + + // Make sure Cargo is aware of the removal of `--no-deps`. + lint_path_dep(); + + let successful_build = || { + let output = Command::new(&*CLIPPY_PATH) + .current_dir(&cwd) + .env("CLIPPY_DOGFOOD", "1") + .env("CARGO_INCREMENTAL", "0") + .arg("clippy") + .args(&["-p", "subcrate"]) + .arg("--") + .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir + .output() + .unwrap(); + println!("status: {}", output.status); + println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); + println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); - // Test that without the `--no-deps` argument, `path_dep` is linted. - let output = Command::new(&*CLIPPY_PATH) - .current_dir(&cwd) - .env("CLIPPY_DOGFOOD", "1") - .env("CARGO_INCREMENTAL", "0") - .arg("clippy") - .args(&["-p", "subcrate"]) - .arg("--") - .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir - .args(&["--cfg", r#"feature="primary_package_test""#]) - .output() - .unwrap(); - println!("status: {}", output.status); - println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); - println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); + assert!(output.status.success()); + + output + }; + + // Trigger a sucessful build, so Cargo would like to cache the build result. + successful_build(); + + // Make sure there's no spurious rebuild when nothing changes. + let stderr = String::from_utf8(successful_build().stderr).unwrap(); + assert!(!stderr.contains("Compiling")); + assert!(!stderr.contains("Checking")); + assert!(stderr.contains("Finished")); - assert!(!output.status.success()); + // Make sure Cargo is aware of the new `--cfg` flag. + lint_path_dep(); } // run clippy on remaining subprojects and fail the test if lint warnings are reported -- cgit 1.4.1-3-g733a5 From 39c5e863378270c25ef75f3ae53d908565bc2619 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 4 Mar 2021 11:06:24 -0500 Subject: When checking if two empty hir blocks are equal also check to see if the tokens used are the same as well --- clippy_utils/src/hir_utils.rs | 81 +++++++++++++++++++++++++++++++++++++--- clippy_utils/src/lib.rs | 1 + tests/ui/match_same_arms2.rs | 29 ++++++++++++++ tests/ui/match_same_arms2.stderr | 27 +++++++++++++- 4 files changed, 130 insertions(+), 8 deletions(-) (limited to 'tests') diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 81be9254cbe..e28ad27d9a6 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -1,5 +1,5 @@ use crate::consts::{constant_context, constant_simple}; -use crate::differing_macro_contexts; +use crate::{differing_macro_contexts, snippet_opt}; use rustc_ast::ast::InlineAsmTemplatePiece; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; @@ -9,6 +9,7 @@ use rustc_hir::{ GenericArg, GenericArgs, Guard, HirId, InlineAsmOperand, Lifetime, LifetimeName, ParamName, Pat, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding, }; +use rustc_lexer::{tokenize, TokenKind}; use rustc_lint::LateContext; use rustc_middle::ich::StableHashingContextProvider; use rustc_middle::ty::TypeckResults; @@ -110,8 +111,54 @@ impl HirEqInterExpr<'_, '_, '_> { /// Checks whether two blocks are the same. fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool { - over(&left.stmts, &right.stmts, |l, r| self.eq_stmt(l, r)) - && both(&left.expr, &right.expr, |l, r| self.eq_expr(l, r)) + match (left.stmts, left.expr, right.stmts, right.expr) { + ([], None, [], None) => { + // For empty blocks, check to see if the tokens are equal. This will catch the case where a macro + // expanded to nothing, or the cfg attribute was used. + let (left, right) = match ( + snippet_opt(self.inner.cx, left.span), + snippet_opt(self.inner.cx, right.span), + ) { + (Some(left), Some(right)) => (left, right), + _ => return true, + }; + let mut left_pos = 0; + let left = tokenize(&left) + .map(|t| { + let end = left_pos + t.len; + let s = &left[left_pos..end]; + left_pos = end; + (t, s) + }) + .filter(|(t, _)| { + !matches!( + t.kind, + TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace + ) + }) + .map(|(_, s)| s); + let mut right_pos = 0; + let right = tokenize(&right) + .map(|t| { + let end = right_pos + t.len; + let s = &right[right_pos..end]; + right_pos = end; + (t, s) + }) + .filter(|(t, _)| { + !matches!( + t.kind, + TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace + ) + }) + .map(|(_, s)| s); + left.eq(right) + }, + _ => { + over(&left.stmts, &right.stmts, |l, r| self.eq_stmt(l, r)) + && both(&left.expr, &right.expr, |l, r| self.eq_expr(l, r)) + }, + } } #[allow(clippy::similar_names)] @@ -131,7 +178,10 @@ impl HirEqInterExpr<'_, '_, '_> { } } - let is_eq = match (&reduce_exprkind(&left.kind), &reduce_exprkind(&right.kind)) { + let is_eq = match ( + &reduce_exprkind(self.inner.cx, &left.kind), + &reduce_exprkind(self.inner.cx, &right.kind), + ) { (&ExprKind::AddrOf(lb, l_mut, ref le), &ExprKind::AddrOf(rb, r_mut, ref re)) => { lb == rb && l_mut == r_mut && self.eq_expr(le, re) }, @@ -360,11 +410,30 @@ impl HirEqInterExpr<'_, '_, '_> { } /// Some simple reductions like `{ return }` => `return` -fn reduce_exprkind<'hir>(kind: &'hir ExprKind<'hir>) -> &ExprKind<'hir> { +fn reduce_exprkind<'hir>(cx: &LateContext<'_>, kind: &'hir ExprKind<'hir>) -> &'hir ExprKind<'hir> { if let ExprKind::Block(block, _) = kind { match (block.stmts, block.expr) { + // From an `if let` expression without an `else` block. The arm for the implicit wild pattern is an empty + // block with an empty span. + ([], None) if block.span.is_empty() => &ExprKind::Tup(&[]), // `{}` => `()` - ([], None) => &ExprKind::Tup(&[]), + ([], None) => match snippet_opt(cx, block.span) { + // Don't reduce if there are any tokens contained in the braces + Some(snip) + if tokenize(&snip) + .map(|t| t.kind) + .filter(|t| { + !matches!( + t, + TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace + ) + }) + .ne([TokenKind::OpenBrace, TokenKind::CloseBrace].iter().cloned()) => + { + kind + }, + _ => &ExprKind::Tup(&[]), + }, ([], Some(expr)) => match expr.kind { // `{ return .. }` => `return ..` ExprKind::Ret(..) => &expr.kind, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 00455d4102f..0395079901c 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -14,6 +14,7 @@ extern crate rustc_errors; extern crate rustc_hir; extern crate rustc_hir_pretty; extern crate rustc_infer; +extern crate rustc_lexer; extern crate rustc_lint; extern crate rustc_middle; extern crate rustc_mir; diff --git a/tests/ui/match_same_arms2.rs b/tests/ui/match_same_arms2.rs index 06d91497242..da4e3020d5b 100644 --- a/tests/ui/match_same_arms2.rs +++ b/tests/ui/match_same_arms2.rs @@ -120,6 +120,35 @@ fn match_same_arms() { }, } + // False positive #1390 + macro_rules! empty { + ($e:expr) => {}; + } + match 0 { + 0 => { + empty!(0); + }, + 1 => { + empty!(1); + }, + x => { + empty!(x); + }, + }; + + // still lint if the tokens are the same + match 0 { + 0 => { + empty!(0); + }, + 1 => { + empty!(0); + }, + x => { + empty!(x); + }, + } + match_expr_like_matches_macro_priority(); } diff --git a/tests/ui/match_same_arms2.stderr b/tests/ui/match_same_arms2.stderr index fccaf805616..95f9494cdc9 100644 --- a/tests/ui/match_same_arms2.stderr +++ b/tests/ui/match_same_arms2.stderr @@ -141,8 +141,31 @@ LL | Ok(3) => println!("ok"), | ^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) +error: this `match` has identical arm bodies + --> $DIR/match_same_arms2.rs:144:14 + | +LL | 1 => { + | ______________^ +LL | | empty!(0); +LL | | }, + | |_________^ + | +note: same as this + --> $DIR/match_same_arms2.rs:141:14 + | +LL | 0 => { + | ______________^ +LL | | empty!(0); +LL | | }, + | |_________^ +help: consider refactoring into `0 | 1` + --> $DIR/match_same_arms2.rs:141:9 + | +LL | 0 => { + | ^ + error: match expression looks like `matches!` macro - --> $DIR/match_same_arms2.rs:133:16 + --> $DIR/match_same_arms2.rs:162:16 | LL | let _ans = match x { | ________________^ @@ -154,5 +177,5 @@ LL | | }; | = note: `-D clippy::match-like-matches-macro` implied by `-D warnings` -error: aborting due to 8 previous errors +error: aborting due to 9 previous errors -- cgit 1.4.1-3-g733a5 From 5ebe6d3234c285be553ad2f1a4b965d497c1d573 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 5 Mar 2021 10:30:26 +0100 Subject: Fix dogfood test Since clippy cannot be a workspace, we have to check the sub-crates separately --- tests/dogfood.rs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 8fe48a67beb..89526648d2e 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -23,10 +23,9 @@ fn dogfood_clippy() { .current_dir(root_dir) .env("CLIPPY_DOGFOOD", "1") .env("CARGO_INCREMENTAL", "0") - .arg("clippy-preview") + .arg("clippy") .arg("--all-targets") .arg("--all-features") - .args(&["-p", "clippy_lints", "-p", "clippy_utils", "-p", "rustc_tools_util"]) .arg("--") .args(&["-D", "clippy::all"]) .args(&["-D", "clippy::pedantic"]) @@ -125,19 +124,30 @@ fn dogfood_subprojects() { "clippy_workspace_tests/subcrate", "clippy_workspace_tests/subcrate/src", "clippy_dev", + "clippy_lints", + "clippy_utils", "rustc_tools_util", ] { - let output = Command::new(&*CLIPPY_PATH) + let mut command = Command::new(&*CLIPPY_PATH); + command .current_dir(root_dir.join(d)) .env("CLIPPY_DOGFOOD", "1") .env("CARGO_INCREMENTAL", "0") .arg("clippy") + .arg("--all-targets") + .arg("--all-features") .arg("--") .args(&["-D", "clippy::all"]) .args(&["-D", "clippy::pedantic"]) - .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir - .output() - .unwrap(); + .arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir + + // internal lints only exist if we build with the internal-lints feature + if cfg!(feature = "internal-lints") { + command.args(&["-D", "clippy::internal"]); + } + + let output = command.output().unwrap(); + println!("status: {}", output.status); println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); -- cgit 1.4.1-3-g733a5 From a672d335a248034369cd41d4e08e02cf378c0e4b Mon Sep 17 00:00:00 2001 From: Yusuke Tanaka Date: Sun, 7 Mar 2021 02:08:46 +0900 Subject: Implement new lint: if_then_some_else_none --- CHANGELOG.md | 1 + clippy_lints/src/if_then_some_else_none.rs | 88 ++++++++++++++++++++++++++++++ clippy_lints/src/lib.rs | 4 ++ tests/ui/if_then_some_else_none.rs | 66 ++++++++++++++++++++++ tests/ui/if_then_some_else_none.stderr | 16 ++++++ 5 files changed, 175 insertions(+) create mode 100644 clippy_lints/src/if_then_some_else_none.rs create mode 100644 tests/ui/if_then_some_else_none.rs create mode 100644 tests/ui/if_then_some_else_none.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 41c334c6816..f7916511edf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2103,6 +2103,7 @@ Released 2018-09-13 [`if_let_some_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_some_result [`if_not_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_not_else [`if_same_then_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_same_then_else +[`if_then_some_else_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none [`ifs_same_cond`]: https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond [`implicit_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_clone [`implicit_hasher`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_hasher diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs new file mode 100644 index 00000000000..0bd393f8996 --- /dev/null +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -0,0 +1,88 @@ +use crate::utils; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for if-else that could be written to `bool::then`. + /// + /// **Why is this bad?** Looks a little redundant. Using `bool::then` helps it have less lines of code. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// # let v = vec![0]; + /// let a = if v.is_empty() { + /// println!("true!"); + /// Some(42) + /// } else { + /// None + /// }; + /// ``` + /// + /// Could be written: + /// + /// ```rust + /// # let v = vec![0]; + /// let a = v.is_empty().then(|| { + /// println!("true!"); + /// 42 + /// }); + /// ``` + pub IF_THEN_SOME_ELSE_NONE, + restriction, + "Finds if-else that could be written using `bool::then`" +} + +declare_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]); + +impl LateLintPass<'_> for IfThenSomeElseNone { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) { + if in_external_macro(cx.sess(), expr.span) { + return; + } + + // We only care about the top-most `if` in the chain + if utils::parent_node_is_if_expr(expr, cx) { + return; + } + + if_chain! { + if let ExprKind::If(ref cond, ref then, Some(ref els)) = expr.kind; + if let ExprKind::Block(ref then_block, _) = then.kind; + if let Some(ref then_expr) = then_block.expr; + if let ExprKind::Call(ref then_call, [then_arg]) = then_expr.kind; + if let ExprKind::Path(ref then_call_qpath) = then_call.kind; + if utils::match_qpath(then_call_qpath, &utils::paths::OPTION_SOME); + if let ExprKind::Block(ref els_block, _) = els.kind; + if els_block.stmts.is_empty(); + if let Some(ref els_expr) = els_block.expr; + if let ExprKind::Path(ref els_call_qpath) = els_expr.kind; + if utils::match_qpath(els_call_qpath, &utils::paths::OPTION_NONE); + then { + let mut applicability = Applicability::MachineApplicable; + let cond_snip = utils::snippet_with_applicability(cx, cond.span, "[condition]", &mut applicability); + let arg_snip = utils::snippet_with_applicability(cx, then_arg.span, "", &mut applicability); + let sugg = format!( + "{}.then(|| {{ /* snippet */ {} }})", + cond_snip, + arg_snip, + ); + utils::span_lint_and_sugg( + cx, + IF_THEN_SOME_ELSE_NONE, + expr.span, + "this could be simplified with `bool::then`", + "try this", + sugg, + applicability, + ); + } + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 8259fd3c320..9a7dedf416e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -230,6 +230,7 @@ mod identity_op; mod if_let_mutex; mod if_let_some_result; mod if_not_else; +mod if_then_some_else_none; mod implicit_return; mod implicit_saturating_sub; mod inconsistent_struct_constructor; @@ -667,6 +668,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &if_let_mutex::IF_LET_MUTEX, &if_let_some_result::IF_LET_SOME_RESULT, &if_not_else::IF_NOT_ELSE, + &if_then_some_else_none::IF_THEN_SOME_ELSE_NONE, &implicit_return::IMPLICIT_RETURN, &implicit_saturating_sub::IMPLICIT_SATURATING_SUB, &inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR, @@ -1282,6 +1284,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box redundant_slicing::RedundantSlicing); store.register_late_pass(|| box from_str_radix_10::FromStrRadix10); store.register_late_pass(|| box manual_map::ManualMap); + store.register_late_pass(|| box if_then_some_else_none::IfThenSomeElseNone); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1297,6 +1300,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), diff --git a/tests/ui/if_then_some_else_none.rs b/tests/ui/if_then_some_else_none.rs new file mode 100644 index 00000000000..b19e2a50010 --- /dev/null +++ b/tests/ui/if_then_some_else_none.rs @@ -0,0 +1,66 @@ +#![warn(clippy::if_then_some_else_none)] + +fn main() { + // Should issue an error. + let _ = if foo() { + println!("true!"); + Some("foo") + } else { + None + }; + + // Should not issue an error since the `else` block has a statement besides `None`. + let _ = if foo() { + println!("true!"); + Some("foo") + } else { + eprintln!("false..."); + None + }; + + // Should not issue an error since there are more than 2 blocks in the if-else chain. + let _ = if foo() { + println!("foo true!"); + Some("foo") + } else if bar() { + println!("bar true!"); + Some("bar") + } else { + None + }; + + let _ = if foo() { + println!("foo true!"); + Some("foo") + } else { + bar().then(|| { + println!("bar true!"); + "bar" + }) + }; + + // Should not issue an error since the `then` block has `None`, not `Some`. + let _ = if foo() { None } else { Some("foo is false") }; + + // Should not issue an error since the `else` block doesn't use `None` directly. + let _ = if foo() { Some("foo is true") } else { into_none() }; + + // Should not issue an error since the `then` block doesn't use `Some` directly. + let _ = if foo() { into_some("foo") } else { None }; +} + +fn foo() -> bool { + unimplemented!() +} + +fn bar() -> bool { + unimplemented!() +} + +fn into_some(v: T) -> Option { + Some(v) +} + +fn into_none() -> Option { + None +} diff --git a/tests/ui/if_then_some_else_none.stderr b/tests/ui/if_then_some_else_none.stderr new file mode 100644 index 00000000000..7ad9c4ce79d --- /dev/null +++ b/tests/ui/if_then_some_else_none.stderr @@ -0,0 +1,16 @@ +error: this could be simplified with `bool::then` + --> $DIR/if_then_some_else_none.rs:5:13 + | +LL | let _ = if foo() { + | _____________^ +LL | | println!("true!"); +LL | | Some("foo") +LL | | } else { +LL | | None +LL | | }; + | |_____^ help: try this: `foo().then(|| { /* snippet */ "foo" })` + | + = note: `-D clippy::if-then-some-else-none` implied by `-D warnings` + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From 47145dec36fbe99960f45ee7065261e2dcfed152 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 5 Mar 2021 13:01:13 -0500 Subject: `len_without_is_empty` will now consider multiple impl blocks `len_without_is_empty` will now consider `#[allow]` on both the `len` method, and the type definition --- clippy_lints/src/len_zero.rs | 166 ++++++++++++++++++++++++++--------- clippy_utils/src/lib.rs | 21 ++++- tests/ui/len_without_is_empty.rs | 45 ++++++++++ tests/ui/len_without_is_empty.stderr | 76 +++++++++------- 4 files changed, 231 insertions(+), 77 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index dab3e0565ca..1e1023b2743 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -1,11 +1,17 @@ -use crate::utils::{get_item_name, snippet_with_applicability, span_lint, span_lint_and_sugg}; +use crate::utils::{ + get_item_name, get_parent_as_impl, is_allowed, snippet_with_applicability, span_lint, span_lint_and_sugg, + span_lint_and_then, +}; +use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir::def_id::DefId; -use rustc_hir::{AssocItemKind, BinOpKind, Expr, ExprKind, Impl, ImplItemRef, Item, ItemKind, TraitItemRef}; +use rustc_hir::{ + def_id::DefId, AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, ImplItem, ImplItemKind, ImplicitSelfKind, Item, + ItemKind, Mutability, Node, TraitItemRef, TyKind, +}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; +use rustc_middle::ty::{self, AssocKind, FnSig}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::{Span, Spanned, Symbol}; @@ -113,14 +119,38 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { return; } - match item.kind { - ItemKind::Trait(_, _, _, _, ref trait_items) => check_trait_items(cx, item, trait_items), - ItemKind::Impl(Impl { - of_trait: None, - items: ref impl_items, - .. - }) => check_impl_items(cx, item, impl_items), - _ => (), + if let ItemKind::Trait(_, _, _, _, ref trait_items) = item.kind { + check_trait_items(cx, item, trait_items); + } + } + + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { + if_chain! { + if item.ident.as_str() == "len"; + if let ImplItemKind::Fn(sig, _) = &item.kind; + if sig.decl.implicit_self.has_implicit_self(); + if cx.access_levels.is_exported(item.hir_id()); + if matches!(sig.decl.output, FnRetTy::Return(_)); + if let Some(imp) = get_parent_as_impl(cx.tcx, item.hir_id()); + if imp.of_trait.is_none(); + if let TyKind::Path(ty_path) = &imp.self_ty.kind; + if let Some(ty_id) = cx.qpath_res(ty_path, imp.self_ty.hir_id).opt_def_id(); + if let Some(local_id) = ty_id.as_local(); + let ty_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_id); + if !is_allowed(cx, LEN_WITHOUT_IS_EMPTY, ty_hir_id); + then { + let (name, kind) = match cx.tcx.hir().find(ty_hir_id) { + Some(Node::ForeignItem(x)) => (x.ident.name, "extern type"), + Some(Node::Item(x)) => match x.kind { + ItemKind::Struct(..) => (x.ident.name, "struct"), + ItemKind::Enum(..) => (x.ident.name, "enum"), + ItemKind::Union(..) => (x.ident.name, "union"), + _ => (x.ident.name, "type"), + } + _ => return, + }; + check_for_is_empty(cx, sig.span, sig.decl.implicit_self, ty_id, name, kind) + } } } @@ -202,40 +232,94 @@ fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items } } -fn check_impl_items(cx: &LateContext<'_>, item: &Item<'_>, impl_items: &[ImplItemRef<'_>]) { - fn is_named_self(cx: &LateContext<'_>, item: &ImplItemRef<'_>, name: &str) -> bool { - item.ident.name.as_str() == name - && if let AssocItemKind::Fn { has_self } = item.kind { - has_self && cx.tcx.fn_sig(item.id.def_id).inputs().skip_binder().len() == 1 - } else { - false - } +/// Checks if the given signature matches the expectations for `is_empty` +fn check_is_empty_sig(cx: &LateContext<'_>, sig: FnSig<'_>, self_kind: ImplicitSelfKind) -> bool { + match &**sig.inputs_and_output { + [arg, res] if *res == cx.tcx.types.bool => { + matches!( + (arg.kind(), self_kind), + (ty::Ref(_, _, Mutability::Not), ImplicitSelfKind::ImmRef) + | (ty::Ref(_, _, Mutability::Mut), ImplicitSelfKind::MutRef) + ) || (!arg.is_ref() && matches!(self_kind, ImplicitSelfKind::Imm | ImplicitSelfKind::Mut)) + }, + _ => false, } +} - let is_empty = if let Some(is_empty) = impl_items.iter().find(|i| is_named_self(cx, i, "is_empty")) { - if cx.access_levels.is_exported(is_empty.id.hir_id()) { - return; - } - "a private" - } else { - "no corresponding" - }; - - if let Some(i) = impl_items.iter().find(|i| is_named_self(cx, i, "len")) { - if cx.access_levels.is_exported(i.id.hir_id()) { - let ty = cx.tcx.type_of(item.def_id); +/// Checks if the given type has an `is_empty` method with the appropriate signature. +fn check_for_is_empty( + cx: &LateContext<'_>, + span: Span, + self_kind: ImplicitSelfKind, + impl_ty: DefId, + item_name: Symbol, + item_kind: &str, +) { + let is_empty = Symbol::intern("is_empty"); + let is_empty = cx + .tcx + .inherent_impls(impl_ty) + .iter() + .flat_map(|&id| cx.tcx.associated_items(id).filter_by_name_unhygienic(is_empty)) + .find(|item| item.kind == AssocKind::Fn); - span_lint( - cx, - LEN_WITHOUT_IS_EMPTY, - item.span, - &format!( - "item `{}` has a public `len` method but {} `is_empty` method", - ty, is_empty + let (msg, is_empty_span, self_kind) = match is_empty { + None => ( + format!( + "{} `{}` has a public `len` method, but no `is_empty` method", + item_kind, + item_name.as_str(), + ), + None, + None, + ), + Some(is_empty) + if !cx + .access_levels + .is_exported(cx.tcx.hir().local_def_id_to_hir_id(is_empty.def_id.expect_local())) => + { + ( + format!( + "{} `{}` has a public `len` method, but a private `is_empty` method", + item_kind, + item_name.as_str(), ), - ); + Some(cx.tcx.def_span(is_empty.def_id)), + None, + ) + }, + Some(is_empty) + if !(is_empty.fn_has_self_parameter + && check_is_empty_sig(cx, cx.tcx.fn_sig(is_empty.def_id).skip_binder(), self_kind)) => + { + ( + format!( + "{} `{}` has a public `len` method, but the `is_empty` method has an unexpected signature", + item_kind, + item_name.as_str(), + ), + Some(cx.tcx.def_span(is_empty.def_id)), + Some(self_kind), + ) + }, + Some(_) => return, + }; + + span_lint_and_then(cx, LEN_WITHOUT_IS_EMPTY, span, &msg, |db| { + if let Some(span) = is_empty_span { + db.span_note(span, "`is_empty` defined here"); } - } + if let Some(self_kind) = self_kind { + db.note(&format!( + "expected signature: `({}self) -> bool`", + match self_kind { + ImplicitSelfKind::ImmRef => "&", + ImplicitSelfKind::MutRef => "&mut ", + _ => "", + } + )); + } + }); } fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_>, op: &str, compare_to: u32) { diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index daeced6bad4..6582ad71707 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -63,9 +63,9 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::Node; use rustc_hir::{ - def, Arm, Block, Body, Constness, Crate, Expr, ExprKind, FnDecl, GenericArgs, HirId, ImplItem, ImplItemKind, Item, - ItemKind, MatchSource, Param, Pat, PatKind, Path, PathSegment, QPath, TraitItem, TraitItemKind, TraitRef, TyKind, - Unsafety, + def, Arm, Block, Body, Constness, Crate, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, + Item, ItemKind, MatchSource, Param, Pat, PatKind, Path, PathSegment, QPath, TraitItem, TraitItemKind, TraitRef, + TyKind, Unsafety, }; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, Level, Lint, LintContext}; @@ -1004,6 +1004,21 @@ pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio }) } +/// Gets the parent node if it's an impl block. +pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> { + let map = tcx.hir(); + match map.parent_iter(id).next() { + Some(( + _, + Node::Item(Item { + kind: ItemKind::Impl(imp), + .. + }), + )) => Some(imp), + _ => None, + } +} + /// Returns the base type for HIR references and pointers. pub fn walk_ptrs_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> { match ty.kind { diff --git a/tests/ui/len_without_is_empty.rs b/tests/ui/len_without_is_empty.rs index b5211318a15..6b3636a482e 100644 --- a/tests/ui/len_without_is_empty.rs +++ b/tests/ui/len_without_is_empty.rs @@ -34,6 +34,24 @@ impl PubAllowed { } } +pub struct PubAllowedFn; + +impl PubAllowedFn { + #[allow(clippy::len_without_is_empty)] + pub fn len(&self) -> isize { + 1 + } +} + +#[allow(clippy::len_without_is_empty)] +pub struct PubAllowedStruct; + +impl PubAllowedStruct { + pub fn len(&self) -> isize { + 1 + } +} + pub trait PubTraitsToo { fn len(&self) -> isize; } @@ -68,6 +86,18 @@ impl HasWrongIsEmpty { } } +pub struct MismatchedSelf; + +impl MismatchedSelf { + pub fn len(self) -> isize { + 1 + } + + pub fn is_empty(&self) -> bool { + false + } +} + struct NotPubOne; impl NotPubOne { @@ -142,4 +172,19 @@ pub trait DependsOnFoo: Foo { fn len(&mut self) -> usize; } +pub struct MultipleImpls; + +// issue #1562 +impl MultipleImpls { + pub fn len(&self) -> usize { + 1 + } +} + +impl MultipleImpls { + pub fn is_empty(&self) -> bool { + false + } +} + fn main() {} diff --git a/tests/ui/len_without_is_empty.stderr b/tests/ui/len_without_is_empty.stderr index d79c300c074..f106506faf4 100644 --- a/tests/ui/len_without_is_empty.stderr +++ b/tests/ui/len_without_is_empty.stderr @@ -1,54 +1,64 @@ -error: item `PubOne` has a public `len` method but no corresponding `is_empty` method - --> $DIR/len_without_is_empty.rs:6:1 +error: struct `PubOne` has a public `len` method, but no `is_empty` method + --> $DIR/len_without_is_empty.rs:7:5 | -LL | / impl PubOne { -LL | | pub fn len(&self) -> isize { -LL | | 1 -LL | | } -LL | | } - | |_^ +LL | pub fn len(&self) -> isize { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::len-without-is-empty` implied by `-D warnings` error: trait `PubTraitsToo` has a `len` method but no (possibly inherited) `is_empty` method - --> $DIR/len_without_is_empty.rs:37:1 + --> $DIR/len_without_is_empty.rs:55:1 | LL | / pub trait PubTraitsToo { LL | | fn len(&self) -> isize; LL | | } | |_^ -error: item `HasIsEmpty` has a public `len` method but a private `is_empty` method - --> $DIR/len_without_is_empty.rs:49:1 - | -LL | / impl HasIsEmpty { -LL | | pub fn len(&self) -> isize { -LL | | 1 -LL | | } -... | -LL | | } -LL | | } - | |_^ +error: struct `HasIsEmpty` has a public `len` method, but a private `is_empty` method + --> $DIR/len_without_is_empty.rs:68:5 + | +LL | pub fn len(&self) -> isize { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: `is_empty` defined here + --> $DIR/len_without_is_empty.rs:72:5 + | +LL | fn is_empty(&self) -> bool { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: item `HasWrongIsEmpty` has a public `len` method but no corresponding `is_empty` method - --> $DIR/len_without_is_empty.rs:61:1 - | -LL | / impl HasWrongIsEmpty { -LL | | pub fn len(&self) -> isize { -LL | | 1 -LL | | } -... | -LL | | } -LL | | } - | |_^ +error: struct `HasWrongIsEmpty` has a public `len` method, but the `is_empty` method has an unexpected signature + --> $DIR/len_without_is_empty.rs:80:5 + | +LL | pub fn len(&self) -> isize { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: `is_empty` defined here + --> $DIR/len_without_is_empty.rs:84:5 + | +LL | pub fn is_empty(&self, x: u32) -> bool { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: expected signature: `(&self) -> bool` + +error: struct `MismatchedSelf` has a public `len` method, but the `is_empty` method has an unexpected signature + --> $DIR/len_without_is_empty.rs:92:5 + | +LL | pub fn len(self) -> isize { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: `is_empty` defined here + --> $DIR/len_without_is_empty.rs:96:5 + | +LL | pub fn is_empty(&self) -> bool { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: expected signature: `(self) -> bool` error: trait `DependsOnFoo` has a `len` method but no (possibly inherited) `is_empty` method - --> $DIR/len_without_is_empty.rs:141:1 + --> $DIR/len_without_is_empty.rs:171:1 | LL | / pub trait DependsOnFoo: Foo { LL | | fn len(&mut self) -> usize; LL | | } | |_^ -error: aborting due to 5 previous errors +error: aborting due to 6 previous errors -- cgit 1.4.1-3-g733a5 From b27cbda32b67b4fdd9113ed894b810fc8f3e180d Mon Sep 17 00:00:00 2001 From: Andrea Nall Date: Sun, 7 Mar 2021 21:45:41 -0600 Subject: make is_normalizable more strict --- clippy_utils/src/lib.rs | 42 +++++++++++++++++++++++++++++++++++++++--- tests/ui/crashes/ice-6840.rs | 23 +++++++++++++++++++++++ 2 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 tests/ui/crashes/ice-6840.rs (limited to 'tests') diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 3845667802d..999b39852cd 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -85,6 +85,7 @@ use rustc_trait_selection::traits::query::normalize::AtExt; use smallvec::SmallVec; use crate::consts::{constant, Constant}; +use std::collections::HashMap; pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Option { if let Ok(version) = RustcVersion::parse(msrv) { @@ -1488,10 +1489,45 @@ pub fn match_function_call<'tcx>( /// Checks if `Ty` is normalizable. This function is useful /// to avoid crashes on `layout_of`. pub fn is_normalizable<'tcx>(cx: &LateContext<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool { - cx.tcx.infer_ctxt().enter(|infcx| { + is_normalizable_helper(cx, param_env, ty, &mut HashMap::new()) +} + +fn is_normalizable_helper<'tcx>( + cx: &LateContext<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ty: Ty<'tcx>, + cache: &mut HashMap, bool>, +) -> bool { + if let Some(&cached_result) = cache.get(ty) { + return cached_result; + } + cache.insert(ty, false); // prevent recursive loops + let result = cx.tcx.infer_ctxt().enter(|infcx| { let cause = rustc_middle::traits::ObligationCause::dummy(); - infcx.at(&cause, param_env).normalize(ty).is_ok() - }) + if infcx.at(&cause, param_env).normalize(ty).is_err() { + false + } else { + match ty.kind() { + ty::Adt(def, substs) => !def.variants.iter().any(|variant| { + variant + .fields + .iter() + .any(|field| !is_normalizable_helper(cx, param_env, field.ty(cx.tcx, substs), cache)) + }), + ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => { + is_normalizable_helper(cx, param_env, pointee, cache) + }, + ty::Array(inner_ty, _) | ty::Slice(inner_ty) => is_normalizable_helper(cx, param_env, inner_ty, cache), + ty::Tuple(tys) => !tys.iter().any(|inner| match inner.unpack() { + GenericArgKind::Type(inner_ty) => !is_normalizable_helper(cx, param_env, inner_ty, cache), + _ => false, + }), + _ => true, + } + } + }); + cache.insert(ty, result); + result } pub fn match_def_path<'tcx>(cx: &LateContext<'tcx>, did: DefId, syms: &[&str]) -> bool { diff --git a/tests/ui/crashes/ice-6840.rs b/tests/ui/crashes/ice-6840.rs new file mode 100644 index 00000000000..a749eefb635 --- /dev/null +++ b/tests/ui/crashes/ice-6840.rs @@ -0,0 +1,23 @@ +//! This is a reproducer for the ICE 6840: https://github.com/rust-lang/rust-clippy/issues/6840. +//! The ICE is caused by `TyCtxt::layout_of` and `is_normalizable` not being strict enough +#![allow(dead_code)] +use std::collections::HashMap; + +pub trait Rule { + type DependencyKey; +} + +pub struct RuleEdges { + dependencies: R::DependencyKey, +} + +type RuleDependencyEdges = HashMap>; + +// and additional potential variants +type RuleDependencyEdgesArray = HashMap; 8]>; +type RuleDependencyEdgesSlice = HashMap]>; +type RuleDependencyEdgesRef = HashMap>; +type RuleDependencyEdgesRaw = HashMap>; +type RuleDependencyEdgesTuple = HashMap, RuleEdges)>; + +fn main() {} -- cgit 1.4.1-3-g733a5 From 2d53b6b82412a18734478c0086090dc3a2b5c5cc Mon Sep 17 00:00:00 2001 From: hyd-dev Date: Mon, 8 Mar 2021 18:29:36 +0800 Subject: Move `test_no_deps_ignores_path_deps_in_workspaces()` out of `dogfood_subprojects()` --- tests/dogfood.rs | 158 +++++++++++++++++++++++++++---------------------------- 1 file changed, 79 insertions(+), 79 deletions(-) (limited to 'tests') diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 2505836a5ed..296eeb4aabd 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -46,28 +46,73 @@ fn dogfood_clippy() { assert!(output.status.success()); } -#[test] -fn dogfood_subprojects() { - fn test_no_deps_ignores_path_deps_in_workspaces() { - if cargo::is_rustc_test_suite() { - return; - } - let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - let target_dir = root.join("target").join("dogfood"); - let cwd = root.join("clippy_workspace_tests"); - - // Make sure we start with a clean state - Command::new("cargo") +fn test_no_deps_ignores_path_deps_in_workspaces() { + if cargo::is_rustc_test_suite() { + return; + } + let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let target_dir = root.join("target").join("dogfood"); + let cwd = root.join("clippy_workspace_tests"); + + // Make sure we start with a clean state + Command::new("cargo") + .current_dir(&cwd) + .env("CARGO_TARGET_DIR", &target_dir) + .arg("clean") + .args(&["-p", "subcrate"]) + .args(&["-p", "path_dep"]) + .output() + .unwrap(); + + // `path_dep` is a path dependency of `subcrate` that would trigger a denied lint. + // Make sure that with the `--no-deps` argument Clippy does not run on `path_dep`. + let output = Command::new(&*CLIPPY_PATH) + .current_dir(&cwd) + .env("CLIPPY_DOGFOOD", "1") + .env("CARGO_INCREMENTAL", "0") + .arg("clippy") + .args(&["-p", "subcrate"]) + .arg("--") + .arg("--no-deps") + .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir + .args(&["--cfg", r#"feature="primary_package_test""#]) + .output() + .unwrap(); + println!("status: {}", output.status); + println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); + println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); + + assert!(output.status.success()); + + let lint_path_dep = || { + // Test that without the `--no-deps` argument, `path_dep` is linted. + let output = Command::new(&*CLIPPY_PATH) .current_dir(&cwd) - .env("CARGO_TARGET_DIR", &target_dir) - .arg("clean") + .env("CLIPPY_DOGFOOD", "1") + .env("CARGO_INCREMENTAL", "0") + .arg("clippy") .args(&["-p", "subcrate"]) - .args(&["-p", "path_dep"]) + .arg("--") + .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir + .args(&["--cfg", r#"feature="primary_package_test""#]) .output() .unwrap(); + println!("status: {}", output.status); + println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); + println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); + + assert!(!output.status.success()); + assert!( + String::from_utf8(output.stderr) + .unwrap() + .contains("error: empty `loop {}` wastes CPU cycles") + ); + }; + + // Make sure Cargo is aware of the removal of `--no-deps`. + lint_path_dep(); - // `path_dep` is a path dependency of `subcrate` that would trigger a denied lint. - // Make sure that with the `--no-deps` argument Clippy does not run on `path_dep`. + let successful_build = || { let output = Command::new(&*CLIPPY_PATH) .current_dir(&cwd) .env("CLIPPY_DOGFOOD", "1") @@ -75,9 +120,7 @@ fn dogfood_subprojects() { .arg("clippy") .args(&["-p", "subcrate"]) .arg("--") - .arg("--no-deps") .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir - .args(&["--cfg", r#"feature="primary_package_test""#]) .output() .unwrap(); println!("status: {}", output.status); @@ -86,67 +129,24 @@ fn dogfood_subprojects() { assert!(output.status.success()); - let lint_path_dep = || { - // Test that without the `--no-deps` argument, `path_dep` is linted. - let output = Command::new(&*CLIPPY_PATH) - .current_dir(&cwd) - .env("CLIPPY_DOGFOOD", "1") - .env("CARGO_INCREMENTAL", "0") - .arg("clippy") - .args(&["-p", "subcrate"]) - .arg("--") - .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir - .args(&["--cfg", r#"feature="primary_package_test""#]) - .output() - .unwrap(); - println!("status: {}", output.status); - println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); - println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); - - assert!(!output.status.success()); - assert!( - String::from_utf8(output.stderr) - .unwrap() - .contains("error: empty `loop {}` wastes CPU cycles") - ); - }; - - // Make sure Cargo is aware of the removal of `--no-deps`. - lint_path_dep(); - - let successful_build = || { - let output = Command::new(&*CLIPPY_PATH) - .current_dir(&cwd) - .env("CLIPPY_DOGFOOD", "1") - .env("CARGO_INCREMENTAL", "0") - .arg("clippy") - .args(&["-p", "subcrate"]) - .arg("--") - .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir - .output() - .unwrap(); - println!("status: {}", output.status); - println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); - println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); - - assert!(output.status.success()); - - output - }; - - // Trigger a sucessful build, so Cargo would like to cache the build result. - successful_build(); - - // Make sure there's no spurious rebuild when nothing changes. - let stderr = String::from_utf8(successful_build().stderr).unwrap(); - assert!(!stderr.contains("Compiling")); - assert!(!stderr.contains("Checking")); - assert!(stderr.contains("Finished")); - - // Make sure Cargo is aware of the new `--cfg` flag. - lint_path_dep(); - } + output + }; + + // Trigger a sucessful build, so Cargo would like to cache the build result. + successful_build(); + + // Make sure there's no spurious rebuild when nothing changes. + let stderr = String::from_utf8(successful_build().stderr).unwrap(); + assert!(!stderr.contains("Compiling")); + assert!(!stderr.contains("Checking")); + assert!(stderr.contains("Finished")); + // Make sure Cargo is aware of the new `--cfg` flag. + lint_path_dep(); +} + +#[test] +fn dogfood_subprojects() { // run clippy on remaining subprojects and fail the test if lint warnings are reported if cargo::is_rustc_test_suite() { return; -- cgit 1.4.1-3-g733a5 From f2a85cb42ae64bc5a82eaee49d92b6f9d93153fe Mon Sep 17 00:00:00 2001 From: Yusuke Tanaka Date: Mon, 8 Mar 2021 22:52:03 +0900 Subject: Set 1.50 as msrv of if_then_some_else_none --- clippy_lints/src/if_then_some_else_none.rs | 24 ++++++++++++++++++++++-- clippy_lints/src/lib.rs | 2 +- tests/ui/if_then_some_else_none.rs | 22 ++++++++++++++++++++++ tests/ui/if_then_some_else_none.stderr | 16 ++++++++++++++-- 4 files changed, 59 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index 0bd393f8996..aadadd0d934 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -4,7 +4,10 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +const IF_THEN_SOME_ELSE_NONE_MSRV: RustcVersion = RustcVersion::new(1, 50, 0); declare_clippy_lint! { /// **What it does:** Checks for if-else that could be written to `bool::then`. @@ -39,10 +42,25 @@ declare_clippy_lint! { "Finds if-else that could be written using `bool::then`" } -declare_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]); +pub struct IfThenSomeElseNone { + msrv: Option, +} + +impl IfThenSomeElseNone { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]); impl LateLintPass<'_> for IfThenSomeElseNone { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) { + if !utils::meets_msrv(self.msrv.as_ref(), &IF_THEN_SOME_ELSE_NONE_MSRV) { + return; + } + if in_external_macro(cx.sess(), expr.span) { return; } @@ -85,4 +103,6 @@ impl LateLintPass<'_> for IfThenSomeElseNone { } } } + + extract_msrv_attr!(LateContext); } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 9a7dedf416e..2e4c1e3bdf8 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1284,7 +1284,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box redundant_slicing::RedundantSlicing); store.register_late_pass(|| box from_str_radix_10::FromStrRadix10); store.register_late_pass(|| box manual_map::ManualMap); - store.register_late_pass(|| box if_then_some_else_none::IfThenSomeElseNone); + store.register_late_pass(move || box if_then_some_else_none::IfThenSomeElseNone::new(msrv)); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), diff --git a/tests/ui/if_then_some_else_none.rs b/tests/ui/if_then_some_else_none.rs index b19e2a50010..337292fd9b3 100644 --- a/tests/ui/if_then_some_else_none.rs +++ b/tests/ui/if_then_some_else_none.rs @@ -1,4 +1,5 @@ #![warn(clippy::if_then_some_else_none)] +#![feature(custom_inner_attributes)] fn main() { // Should issue an error. @@ -49,6 +50,27 @@ fn main() { let _ = if foo() { into_some("foo") } else { None }; } +fn _msrv_1_49() { + #![clippy::msrv = "1.49"] + // `bool::then` was stabilized in 1.50. Do not lint this + let _ = if foo() { + println!("true!"); + Some("foo") + } else { + None + }; +} + +fn _msrv_1_50() { + #![clippy::msrv = "1.50"] + let _ = if foo() { + println!("true!"); + Some("foo") + } else { + None + }; +} + fn foo() -> bool { unimplemented!() } diff --git a/tests/ui/if_then_some_else_none.stderr b/tests/ui/if_then_some_else_none.stderr index 7ad9c4ce79d..19c96f900a3 100644 --- a/tests/ui/if_then_some_else_none.stderr +++ b/tests/ui/if_then_some_else_none.stderr @@ -1,5 +1,5 @@ error: this could be simplified with `bool::then` - --> $DIR/if_then_some_else_none.rs:5:13 + --> $DIR/if_then_some_else_none.rs:6:13 | LL | let _ = if foo() { | _____________^ @@ -12,5 +12,17 @@ LL | | }; | = note: `-D clippy::if-then-some-else-none` implied by `-D warnings` -error: aborting due to previous error +error: this could be simplified with `bool::then` + --> $DIR/if_then_some_else_none.rs:66:13 + | +LL | let _ = if foo() { + | _____________^ +LL | | println!("true!"); +LL | | Some("foo") +LL | | } else { +LL | | None +LL | | }; + | |_____^ help: try this: `foo().then(|| { /* snippet */ "foo" })` + +error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From 0327c2e04106e258949dbf96e58bb582e2960c6b Mon Sep 17 00:00:00 2001 From: Yusuke Tanaka Date: Mon, 8 Mar 2021 23:19:57 +0900 Subject: Output help instead of suggestion in `if_then_some_else_none` diagnose --- clippy_lints/src/if_then_some_else_none.rs | 17 +++++++---------- tests/ui/if_then_some_else_none.rs | 4 ++-- tests/ui/if_then_some_else_none.stderr | 9 ++++++--- 3 files changed, 15 insertions(+), 15 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index aadadd0d934..569a7f06f95 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -1,6 +1,5 @@ use crate::utils; use if_chain::if_chain; -use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -83,22 +82,20 @@ impl LateLintPass<'_> for IfThenSomeElseNone { if let ExprKind::Path(ref els_call_qpath) = els_expr.kind; if utils::match_qpath(els_call_qpath, &utils::paths::OPTION_NONE); then { - let mut applicability = Applicability::MachineApplicable; - let cond_snip = utils::snippet_with_applicability(cx, cond.span, "[condition]", &mut applicability); - let arg_snip = utils::snippet_with_applicability(cx, then_arg.span, "", &mut applicability); - let sugg = format!( - "{}.then(|| {{ /* snippet */ {} }})", + let cond_snip = utils::snippet(cx, cond.span, "[condition]"); + let arg_snip = utils::snippet(cx, then_arg.span, ""); + let help = format!( + "consider using `bool::then` like: `{}.then(|| {{ /* snippet */ {} }})`", cond_snip, arg_snip, ); - utils::span_lint_and_sugg( + utils::span_lint_and_help( cx, IF_THEN_SOME_ELSE_NONE, expr.span, "this could be simplified with `bool::then`", - "try this", - sugg, - applicability, + None, + &help, ); } } diff --git a/tests/ui/if_then_some_else_none.rs b/tests/ui/if_then_some_else_none.rs index 337292fd9b3..14a5fe76245 100644 --- a/tests/ui/if_then_some_else_none.rs +++ b/tests/ui/if_then_some_else_none.rs @@ -55,7 +55,7 @@ fn _msrv_1_49() { // `bool::then` was stabilized in 1.50. Do not lint this let _ = if foo() { println!("true!"); - Some("foo") + Some(149) } else { None }; @@ -65,7 +65,7 @@ fn _msrv_1_50() { #![clippy::msrv = "1.50"] let _ = if foo() { println!("true!"); - Some("foo") + Some(150) } else { None }; diff --git a/tests/ui/if_then_some_else_none.stderr b/tests/ui/if_then_some_else_none.stderr index 19c96f900a3..722c52b1cb4 100644 --- a/tests/ui/if_then_some_else_none.stderr +++ b/tests/ui/if_then_some_else_none.stderr @@ -8,9 +8,10 @@ LL | | Some("foo") LL | | } else { LL | | None LL | | }; - | |_____^ help: try this: `foo().then(|| { /* snippet */ "foo" })` + | |_____^ | = note: `-D clippy::if-then-some-else-none` implied by `-D warnings` + = help: consider using `bool::then` like: `foo().then(|| { /* snippet */ "foo" })` error: this could be simplified with `bool::then` --> $DIR/if_then_some_else_none.rs:66:13 @@ -18,11 +19,13 @@ error: this could be simplified with `bool::then` LL | let _ = if foo() { | _____________^ LL | | println!("true!"); -LL | | Some("foo") +LL | | Some(150) LL | | } else { LL | | None LL | | }; - | |_____^ help: try this: `foo().then(|| { /* snippet */ "foo" })` + | |_____^ + | + = help: consider using `bool::then` like: `foo().then(|| { /* snippet */ 150 })` error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From bf98aa6fb8e02db15ad18d517145f13a5bed2921 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 8 Mar 2021 12:59:58 -0600 Subject: Fix redundant closure with macros --- clippy_lints/src/eta_reduction.rs | 24 +++++++++++++++++++++++- tests/ui/eta.fixed | 15 +++++++++++++++ tests/ui/eta.rs | 15 +++++++++++++++ tests/ui/eta.stderr | 32 +++++++++++++++++++------------- 4 files changed, 72 insertions(+), 14 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 1a722d39f73..ab385fc1f8a 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -10,6 +10,8 @@ use crate::utils::{ implements_trait, is_adjusted, iter_input_pats, snippet_opt, span_lint_and_sugg, span_lint_and_then, type_is_unsafe_function, }; +use clippy_utils::higher; +use clippy_utils::higher::VecArgs; declare_clippy_lint! { /// **What it does:** Checks for closures which just call another function where @@ -74,7 +76,10 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { match expr.kind { ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) => { for arg in args { - check_closure(cx, arg) + // skip `foo(macro!())` + if arg.span.ctxt() == expr.span.ctxt() { + check_closure(cx, arg) + } } }, _ => (), @@ -87,6 +92,23 @@ fn check_closure(cx: &LateContext<'_>, expr: &Expr<'_>) { let body = cx.tcx.hir().body(eid); let ex = &body.value; + if ex.span.ctxt() != expr.span.ctxt() { + if let Some(VecArgs::Vec(&[])) = higher::vec_macro(cx, ex) { + // replace `|| vec![]` with `Vec::new` + span_lint_and_sugg( + cx, + REDUNDANT_CLOSURE, + expr.span, + "redundant closure found", + "remove closure as shown", + "std::vec::Vec::new".into(), + Applicability::MachineApplicable, + ); + } + // skip `foo(|| macro!())` + return; + } + if_chain!( if let ExprKind::Call(ref caller, ref args) = ex.kind; diff --git a/tests/ui/eta.fixed b/tests/ui/eta.fixed index 1b34c2f74eb..2be2283e3fd 100644 --- a/tests/ui/eta.fixed +++ b/tests/ui/eta.fixed @@ -16,10 +16,25 @@ use std::path::PathBuf; +macro_rules! mac { + () => { + foobar() + }; +} + +macro_rules! closure_mac { + () => { + |n| foo(n) + }; +} + fn main() { let a = Some(1u8).map(foo); meta(foo); let c = Some(1u8).map(|a| {1+2; foo}(a)); + true.then(|| mac!()); // don't lint function in macro expansion + Some(1).map(closure_mac!()); // don't lint closure in macro expansion + let _: Option> = true.then(std::vec::Vec::new); // special case vec! let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted? all(&[1, 2, 3], &2, |x, y| below(x, y)); //is adjusted unsafe { diff --git a/tests/ui/eta.rs b/tests/ui/eta.rs index 4f050bd8479..f0373f9ccf6 100644 --- a/tests/ui/eta.rs +++ b/tests/ui/eta.rs @@ -16,10 +16,25 @@ use std::path::PathBuf; +macro_rules! mac { + () => { + foobar() + }; +} + +macro_rules! closure_mac { + () => { + |n| foo(n) + }; +} + fn main() { let a = Some(1u8).map(|a| foo(a)); meta(|a| foo(a)); let c = Some(1u8).map(|a| {1+2; foo}(a)); + true.then(|| mac!()); // don't lint function in macro expansion + Some(1).map(closure_mac!()); // don't lint closure in macro expansion + let _: Option> = true.then(|| vec![]); // special case vec! let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted? all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted unsafe { diff --git a/tests/ui/eta.stderr b/tests/ui/eta.stderr index 16aa1b07733..34d6dd46311 100644 --- a/tests/ui/eta.stderr +++ b/tests/ui/eta.stderr @@ -1,5 +1,5 @@ error: redundant closure found - --> $DIR/eta.rs:20:27 + --> $DIR/eta.rs:32:27 | LL | let a = Some(1u8).map(|a| foo(a)); | ^^^^^^^^^^ help: remove closure as shown: `foo` @@ -7,13 +7,19 @@ LL | let a = Some(1u8).map(|a| foo(a)); = note: `-D clippy::redundant-closure` implied by `-D warnings` error: redundant closure found - --> $DIR/eta.rs:21:10 + --> $DIR/eta.rs:33:10 | LL | meta(|a| foo(a)); | ^^^^^^^^^^ help: remove closure as shown: `foo` +error: redundant closure found + --> $DIR/eta.rs:37:40 + | +LL | let _: Option> = true.then(|| vec![]); // special case vec! + | ^^^^^^^^^ help: remove closure as shown: `std::vec::Vec::new` + error: this expression borrows a reference (`&u8`) that is immediately dereferenced by the compiler - --> $DIR/eta.rs:24:21 + --> $DIR/eta.rs:39:21 | LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted | ^^^ help: change this to: `&2` @@ -21,13 +27,13 @@ LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted = note: `-D clippy::needless-borrow` implied by `-D warnings` error: redundant closure found - --> $DIR/eta.rs:31:27 + --> $DIR/eta.rs:46:27 | LL | let e = Some(1u8).map(|a| generic(a)); | ^^^^^^^^^^^^^^ help: remove closure as shown: `generic` error: redundant closure found - --> $DIR/eta.rs:74:51 + --> $DIR/eta.rs:89:51 | LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo()); | ^^^^^^^^^^^ help: remove closure as shown: `TestStruct::foo` @@ -35,46 +41,46 @@ LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo()); = note: `-D clippy::redundant-closure-for-method-calls` implied by `-D warnings` error: redundant closure found - --> $DIR/eta.rs:76:51 + --> $DIR/eta.rs:91:51 | LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo()); | ^^^^^^^^^^^^^^^^^ help: remove closure as shown: `TestTrait::trait_foo` error: redundant closure found - --> $DIR/eta.rs:79:42 + --> $DIR/eta.rs:94:42 | LL | let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear()); | ^^^^^^^^^^^^^ help: remove closure as shown: `std::vec::Vec::clear` error: redundant closure found - --> $DIR/eta.rs:84:29 + --> $DIR/eta.rs:99:29 | LL | let e = Some("str").map(|s| s.to_string()); | ^^^^^^^^^^^^^^^^^ help: remove closure as shown: `std::string::ToString::to_string` error: redundant closure found - --> $DIR/eta.rs:86:27 + --> $DIR/eta.rs:101:27 | LL | let e = Some('a').map(|s| s.to_uppercase()); | ^^^^^^^^^^^^^^^^^^^^ help: remove closure as shown: `char::to_uppercase` error: redundant closure found - --> $DIR/eta.rs:89:65 + --> $DIR/eta.rs:104:65 | LL | let e: std::vec::Vec = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove closure as shown: `char::to_ascii_uppercase` error: redundant closure found - --> $DIR/eta.rs:172:27 + --> $DIR/eta.rs:187:27 | LL | let a = Some(1u8).map(|a| foo_ptr(a)); | ^^^^^^^^^^^^^^ help: remove closure as shown: `foo_ptr` error: redundant closure found - --> $DIR/eta.rs:177:27 + --> $DIR/eta.rs:192:27 | LL | let a = Some(1u8).map(|a| closure(a)); | ^^^^^^^^^^^^^^ help: remove closure as shown: `closure` -error: aborting due to 12 previous errors +error: aborting due to 13 previous errors -- cgit 1.4.1-3-g733a5 From 8c540dcd2d1d240219c1a4aa28dd745ea010b501 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 8 Mar 2021 13:05:13 -0600 Subject: Improve the redundant_closure message --- clippy_lints/src/eta_reduction.rs | 12 +++++----- tests/ui/eta.stderr | 48 +++++++++++++++++++-------------------- 2 files changed, 30 insertions(+), 30 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index ab385fc1f8a..c461732fd36 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -99,8 +99,8 @@ fn check_closure(cx: &LateContext<'_>, expr: &Expr<'_>) { cx, REDUNDANT_CLOSURE, expr.span, - "redundant closure found", - "remove closure as shown", + "redundant closure", + "replace the closure with `Vec::new`", "std::vec::Vec::new".into(), Applicability::MachineApplicable, ); @@ -129,11 +129,11 @@ fn check_closure(cx: &LateContext<'_>, expr: &Expr<'_>) { if compare_inputs(&mut iter_input_pats(decl, body), &mut args.iter()); then { - span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure found", |diag| { + span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| { if let Some(snippet) = snippet_opt(cx, caller.span) { diag.span_suggestion( expr.span, - "remove closure as shown", + "replace the closure with the function itself", snippet, Applicability::MachineApplicable, ); @@ -163,8 +163,8 @@ fn check_closure(cx: &LateContext<'_>, expr: &Expr<'_>) { cx, REDUNDANT_CLOSURE_FOR_METHOD_CALLS, expr.span, - "redundant closure found", - "remove closure as shown", + "redundant closure", + "replace the closure with the method itself", format!("{}::{}", name, path.ident.name), Applicability::MachineApplicable, ); diff --git a/tests/ui/eta.stderr b/tests/ui/eta.stderr index 34d6dd46311..57ed6527966 100644 --- a/tests/ui/eta.stderr +++ b/tests/ui/eta.stderr @@ -1,22 +1,22 @@ -error: redundant closure found +error: redundant closure --> $DIR/eta.rs:32:27 | LL | let a = Some(1u8).map(|a| foo(a)); - | ^^^^^^^^^^ help: remove closure as shown: `foo` + | ^^^^^^^^^^ help: replace the closure with the function itself: `foo` | = note: `-D clippy::redundant-closure` implied by `-D warnings` -error: redundant closure found +error: redundant closure --> $DIR/eta.rs:33:10 | LL | meta(|a| foo(a)); - | ^^^^^^^^^^ help: remove closure as shown: `foo` + | ^^^^^^^^^^ help: replace the closure with the function itself: `foo` -error: redundant closure found +error: redundant closure --> $DIR/eta.rs:37:40 | LL | let _: Option> = true.then(|| vec![]); // special case vec! - | ^^^^^^^^^ help: remove closure as shown: `std::vec::Vec::new` + | ^^^^^^^^^ help: replace the closure with `Vec::new`: `std::vec::Vec::new` error: this expression borrows a reference (`&u8`) that is immediately dereferenced by the compiler --> $DIR/eta.rs:39:21 @@ -26,61 +26,61 @@ LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted | = note: `-D clippy::needless-borrow` implied by `-D warnings` -error: redundant closure found +error: redundant closure --> $DIR/eta.rs:46:27 | LL | let e = Some(1u8).map(|a| generic(a)); - | ^^^^^^^^^^^^^^ help: remove closure as shown: `generic` + | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `generic` -error: redundant closure found +error: redundant closure --> $DIR/eta.rs:89:51 | LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo()); - | ^^^^^^^^^^^ help: remove closure as shown: `TestStruct::foo` + | ^^^^^^^^^^^ help: replace the closure with the method itself: `TestStruct::foo` | = note: `-D clippy::redundant-closure-for-method-calls` implied by `-D warnings` -error: redundant closure found +error: redundant closure --> $DIR/eta.rs:91:51 | LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo()); - | ^^^^^^^^^^^^^^^^^ help: remove closure as shown: `TestTrait::trait_foo` + | ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `TestTrait::trait_foo` -error: redundant closure found +error: redundant closure --> $DIR/eta.rs:94:42 | LL | let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear()); - | ^^^^^^^^^^^^^ help: remove closure as shown: `std::vec::Vec::clear` + | ^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::vec::Vec::clear` -error: redundant closure found +error: redundant closure --> $DIR/eta.rs:99:29 | LL | let e = Some("str").map(|s| s.to_string()); - | ^^^^^^^^^^^^^^^^^ help: remove closure as shown: `std::string::ToString::to_string` + | ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::string::ToString::to_string` -error: redundant closure found +error: redundant closure --> $DIR/eta.rs:101:27 | LL | let e = Some('a').map(|s| s.to_uppercase()); - | ^^^^^^^^^^^^^^^^^^^^ help: remove closure as shown: `char::to_uppercase` + | ^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_uppercase` -error: redundant closure found +error: redundant closure --> $DIR/eta.rs:104:65 | LL | let e: std::vec::Vec = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove closure as shown: `char::to_ascii_uppercase` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_ascii_uppercase` -error: redundant closure found +error: redundant closure --> $DIR/eta.rs:187:27 | LL | let a = Some(1u8).map(|a| foo_ptr(a)); - | ^^^^^^^^^^^^^^ help: remove closure as shown: `foo_ptr` + | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo_ptr` -error: redundant closure found +error: redundant closure --> $DIR/eta.rs:192:27 | LL | let a = Some(1u8).map(|a| closure(a)); - | ^^^^^^^^^^^^^^ help: remove closure as shown: `closure` + | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `closure` error: aborting due to 13 previous errors -- cgit 1.4.1-3-g733a5 From e322c773e3db22aadd60ce9a1803076fcbfeef2d Mon Sep 17 00:00:00 2001 From: Andrea Nall Date: Mon, 8 Mar 2021 23:03:45 -0600 Subject: use TyS::walk --- clippy_utils/src/lib.rs | 19 ++++++++----------- tests/ui/crashes/ice-6840.rs | 10 +++++++++- 2 files changed, 17 insertions(+), 12 deletions(-) (limited to 'tests') diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 999b39852cd..ac29a09ae0e 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1504,9 +1504,7 @@ fn is_normalizable_helper<'tcx>( cache.insert(ty, false); // prevent recursive loops let result = cx.tcx.infer_ctxt().enter(|infcx| { let cause = rustc_middle::traits::ObligationCause::dummy(); - if infcx.at(&cause, param_env).normalize(ty).is_err() { - false - } else { + if infcx.at(&cause, param_env).normalize(ty).is_ok() { match ty.kind() { ty::Adt(def, substs) => !def.variants.iter().any(|variant| { variant @@ -1514,16 +1512,15 @@ fn is_normalizable_helper<'tcx>( .iter() .any(|field| !is_normalizable_helper(cx, param_env, field.ty(cx.tcx, substs), cache)) }), - ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => { - is_normalizable_helper(cx, param_env, pointee, cache) - }, - ty::Array(inner_ty, _) | ty::Slice(inner_ty) => is_normalizable_helper(cx, param_env, inner_ty, cache), - ty::Tuple(tys) => !tys.iter().any(|inner| match inner.unpack() { - GenericArgKind::Type(inner_ty) => !is_normalizable_helper(cx, param_env, inner_ty, cache), - _ => false, + _ => !ty.walk().any(|generic_arg| !match generic_arg.unpack() { + GenericArgKind::Type(inner_ty) if inner_ty != ty => { + is_normalizable_helper(cx, param_env, inner_ty, cache) + }, + _ => true, // if inner_ty == ty, we've already checked it }), - _ => true, } + } else { + false } }); cache.insert(ty, result); diff --git a/tests/ui/crashes/ice-6840.rs b/tests/ui/crashes/ice-6840.rs index a749eefb635..d789f60c5d5 100644 --- a/tests/ui/crashes/ice-6840.rs +++ b/tests/ui/crashes/ice-6840.rs @@ -13,11 +13,19 @@ pub struct RuleEdges { type RuleDependencyEdges = HashMap>; -// and additional potential variants +// reproducer from the GitHub issue ends here +// but check some additional variants type RuleDependencyEdgesArray = HashMap; 8]>; type RuleDependencyEdgesSlice = HashMap]>; type RuleDependencyEdgesRef = HashMap>; type RuleDependencyEdgesRaw = HashMap>; type RuleDependencyEdgesTuple = HashMap, RuleEdges)>; +// and an additional checks to make sure fix doesn't have stack-overflow issue +// on self-containing types +pub struct SelfContaining { + inner: Box, +} +type SelfContainingEdges = HashMap; + fn main() {} -- cgit 1.4.1-3-g733a5 From 2547edb84272aeb3d01e2b61640a9107c01b281b Mon Sep 17 00:00:00 2001 From: Mateusz Gacek <96mateusz.gacek@gmail.com> Date: Thu, 11 Mar 2021 23:47:15 -0800 Subject: wrong_self_convention: fix lint in case of `to_*_mut` method When a method starts with `to_` and ends with `_mut`, it should expect a `&mut self` parameter, otherwise `&self`. --- clippy_lints/src/methods/mod.rs | 15 ++-- clippy_lints/src/methods/wrong_self_convention.rs | 72 +++++++++++++---- tests/ui/def_id_nocore.stderr | 3 +- tests/ui/wrong_self_convention.stderr | 95 +++++++++++++++++------ tests/ui/wrong_self_conventions_mut.rs | 30 +++++++ tests/ui/wrong_self_conventions_mut.stderr | 19 +++++ 6 files changed, 189 insertions(+), 45 deletions(-) create mode 100644 tests/ui/wrong_self_conventions_mut.rs create mode 100644 tests/ui/wrong_self_conventions_mut.stderr (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 7fd14c4f9b1..16d5e3bb43f 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -191,13 +191,14 @@ declare_clippy_lint! { /// **What it does:** Checks for methods with certain name prefixes and which /// doesn't match how self is taken. The actual rules are: /// - /// |Prefix |`self` taken | - /// |-------|----------------------| - /// |`as_` |`&self` or `&mut self`| - /// |`from_`| none | - /// |`into_`|`self` | - /// |`is_` |`&self` or none | - /// |`to_` |`&self` | + /// |Prefix |Postfix |`self` taken | + /// |-------|------------|----------------------| + /// |`as_` | none |`&self` or `&mut self`| + /// |`from_`| none | none | + /// |`into_`| none |`self` | + /// |`is_` | none |`&self` or none | + /// |`to_` | `_mut` |`&mut &self` | + /// |`to_` | not `_mut` |`&self` | /// /// **Why is this bad?** Consistency breeds readability. If you follow the /// conventions, your users won't be surprised that they, e.g., need to supply a diff --git a/clippy_lints/src/methods/wrong_self_convention.rs b/clippy_lints/src/methods/wrong_self_convention.rs index 90fab577436..c8bcad7be3e 100644 --- a/clippy_lints/src/methods/wrong_self_convention.rs +++ b/clippy_lints/src/methods/wrong_self_convention.rs @@ -1,5 +1,5 @@ use crate::methods::SelfKind; -use crate::utils::span_lint; +use crate::utils::span_lint_and_help; use rustc_lint::LateContext; use rustc_middle::ty::TyS; use rustc_span::source_map::Span; @@ -9,18 +9,22 @@ use super::WRONG_PUB_SELF_CONVENTION; use super::WRONG_SELF_CONVENTION; #[rustfmt::skip] -const CONVENTIONS: [(Convention, &[SelfKind]); 7] = [ - (Convention::Eq("new"), &[SelfKind::No]), - (Convention::StartsWith("as_"), &[SelfKind::Ref, SelfKind::RefMut]), - (Convention::StartsWith("from_"), &[SelfKind::No]), - (Convention::StartsWith("into_"), &[SelfKind::Value]), - (Convention::StartsWith("is_"), &[SelfKind::Ref, SelfKind::No]), - (Convention::Eq("to_mut"), &[SelfKind::RefMut]), - (Convention::StartsWith("to_"), &[SelfKind::Ref]), +const CONVENTIONS: [(&[Convention], &[SelfKind]); 8] = [ + (&[Convention::Eq("new")], &[SelfKind::No]), + (&[Convention::StartsWith("as_")], &[SelfKind::Ref, SelfKind::RefMut]), + (&[Convention::StartsWith("from_")], &[SelfKind::No]), + (&[Convention::StartsWith("into_")], &[SelfKind::Value]), + (&[Convention::StartsWith("is_")], &[SelfKind::Ref, SelfKind::No]), + (&[Convention::Eq("to_mut")], &[SelfKind::RefMut]), + (&[Convention::StartsWith("to_"), Convention::EndsWith("_mut")], &[SelfKind::RefMut]), + (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut")], &[SelfKind::Ref]), ]; + enum Convention { Eq(&'static str), StartsWith(&'static str), + EndsWith(&'static str), + NotEndsWith(&'static str), } impl Convention { @@ -29,6 +33,8 @@ impl Convention { match *self { Self::Eq(this) => this == other, Self::StartsWith(this) => other.starts_with(this) && this != other, + Self::EndsWith(this) => other.ends_with(this) && this != other, + Self::NotEndsWith(this) => !Self::EndsWith(this).check(other), } } } @@ -38,6 +44,8 @@ impl fmt::Display for Convention { match *self { Self::Eq(this) => this.fmt(f), Self::StartsWith(this) => this.fmt(f).and_then(|_| '*'.fmt(f)), + Self::EndsWith(this) => '*'.fmt(f).and_then(|_| this.fmt(f)), + Self::NotEndsWith(this) => '~'.fmt(f).and_then(|_| this.fmt(f)), } } } @@ -55,21 +63,59 @@ pub(super) fn check<'tcx>( } else { WRONG_SELF_CONVENTION }; - if let Some((ref conv, self_kinds)) = &CONVENTIONS.iter().find(|(ref conv, _)| conv.check(item_name)) { + if let Some((conventions, self_kinds)) = &CONVENTIONS + .iter() + .find(|(convs, _)| convs.iter().all(|conv| conv.check(item_name))) + { if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) { - span_lint( + let suggestion = { + if conventions.len() > 1 { + let special_case = { + // Don't mention `NotEndsWith` when there is also `StartsWith` convention present + if conventions.len() == 2 { + match conventions { + [Convention::StartsWith(starts_with), Convention::NotEndsWith(_)] + | [Convention::NotEndsWith(_), Convention::StartsWith(starts_with)] => { + Some(format!("methods called `{}`", Convention::StartsWith(starts_with))) + }, + _ => None, + } + } else { + None + } + }; + + if let Some(suggestion) = special_case { + suggestion + } else { + let s = conventions + .iter() + .map(|c| format!("`{}`", &c.to_string())) + .collect::>() + .join(" and "); + + format!("methods called like this: ({})", &s) + } + } else { + format!("methods called `{}`", &conventions[0]) + } + }; + + span_lint_and_help( cx, lint, first_arg_span, &format!( - "methods called `{}` usually take {}; consider choosing a less ambiguous name", - conv, + "{} usually take {}", + suggestion, &self_kinds .iter() .map(|k| k.description()) .collect::>() .join(" or ") ), + None, + "consider choosing a less ambiguous name", ); } } diff --git a/tests/ui/def_id_nocore.stderr b/tests/ui/def_id_nocore.stderr index ed87a50547d..a3e9cc75b08 100644 --- a/tests/ui/def_id_nocore.stderr +++ b/tests/ui/def_id_nocore.stderr @@ -1,10 +1,11 @@ -error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name +error: methods called `as_*` usually take self by reference or self by mutable reference --> $DIR/def_id_nocore.rs:26:19 | LL | pub fn as_ref(self) -> &'static str { | ^^^^ | = note: `-D clippy::wrong-self-convention` implied by `-D warnings` + = help: consider choosing a less ambiguous name error: aborting due to previous error diff --git a/tests/ui/wrong_self_convention.stderr b/tests/ui/wrong_self_convention.stderr index 32bd9075bd5..f43fea0d513 100644 --- a/tests/ui/wrong_self_convention.stderr +++ b/tests/ui/wrong_self_convention.stderr @@ -1,148 +1,195 @@ -error: methods called `from_*` usually take no self; consider choosing a less ambiguous name +error: methods called `from_*` usually take no self --> $DIR/wrong_self_convention.rs:18:17 | LL | fn from_i32(self) {} | ^^^^ | = note: `-D clippy::wrong-self-convention` implied by `-D warnings` + = help: consider choosing a less ambiguous name -error: methods called `from_*` usually take no self; consider choosing a less ambiguous name +error: methods called `from_*` usually take no self --> $DIR/wrong_self_convention.rs:24:21 | LL | pub fn from_i64(self) {} | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name +error: methods called `as_*` usually take self by reference or self by mutable reference --> $DIR/wrong_self_convention.rs:36:15 | LL | fn as_i32(self) {} | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name +error: methods called `into_*` usually take self by value --> $DIR/wrong_self_convention.rs:38:17 | LL | fn into_i32(&self) {} | ^^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name +error: methods called `is_*` usually take self by reference or no self --> $DIR/wrong_self_convention.rs:40:15 | LL | fn is_i32(self) {} | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name +error: methods called `to_*` usually take self by reference --> $DIR/wrong_self_convention.rs:42:15 | LL | fn to_i32(self) {} | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `from_*` usually take no self; consider choosing a less ambiguous name +error: methods called `from_*` usually take no self --> $DIR/wrong_self_convention.rs:44:17 | LL | fn from_i32(self) {} | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name +error: methods called `as_*` usually take self by reference or self by mutable reference --> $DIR/wrong_self_convention.rs:46:19 | LL | pub fn as_i64(self) {} | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name +error: methods called `into_*` usually take self by value --> $DIR/wrong_self_convention.rs:47:21 | LL | pub fn into_i64(&self) {} | ^^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name +error: methods called `is_*` usually take self by reference or no self --> $DIR/wrong_self_convention.rs:48:19 | LL | pub fn is_i64(self) {} | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name +error: methods called `to_*` usually take self by reference --> $DIR/wrong_self_convention.rs:49:19 | LL | pub fn to_i64(self) {} | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `from_*` usually take no self; consider choosing a less ambiguous name +error: methods called `from_*` usually take no self --> $DIR/wrong_self_convention.rs:50:21 | LL | pub fn from_i64(self) {} | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name +error: methods called `as_*` usually take self by reference or self by mutable reference --> $DIR/wrong_self_convention.rs:95:19 | LL | fn as_i32(self) {} | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name +error: methods called `into_*` usually take self by value --> $DIR/wrong_self_convention.rs:98:25 | LL | fn into_i32_ref(&self) {} | ^^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name +error: methods called `is_*` usually take self by reference or no self --> $DIR/wrong_self_convention.rs:100:19 | LL | fn is_i32(self) {} | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name +error: methods called `to_*` usually take self by reference --> $DIR/wrong_self_convention.rs:102:19 | LL | fn to_i32(self) {} | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `from_*` usually take no self; consider choosing a less ambiguous name +error: methods called `from_*` usually take no self --> $DIR/wrong_self_convention.rs:104:21 | LL | fn from_i32(self) {} | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name +error: methods called `as_*` usually take self by reference or self by mutable reference --> $DIR/wrong_self_convention.rs:119:19 | LL | fn as_i32(self); | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name +error: methods called `into_*` usually take self by value --> $DIR/wrong_self_convention.rs:122:25 | LL | fn into_i32_ref(&self); | ^^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name +error: methods called `is_*` usually take self by reference or no self --> $DIR/wrong_self_convention.rs:124:19 | LL | fn is_i32(self); | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name +error: methods called `to_*` usually take self by reference --> $DIR/wrong_self_convention.rs:126:19 | LL | fn to_i32(self); | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `from_*` usually take no self; consider choosing a less ambiguous name +error: methods called `from_*` usually take no self --> $DIR/wrong_self_convention.rs:128:21 | LL | fn from_i32(self); | ^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name +error: methods called `into_*` usually take self by value --> $DIR/wrong_self_convention.rs:146:25 | LL | fn into_i32_ref(&self); | ^^^^^ + | + = help: consider choosing a less ambiguous name -error: methods called `from_*` usually take no self; consider choosing a less ambiguous name +error: methods called `from_*` usually take no self --> $DIR/wrong_self_convention.rs:152:21 | LL | fn from_i32(self); | ^^^^ + | + = help: consider choosing a less ambiguous name error: aborting due to 24 previous errors diff --git a/tests/ui/wrong_self_conventions_mut.rs b/tests/ui/wrong_self_conventions_mut.rs new file mode 100644 index 00000000000..486a0d77235 --- /dev/null +++ b/tests/ui/wrong_self_conventions_mut.rs @@ -0,0 +1,30 @@ +// edition:2018 +#![warn(clippy::wrong_self_convention)] +#![allow(dead_code)] + +fn main() {} + +mod issue6758 { + pub enum Test { + One(T), + Many(Vec), + } + + impl Test { + // If a method starts with `to_` and not ends with `_mut` it should expect `&self` + pub fn to_many(&mut self) -> Option<&mut [T]> { + match self { + Self::Many(data) => Some(data), + _ => None, + } + } + + // If a method starts with `to_` and ends with `_mut` it should expect `&mut self` + pub fn to_many_mut(&self) -> Option<&[T]> { + match self { + Self::Many(data) => Some(data), + _ => None, + } + } + } +} diff --git a/tests/ui/wrong_self_conventions_mut.stderr b/tests/ui/wrong_self_conventions_mut.stderr new file mode 100644 index 00000000000..7662b38e67d --- /dev/null +++ b/tests/ui/wrong_self_conventions_mut.stderr @@ -0,0 +1,19 @@ +error: methods called `to_*` usually take self by reference + --> $DIR/wrong_self_conventions_mut.rs:15:24 + | +LL | pub fn to_many(&mut self) -> Option<&mut [T]> { + | ^^^^^^^^^ + | + = note: `-D clippy::wrong-self-convention` implied by `-D warnings` + = help: consider choosing a less ambiguous name + +error: methods called like this: (`to_*` and `*_mut`) usually take self by mutable reference + --> $DIR/wrong_self_conventions_mut.rs:23:28 + | +LL | pub fn to_many_mut(&self) -> Option<&[T]> { + | ^^^^^ + | + = help: consider choosing a less ambiguous name + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From 11d2af7e9694e8e63000e33f77d4926e936e3822 Mon Sep 17 00:00:00 2001 From: Yusuke Tanaka Date: Fri, 12 Mar 2021 20:46:46 +0900 Subject: Improve suggestion and make it work for macros --- clippy_lints/src/if_then_some_else_none.rs | 18 ++++++++++++---- tests/ui/if_then_some_else_none.rs | 16 ++++++++++++++ tests/ui/if_then_some_else_none.stderr | 34 ++++++++++++++++++++++++++++-- 3 files changed, 62 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index 569a7f06f95..a527f51b1fd 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -82,12 +82,22 @@ impl LateLintPass<'_> for IfThenSomeElseNone { if let ExprKind::Path(ref els_call_qpath) = els_expr.kind; if utils::match_qpath(els_call_qpath, &utils::paths::OPTION_NONE); then { - let cond_snip = utils::snippet(cx, cond.span, "[condition]"); - let arg_snip = utils::snippet(cx, then_arg.span, ""); + let cond_snip = utils::snippet_with_macro_callsite(cx, cond.span, "[condition]"); + let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) { + format!("({})", cond_snip) + } else { + cond_snip.into_owned() + }; + let arg_snip = utils::snippet_with_macro_callsite(cx, then_arg.span, ""); + let closure_body = if then_block.stmts.is_empty() { + arg_snip.into_owned() + } else { + format!("{{ /* snippet */ {} }}", arg_snip) + }; let help = format!( - "consider using `bool::then` like: `{}.then(|| {{ /* snippet */ {} }})`", + "consider using `bool::then` like: `{}.then(|| {})`", cond_snip, - arg_snip, + closure_body, ); utils::span_lint_and_help( cx, diff --git a/tests/ui/if_then_some_else_none.rs b/tests/ui/if_then_some_else_none.rs index 14a5fe76245..54789bf9320 100644 --- a/tests/ui/if_then_some_else_none.rs +++ b/tests/ui/if_then_some_else_none.rs @@ -10,6 +10,22 @@ fn main() { None }; + // Should issue an error when macros are used. + let _ = if matches!(true, true) { + println!("true!"); + Some(matches!(true, false)) + } else { + None + }; + + // Should issue an error. Binary expression `o < 32` should be parenthesized. + let x = Some(5); + let _ = x.and_then(|o| if o < 32 { Some(o) } else { None }); + + // Should issue an error. Unary expression `!x` should be parenthesized. + let x = true; + let _ = if !x { Some(0) } else { None }; + // Should not issue an error since the `else` block has a statement besides `None`. let _ = if foo() { println!("true!"); diff --git a/tests/ui/if_then_some_else_none.stderr b/tests/ui/if_then_some_else_none.stderr index 722c52b1cb4..8cb22d569f4 100644 --- a/tests/ui/if_then_some_else_none.stderr +++ b/tests/ui/if_then_some_else_none.stderr @@ -14,7 +14,37 @@ LL | | }; = help: consider using `bool::then` like: `foo().then(|| { /* snippet */ "foo" })` error: this could be simplified with `bool::then` - --> $DIR/if_then_some_else_none.rs:66:13 + --> $DIR/if_then_some_else_none.rs:14:13 + | +LL | let _ = if matches!(true, true) { + | _____________^ +LL | | println!("true!"); +LL | | Some(matches!(true, false)) +LL | | } else { +LL | | None +LL | | }; + | |_____^ + | + = help: consider using `bool::then` like: `matches!(true, true).then(|| { /* snippet */ matches!(true, false) })` + +error: this could be simplified with `bool::then` + --> $DIR/if_then_some_else_none.rs:23:28 + | +LL | let _ = x.and_then(|o| if o < 32 { Some(o) } else { None }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using `bool::then` like: `(o < 32).then(|| o)` + +error: this could be simplified with `bool::then` + --> $DIR/if_then_some_else_none.rs:27:13 + | +LL | let _ = if !x { Some(0) } else { None }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using `bool::then` like: `(!x).then(|| 0)` + +error: this could be simplified with `bool::then` + --> $DIR/if_then_some_else_none.rs:82:13 | LL | let _ = if foo() { | _____________^ @@ -27,5 +57,5 @@ LL | | }; | = help: consider using `bool::then` like: `foo().then(|| { /* snippet */ 150 })` -error: aborting due to 2 previous errors +error: aborting due to 5 previous errors -- cgit 1.4.1-3-g733a5 From 6bc5fe4a88d1c6dce50c7be169f8be5d77d4f7e7 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Fri, 12 Mar 2021 20:40:23 +0100 Subject: inconsistent_struct_constructor: try to make message and lint description a bit clearer --- clippy_lints/src/inconsistent_struct_constructor.rs | 12 +++++++----- tests/ui/inconsistent_struct_constructor.stderr | 4 ++-- 2 files changed, 9 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/inconsistent_struct_constructor.rs b/clippy_lints/src/inconsistent_struct_constructor.rs index 4f35e13c85a..49c17a12102 100644 --- a/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/clippy_lints/src/inconsistent_struct_constructor.rs @@ -10,8 +10,9 @@ use if_chain::if_chain; use crate::utils::{snippet, span_lint_and_sugg}; declare_clippy_lint! { - /// **What it does:** Checks for struct constructors where the order of the field init - /// shorthand in the constructor is inconsistent with the order in the struct definition. + /// **What it does:** Checks for struct constructors where all fields are shorthand and + /// the order of the field init shorthand in the constructor is inconsistent + /// with the order in the struct definition. /// /// **Why is this bad?** Since the order of fields in a constructor doesn't affect the /// resulted instance as the below example indicates, @@ -25,11 +26,11 @@ declare_clippy_lint! { /// let x = 1; /// let y = 2; /// - /// // This assertion never fails. + /// // This assertion never fails: /// assert_eq!(Foo { x, y }, Foo { y, x }); /// ``` /// - /// inconsistent order means nothing and just decreases readability and consistency. + /// inconsistent order can be confusing and decreases readability and consistency. /// /// **Known problems:** None. /// @@ -42,6 +43,7 @@ declare_clippy_lint! { /// } /// let x = 1; /// let y = 2; + /// /// Foo { y, x }; /// ``` /// @@ -107,7 +109,7 @@ impl LateLintPass<'_> for InconsistentStructConstructor { cx, INCONSISTENT_STRUCT_CONSTRUCTOR, expr.span, - "inconsistent struct constructor", + "struct constructor field order is inconsistent with struct definition field order", "try", sugg, Applicability::MachineApplicable, diff --git a/tests/ui/inconsistent_struct_constructor.stderr b/tests/ui/inconsistent_struct_constructor.stderr index d7abe44f254..d021bb19579 100644 --- a/tests/ui/inconsistent_struct_constructor.stderr +++ b/tests/ui/inconsistent_struct_constructor.stderr @@ -1,4 +1,4 @@ -error: inconsistent struct constructor +error: struct constructor field order is inconsistent with struct definition field order --> $DIR/inconsistent_struct_constructor.rs:25:9 | LL | Foo { y, x, z }; @@ -6,7 +6,7 @@ LL | Foo { y, x, z }; | = note: `-D clippy::inconsistent-struct-constructor` implied by `-D warnings` -error: inconsistent struct constructor +error: struct constructor field order is inconsistent with struct definition field order --> $DIR/inconsistent_struct_constructor.rs:43:9 | LL | / Foo { -- cgit 1.4.1-3-g733a5 From c86ba7f92d9627d2170827c07449542c7c0f7d77 Mon Sep 17 00:00:00 2001 From: Mateusz Gacek <96mateusz.gacek@gmail.com> Date: Mon, 1 Mar 2021 16:49:52 +0100 Subject: mem_replace_with_default: recognize some std library ctors --- clippy_lints/src/mem_replace.rs | 17 ++++++++- clippy_utils/src/paths.rs | 8 +++++ tests/ui/mem_replace.fixed | 29 +++++++++++++++ tests/ui/mem_replace.rs | 29 +++++++++++++++ tests/ui/mem_replace.stderr | 78 +++++++++++++++++++++++++++++++++++++---- 5 files changed, 154 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index 19087b02077..2f71e22b4fc 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -200,7 +200,22 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr< if !in_external_macro(cx.tcx.sess, expr_span); if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind; if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id(); - if match_def_path(cx, repl_def_id, &paths::DEFAULT_TRAIT_METHOD); + + let defaults = &[ + paths::DEFAULT_TRAIT_METHOD.as_ref(), + paths::STRING_NEW.as_ref(), + paths::VEC_NEW.as_ref(), + paths::VEC_DEQUE_NEW.as_ref(), + paths::LINKED_LIST_NEW.as_ref(), + paths::HASHMAP_NEW.as_ref(), + paths::BTREEMAP_NEW.as_ref(), + paths::HASHSET_NEW.as_ref(), + paths::BTREESET_NEW.as_ref(), + paths::BINARY_HEAP_NEW.as_ref(), + ]; + + if defaults.iter().any(|x| match_def_path(cx, repl_def_id, &x)); + then { span_lint_and_then( cx, diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 560614efc74..8234ab7282c 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -11,10 +11,13 @@ 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"]; pub const BINARY_HEAP: [&str; 4] = ["alloc", "collections", "binary_heap", "BinaryHeap"]; +pub const BINARY_HEAP_NEW: [&str; 5] = ["alloc", "collections", "binary_heap", "BinaryHeap", "new"]; pub const BORROW_TRAIT: [&str; 3] = ["core", "borrow", "Borrow"]; pub const BTREEMAP: [&str; 5] = ["alloc", "collections", "btree", "map", "BTreeMap"]; +pub const BTREEMAP_NEW: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "new"]; pub const BTREEMAP_ENTRY: [&str; 6] = ["alloc", "collections", "btree", "map", "entry", "Entry"]; pub const BTREESET: [&str; 5] = ["alloc", "collections", "btree", "set", "BTreeSet"]; +pub const BTREESET_NEW: [&str; 6] = ["alloc", "collections", "btree", "set", "BTreeSet", "new"]; pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"]; pub const CMP_MAX: [&str; 3] = ["core", "cmp", "max"]; pub const CMP_MIN: [&str; 3] = ["core", "cmp", "min"]; @@ -46,8 +49,10 @@ pub const FROM_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "From pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; pub const HASH: [&str; 3] = ["core", "hash", "Hash"]; pub const HASHMAP: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"]; +pub const HASHMAP_NEW: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "new"]; pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"]; pub const HASHSET: [&str; 5] = ["std", "collections", "hash", "set", "HashSet"]; +pub const HASHSET_NEW: [&str; 6] = ["std", "collections", "hash", "set", "HashSet", "new"]; #[cfg(feature = "internal-lints")] pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"]; #[cfg(feature = "internal-lints")] @@ -67,6 +72,7 @@ pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"]; #[cfg(feature = "internal-lints")] pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"]; pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"]; +pub const LINKED_LIST_NEW: [&str; 5] = ["alloc", "collections", "linked_list", "LinkedList", "new"]; #[cfg(feature = "internal-lints")] pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"]; pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"]; @@ -134,6 +140,7 @@ pub const STD_CONVERT_IDENTITY: [&str; 3] = ["std", "convert", "identity"]; pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"]; pub const STD_MEM_TRANSMUTE: [&str; 3] = ["std", "mem", "transmute"]; pub const STD_PTR_NULL: [&str; 3] = ["std", "ptr", "null"]; +pub const STRING_NEW: [&str; 4] = ["alloc", "string", "String", "new"]; pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"]; pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"]; pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "", "ends_with"]; @@ -161,6 +168,7 @@ pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"]; pub const VEC_AS_MUT_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_mut_slice"]; pub const VEC_AS_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_slice"]; pub const VEC_DEQUE: [&str; 4] = ["alloc", "collections", "vec_deque", "VecDeque"]; +pub const VEC_DEQUE_NEW: [&str; 5] = ["alloc", "collections", "vec_deque", "VecDeque", "new"]; 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"]; diff --git a/tests/ui/mem_replace.fixed b/tests/ui/mem_replace.fixed index 54e962e7116..3b6224254a0 100644 --- a/tests/ui/mem_replace.fixed +++ b/tests/ui/mem_replace.fixed @@ -7,6 +7,7 @@ clippy::mem_replace_with_default )] +use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque}; use std::mem; fn replace_option_with_none() { @@ -19,9 +20,37 @@ fn replace_option_with_none() { fn replace_with_default() { let mut s = String::from("foo"); let _ = std::mem::take(&mut s); + let s = &mut String::from("foo"); let _ = std::mem::take(s); let _ = std::mem::take(s); + + let mut v = vec![123]; + let _ = std::mem::take(&mut v); + let _ = std::mem::take(&mut v); + let _ = std::mem::take(&mut v); + let _ = std::mem::take(&mut v); + + let mut hash_map: HashMap = HashMap::new(); + let _ = std::mem::take(&mut hash_map); + + let mut btree_map: BTreeMap = BTreeMap::new(); + let _ = std::mem::take(&mut btree_map); + + let mut vd: VecDeque = VecDeque::new(); + let _ = std::mem::take(&mut vd); + + let mut hash_set: HashSet<&str> = HashSet::new(); + let _ = std::mem::take(&mut hash_set); + + let mut btree_set: BTreeSet<&str> = BTreeSet::new(); + let _ = std::mem::take(&mut btree_set); + + let mut list: LinkedList = LinkedList::new(); + let _ = std::mem::take(&mut list); + + let mut binary_heap: BinaryHeap = BinaryHeap::new(); + let _ = std::mem::take(&mut binary_heap); } fn main() { diff --git a/tests/ui/mem_replace.rs b/tests/ui/mem_replace.rs index 60f52781071..0a36db9e921 100644 --- a/tests/ui/mem_replace.rs +++ b/tests/ui/mem_replace.rs @@ -7,6 +7,7 @@ clippy::mem_replace_with_default )] +use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque}; use std::mem; fn replace_option_with_none() { @@ -19,9 +20,37 @@ fn replace_option_with_none() { fn replace_with_default() { let mut s = String::from("foo"); let _ = std::mem::replace(&mut s, String::default()); + let s = &mut String::from("foo"); let _ = std::mem::replace(s, String::default()); let _ = std::mem::replace(s, Default::default()); + + let mut v = vec![123]; + let _ = std::mem::replace(&mut v, Vec::default()); + let _ = std::mem::replace(&mut v, Default::default()); + let _ = std::mem::replace(&mut v, Vec::new()); + let _ = std::mem::replace(&mut v, vec![]); + + let mut hash_map: HashMap = HashMap::new(); + let _ = std::mem::replace(&mut hash_map, HashMap::new()); + + let mut btree_map: BTreeMap = BTreeMap::new(); + let _ = std::mem::replace(&mut btree_map, BTreeMap::new()); + + let mut vd: VecDeque = VecDeque::new(); + let _ = std::mem::replace(&mut vd, VecDeque::new()); + + let mut hash_set: HashSet<&str> = HashSet::new(); + let _ = std::mem::replace(&mut hash_set, HashSet::new()); + + let mut btree_set: BTreeSet<&str> = BTreeSet::new(); + let _ = std::mem::replace(&mut btree_set, BTreeSet::new()); + + let mut list: LinkedList = LinkedList::new(); + let _ = std::mem::replace(&mut list, LinkedList::new()); + + let mut binary_heap: BinaryHeap = BinaryHeap::new(); + let _ = std::mem::replace(&mut binary_heap, BinaryHeap::new()); } fn main() { diff --git a/tests/ui/mem_replace.stderr b/tests/ui/mem_replace.stderr index 245d33aa4f2..f8aa1538bff 100644 --- a/tests/ui/mem_replace.stderr +++ b/tests/ui/mem_replace.stderr @@ -1,5 +1,5 @@ error: replacing an `Option` with `None` - --> $DIR/mem_replace.rs:14:13 + --> $DIR/mem_replace.rs:15:13 | LL | let _ = mem::replace(&mut an_option, None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()` @@ -7,13 +7,13 @@ LL | let _ = mem::replace(&mut an_option, None); = note: `-D clippy::mem-replace-option-with-none` implied by `-D warnings` error: replacing an `Option` with `None` - --> $DIR/mem_replace.rs:16:13 + --> $DIR/mem_replace.rs:17:13 | LL | let _ = mem::replace(an_option, None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:21:13 + --> $DIR/mem_replace.rs:22:13 | LL | let _ = std::mem::replace(&mut s, String::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)` @@ -21,16 +21,82 @@ LL | let _ = std::mem::replace(&mut s, String::default()); = note: `-D clippy::mem-replace-with-default` implied by `-D warnings` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:23:13 + --> $DIR/mem_replace.rs:25:13 | LL | let _ = std::mem::replace(s, String::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> $DIR/mem_replace.rs:24:13 + --> $DIR/mem_replace.rs:26:13 | LL | let _ = std::mem::replace(s, Default::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)` -error: aborting due to 5 previous errors +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:29:13 + | +LL | let _ = std::mem::replace(&mut v, Vec::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` + +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:30:13 + | +LL | let _ = std::mem::replace(&mut v, Default::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` + +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:31:13 + | +LL | let _ = std::mem::replace(&mut v, Vec::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` + +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:32:13 + | +LL | let _ = std::mem::replace(&mut v, vec![]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)` + +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:35:13 + | +LL | let _ = std::mem::replace(&mut hash_map, HashMap::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut hash_map)` + +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:38:13 + | +LL | let _ = std::mem::replace(&mut btree_map, BTreeMap::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut btree_map)` + +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:41:13 + | +LL | let _ = std::mem::replace(&mut vd, VecDeque::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut vd)` + +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:44:13 + | +LL | let _ = std::mem::replace(&mut hash_set, HashSet::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut hash_set)` + +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:47:13 + | +LL | let _ = std::mem::replace(&mut btree_set, BTreeSet::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut btree_set)` + +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:50:13 + | +LL | let _ = std::mem::replace(&mut list, LinkedList::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut list)` + +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:53:13 + | +LL | let _ = std::mem::replace(&mut binary_heap, BinaryHeap::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut binary_heap)` + +error: aborting due to 16 previous errors -- cgit 1.4.1-3-g733a5 From 6d2236f5036e89b69bdba4a364d73d159ac74187 Mon Sep 17 00:00:00 2001 From: iobtl Date: Sat, 13 Mar 2021 16:54:59 +0800 Subject: replace span_lint with span_lint_and_sugg along with error message --- clippy_lints/src/casts/unnecessary_cast.rs | 7 +++++-- tests/ui/unnecessary_cast.stderr | 6 +++--- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index fa2a07ef1da..171cac091c2 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -7,7 +7,7 @@ use rustc_middle::ty::{self, FloatTy, InferTy, Ty}; use if_chain::if_chain; -use crate::utils::{numeric_literal::NumericLiteral, snippet_opt, span_lint, span_lint_and_sugg}; +use crate::utils::{numeric_literal::NumericLiteral, snippet_opt, span_lint_and_sugg}; use super::UNNECESSARY_CAST; @@ -46,7 +46,7 @@ pub(super) fn check( LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => {}, _ => { if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) { - span_lint( + span_lint_and_sugg( cx, UNNECESSARY_CAST, expr.span, @@ -54,6 +54,9 @@ pub(super) fn check( "casting to the same type is unnecessary (`{}` -> `{}`)", cast_from, cast_to ), + "try", + literal_str, + Applicability::MachineApplicable, ); return true; } diff --git a/tests/ui/unnecessary_cast.stderr b/tests/ui/unnecessary_cast.stderr index 8981d13e8ea..87261cd8333 100644 --- a/tests/ui/unnecessary_cast.stderr +++ b/tests/ui/unnecessary_cast.stderr @@ -2,7 +2,7 @@ error: casting to the same type is unnecessary (`i32` -> `i32`) --> $DIR/unnecessary_cast.rs:6:5 | LL | 1i32 as i32; - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ help: try: `1i32` | = note: `-D clippy::unnecessary-cast` implied by `-D warnings` @@ -10,13 +10,13 @@ error: casting to the same type is unnecessary (`f32` -> `f32`) --> $DIR/unnecessary_cast.rs:7:5 | LL | 1f32 as f32; - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ help: try: `1f32` error: casting to the same type is unnecessary (`bool` -> `bool`) --> $DIR/unnecessary_cast.rs:8:5 | LL | false as bool; - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ help: try: `false` error: aborting due to 3 previous errors -- cgit 1.4.1-3-g733a5 From a261bc5fadc3c72fd8828d64c4281097b2e7c0b0 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 2 Mar 2021 13:32:56 -0500 Subject: Make `explicit_deref_methods` check for multiple deref calls Fix suggestion for `explicit_deref_methods`. Sometimes `&**` is needed, sometimes nothing is needed. Allow `explicit_deref_methods` to trigger in a few new contexts. `explicit_deref_methods` will now consider ufcs calls --- clippy_lints/src/dereference.rs | 369 +++++++++++++++++++++++++++------ clippy_lints/src/lib.rs | 2 +- clippy_lints/src/manual_map.rs | 8 +- clippy_lints/src/redundant_deref.rs | 63 ++++++ clippy_utils/src/lib.rs | 82 ++++++-- tests/ui/dereference.fixed | 93 --------- tests/ui/dereference.rs | 93 --------- tests/ui/dereference.stderr | 70 ------- tests/ui/explicit_deref_methods.fixed | 93 +++++++++ tests/ui/explicit_deref_methods.rs | 93 +++++++++ tests/ui/explicit_deref_methods.stderr | 70 +++++++ 11 files changed, 687 insertions(+), 349 deletions(-) create mode 100644 clippy_lints/src/redundant_deref.rs delete mode 100644 tests/ui/dereference.fixed delete mode 100644 tests/ui/dereference.rs delete mode 100644 tests/ui/dereference.stderr create mode 100644 tests/ui/explicit_deref_methods.fixed create mode 100644 tests/ui/explicit_deref_methods.rs create mode 100644 tests/ui/explicit_deref_methods.stderr (limited to 'tests') diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index b5fb51af1c7..a33634ca34e 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1,11 +1,13 @@ -use crate::utils::{get_parent_expr, implements_trait, snippet, span_lint_and_sugg}; -use if_chain::if_chain; -use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX, PREC_PREFIX}; +use crate::utils::{ + get_node_span, get_parent_node, in_macro, is_allowed, peel_mid_ty_refs, snippet_with_context, span_lint_and_sugg, +}; +use rustc_ast::util::parser::PREC_PREFIX; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind}; +use rustc_hir::{BorrowKind, Destination, Expr, ExprKind, HirId, MatchSource, Mutability, Node, UnOp}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Span; +use rustc_middle::ty::{self, adjustment::Adjustment, Ty, TyCtxt, TyS, TypeckResults}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{symbol::sym, Span}; declare_clippy_lint! { /// **What it does:** Checks for explicit `deref()` or `deref_mut()` method calls. @@ -34,76 +36,313 @@ declare_clippy_lint! { "Explicit use of deref or deref_mut method while not in a method chain." } -declare_lint_pass!(Dereferencing => [ - EXPLICIT_DEREF_METHODS +impl_lint_pass!(Dereferencing => [ + EXPLICIT_DEREF_METHODS, ]); +#[derive(Default)] +pub struct Dereferencing { + state: Option<(State, StateData)>, + + // While parsing a `deref` method call in ufcs form, the path to the function is itself an + // expression. This is to store the id of that expression so it can be skipped when + // `check_expr` is called for it. + skip_expr: Option, +} + +struct StateData { + /// Span of the top level expression + span: Span, + /// The required mutability + target_mut: Mutability, +} + +enum State { + // Any number of deref method calls. + DerefMethod { + // The number of calls in a sequence which changed the referenced type + ty_changed_count: usize, + is_final_ufcs: bool, + }, +} + +// A reference operation considered by this lint pass +enum RefOp { + Method, + Deref, + AddrOf, +} + impl<'tcx> LateLintPass<'tcx> for Dereferencing { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - if !expr.span.from_expansion(); - if let ExprKind::MethodCall(ref method_name, _, ref args, _) = &expr.kind; - if args.len() == 1; - - then { - if let Some(parent_expr) = get_parent_expr(cx, expr) { - // Check if we have the whole call chain here - if let ExprKind::MethodCall(..) = parent_expr.kind { - return; - } - // Check for Expr that we don't want to be linted - let precedence = parent_expr.precedence(); - match precedence { - // Lint a Call is ok though - ExprPrecedence::Call | ExprPrecedence::AddrOf => (), - _ => { - if precedence.order() >= PREC_PREFIX && precedence.order() <= PREC_POSTFIX { - return; - } - } + // Skip path expressions from deref calls. e.g. `Deref::deref(e)` + if Some(expr.hir_id) == self.skip_expr.take() { + return; + } + + // Stop processing sub expressions when a macro call is seen + if in_macro(expr.span) { + if let Some((state, data)) = self.state.take() { + report(cx, expr, state, data); + } + return; + } + + let typeck = cx.typeck_results(); + let (kind, sub_expr) = if let Some(x) = try_parse_ref_op(cx.tcx, typeck, expr) { + x + } else { + // The whole chain of reference operations has been seen + if let Some((state, data)) = self.state.take() { + report(cx, expr, state, data); + } + return; + }; + + match (self.state.take(), kind) { + (None, kind) => { + let parent = get_parent_node(cx.tcx, expr.hir_id); + // This is an odd case. The expression is a macro argument, but the top level + // address of expression is inserted by the compiler. + if matches!(kind, RefOp::AddrOf) && parent.and_then(get_node_span).map_or(false, in_macro) { + return; + } + + let expr_adjustments = find_adjustments(cx.tcx, typeck, expr); + let expr_ty = typeck.expr_ty(expr); + let target_mut = + if let ty::Ref(_, _, mutability) = *expr_adjustments.last().map_or(expr_ty, |a| a.target).kind() { + mutability + } else { + Mutability::Not + }; + + match kind { + RefOp::Method + if !is_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id) + && is_linted_explicit_deref_position(parent, expr.hir_id) => + { + self.state = Some(( + State::DerefMethod { + ty_changed_count: if deref_method_same_type(expr_ty, typeck.expr_ty(sub_expr)) { + 0 + } else { + 1 + }, + is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)), + }, + StateData { + span: expr.span, + target_mut, + }, + )); } + _ => (), } - let name = method_name.ident.as_str(); - lint_deref(cx, &*name, &args[0], args[0].span, expr.span); - } + }, + (Some((State::DerefMethod { ty_changed_count, .. }, data)), RefOp::Method) => { + self.state = Some(( + State::DerefMethod { + ty_changed_count: if deref_method_same_type(typeck.expr_ty(expr), typeck.expr_ty(sub_expr)) { + ty_changed_count + } else { + ty_changed_count + 1 + }, + is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)), + }, + data, + )); + }, + + (Some((state, data)), _) => report(cx, expr, state, data), } } } -fn lint_deref(cx: &LateContext<'_>, method_name: &str, call_expr: &Expr<'_>, var_span: Span, expr_span: Span) { - match method_name { - "deref" => { - let impls_deref_trait = cx.tcx.lang_items().deref_trait().map_or(false, |id| { - implements_trait(cx, cx.typeck_results().expr_ty(&call_expr), id, &[]) - }); - if impls_deref_trait { - span_lint_and_sugg( - cx, - EXPLICIT_DEREF_METHODS, - expr_span, - "explicit deref method call", - "try this", - format!("&*{}", &snippet(cx, var_span, "..")), - Applicability::MachineApplicable, - ); - } +fn try_parse_ref_op( + tcx: TyCtxt<'tcx>, + typeck: &'tcx TypeckResults<'_>, + expr: &'tcx Expr<'_>, +) -> Option<(RefOp, &'tcx Expr<'tcx>)> { + let (def_id, arg) = match expr.kind { + ExprKind::MethodCall(_, _, [arg], _) => (typeck.type_dependent_def_id(expr.hir_id)?, arg), + ExprKind::Call( + Expr { + kind: ExprKind::Path(path), + hir_id, + .. + }, + [arg], + ) => (typeck.qpath_res(path, *hir_id).opt_def_id()?, arg), + ExprKind::Unary(UnOp::Deref, sub_expr) if !typeck.expr_ty(sub_expr).is_unsafe_ptr() => { + return Some((RefOp::Deref, sub_expr)); }, - "deref_mut" => { - let impls_deref_mut_trait = cx.tcx.lang_items().deref_mut_trait().map_or(false, |id| { - implements_trait(cx, cx.typeck_results().expr_ty(&call_expr), id, &[]) - }); - if impls_deref_mut_trait { - span_lint_and_sugg( - cx, - EXPLICIT_DEREF_METHODS, - expr_span, - "explicit deref_mut method call", - "try this", - format!("&mut *{}", &snippet(cx, var_span, "..")), - Applicability::MachineApplicable, - ); - } + ExprKind::AddrOf(BorrowKind::Ref, _, sub_expr) => return Some((RefOp::AddrOf, sub_expr)), + _ => return None, + }; + (tcx.is_diagnostic_item(sym::deref_method, def_id) + || tcx.trait_of_item(def_id)? == tcx.lang_items().deref_mut_trait()?) + .then(|| (RefOp::Method, arg)) +} + +// Checks whether the type for a deref call actually changed the type, not just the mutability of +// the reference. +fn deref_method_same_type(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool { + match (result_ty.kind(), arg_ty.kind()) { + (ty::Ref(_, result_ty, _), ty::Ref(_, arg_ty, _)) => TyS::same_type(result_ty, arg_ty), + + // The result type for a deref method is always a reference + // Not matching the previous pattern means the argument type is not a reference + // This means that the type did change + _ => false, + } +} + +// Adjustments are sometimes made in the parent block rather than the expression itself. +fn find_adjustments( + tcx: TyCtxt<'tcx>, + typeck: &'tcx TypeckResults<'_>, + expr: &'tcx Expr<'_>, +) -> &'tcx [Adjustment<'tcx>] { + let map = tcx.hir(); + let mut iter = map.parent_iter(expr.hir_id); + let mut prev = expr; + + loop { + match typeck.expr_adjustments(prev) { + [] => (), + a => break a, + }; + + match iter.next().map(|(_, x)| x) { + Some(Node::Block(_)) => { + if let Some((_, Node::Expr(e))) = iter.next() { + prev = e; + } else { + // This shouldn't happen. Blocks are always contained in an expression. + break &[]; + } + }, + Some(Node::Expr(&Expr { + kind: ExprKind::Break(Destination { target_id: Ok(id), .. }, _), + .. + })) => { + if let Some(Node::Expr(e)) = map.find(id) { + prev = e; + iter = map.parent_iter(id); + continue; + } + // This shouldn't happen. The destination should definitely exist at this point. + break &[]; + }, + _ => break &[], + } + } +} + +// Checks whether the parent node is a suitable context for switching from a deref method to the +// deref operator. +fn is_linted_explicit_deref_position(parent: Option>, child_id: HirId) -> bool { + let parent = match parent { + Some(Node::Expr(e)) => e, + _ => return true, + }; + match parent.kind { + // Leave deref calls in the middle of a method chain. + // e.g. x.deref().foo() + ExprKind::MethodCall(_, _, [self_arg, ..], _) if self_arg.hir_id == child_id => false, + + // Leave deref calls resulting in a called function + // e.g. (x.deref())() + ExprKind::Call(func_expr, _) if func_expr.hir_id == child_id => false, + + // Makes an ugly suggestion + // e.g. *x.deref() => *&*x + ExprKind::Unary(UnOp::Deref, _) + // Postfix expressions would require parens + | ExprKind::Match(_, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar) + | ExprKind::Field(..) + | ExprKind::Index(..) + | ExprKind::Err => false, + + ExprKind::Box(..) + | ExprKind::ConstBlock(..) + | ExprKind::Array(_) + | ExprKind::Call(..) + | ExprKind::MethodCall(..) + | ExprKind::Tup(..) + | ExprKind::Binary(..) + | ExprKind::Unary(..) + | ExprKind::Lit(..) + | ExprKind::Cast(..) + | ExprKind::Type(..) + | ExprKind::DropTemps(..) + | ExprKind::If(..) + | ExprKind::Loop(..) + | ExprKind::Match(..) + | ExprKind::Closure(..) + | ExprKind::Block(..) + | ExprKind::Assign(..) + | ExprKind::AssignOp(..) + | ExprKind::Path(..) + | ExprKind::AddrOf(..) + | ExprKind::Break(..) + | ExprKind::Continue(..) + | ExprKind::Ret(..) + | ExprKind::InlineAsm(..) + | ExprKind::LlvmInlineAsm(..) + | ExprKind::Struct(..) + | ExprKind::Repeat(..) + | ExprKind::Yield(..) => true, + } +} + +#[allow(clippy::needless_pass_by_value)] +fn report(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: StateData) { + match state { + State::DerefMethod { + ty_changed_count, + is_final_ufcs, + } => { + let mut app = Applicability::MachineApplicable; + let (expr_str, expr_is_macro_call) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app); + let ty = cx.typeck_results().expr_ty(expr); + let (_, ref_count) = peel_mid_ty_refs(ty); + let deref_str = if ty_changed_count >= ref_count && ref_count != 0 { + // a deref call changing &T -> &U requires two deref operators the first time + // this occurs. One to remove the reference, a second to call the deref impl. + "*".repeat(ty_changed_count + 1) + } else { + "*".repeat(ty_changed_count) + }; + let addr_of_str = if ty_changed_count < ref_count { + // Check if a reborrow from &mut T -> &T is required. + if data.target_mut == Mutability::Not && matches!(ty.kind(), ty::Ref(_, _, Mutability::Mut)) { + "&*" + } else { + "" + } + } else if data.target_mut == Mutability::Mut { + "&mut " + } else { + "&" + }; + + let expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence().order() < PREC_PREFIX { + format!("({})", expr_str) + } else { + expr_str.into_owned() + }; + + span_lint_and_sugg( + cx, + EXPLICIT_DEREF_METHODS, + data.span, + "explicit `deref` method call", + "try this", + format!("{}{}{}", addr_of_str, deref_str, expr_str), + app, + ); }, - _ => (), } } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 04e151df8e8..bf5688aa95c 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1241,7 +1241,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box verbose_file_reads::VerboseFileReads); store.register_late_pass(|| box redundant_pub_crate::RedundantPubCrate::default()); store.register_late_pass(|| box unnamed_address::UnnamedAddress); - store.register_late_pass(|| box dereference::Dereferencing); + store.register_late_pass(|| box dereference::Dereferencing::default()); store.register_late_pass(|| box option_if_let_else::OptionIfLetElse); store.register_late_pass(|| box future_not_send::FutureNotSend); store.register_late_pass(|| box if_let_mutex::IfLetMutex); diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs index ac1d51e1993..ea4cedc6754 100644 --- a/clippy_lints/src/manual_map.rs +++ b/clippy_lints/src/manual_map.rs @@ -129,7 +129,7 @@ impl LateLintPass<'_> for ManualMap { // Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or // it's being passed by value. let scrutinee = peel_hir_expr_refs(scrutinee).0; - let scrutinee_str = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app); + let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app); let scrutinee_str = if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX { format!("({})", scrutinee_str) @@ -160,7 +160,7 @@ impl LateLintPass<'_> for ManualMap { "|{}{}| {}", annotation, some_binding, - snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app) + snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app).0 ) }, } @@ -168,8 +168,8 @@ impl LateLintPass<'_> for ManualMap { // TODO: handle explicit reference annotations. format!( "|{}| {}", - snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app), - snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app) + snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0, + snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app).0 ) } else { // Refutable bindings and mixed reference annotations can't be handled by `map`. diff --git a/clippy_lints/src/redundant_deref.rs b/clippy_lints/src/redundant_deref.rs new file mode 100644 index 00000000000..029583720d2 --- /dev/null +++ b/clippy_lints/src/redundant_deref.rs @@ -0,0 +1,63 @@ +// use crate::utils::{get_parent_expr, snippet_with_applicability, span_lint_and_sugg}; +// use if_chain::if_chain; +// use rustc_errors::Applicability; +// use rustc_hir::{Expr, ExprKind, UnOp}; +// use rustc_lint::{LateContext, LateLintPass, LintContext}; +// use rustc_middle::lint::in_external_macro; +// use rustc_session::{declare_lint_pass, declare_tool_lint}; + +// declare_clippy_lint! { +// /// **What it does:** Checks for uses of the dereference operator which would be covered by +// /// auto-dereferencing. +// /// +// /// **Why is this bad?** This unnecessarily complicates the code. +// /// +// /// **Known problems:** None. +// /// +// /// **Example:** +// /// +// /// ```rust +// /// fn foo(_: &str) {} +// /// foo(&*String::new()) +// /// ``` +// /// Use instead: +// /// ```rust +// /// fn foo(_: &str) {} +// /// foo(&String::new()) +// /// ``` +// pub REDUNDANT_DEREF, +// style, +// "default lint description" +// } + +// declare_lint_pass!(RedundantDeref => [REDUNDANT_DEREF]); + +// impl LateLintPass<'_> for RedundantDeref { +// fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { +// if_chain! { +// if let ExprKind::AddrOf(_, _, addr_expr) = expr.kind; +// if let ExprKind::Unary(UnOp::UnDeref, deref_expr) = addr_expr.kind; +// if !in_external_macro(cx.sess(), expr.span); +// if let Some(parent_expr) = get_parent_expr(cx, expr); +// if match parent_expr.kind { +// ExprKind::Call(func, _) => func.hir_id != expr.hir_id, +// ExprKind::MethodCall(..) => true, +// _ => false, +// }; +// if !cx.typeck_results().expr_ty(deref_expr).is_unsafe_ptr(); +// then { +// let mut app = Applicability::MachineApplicable; +// let sugg = format!("&{}", snippet_with_applicability(cx, deref_expr.span, "_", &mut app)); +// span_lint_and_sugg( +// cx, +// REDUNDANT_DEREF, +// expr.span, +// "redundant dereference", +// "remove the dereference", +// sugg, +// app, +// ); +// } +// } +// } +// } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 3dad21d2028..2ac2298ff74 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -61,11 +61,11 @@ use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; -use rustc_hir::Node; use rustc_hir::{ - def, Arm, Block, Body, Constness, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, Item, - ItemKind, LangItem, MatchSource, Param, Pat, PatKind, Path, PathSegment, QPath, TraitItem, TraitItemKind, TraitRef, - TyKind, Unsafety, + def, Arm, Block, Body, Constness, CrateItem, Expr, ExprKind, FnDecl, ForeignItem, GenericArgs, GenericParam, HirId, + Impl, ImplItem, ImplItemKind, Item, ItemKind, LangItem, Lifetime, Local, MacroDef, MatchSource, Node, Param, Pat, + PatKind, Path, PathSegment, QPath, Stmt, StructField, TraitItem, TraitItemKind, TraitRef, TyKind, Unsafety, + Variant, Visibility, }; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, Level, Lint, LintContext}; @@ -78,7 +78,7 @@ use rustc_session::Session; use rustc_span::hygiene::{self, ExpnKind, MacroKind}; use rustc_span::source_map::original_sp; use rustc_span::sym; -use rustc_span::symbol::{kw, Symbol}; +use rustc_span::symbol::{kw, Ident, Symbol}; use rustc_span::{BytePos, Pos, Span, SyntaxContext, DUMMY_SP}; use rustc_target::abi::Integer; use rustc_trait_selection::traits::query::normalize::AtExt; @@ -852,26 +852,31 @@ pub fn snippet_block_with_applicability<'a, T: LintContext>( /// e.g. Given the expression `&vec![]`, getting a snippet from the span for `vec![]` as a HIR node /// would result in `box []`. If given the context of the address of expression, this function will /// correctly get a snippet of `vec![]`. +/// +/// This will also return whether or not the snippet is a macro call. pub fn snippet_with_context( cx: &LateContext<'_>, span: Span, outer: SyntaxContext, default: &'a str, applicability: &mut Applicability, -) -> Cow<'a, str> { +) -> (Cow<'a, str>, bool) { let outer_span = hygiene::walk_chain(span, outer); - let span = if outer_span.ctxt() == outer { - outer_span + let (span, is_macro_call) = if outer_span.ctxt() == outer { + (outer_span, span.ctxt() != outer) } else { // The span is from a macro argument, and the outer context is the macro using the argument if *applicability != Applicability::Unspecified { *applicability = Applicability::MaybeIncorrect; } // TODO: get the argument span. - span + (span, false) }; - snippet_with_applicability(cx, span, default, applicability) + ( + snippet_with_applicability(cx, span, default, applicability), + is_macro_call, + ) } /// Returns a new Span that extends the original Span to the first non-whitespace char of the first @@ -1013,21 +1018,52 @@ fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option, .join("\n") } +/// Gets the span of the node, if there is one. +pub fn get_node_span(node: Node<'_>) -> Option { + match node { + Node::Param(Param { span, .. }) + | Node::Item(Item { span, .. }) + | Node::ForeignItem(ForeignItem { span, .. }) + | Node::TraitItem(TraitItem { span, .. }) + | Node::ImplItem(ImplItem { span, .. }) + | Node::Variant(Variant { span, .. }) + | Node::Field(StructField { span, .. }) + | Node::Expr(Expr { span, .. }) + | Node::Stmt(Stmt { span, .. }) + | Node::PathSegment(PathSegment { + ident: Ident { span, .. }, + .. + }) + | Node::Ty(hir::Ty { span, .. }) + | Node::TraitRef(TraitRef { + path: Path { span, .. }, + .. + }) + | Node::Binding(Pat { span, .. }) + | Node::Pat(Pat { span, .. }) + | Node::Arm(Arm { span, .. }) + | Node::Block(Block { span, .. }) + | Node::Local(Local { span, .. }) + | Node::MacroDef(MacroDef { span, .. }) + | Node::Lifetime(Lifetime { span, .. }) + | Node::GenericParam(GenericParam { span, .. }) + | Node::Visibility(Visibility { span, .. }) + | Node::Crate(CrateItem { span, .. }) => Some(*span), + Node::Ctor(_) | Node::AnonConst(_) => None, + } +} + +/// Gets the parent node, if any. +pub fn get_parent_node(tcx: TyCtxt<'_>, id: HirId) -> Option> { + tcx.hir().parent_iter(id).next().map(|(_, node)| node) +} + /// Gets the parent expression, if any –- this is useful to constrain a lint. pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> { - let map = &cx.tcx.hir(); - let hir_id = e.hir_id; - let parent_id = map.get_parent_node(hir_id); - if hir_id == parent_id { - return None; - } - map.find(parent_id).and_then(|node| { - if let Node::Expr(parent) = node { - Some(parent) - } else { - None - } - }) + match get_parent_node(cx.tcx, e.hir_id) { + Some(Node::Expr(parent)) => Some(parent), + _ => None, + } } pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> { diff --git a/tests/ui/dereference.fixed b/tests/ui/dereference.fixed deleted file mode 100644 index 459ca91b93b..00000000000 --- a/tests/ui/dereference.fixed +++ /dev/null @@ -1,93 +0,0 @@ -// run-rustfix - -#![allow(unused_variables, clippy::many_single_char_names, clippy::clone_double_ref)] -#![warn(clippy::explicit_deref_methods)] - -use std::ops::{Deref, DerefMut}; - -fn concat(deref_str: &str) -> String { - format!("{}bar", deref_str) -} - -fn just_return(deref_str: &str) -> &str { - deref_str -} - -struct CustomVec(Vec); -impl Deref for CustomVec { - type Target = Vec; - - fn deref(&self) -> &Vec { - &self.0 - } -} - -fn main() { - let a: &mut String = &mut String::from("foo"); - - // these should require linting - - let b: &str = &*a; - - let b: &mut str = &mut *a; - - // both derefs should get linted here - let b: String = format!("{}, {}", &*a, &*a); - - println!("{}", &*a); - - #[allow(clippy::match_single_binding)] - match &*a { - _ => (), - } - - let b: String = concat(&*a); - - let b = &*just_return(a); - - let b: String = concat(&*just_return(a)); - - let b: &str = &*a.deref(); - - let opt_a = Some(a.clone()); - let b = &*opt_a.unwrap(); - - // following should not require linting - - let cv = CustomVec(vec![0, 42]); - let c = cv.deref()[0]; - - let b: &str = &*a.deref(); - - let b: String = a.deref().clone(); - - let b: usize = a.deref_mut().len(); - - let b: &usize = &a.deref().len(); - - let b: &str = &*a; - - let b: &mut str = &mut *a; - - macro_rules! expr_deref { - ($body:expr) => { - $body.deref() - }; - } - let b: &str = expr_deref!(a); - - // The struct does not implement Deref trait - #[derive(Copy, Clone)] - struct NoLint(u32); - impl NoLint { - pub fn deref(self) -> u32 { - self.0 - } - pub fn deref_mut(self) -> u32 { - self.0 - } - } - let no_lint = NoLint(42); - let b = no_lint.deref(); - let b = no_lint.deref_mut(); -} diff --git a/tests/ui/dereference.rs b/tests/ui/dereference.rs deleted file mode 100644 index 8dc5272e67f..00000000000 --- a/tests/ui/dereference.rs +++ /dev/null @@ -1,93 +0,0 @@ -// run-rustfix - -#![allow(unused_variables, clippy::many_single_char_names, clippy::clone_double_ref)] -#![warn(clippy::explicit_deref_methods)] - -use std::ops::{Deref, DerefMut}; - -fn concat(deref_str: &str) -> String { - format!("{}bar", deref_str) -} - -fn just_return(deref_str: &str) -> &str { - deref_str -} - -struct CustomVec(Vec); -impl Deref for CustomVec { - type Target = Vec; - - fn deref(&self) -> &Vec { - &self.0 - } -} - -fn main() { - let a: &mut String = &mut String::from("foo"); - - // these should require linting - - let b: &str = a.deref(); - - let b: &mut str = a.deref_mut(); - - // both derefs should get linted here - let b: String = format!("{}, {}", a.deref(), a.deref()); - - println!("{}", a.deref()); - - #[allow(clippy::match_single_binding)] - match a.deref() { - _ => (), - } - - let b: String = concat(a.deref()); - - let b = just_return(a).deref(); - - let b: String = concat(just_return(a).deref()); - - let b: &str = a.deref().deref(); - - let opt_a = Some(a.clone()); - let b = opt_a.unwrap().deref(); - - // following should not require linting - - let cv = CustomVec(vec![0, 42]); - let c = cv.deref()[0]; - - let b: &str = &*a.deref(); - - let b: String = a.deref().clone(); - - let b: usize = a.deref_mut().len(); - - let b: &usize = &a.deref().len(); - - let b: &str = &*a; - - let b: &mut str = &mut *a; - - macro_rules! expr_deref { - ($body:expr) => { - $body.deref() - }; - } - let b: &str = expr_deref!(a); - - // The struct does not implement Deref trait - #[derive(Copy, Clone)] - struct NoLint(u32); - impl NoLint { - pub fn deref(self) -> u32 { - self.0 - } - pub fn deref_mut(self) -> u32 { - self.0 - } - } - let no_lint = NoLint(42); - let b = no_lint.deref(); - let b = no_lint.deref_mut(); -} diff --git a/tests/ui/dereference.stderr b/tests/ui/dereference.stderr deleted file mode 100644 index d26b462a433..00000000000 --- a/tests/ui/dereference.stderr +++ /dev/null @@ -1,70 +0,0 @@ -error: explicit deref method call - --> $DIR/dereference.rs:30:19 - | -LL | let b: &str = a.deref(); - | ^^^^^^^^^ help: try this: `&*a` - | - = note: `-D clippy::explicit-deref-methods` implied by `-D warnings` - -error: explicit deref_mut method call - --> $DIR/dereference.rs:32:23 - | -LL | let b: &mut str = a.deref_mut(); - | ^^^^^^^^^^^^^ help: try this: `&mut *a` - -error: explicit deref method call - --> $DIR/dereference.rs:35:39 - | -LL | let b: String = format!("{}, {}", a.deref(), a.deref()); - | ^^^^^^^^^ help: try this: `&*a` - -error: explicit deref method call - --> $DIR/dereference.rs:35:50 - | -LL | let b: String = format!("{}, {}", a.deref(), a.deref()); - | ^^^^^^^^^ help: try this: `&*a` - -error: explicit deref method call - --> $DIR/dereference.rs:37:20 - | -LL | println!("{}", a.deref()); - | ^^^^^^^^^ help: try this: `&*a` - -error: explicit deref method call - --> $DIR/dereference.rs:40:11 - | -LL | match a.deref() { - | ^^^^^^^^^ help: try this: `&*a` - -error: explicit deref method call - --> $DIR/dereference.rs:44:28 - | -LL | let b: String = concat(a.deref()); - | ^^^^^^^^^ help: try this: `&*a` - -error: explicit deref method call - --> $DIR/dereference.rs:46:13 - | -LL | let b = just_return(a).deref(); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*just_return(a)` - -error: explicit deref method call - --> $DIR/dereference.rs:48:28 - | -LL | let b: String = concat(just_return(a).deref()); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*just_return(a)` - -error: explicit deref method call - --> $DIR/dereference.rs:50:19 - | -LL | let b: &str = a.deref().deref(); - | ^^^^^^^^^^^^^^^^^ help: try this: `&*a.deref()` - -error: explicit deref method call - --> $DIR/dereference.rs:53:13 - | -LL | let b = opt_a.unwrap().deref(); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*opt_a.unwrap()` - -error: aborting due to 11 previous errors - diff --git a/tests/ui/explicit_deref_methods.fixed b/tests/ui/explicit_deref_methods.fixed new file mode 100644 index 00000000000..8155fdf9950 --- /dev/null +++ b/tests/ui/explicit_deref_methods.fixed @@ -0,0 +1,93 @@ +// run-rustfix + +#![allow(unused_variables, clippy::many_single_char_names, clippy::clone_double_ref)] +#![warn(clippy::explicit_deref_methods)] + +use std::ops::{Deref, DerefMut}; + +fn concat(deref_str: &str) -> String { + format!("{}bar", deref_str) +} + +fn just_return(deref_str: &str) -> &str { + deref_str +} + +struct CustomVec(Vec); +impl Deref for CustomVec { + type Target = Vec; + + fn deref(&self) -> &Vec { + &self.0 + } +} + +fn main() { + let a: &mut String = &mut String::from("foo"); + + // these should require linting + + let b: &str = &*a; + + let b: &mut str = &mut **a; + + // both derefs should get linted here + let b: String = format!("{}, {}", &*a, &*a); + + println!("{}", &*a); + + #[allow(clippy::match_single_binding)] + match &*a { + _ => (), + } + + let b: String = concat(&*a); + + let b = just_return(a); + + let b: String = concat(just_return(a)); + + let b: &str = &**a; + + let opt_a = Some(a.clone()); + let b = &*opt_a.unwrap(); + + // following should not require linting + + let cv = CustomVec(vec![0, 42]); + let c = cv.deref()[0]; + + let b: &str = &*a.deref(); + + let b: String = a.deref().clone(); + + let b: usize = a.deref_mut().len(); + + let b: &usize = &a.deref().len(); + + let b: &str = &*a; + + let b: &mut str = &mut *a; + + macro_rules! expr_deref { + ($body:expr) => { + $body.deref() + }; + } + let b: &str = expr_deref!(a); + + // The struct does not implement Deref trait + #[derive(Copy, Clone)] + struct NoLint(u32); + impl NoLint { + pub fn deref(self) -> u32 { + self.0 + } + pub fn deref_mut(self) -> u32 { + self.0 + } + } + let no_lint = NoLint(42); + let b = no_lint.deref(); + let b = no_lint.deref_mut(); +} diff --git a/tests/ui/explicit_deref_methods.rs b/tests/ui/explicit_deref_methods.rs new file mode 100644 index 00000000000..8dc5272e67f --- /dev/null +++ b/tests/ui/explicit_deref_methods.rs @@ -0,0 +1,93 @@ +// run-rustfix + +#![allow(unused_variables, clippy::many_single_char_names, clippy::clone_double_ref)] +#![warn(clippy::explicit_deref_methods)] + +use std::ops::{Deref, DerefMut}; + +fn concat(deref_str: &str) -> String { + format!("{}bar", deref_str) +} + +fn just_return(deref_str: &str) -> &str { + deref_str +} + +struct CustomVec(Vec); +impl Deref for CustomVec { + type Target = Vec; + + fn deref(&self) -> &Vec { + &self.0 + } +} + +fn main() { + let a: &mut String = &mut String::from("foo"); + + // these should require linting + + let b: &str = a.deref(); + + let b: &mut str = a.deref_mut(); + + // both derefs should get linted here + let b: String = format!("{}, {}", a.deref(), a.deref()); + + println!("{}", a.deref()); + + #[allow(clippy::match_single_binding)] + match a.deref() { + _ => (), + } + + let b: String = concat(a.deref()); + + let b = just_return(a).deref(); + + let b: String = concat(just_return(a).deref()); + + let b: &str = a.deref().deref(); + + let opt_a = Some(a.clone()); + let b = opt_a.unwrap().deref(); + + // following should not require linting + + let cv = CustomVec(vec![0, 42]); + let c = cv.deref()[0]; + + let b: &str = &*a.deref(); + + let b: String = a.deref().clone(); + + let b: usize = a.deref_mut().len(); + + let b: &usize = &a.deref().len(); + + let b: &str = &*a; + + let b: &mut str = &mut *a; + + macro_rules! expr_deref { + ($body:expr) => { + $body.deref() + }; + } + let b: &str = expr_deref!(a); + + // The struct does not implement Deref trait + #[derive(Copy, Clone)] + struct NoLint(u32); + impl NoLint { + pub fn deref(self) -> u32 { + self.0 + } + pub fn deref_mut(self) -> u32 { + self.0 + } + } + let no_lint = NoLint(42); + let b = no_lint.deref(); + let b = no_lint.deref_mut(); +} diff --git a/tests/ui/explicit_deref_methods.stderr b/tests/ui/explicit_deref_methods.stderr new file mode 100644 index 00000000000..35db6ef0905 --- /dev/null +++ b/tests/ui/explicit_deref_methods.stderr @@ -0,0 +1,70 @@ +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:30:19 + | +LL | let b: &str = a.deref(); + | ^^^^^^^^^ help: try this: `&*a` + | + = note: `-D clippy::explicit-deref-methods` implied by `-D warnings` + +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:32:23 + | +LL | let b: &mut str = a.deref_mut(); + | ^^^^^^^^^^^^^ help: try this: `&mut **a` + +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:35:39 + | +LL | let b: String = format!("{}, {}", a.deref(), a.deref()); + | ^^^^^^^^^ help: try this: `&*a` + +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:35:50 + | +LL | let b: String = format!("{}, {}", a.deref(), a.deref()); + | ^^^^^^^^^ help: try this: `&*a` + +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:37:20 + | +LL | println!("{}", a.deref()); + | ^^^^^^^^^ help: try this: `&*a` + +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:40:11 + | +LL | match a.deref() { + | ^^^^^^^^^ help: try this: `&*a` + +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:44:28 + | +LL | let b: String = concat(a.deref()); + | ^^^^^^^^^ help: try this: `&*a` + +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:46:13 + | +LL | let b = just_return(a).deref(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `just_return(a)` + +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:48:28 + | +LL | let b: String = concat(just_return(a).deref()); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `just_return(a)` + +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:50:19 + | +LL | let b: &str = a.deref().deref(); + | ^^^^^^^^^^^^^^^^^ help: try this: `&**a` + +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:53:13 + | +LL | let b = opt_a.unwrap().deref(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*opt_a.unwrap()` + +error: aborting due to 11 previous errors + -- cgit 1.4.1-3-g733a5 From 704f7a8e5025cdf5765493b4369290b65230c5d1 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 12 Mar 2021 20:13:45 -0500 Subject: Keep track of whether `deref` or `deref_mut` was called Remove more unnecessary code --- clippy_lints/src/dereference.rs | 75 ++++++++-------------------------- tests/ui/explicit_deref_methods.stderr | 2 +- 2 files changed, 17 insertions(+), 60 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 6185cf50b35..a1d0110929c 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1,9 +1,9 @@ use crate::utils::{get_parent_node, in_macro, is_allowed, peel_mid_ty_refs, snippet_with_context, span_lint_and_sugg}; use rustc_ast::util::parser::PREC_PREFIX; use rustc_errors::Applicability; -use rustc_hir::{BorrowKind, Destination, Expr, ExprKind, HirId, MatchSource, Mutability, Node, UnOp}; +use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node, UnOp}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, adjustment::Adjustment, Ty, TyCtxt, TyS, TypeckResults}; +use rustc_middle::ty::{self, Ty, TyCtxt, TyS, TypeckResults}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{symbol::sym, Span}; @@ -66,7 +66,7 @@ enum State { // A reference operation considered by this lint pass enum RefOp { - Method, + Method(Mutability), Deref, AddrOf, } @@ -100,18 +100,10 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { match (self.state.take(), kind) { (None, kind) => { let parent = get_parent_node(cx.tcx, expr.hir_id); - - let expr_adjustments = find_adjustments(cx.tcx, typeck, expr); let expr_ty = typeck.expr_ty(expr); - let target_mut = - if let ty::Ref(_, _, mutability) = *expr_adjustments.last().map_or(expr_ty, |a| a.target).kind() { - mutability - } else { - Mutability::Not - }; match kind { - RefOp::Method + RefOp::Method(target_mut) if !is_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id) && is_linted_explicit_deref_position(parent, expr.hir_id) => { @@ -133,7 +125,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { _ => (), } }, - (Some((State::DerefMethod { ty_changed_count, .. }, data)), RefOp::Method) => { + (Some((State::DerefMethod { ty_changed_count, .. }, data)), RefOp::Method(_)) => { self.state = Some(( State::DerefMethod { ty_changed_count: if deref_method_same_type(typeck.expr_ty(expr), typeck.expr_ty(sub_expr)) { @@ -173,9 +165,13 @@ fn try_parse_ref_op( ExprKind::AddrOf(BorrowKind::Ref, _, sub_expr) => return Some((RefOp::AddrOf, sub_expr)), _ => return None, }; - (tcx.is_diagnostic_item(sym::deref_method, def_id) - || tcx.trait_of_item(def_id)? == tcx.lang_items().deref_mut_trait()?) - .then(|| (RefOp::Method, arg)) + if tcx.is_diagnostic_item(sym::deref_method, def_id) { + Some((RefOp::Method(Mutability::Not), arg)) + } else if tcx.trait_of_item(def_id)? == tcx.lang_items().deref_mut_trait()? { + Some((RefOp::Method(Mutability::Mut), arg)) + } else { + None + } } // Checks whether the type for a deref call actually changed the type, not just the mutability of @@ -191,48 +187,6 @@ fn deref_method_same_type(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool { } } -// Adjustments are sometimes made in the parent block rather than the expression itself. -fn find_adjustments( - tcx: TyCtxt<'tcx>, - typeck: &'tcx TypeckResults<'_>, - expr: &'tcx Expr<'_>, -) -> &'tcx [Adjustment<'tcx>] { - let map = tcx.hir(); - let mut iter = map.parent_iter(expr.hir_id); - let mut prev = expr; - - loop { - match typeck.expr_adjustments(prev) { - [] => (), - a => break a, - }; - - match iter.next().map(|(_, x)| x) { - Some(Node::Block(_)) => { - if let Some((_, Node::Expr(e))) = iter.next() { - prev = e; - } else { - // This shouldn't happen. Blocks are always contained in an expression. - break &[]; - } - }, - Some(Node::Expr(&Expr { - kind: ExprKind::Break(Destination { target_id: Ok(id), .. }, _), - .. - })) => { - if let Some(Node::Expr(e)) = map.find(id) { - prev = e; - iter = map.parent_iter(id); - continue; - } - // This shouldn't happen. The destination should definitely exist at this point. - break &[]; - }, - _ => break &[], - } - } -} - // Checks whether the parent node is a suitable context for switching from a deref method to the // deref operator. fn is_linted_explicit_deref_position(parent: Option>, child_id: HirId) -> bool { @@ -331,7 +285,10 @@ fn report(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: Stat cx, EXPLICIT_DEREF_METHODS, data.span, - "explicit `deref` method call", + match data.target_mut { + Mutability::Not => "explicit `deref` method call", + Mutability::Mut => "explicit `deref_mut` method call", + }, "try this", format!("{}{}{}", addr_of_str, deref_str, expr_str), app, diff --git a/tests/ui/explicit_deref_methods.stderr b/tests/ui/explicit_deref_methods.stderr index 35db6ef0905..335c0e4bb9d 100644 --- a/tests/ui/explicit_deref_methods.stderr +++ b/tests/ui/explicit_deref_methods.stderr @@ -6,7 +6,7 @@ LL | let b: &str = a.deref(); | = note: `-D clippy::explicit-deref-methods` implied by `-D warnings` -error: explicit `deref` method call +error: explicit `deref_mut` method call --> $DIR/explicit_deref_methods.rs:32:23 | LL | let b: &mut str = a.deref_mut(); -- cgit 1.4.1-3-g733a5 From 2713ad43425bef040e3c7d55618d18035fe68c1a Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 13 Mar 2021 08:27:42 -0500 Subject: Properly lint macro arguments for `explicit_deref_methods` --- clippy_lints/src/dereference.rs | 6 +++--- tests/ui/explicit_deref_methods.fixed | 2 ++ tests/ui/explicit_deref_methods.rs | 2 ++ tests/ui/explicit_deref_methods.stderr | 8 +++++++- 4 files changed, 14 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index a1d0110929c..40ed6d8d154 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -105,7 +105,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { match kind { RefOp::Method(target_mut) if !is_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id) - && is_linted_explicit_deref_position(parent, expr.hir_id) => + && is_linted_explicit_deref_position(parent, expr.hir_id, expr.span) => { self.state = Some(( State::DerefMethod { @@ -189,9 +189,9 @@ fn deref_method_same_type(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool { // Checks whether the parent node is a suitable context for switching from a deref method to the // deref operator. -fn is_linted_explicit_deref_position(parent: Option>, child_id: HirId) -> bool { +fn is_linted_explicit_deref_position(parent: Option>, child_id: HirId, child_span: Span) -> bool { let parent = match parent { - Some(Node::Expr(e)) => e, + Some(Node::Expr(e)) if e.span.ctxt() == child_span.ctxt() => e, _ => return true, }; match parent.kind { diff --git a/tests/ui/explicit_deref_methods.fixed b/tests/ui/explicit_deref_methods.fixed index 8155fdf9950..51d0468e47c 100644 --- a/tests/ui/explicit_deref_methods.fixed +++ b/tests/ui/explicit_deref_methods.fixed @@ -76,6 +76,8 @@ fn main() { } let b: &str = expr_deref!(a); + let b: &str = expr_deref!(&*a); + // The struct does not implement Deref trait #[derive(Copy, Clone)] struct NoLint(u32); diff --git a/tests/ui/explicit_deref_methods.rs b/tests/ui/explicit_deref_methods.rs index 8dc5272e67f..680664bd4f6 100644 --- a/tests/ui/explicit_deref_methods.rs +++ b/tests/ui/explicit_deref_methods.rs @@ -76,6 +76,8 @@ fn main() { } let b: &str = expr_deref!(a); + let b: &str = expr_deref!(a.deref()); + // The struct does not implement Deref trait #[derive(Copy, Clone)] struct NoLint(u32); diff --git a/tests/ui/explicit_deref_methods.stderr b/tests/ui/explicit_deref_methods.stderr index 335c0e4bb9d..8035d77d18d 100644 --- a/tests/ui/explicit_deref_methods.stderr +++ b/tests/ui/explicit_deref_methods.stderr @@ -66,5 +66,11 @@ error: explicit `deref` method call LL | let b = opt_a.unwrap().deref(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*opt_a.unwrap()` -error: aborting due to 11 previous errors +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:79:31 + | +LL | let b: &str = expr_deref!(a.deref()); + | ^^^^^^^^^ help: try this: `&*a` + +error: aborting due to 12 previous errors -- cgit 1.4.1-3-g733a5 From 1054eb0c853aefec9787b57e103d3a1432169263 Mon Sep 17 00:00:00 2001 From: iobtl Date: Sun, 14 Mar 2021 08:09:08 +0800 Subject: use lint_unnecessary_cast for literals, suggest `_` if not present --- clippy_lints/src/casts/unnecessary_cast.rs | 9 +++++++++ tests/ui/unnecessary_cast.stderr | 8 ++++---- 2 files changed, 13 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index 171cac091c2..c43bbf32949 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -44,6 +44,15 @@ pub(super) fn check( lint_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to); }, LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => {}, + LitKind::Int(_, LitIntType::Signed(_) | LitIntType::Unsigned(_)) + | LitKind::Float(_, LitFloatType::Suffixed(_)) + if cast_from.kind() == cast_to.kind() => + { + if let Some(src) = snippet_opt(cx, lit.span) { + let num_lit = NumericLiteral::from_lit_kind(&src, &lit.node).unwrap(); + lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to); + } + }, _ => { if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) { span_lint_and_sugg( diff --git a/tests/ui/unnecessary_cast.stderr b/tests/ui/unnecessary_cast.stderr index 87261cd8333..70aa448af68 100644 --- a/tests/ui/unnecessary_cast.stderr +++ b/tests/ui/unnecessary_cast.stderr @@ -1,16 +1,16 @@ -error: casting to the same type is unnecessary (`i32` -> `i32`) +error: casting integer literal to `i32` is unnecessary --> $DIR/unnecessary_cast.rs:6:5 | LL | 1i32 as i32; - | ^^^^^^^^^^^ help: try: `1i32` + | ^^^^^^^^^^^ help: try: `1_i32` | = note: `-D clippy::unnecessary-cast` implied by `-D warnings` -error: casting to the same type is unnecessary (`f32` -> `f32`) +error: casting float literal to `f32` is unnecessary --> $DIR/unnecessary_cast.rs:7:5 | LL | 1f32 as f32; - | ^^^^^^^^^^^ help: try: `1f32` + | ^^^^^^^^^^^ help: try: `1_f32` error: casting to the same type is unnecessary (`bool` -> `bool`) --> $DIR/unnecessary_cast.rs:8:5 -- cgit 1.4.1-3-g733a5 From ecf0c76c369a80c1a2de61e25c94b7df5dc7d164 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Wed, 25 Mar 2020 21:13:24 -0400 Subject: Fix suspicious_map false positives --- clippy_lints/src/methods/mod.rs | 2 +- clippy_lints/src/methods/suspicious_map.rs | 45 ++++++++++++++++----- clippy_utils/src/lib.rs | 64 ++++++++++++++++++++++++++++-- tests/ui/suspicious_map.rs | 27 +++++++++++++ tests/ui/suspicious_map.stderr | 10 ++++- 5 files changed, 132 insertions(+), 16 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 7fd14c4f9b1..45e906cf468 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1739,7 +1739,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { unnecessary_filter_map::check(cx, expr, arg_lists[0]); filter_map_identity::check(cx, expr, arg_lists[0], method_spans[0]); }, - ["count", "map"] => suspicious_map::check(cx, expr), + ["count", "map"] => suspicious_map::check(cx, expr, arg_lists[1], arg_lists[0]), ["assume_init"] => uninit_assumed_init::check(cx, &arg_lists[0][0], expr), ["unwrap_or", arith @ ("checked_add" | "checked_sub" | "checked_mul")] => { manual_saturating_arithmetic::check(cx, expr, &arg_lists, &arith["checked_".len()..]) diff --git a/clippy_lints/src/methods/suspicious_map.rs b/clippy_lints/src/methods/suspicious_map.rs index e135a826dc4..0ffa71de30c 100644 --- a/clippy_lints/src/methods/suspicious_map.rs +++ b/clippy_lints/src/methods/suspicious_map.rs @@ -1,16 +1,41 @@ -use crate::utils::span_lint_and_help; +use crate::utils::usage::mutated_variables; +use crate::utils::{expr_or_init, is_trait_method, span_lint_and_help}; +use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::LateContext; +use rustc_span::sym; use super::SUSPICIOUS_MAP; -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>) { - span_lint_and_help( - cx, - SUSPICIOUS_MAP, - 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`", - ); +pub fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &hir::Expr<'_>, + map_args: &[hir::Expr<'_>], + count_args: &[hir::Expr<'_>], +) { + if_chain! { + if let [count_recv] = count_args; + if let [_, map_arg] = map_args; + if is_trait_method(cx, count_recv, sym::Iterator); + let closure = expr_or_init(cx, map_arg); + if let Some(body_id) = cx.tcx.hir().maybe_body_owned_by(closure.hir_id); + let closure_body = cx.tcx.hir().body(body_id); + if !cx.typeck_results().expr_ty(&closure_body.value).is_unit(); + then { + if let Some(map_mutated_vars) = mutated_variables(&closure_body.value, cx) { + // A variable is used mutably inside of the closure. Suppress the lint. + if !map_mutated_vars.is_empty() { + return; + } + } + span_lint_and_help( + cx, + SUSPICIOUS_MAP, + 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`", + ); + } + } } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 0ddc915862c..98cdeae739f 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -62,10 +62,10 @@ use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::{ - def, Arm, Block, Body, Constness, CrateItem, Expr, ExprKind, FnDecl, ForeignItem, GenericArgs, GenericParam, HirId, - Impl, ImplItem, ImplItemKind, Item, ItemKind, LangItem, Lifetime, Local, MacroDef, MatchSource, Node, Param, Pat, - PatKind, Path, PathSegment, QPath, Stmt, StructField, TraitItem, TraitItemKind, TraitRef, TyKind, Unsafety, - Variant, Visibility, + def, Arm, BindingAnnotation, Block, Body, Constness, CrateItem, Expr, ExprKind, FnDecl, ForeignItem, GenericArgs, + GenericParam, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, LangItem, Lifetime, Local, MacroDef, + MatchSource, Node, Param, Pat, PatKind, Path, PathSegment, QPath, Stmt, StructField, TraitItem, TraitItemKind, + TraitRef, TyKind, Unsafety, Variant, Visibility, }; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, Level, Lint, LintContext}; @@ -138,6 +138,62 @@ pub fn differing_macro_contexts(lhs: Span, rhs: Span) -> bool { rhs.ctxt() != lhs.ctxt() } +/// If the given expression is a local binding, find the initializer expression. +/// If that initializer expression is another local binding, find its initializer again. +/// This process repeats as long as possible (but usually no more than once). Initializer +/// expressions with adjustments are ignored. If this is not desired, use [`find_binding_init`] +/// instead. +/// +/// Examples: +/// ```ignore +/// let abc = 1; +/// // ^ output +/// let def = abc; +/// dbg!(def) +/// // ^^^ input +/// +/// // or... +/// let abc = 1; +/// let def = abc + 2; +/// // ^^^^^^^ output +/// dbg!(def) +/// // ^^^ input +/// ``` +pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> { + while let Some(init) = path_to_local(expr) + .and_then(|id| find_binding_init(cx, id)) + .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty()) + { + expr = init; + } + expr +} + +/// Finds the initializer expression for a local binding. Returns `None` if the binding is mutable. +/// By only considering immutable bindings, we guarantee that the returned expression represents the +/// value of the binding wherever it is referenced. +/// +/// Example: +/// ```ignore +/// let abc = 1; +/// // ^ output +/// dbg!(abc) +/// // ^^^ input +/// ``` +pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> { + let hir = cx.tcx.hir(); + if_chain! { + if let Some(Node::Binding(pat)) = hir.find(hir_id); + if matches!(pat.kind, PatKind::Binding(BindingAnnotation::Unannotated, ..)); + let parent = hir.get_parent_node(hir_id); + if let Some(Node::Local(local)) = hir.find(parent); + then { + return local.init; + } + } + None +} + /// Returns `true` if the given `NodeId` is inside a constant context /// /// # Example diff --git a/tests/ui/suspicious_map.rs b/tests/ui/suspicious_map.rs index d838d8fde21..3a2a10cf09e 100644 --- a/tests/ui/suspicious_map.rs +++ b/tests/ui/suspicious_map.rs @@ -2,4 +2,31 @@ fn main() { let _ = (0..3).map(|x| x + 2).count(); + + let f = |x| x + 1; + let _ = (0..3).map(f).count(); +} + +fn negative() { + // closure with side effects + let mut sum = 0; + let _ = (0..3).map(|x| sum += x).count(); + + // closure variable with side effects + let ext_closure = |x| sum += x; + let _ = (0..3).map(ext_closure).count(); + + // closure that returns unit + let _ = (0..3) + .map(|x| { + // do nothing + }) + .count(); + + // external function + let _ = (0..3).map(do_something).count(); +} + +fn do_something(t: T) -> String { + unimplemented!() } diff --git a/tests/ui/suspicious_map.stderr b/tests/ui/suspicious_map.stderr index e1b4ba40376..8c3f36584a5 100644 --- a/tests/ui/suspicious_map.stderr +++ b/tests/ui/suspicious_map.stderr @@ -7,5 +7,13 @@ 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` -error: aborting due to previous error +error: this call to `map()` won't have an effect on the call to `count()` + --> $DIR/suspicious_map.rs:7:13 + | +LL | let _ = (0..3).map(f).count(); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = help: make sure you did not confuse `map` with `filter` or `for_each` + +error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From c5b3a719ed85ae48e813ee07cb9e7b56c6a763d9 Mon Sep 17 00:00:00 2001 From: Yukio Tanaka Date: Tue, 16 Mar 2021 19:46:40 +0900 Subject: Fix FP of `manual_unwrap_or` in const fn --- clippy_lints/src/manual_unwrap_or.rs | 33 ++++++++++++++++++++++++++++----- tests/ui/manual_unwrap_or.fixed | 15 +++++++++++++++ tests/ui/manual_unwrap_or.rs | 15 +++++++++++++++ 3 files changed, 58 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index 0e030e0e261..203f018a9e2 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -6,12 +6,12 @@ use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Arm, Expr, ExprKind, Pat, PatKind}; +use rustc_hir::{hir_id::HirId, intravisit::FnKind, Arm, Body, Expr, ExprKind, FnDecl, Pat, PatKind, StmtKind}; use rustc_lint::LintContext; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::sym; +use rustc_span::{source_map::Span, sym}; declare_clippy_lint! { /// **What it does:** @@ -44,11 +44,34 @@ declare_clippy_lint! { declare_lint_pass!(ManualUnwrapOr => [MANUAL_UNWRAP_OR]); impl LateLintPass<'_> for ManualUnwrapOr { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if in_external_macro(cx.sess(), expr.span) { + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + kind: FnKind<'tcx>, + _: &'tcx FnDecl<'tcx>, + body: &'tcx Body<'tcx>, + span: Span, + _: HirId, + ) { + if in_external_macro(cx.sess(), span) { return; } - lint_manual_unwrap_or(cx, expr); + if_chain! { + if let FnKind::ItemFn(_, _, header, _) = kind; + if !header.is_const(); + let expr = &body.value; + if let ExprKind::Block(block, _) = expr.kind; + then { + for stmt in block.stmts { + if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = &stmt.kind { + lint_manual_unwrap_or(cx, expr); + } + } + if let Some(expr) = block.expr { + lint_manual_unwrap_or(cx, expr); + } + } + } } } diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index 81d903c15d3..2f57957f55b 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -136,4 +136,19 @@ fn result_unwrap_or() { }; } +// don't lint in const fn +const fn const_fn_unwrap_or() { + match Some(1) { + Some(s) => s, + None => 0, + }; +} + +const fn const_fn_unwrap() { + match Ok::<&str, &str>("Alice") { + Ok(s) => s, + Err(_) => "Bob", + }; +} + fn main() {} diff --git a/tests/ui/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs index 16105d379c3..1088047da75 100644 --- a/tests/ui/manual_unwrap_or.rs +++ b/tests/ui/manual_unwrap_or.rs @@ -175,4 +175,19 @@ fn result_unwrap_or() { }; } +// don't lint in const fn +const fn const_fn_unwrap_or() { + match Some(1) { + Some(s) => s, + None => 0, + }; +} + +const fn const_fn_unwrap() { + match Ok::<&str, &str>("Alice") { + Ok(s) => s, + Err(_) => "Bob", + }; +} + fn main() {} -- cgit 1.4.1-3-g733a5 From aa5f1f907831e0d7833f87063036895a78a0da1a Mon Sep 17 00:00:00 2001 From: Yukio Tanaka Date: Tue, 16 Mar 2021 19:56:47 +0900 Subject: Fix typo --- tests/ui/manual_unwrap_or.fixed | 4 ++-- tests/ui/manual_unwrap_or.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index 2f57957f55b..f1d3252230b 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -137,14 +137,14 @@ fn result_unwrap_or() { } // don't lint in const fn -const fn const_fn_unwrap_or() { +const fn const_fn_option_unwrap_or() { match Some(1) { Some(s) => s, None => 0, }; } -const fn const_fn_unwrap() { +const fn const_fn_result_unwrap_or() { match Ok::<&str, &str>("Alice") { Ok(s) => s, Err(_) => "Bob", diff --git a/tests/ui/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs index 1088047da75..c9eee25a5b1 100644 --- a/tests/ui/manual_unwrap_or.rs +++ b/tests/ui/manual_unwrap_or.rs @@ -176,14 +176,14 @@ fn result_unwrap_or() { } // don't lint in const fn -const fn const_fn_unwrap_or() { +const fn const_fn_option_unwrap_or() { match Some(1) { Some(s) => s, None => 0, }; } -const fn const_fn_unwrap() { +const fn const_fn_result_unwrap_or() { match Ok::<&str, &str>("Alice") { Ok(s) => s, Err(_) => "Bob", -- cgit 1.4.1-3-g733a5 From 4c1047167d20460dcb84e3d947787ce91d5fd0d4 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 1 Mar 2021 13:28:36 -0500 Subject: More specific spans for `use_debug` lint --- clippy_lints/src/write.rs | 2 +- tests/ui/print.stderr | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index b5470c3bc70..210ddc7465f 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -456,7 +456,7 @@ impl Write { if !self.in_debug_impl && arg.format.ty == "?" { // FIXME: modify rustc's fmt string parser to give us the current span - span_lint(cx, USE_DEBUG, str.span, "use of `Debug`-based formatting"); + span_lint(cx, USE_DEBUG, span, "use of `Debug`-based formatting"); } args.push(arg, span); diff --git a/tests/ui/print.stderr b/tests/ui/print.stderr index 208d9532628..1754c418381 100644 --- a/tests/ui/print.stderr +++ b/tests/ui/print.stderr @@ -1,8 +1,8 @@ error: use of `Debug`-based formatting - --> $DIR/print.rs:11:19 + --> $DIR/print.rs:11:20 | LL | write!(f, "{:?}", 43.1415) - | ^^^^^^ + | ^^^^ | = note: `-D clippy::use-debug` implied by `-D warnings` @@ -33,10 +33,10 @@ LL | print!("Hello {:?}", "World"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of `Debug`-based formatting - --> $DIR/print.rs:28:12 + --> $DIR/print.rs:28:19 | LL | print!("Hello {:?}", "World"); - | ^^^^^^^^^^^^ + | ^^^^ error: use of `print!` --> $DIR/print.rs:30:5 @@ -45,10 +45,10 @@ LL | print!("Hello {:#?}", "#orld"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: use of `Debug`-based formatting - --> $DIR/print.rs:30:12 + --> $DIR/print.rs:30:19 | LL | print!("Hello {:#?}", "#orld"); - | ^^^^^^^^^^^^^ + | ^^^^^ error: aborting due to 8 previous errors -- cgit 1.4.1-3-g733a5 From a7fa2a6fa88477ab2542bb15347e3085d9547f95 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 1 Mar 2021 16:31:04 -0500 Subject: Add suggestion to `write_literal` and `print_literal` Don't lint on a mixture of raw and regular strings Fix spans in format strings --- clippy_lints/src/write.rs | 62 +++++++++++++++++------ tests/ui/print_literal.stderr | 70 +++++++++++++++++++++++--- tests/ui/write_literal.stderr | 70 +++++++++++++++++++++++--- tests/ui/write_literal_2.rs | 27 ++++++++++ tests/ui/write_literal_2.stderr | 106 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 305 insertions(+), 30 deletions(-) create mode 100644 tests/ui/write_literal_2.rs create mode 100644 tests/ui/write_literal_2.stderr (limited to 'tests') diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 210ddc7465f..e416eab7914 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -1,10 +1,11 @@ use std::borrow::Cow; -use std::ops::Range; +use std::iter; +use std::ops::{Deref, Range}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet_opt, snippet_with_applicability}; -use rustc_ast::ast::{Expr, ExprKind, ImplKind, Item, ItemKind, LitKind, MacCall, Path, StrLit, StrStyle}; -use rustc_ast::token; +use rustc_ast::ast::{Expr, ExprKind, ImplKind, Item, ItemKind, MacCall, Path, StrLit, StrStyle}; +use rustc_ast::token::{self, LitKind}; use rustc_ast::tokenstream::TokenStream; use rustc_errors::Applicability; use rustc_lexer::unescape::{self, EscapeError}; @@ -438,7 +439,7 @@ impl Write { fn parse_fmt_string(&self, cx: &EarlyContext<'_>, str: &StrLit) -> Option { use rustc_parse_format::{ParseMode, Parser, Piece}; - let str_sym = str.symbol.as_str(); + let str_sym = str.symbol_unescaped.as_str(); let style = match str.style { StrStyle::Cooked => None, StrStyle::Raw(n) => Some(n as usize), @@ -514,21 +515,17 @@ impl Write { if !parser.eat(&token::Comma) { return (Some(fmtstr), expr); } + + let comma_span = parser.prev_token.span; let token_expr = if let Ok(expr) = parser.parse_expr().map_err(|mut err| err.cancel()) { expr } else { return (Some(fmtstr), None); }; - let (fmt_spans, span) = match &token_expr.kind { - ExprKind::Lit(lit) if !matches!(lit.kind, LitKind::Int(..) | LitKind::Float(..)) => { - (unnamed_args.next().unwrap_or(&[]), token_expr.span) - }, + let (fmt_spans, lit) = match &token_expr.kind { + ExprKind::Lit(lit) => (unnamed_args.next().unwrap_or(&[]), lit), ExprKind::Assign(lhs, rhs, _) => match (&lhs.kind, &rhs.kind) { - (ExprKind::Path(_, p), ExprKind::Lit(lit)) - if !matches!(lit.kind, LitKind::Int(..) | LitKind::Float(..)) => - { - (args.get_named(p), rhs.span) - }, + (ExprKind::Path(_, p), ExprKind::Lit(lit)) => (args.get_named(p), lit), _ => continue, }, _ => { @@ -537,8 +534,45 @@ impl Write { }, }; + let replacement: String = match lit.token.kind { + LitKind::Integer | LitKind::Float | LitKind::Err => continue, + LitKind::StrRaw(_) | LitKind::ByteStrRaw(_) if matches!(fmtstr.style, StrStyle::Raw(_)) => { + lit.token.symbol.as_str().replace("{", "{{").replace("}", "}}") + }, + LitKind::Str | LitKind::ByteStr if matches!(fmtstr.style, StrStyle::Cooked) => { + lit.token.symbol.as_str().replace("{", "{{").replace("}", "}}") + }, + LitKind::StrRaw(_) | LitKind::Str | LitKind::ByteStrRaw(_) | LitKind::ByteStr => continue, + LitKind::Byte | LitKind::Char => match lit.token.symbol.as_str().deref() { + "\"" if matches!(fmtstr.style, StrStyle::Cooked) => "\\\"", + "\"" if matches!(fmtstr.style, StrStyle::Raw(0)) => continue, + "\\\\" if matches!(fmtstr.style, StrStyle::Raw(_)) => "\\", + "\\'" => "'", + "{" => "{{", + "}" => "}}", + x if matches!(fmtstr.style, StrStyle::Raw(_)) && x.starts_with("\\") => continue, + x => x, + } + .into(), + LitKind::Bool => lit.token.symbol.as_str().deref().into(), + }; + if !fmt_spans.is_empty() { - span_lint(cx, lint, span, "literal with an empty format string"); + span_lint_and_then( + cx, + lint, + token_expr.span, + "literal with an empty format string", + |diag| { + diag.multipart_suggestion( + "try this", + iter::once((comma_span.to(token_expr.span), String::new())) + .chain(fmt_spans.iter().cloned().zip(iter::repeat(replacement))) + .collect(), + Applicability::MachineApplicable, + ); + }, + ); } } } diff --git a/tests/ui/print_literal.stderr b/tests/ui/print_literal.stderr index e284aece236..54a4084c89e 100644 --- a/tests/ui/print_literal.stderr +++ b/tests/ui/print_literal.stderr @@ -5,66 +5,120 @@ LL | print!("Hello {}", "world"); | ^^^^^^^ | = note: `-D clippy::print-literal` implied by `-D warnings` +help: try this + | +LL | print!("Hello world"); + | ^^^^^-- error: literal with an empty format string --> $DIR/print_literal.rs:26:36 | LL | println!("Hello {} {}", world, "world"); | ^^^^^^^ + | +help: try this + | +LL | println!("Hello {} world", world); + | ^^^^^ -- error: literal with an empty format string --> $DIR/print_literal.rs:27:26 | LL | println!("Hello {}", "world"); | ^^^^^^^ + | +help: try this + | +LL | println!("Hello world"); + | ^^^^^-- error: literal with an empty format string --> $DIR/print_literal.rs:32:25 | LL | println!("{0} {1}", "hello", "world"); | ^^^^^^^ + | +help: try this + | +LL | println!("hello {1}", "world"); + | ^^^^^ -- error: literal with an empty format string --> $DIR/print_literal.rs:32:34 | LL | println!("{0} {1}", "hello", "world"); | ^^^^^^^ + | +help: try this + | +LL | println!("{0} world", "hello"); + | ^^^^^ -- error: literal with an empty format string --> $DIR/print_literal.rs:33:25 | LL | println!("{1} {0}", "hello", "world"); | ^^^^^^^ + | +help: try this + | +LL | println!("{1} hello", "world"); + | ^^^^^-- error: literal with an empty format string --> $DIR/print_literal.rs:33:34 | LL | println!("{1} {0}", "hello", "world"); | ^^^^^^^ + | +help: try this + | +LL | println!("world {0}", "hello"); + | ^^^^^ -- error: literal with an empty format string - --> $DIR/print_literal.rs:36:35 + --> $DIR/print_literal.rs:36:29 | LL | println!("{foo} {bar}", foo = "hello", bar = "world"); - | ^^^^^^^ + | ^^^^^^^^^^^^^ + | +help: try this + | +LL | println!("hello {bar}", bar = "world"); + | ^^^^^ -- error: literal with an empty format string - --> $DIR/print_literal.rs:36:50 + --> $DIR/print_literal.rs:36:44 | LL | println!("{foo} {bar}", foo = "hello", bar = "world"); - | ^^^^^^^ + | ^^^^^^^^^^^^^ + | +help: try this + | +LL | println!("{foo} world", foo = "hello"); + | ^^^^^ -- error: literal with an empty format string - --> $DIR/print_literal.rs:37:35 + --> $DIR/print_literal.rs:37:29 | LL | println!("{bar} {foo}", foo = "hello", bar = "world"); - | ^^^^^^^ + | ^^^^^^^^^^^^^ + | +help: try this + | +LL | println!("{bar} hello", bar = "world"); + | ^^^^^-- error: literal with an empty format string - --> $DIR/print_literal.rs:37:50 + --> $DIR/print_literal.rs:37:44 | LL | println!("{bar} {foo}", foo = "hello", bar = "world"); - | ^^^^^^^ + | ^^^^^^^^^^^^^ + | +help: try this + | +LL | println!("world {foo}", foo = "hello"); + | ^^^^^ -- error: aborting due to 11 previous errors diff --git a/tests/ui/write_literal.stderr b/tests/ui/write_literal.stderr index e54d89ecf29..507a78e8280 100644 --- a/tests/ui/write_literal.stderr +++ b/tests/ui/write_literal.stderr @@ -5,66 +5,120 @@ LL | write!(&mut v, "Hello {}", "world"); | ^^^^^^^ | = note: `-D clippy::write-literal` implied by `-D warnings` +help: try this + | +LL | write!(&mut v, "Hello world"); + | ^^^^^-- error: literal with an empty format string --> $DIR/write_literal.rs:31:44 | LL | writeln!(&mut v, "Hello {} {}", world, "world"); | ^^^^^^^ + | +help: try this + | +LL | writeln!(&mut v, "Hello {} world", world); + | ^^^^^ -- error: literal with an empty format string --> $DIR/write_literal.rs:32:34 | LL | writeln!(&mut v, "Hello {}", "world"); | ^^^^^^^ + | +help: try this + | +LL | writeln!(&mut v, "Hello world"); + | ^^^^^-- error: literal with an empty format string --> $DIR/write_literal.rs:37:33 | LL | writeln!(&mut v, "{0} {1}", "hello", "world"); | ^^^^^^^ + | +help: try this + | +LL | writeln!(&mut v, "hello {1}", "world"); + | ^^^^^ -- error: literal with an empty format string --> $DIR/write_literal.rs:37:42 | LL | writeln!(&mut v, "{0} {1}", "hello", "world"); | ^^^^^^^ + | +help: try this + | +LL | writeln!(&mut v, "{0} world", "hello"); + | ^^^^^ -- error: literal with an empty format string --> $DIR/write_literal.rs:38:33 | LL | writeln!(&mut v, "{1} {0}", "hello", "world"); | ^^^^^^^ + | +help: try this + | +LL | writeln!(&mut v, "{1} hello", "world"); + | ^^^^^-- error: literal with an empty format string --> $DIR/write_literal.rs:38:42 | LL | writeln!(&mut v, "{1} {0}", "hello", "world"); | ^^^^^^^ + | +help: try this + | +LL | writeln!(&mut v, "world {0}", "hello"); + | ^^^^^ -- error: literal with an empty format string - --> $DIR/write_literal.rs:41:43 + --> $DIR/write_literal.rs:41:37 | LL | writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world"); - | ^^^^^^^ + | ^^^^^^^^^^^^^ + | +help: try this + | +LL | writeln!(&mut v, "hello {bar}", bar = "world"); + | ^^^^^ -- error: literal with an empty format string - --> $DIR/write_literal.rs:41:58 + --> $DIR/write_literal.rs:41:52 | LL | writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world"); - | ^^^^^^^ + | ^^^^^^^^^^^^^ + | +help: try this + | +LL | writeln!(&mut v, "{foo} world", foo = "hello"); + | ^^^^^ -- error: literal with an empty format string - --> $DIR/write_literal.rs:42:43 + --> $DIR/write_literal.rs:42:37 | LL | writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world"); - | ^^^^^^^ + | ^^^^^^^^^^^^^ + | +help: try this + | +LL | writeln!(&mut v, "{bar} hello", bar = "world"); + | ^^^^^-- error: literal with an empty format string - --> $DIR/write_literal.rs:42:58 + --> $DIR/write_literal.rs:42:52 | LL | writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world"); - | ^^^^^^^ + | ^^^^^^^^^^^^^ + | +help: try this + | +LL | writeln!(&mut v, "world {foo}", foo = "hello"); + | ^^^^^ -- error: aborting due to 11 previous errors diff --git a/tests/ui/write_literal_2.rs b/tests/ui/write_literal_2.rs new file mode 100644 index 00000000000..f341e8215e1 --- /dev/null +++ b/tests/ui/write_literal_2.rs @@ -0,0 +1,27 @@ +#![allow(unused_must_use)] +#![warn(clippy::write_literal)] + +use std::io::Write; + +fn main() { + let mut v = Vec::new(); + + writeln!(&mut v, "{}", "{hello}"); + writeln!(&mut v, r"{}", r"{hello}"); + writeln!(&mut v, "{}", '\''); + writeln!(&mut v, "{}", '"'); + writeln!(&mut v, r"{}", '"'); // don't lint + writeln!(&mut v, r"{}", '\''); + writeln!( + &mut v, + "some {}", + "hello \ + world!" + ); + writeln!( + &mut v, + "some {}\ + {} \\ {}", + "1", "2", "3", + ); +} diff --git a/tests/ui/write_literal_2.stderr b/tests/ui/write_literal_2.stderr new file mode 100644 index 00000000000..5b488358011 --- /dev/null +++ b/tests/ui/write_literal_2.stderr @@ -0,0 +1,106 @@ +error: literal with an empty format string + --> $DIR/write_literal_2.rs:9:28 + | +LL | writeln!(&mut v, "{}", "{hello}"); + | ^^^^^^^^^ + | + = note: `-D clippy::write-literal` implied by `-D warnings` +help: try this + | +LL | writeln!(&mut v, "{{hello}}"); + | ^^^^^^^^^-- + +error: literal with an empty format string + --> $DIR/write_literal_2.rs:10:29 + | +LL | writeln!(&mut v, r"{}", r"{hello}"); + | ^^^^^^^^^^ + | +help: try this + | +LL | writeln!(&mut v, r"{{hello}}"); + | ^^^^^^^^^-- + +error: literal with an empty format string + --> $DIR/write_literal_2.rs:11:28 + | +LL | writeln!(&mut v, "{}", '/''); + | ^^^^ + | +help: try this + | +LL | writeln!(&mut v, "'"); + | ^-- + +error: literal with an empty format string + --> $DIR/write_literal_2.rs:12:28 + | +LL | writeln!(&mut v, "{}", '"'); + | ^^^ + | +help: try this + | +LL | writeln!(&mut v, "/""); + | ^^-- + +error: literal with an empty format string + --> $DIR/write_literal_2.rs:14:29 + | +LL | writeln!(&mut v, r"{}", '/''); + | ^^^^ + | +help: try this + | +LL | writeln!(&mut v, r"'"); + | ^-- + +error: literal with an empty format string + --> $DIR/write_literal_2.rs:18:9 + | +LL | / "hello / +LL | | world!" + | |_______________^ + | +help: try this + | +LL | "some hello / +LL | world!" + | + +error: literal with an empty format string + --> $DIR/write_literal_2.rs:25:9 + | +LL | "1", "2", "3", + | ^^^ + | +help: try this + | +LL | "some 1{} / {}", "2", "3", + | ^ -- + +error: literal with an empty format string + --> $DIR/write_literal_2.rs:25:14 + | +LL | "1", "2", "3", + | ^^^ + | +help: try this + | +LL | 2 / {}", +LL | "1", "3", + | + +error: literal with an empty format string + --> $DIR/write_literal_2.rs:25:19 + | +LL | "1", "2", "3", + | ^^^ + | +help: try this + | +LL | {} / 3", +LL | "1", "2", + | + +error: aborting due to 9 previous errors + -- cgit 1.4.1-3-g733a5 From 0b7ab90ecae3c5ab55aa053ea9477cab88ab7f0e Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 6 Mar 2021 19:25:09 -0500 Subject: Improvements to `match_wildcard_for_single_variants` and `wildcard_enum_match_arm` lints Don't lint on `Result` and `Option` types. Considers `or` patterns. Considers variants prefixed with `Self` Suggestions will try to find a common prefix rather than just using the full path --- clippy_lints/src/attrs.rs | 4 +- clippy_lints/src/doc.rs | 2 +- clippy_lints/src/eval_order_dependence.rs | 2 +- clippy_lints/src/loops/never_loop.rs | 2 +- clippy_lints/src/matches.rs | 245 +++++++++++++-------- clippy_lints/src/redundant_clone.rs | 4 +- clippy_lints/src/types/mod.rs | 6 +- clippy_lints/src/unnecessary_wraps.rs | 2 +- clippy_utils/src/lib.rs | 10 + tests/ui/match_wildcard_for_single_variants.fixed | 40 ++++ tests/ui/match_wildcard_for_single_variants.rs | 40 ++++ tests/ui/match_wildcard_for_single_variants.stderr | 44 +++- tests/ui/wildcard_enum_match_arm.fixed | 2 +- tests/ui/wildcard_enum_match_arm.stderr | 2 +- 14 files changed, 293 insertions(+), 112 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index f805f716e6b..a4ddef666c0 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -430,7 +430,7 @@ fn is_relevant_block(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_ |stmt| match &stmt.kind { StmtKind::Local(_) => true, StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, typeck_results, expr), - _ => false, + StmtKind::Item(_) => false, }, ) } @@ -613,7 +613,7 @@ fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) { } } }, - _ => {}, + MetaItemKind::NameValue(..) => {}, } } } diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 5fa278b2897..14338ac8faf 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -583,7 +583,7 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { let returns_nothing = match &sig.decl.output { FnRetTy::Default(..) => true, FnRetTy::Ty(ty) if ty.kind.is_unit() => true, - _ => false, + FnRetTy::Ty(_) => false, }; if returns_nothing && !is_async && !block.stmts.is_empty() { diff --git a/clippy_lints/src/eval_order_dependence.rs b/clippy_lints/src/eval_order_dependence.rs index 5d5b67de843..ea33a4d98fd 100644 --- a/clippy_lints/src/eval_order_dependence.rs +++ b/clippy_lints/src/eval_order_dependence.rs @@ -273,7 +273,7 @@ fn check_stmt<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, stmt: &'tcx Stmt<'_>) - .init .as_ref() .map_or(StopEarly::KeepGoing, |expr| check_expr(vis, expr)), - _ => StopEarly::KeepGoing, + StmtKind::Item(..) => StopEarly::KeepGoing, } } diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 82ec2635aeb..f63a3761a0d 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -78,7 +78,7 @@ fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<&'tcx Expr<'tcx>> { match stmt.kind { StmtKind::Semi(ref e, ..) | StmtKind::Expr(ref e, ..) => Some(e), StmtKind::Local(ref local) => local.init.as_deref(), - _ => None, + StmtKind::Item(..) => None, } } diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index e94c7094cac..e9926e0bca5 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -8,21 +8,21 @@ use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, pe use clippy_utils::visitors::LocalUsedVisitor; use clippy_utils::{ get_parent_expr, in_macro, is_allowed, is_expn_of, is_refutable, is_wild, match_qpath, meets_msrv, path_to_local, - path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, remove_blocks, strip_pat_refs, + path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, remove_blocks, strip_pat_refs, }; use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; -use rustc_hir::def::CtorKind; +use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::{ - Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Guard, HirId, Local, MatchSource, Mutability, Node, Pat, - PatKind, QPath, RangeEnd, + self as hir, Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Guard, HirId, Local, MatchSource, + Mutability, Node, Pat, PatKind, PathSegment, QPath, RangeEnd, TyKind, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{self, Ty, TyS}; +use rustc_middle::ty::{self, Ty, TyS, VariantDef}; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::{Span, Spanned}; @@ -956,114 +956,181 @@ fn check_wild_err_arm<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm } } -fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { - let ty = cx.typeck_results().expr_ty(ex); - if !ty.is_enum() { - // If there isn't a nice closed set of possible values that can be conveniently enumerated, - // don't complain about not enumerating the mall. - return; +enum CommonPrefixSearcher<'a> { + None, + Path(&'a [PathSegment<'a>]), + Mixed, +} +impl CommonPrefixSearcher<'a> { + fn with_path(&mut self, path: &'a [PathSegment<'a>]) { + match path { + [path @ .., _] => self.with_prefix(path), + [] => (), + } } + fn with_prefix(&mut self, path: &'a [PathSegment<'a>]) { + match self { + Self::None => *self = Self::Path(path), + Self::Path(self_path) + if path + .iter() + .map(|p| p.ident.name) + .eq(self_path.iter().map(|p| p.ident.name)) => {}, + Self::Path(_) => *self = Self::Mixed, + Self::Mixed => (), + } + } +} + +#[allow(clippy::too_many_lines)] +fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { + let ty = cx.typeck_results().expr_ty(ex).peel_refs(); + let adt_def = match ty.kind() { + ty::Adt(adt_def, _) + if adt_def.is_enum() + && !(is_type_diagnostic_item(cx, ty, sym::option_type) + || is_type_diagnostic_item(cx, ty, sym::result_type)) => + { + adt_def + }, + _ => return, + }; + // First pass - check for violation, but don't do much book-keeping because this is hopefully // the uncommon case, and the book-keeping is slightly expensive. let mut wildcard_span = None; let mut wildcard_ident = None; + let mut has_non_wild = false; for arm in arms { - if let PatKind::Wild = arm.pat.kind { - wildcard_span = Some(arm.pat.span); - } else if let PatKind::Binding(_, _, ident, None) = arm.pat.kind { - wildcard_span = Some(arm.pat.span); - wildcard_ident = Some(ident); + match peel_hir_pat_refs(arm.pat).0.kind { + PatKind::Wild => wildcard_span = Some(arm.pat.span), + PatKind::Binding(_, _, ident, None) => { + wildcard_span = Some(arm.pat.span); + wildcard_ident = Some(ident); + }, + _ => has_non_wild = true, } } + let wildcard_span = match wildcard_span { + Some(x) if has_non_wild => x, + _ => return, + }; - if let Some(wildcard_span) = wildcard_span { - // Accumulate the variants which should be put in place of the wildcard because they're not - // already covered. + // Accumulate the variants which should be put in place of the wildcard because they're not + // already covered. + let mut missing_variants: Vec<_> = adt_def.variants.iter().collect(); - let mut missing_variants = vec![]; - if let ty::Adt(def, _) = ty.kind() { - for variant in &def.variants { - missing_variants.push(variant); + let mut path_prefix = CommonPrefixSearcher::None; + for arm in arms { + // Guards mean that this case probably isn't exhaustively covered. Technically + // this is incorrect, as we should really check whether each variant is exhaustively + // covered by the set of guards that cover it, but that's really hard to do. + recurse_or_patterns(arm.pat, |pat| { + let path = match &peel_hir_pat_refs(pat).0.kind { + PatKind::Path(path) => { + #[allow(clippy::match_same_arms)] + let id = match cx.qpath_res(path, pat.hir_id) { + Res::Def(DefKind::Const | DefKind::ConstParam | DefKind::AnonConst, _) => return, + Res::Def(_, id) => id, + _ => return, + }; + if arm.guard.is_none() { + missing_variants.retain(|e| e.ctor_def_id != Some(id)); + } + path + }, + PatKind::TupleStruct(path, patterns, ..) => { + if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p)) { + let id = cx.qpath_res(path, pat.hir_id).def_id(); + missing_variants.retain(|e| e.ctor_def_id != Some(id)); + } + path + }, + PatKind::Struct(path, patterns, ..) => { + if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p.pat)) { + let id = cx.qpath_res(path, pat.hir_id).def_id(); + missing_variants.retain(|e| e.def_id != id); + } + path + }, + _ => return, + }; + match path { + QPath::Resolved(_, path) => path_prefix.with_path(path.segments), + QPath::TypeRelative( + hir::Ty { + kind: TyKind::Path(QPath::Resolved(_, path)), + .. + }, + _, + ) => path_prefix.with_prefix(path.segments), + _ => (), } - } + }); + } - for arm in arms { - if arm.guard.is_some() { - // Guards mean that this case probably isn't exhaustively covered. Technically - // this is incorrect, as we should really check whether each variant is exhaustively - // covered by the set of guards that cover it, but that's really hard to do. - continue; - } - if let PatKind::Path(ref path) = arm.pat.kind { - if let QPath::Resolved(_, p) = path { - missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id())); - } - } else if let PatKind::TupleStruct(QPath::Resolved(_, p), ref patterns, ..) = arm.pat.kind { - // Some simple checks for exhaustive patterns. - // There is a room for improvements to detect more cases, - // but it can be more expensive to do so. - let is_pattern_exhaustive = - |pat: &&Pat<'_>| matches!(pat.kind, PatKind::Wild | PatKind::Binding(.., None)); - if patterns.iter().all(is_pattern_exhaustive) { - missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id())); + let format_suggestion = |variant: &VariantDef| { + format!( + "{}{}{}{}", + if let Some(ident) = wildcard_ident { + format!("{} @ ", ident.name) + } else { + String::new() + }, + if let CommonPrefixSearcher::Path(path_prefix) = path_prefix { + let mut s = String::new(); + for seg in path_prefix { + s.push_str(&seg.ident.as_str()); + s.push_str("::"); } + s + } else { + let mut s = cx.tcx.def_path_str(adt_def.did); + s.push_str("::"); + s + }, + variant.ident.name, + match variant.ctor_kind { + CtorKind::Fn if variant.fields.len() == 1 => "(_)", + CtorKind::Fn => "(..)", + CtorKind::Const => "", + CtorKind::Fictive => "{ .. }", } - } - - let mut suggestion: Vec = missing_variants - .iter() - .map(|v| { - let suffix = match v.ctor_kind { - CtorKind::Fn => "(..)", - CtorKind::Const | CtorKind::Fictive => "", - }; - let ident_str = if let Some(ident) = wildcard_ident { - format!("{} @ ", ident.name) - } else { - String::new() - }; - // This path assumes that the enum type is imported into scope. - format!("{}{}{}", ident_str, cx.tcx.def_path_str(v.def_id), suffix) - }) - .collect(); - - if suggestion.is_empty() { - return; - } - - let mut message = "wildcard match will miss any future added variants"; + ) + }; - if let ty::Adt(def, _) = ty.kind() { - if def.is_variant_list_non_exhaustive() { - message = "match on non-exhaustive enum doesn't explicitly match all known variants"; - suggestion.push(String::from("_")); - } - } + match missing_variants.as_slice() { + [] => (), + [x] if !adt_def.is_variant_list_non_exhaustive() => span_lint_and_sugg( + cx, + MATCH_WILDCARD_FOR_SINGLE_VARIANTS, + wildcard_span, + "match on non-exhaustive enum doesn't explicitly match all known variants", + "try this", + format_suggestion(x), + Applicability::MaybeIncorrect, + ), + variants => { + let mut suggestions: Vec<_> = variants.iter().cloned().map(format_suggestion).collect(); + let message = if adt_def.is_variant_list_non_exhaustive() { + suggestions.push("_".into()); + "match on non-exhaustive enum doesn't explicitly match all known variants" + } else { + "wildcard match will miss any future added variants" + }; - if suggestion.len() == 1 { - // No need to check for non-exhaustive enum as in that case len would be greater than 1 span_lint_and_sugg( cx, - MATCH_WILDCARD_FOR_SINGLE_VARIANTS, + WILDCARD_ENUM_MATCH_ARM, wildcard_span, message, "try this", - suggestion[0].clone(), + suggestions.join(" | "), Applicability::MaybeIncorrect, ) - }; - - span_lint_and_sugg( - cx, - WILDCARD_ENUM_MATCH_ARM, - wildcard_span, - message, - "try this", - suggestion.join(" | "), - Applicability::MaybeIncorrect, - ) - } + }, + }; } // If the block contains only a `panic!` macro (as expression or statement) diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 60da2bcb04a..37678fac1d2 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -556,7 +556,7 @@ impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'tcx> { mir::Operand::Copy(p) | mir::Operand::Move(p) => { self.possible_borrower.add(p.local, *dest); }, - _ => (), + mir::Operand::Constant(..) => (), } } } @@ -578,7 +578,7 @@ fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) { let mut visit_op = |op: &mir::Operand<'_>| match op { mir::Operand::Copy(p) | mir::Operand::Move(p) => visit(p.local), - _ => (), + mir::Operand::Constant(..) => (), }; match rvalue { diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index c0cd48e94b2..10c93c6ad06 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -276,7 +276,7 @@ impl<'tcx> LateLintPass<'tcx> for Types { match item.kind { TraitItemKind::Const(ref ty, _) | TraitItemKind::Type(_, Some(ref ty)) => self.check_ty(cx, ty, false), TraitItemKind::Fn(ref sig, _) => self.check_fn_decl(cx, &sig.decl), - _ => (), + TraitItemKind::Type(..) => (), } } @@ -452,7 +452,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeComplexity { TraitItemKind::Const(ref ty, _) | TraitItemKind::Type(_, Some(ref ty)) => self.check_type(cx, ty), TraitItemKind::Fn(FnSig { ref decl, .. }, TraitFn::Required(_)) => self.check_fndecl(cx, decl), // methods with default impl are covered by check_fn - _ => (), + TraitItemKind::Type(..) | TraitItemKind::Fn(_, TraitFn::Provided(_)) => (), } } @@ -460,7 +460,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeComplexity { match item.kind { ImplItemKind::Const(ref ty, _) | ImplItemKind::TyAlias(ref ty) => self.check_type(cx, ty), // methods are covered by check_fn - _ => (), + ImplItemKind::Fn(..) => (), } } diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index 9e227164695..c2be457e9dc 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -71,7 +71,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { } }, FnKind::Closure => return, - _ => (), + FnKind::Method(..) => (), } // Abort if the method is implementing a trait or of it a trait method. diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 3efa84f6b86..7c8ad4fea9a 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -957,6 +957,16 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { } } +/// If the pattern is an `or` pattern, call the function once for each sub pattern. Otherwise, call +/// the function once on the given pattern. +pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) { + if let PatKind::Or(pats) = pat.kind { + pats.iter().cloned().for_each(f) + } else { + f(pat) + } +} + /// Checks for the `#[automatically_derived]` attribute all `#[derive]`d /// implementations have. pub fn is_automatically_derived(attrs: &[ast::Attribute]) -> bool { diff --git a/tests/ui/match_wildcard_for_single_variants.fixed b/tests/ui/match_wildcard_for_single_variants.fixed index 519200977a7..f101e144a13 100644 --- a/tests/ui/match_wildcard_for_single_variants.fixed +++ b/tests/ui/match_wildcard_for_single_variants.fixed @@ -15,6 +15,16 @@ enum Color { Blue, Rgb(u8, u8, u8), } +impl Color { + fn f(self) { + match self { + Self::Red => (), + Self::Green => (), + Self::Blue => (), + Self::Rgb(..) => (), + }; + } +} fn main() { let f = Foo::A; @@ -56,4 +66,34 @@ fn main() { Color::Rgb(255, _, _) => {}, _ => {}, } + + // References shouldn't change anything + match &color { + &Color::Red => (), + Color::Green => (), + &Color::Rgb(..) => (), + Color::Blue => (), + } + + use self::Color as C; + + match color { + C::Red => (), + C::Green => (), + C::Rgb(..) => (), + C::Blue => (), + } + + match color { + C::Red => (), + Color::Green => (), + Color::Rgb(..) => (), + Color::Blue => (), + } + + match Some(0) { + Some(0) => 0, + Some(_) => 1, + _ => 2, + }; } diff --git a/tests/ui/match_wildcard_for_single_variants.rs b/tests/ui/match_wildcard_for_single_variants.rs index 1df917e085c..1ddba87e78f 100644 --- a/tests/ui/match_wildcard_for_single_variants.rs +++ b/tests/ui/match_wildcard_for_single_variants.rs @@ -15,6 +15,16 @@ enum Color { Blue, Rgb(u8, u8, u8), } +impl Color { + fn f(self) { + match self { + Self::Red => (), + Self::Green => (), + Self::Blue => (), + _ => (), + }; + } +} fn main() { let f = Foo::A; @@ -56,4 +66,34 @@ fn main() { Color::Rgb(255, _, _) => {}, _ => {}, } + + // References shouldn't change anything + match &color { + &Color::Red => (), + Color::Green => (), + &Color::Rgb(..) => (), + &_ => (), + } + + use self::Color as C; + + match color { + C::Red => (), + C::Green => (), + C::Rgb(..) => (), + _ => (), + } + + match color { + C::Red => (), + Color::Green => (), + Color::Rgb(..) => (), + _ => (), + } + + match Some(0) { + Some(0) => 0, + Some(_) => 1, + _ => 2, + }; } diff --git a/tests/ui/match_wildcard_for_single_variants.stderr b/tests/ui/match_wildcard_for_single_variants.stderr index 82790aa9e80..e0900c970c3 100644 --- a/tests/ui/match_wildcard_for_single_variants.stderr +++ b/tests/ui/match_wildcard_for_single_variants.stderr @@ -1,28 +1,52 @@ -error: wildcard match will miss any future added variants - --> $DIR/match_wildcard_for_single_variants.rs:24:9 +error: match on non-exhaustive enum doesn't explicitly match all known variants + --> $DIR/match_wildcard_for_single_variants.rs:24:13 | -LL | _ => {}, - | ^ help: try this: `Foo::C` +LL | _ => (), + | ^ help: try this: `Self::Rgb(..)` | = note: `-D clippy::match-wildcard-for-single-variants` implied by `-D warnings` -error: wildcard match will miss any future added variants +error: match on non-exhaustive enum doesn't explicitly match all known variants --> $DIR/match_wildcard_for_single_variants.rs:34:9 | +LL | _ => {}, + | ^ help: try this: `Foo::C` + +error: match on non-exhaustive enum doesn't explicitly match all known variants + --> $DIR/match_wildcard_for_single_variants.rs:44:9 + | LL | _ => {}, | ^ help: try this: `Color::Blue` -error: wildcard match will miss any future added variants - --> $DIR/match_wildcard_for_single_variants.rs:42:9 +error: match on non-exhaustive enum doesn't explicitly match all known variants + --> $DIR/match_wildcard_for_single_variants.rs:52:9 | LL | _ => {}, | ^ help: try this: `Color::Blue` -error: wildcard match will miss any future added variants - --> $DIR/match_wildcard_for_single_variants.rs:48:9 +error: match on non-exhaustive enum doesn't explicitly match all known variants + --> $DIR/match_wildcard_for_single_variants.rs:58:9 | LL | _ => {}, | ^ help: try this: `Color::Blue` -error: aborting due to 4 previous errors +error: match on non-exhaustive enum doesn't explicitly match all known variants + --> $DIR/match_wildcard_for_single_variants.rs:75:9 + | +LL | &_ => (), + | ^^ help: try this: `Color::Blue` + +error: match on non-exhaustive enum doesn't explicitly match all known variants + --> $DIR/match_wildcard_for_single_variants.rs:84:9 + | +LL | _ => (), + | ^ help: try this: `C::Blue` + +error: match on non-exhaustive enum doesn't explicitly match all known variants + --> $DIR/match_wildcard_for_single_variants.rs:91:9 + | +LL | _ => (), + | ^ help: try this: `Color::Blue` + +error: aborting due to 8 previous errors diff --git a/tests/ui/wildcard_enum_match_arm.fixed b/tests/ui/wildcard_enum_match_arm.fixed index c266f684a36..fd754e4c794 100644 --- a/tests/ui/wildcard_enum_match_arm.fixed +++ b/tests/ui/wildcard_enum_match_arm.fixed @@ -77,7 +77,7 @@ fn main() { let error_kind = ErrorKind::NotFound; match error_kind { ErrorKind::NotFound => {}, - std::io::ErrorKind::PermissionDenied | std::io::ErrorKind::ConnectionRefused | std::io::ErrorKind::ConnectionReset | std::io::ErrorKind::ConnectionAborted | std::io::ErrorKind::NotConnected | std::io::ErrorKind::AddrInUse | std::io::ErrorKind::AddrNotAvailable | std::io::ErrorKind::BrokenPipe | std::io::ErrorKind::AlreadyExists | std::io::ErrorKind::WouldBlock | std::io::ErrorKind::InvalidInput | std::io::ErrorKind::InvalidData | std::io::ErrorKind::TimedOut | std::io::ErrorKind::WriteZero | std::io::ErrorKind::Interrupted | std::io::ErrorKind::Other | std::io::ErrorKind::UnexpectedEof | _ => {}, + ErrorKind::PermissionDenied | ErrorKind::ConnectionRefused | ErrorKind::ConnectionReset | ErrorKind::ConnectionAborted | ErrorKind::NotConnected | ErrorKind::AddrInUse | ErrorKind::AddrNotAvailable | ErrorKind::BrokenPipe | ErrorKind::AlreadyExists | ErrorKind::WouldBlock | ErrorKind::InvalidInput | ErrorKind::InvalidData | ErrorKind::TimedOut | ErrorKind::WriteZero | ErrorKind::Interrupted | ErrorKind::Other | ErrorKind::UnexpectedEof | _ => {}, } match error_kind { ErrorKind::NotFound => {}, diff --git a/tests/ui/wildcard_enum_match_arm.stderr b/tests/ui/wildcard_enum_match_arm.stderr index 0da2b68ba0b..8d880b7ce0a 100644 --- a/tests/ui/wildcard_enum_match_arm.stderr +++ b/tests/ui/wildcard_enum_match_arm.stderr @@ -32,7 +32,7 @@ error: match on non-exhaustive enum doesn't explicitly match all known variants --> $DIR/wildcard_enum_match_arm.rs:80:9 | LL | _ => {}, - | ^ help: try this: `std::io::ErrorKind::PermissionDenied | std::io::ErrorKind::ConnectionRefused | std::io::ErrorKind::ConnectionReset | std::io::ErrorKind::ConnectionAborted | std::io::ErrorKind::NotConnected | std::io::ErrorKind::AddrInUse | std::io::ErrorKind::AddrNotAvailable | std::io::ErrorKind::BrokenPipe | std::io::ErrorKind::AlreadyExists | std::io::ErrorKind::WouldBlock | std::io::ErrorKind::InvalidInput | std::io::ErrorKind::InvalidData | std::io::ErrorKind::TimedOut | std::io::ErrorKind::WriteZero | std::io::ErrorKind::Interrupted | std::io::ErrorKind::Other | std::io::ErrorKind::UnexpectedEof | _` + | ^ help: try this: `ErrorKind::PermissionDenied | ErrorKind::ConnectionRefused | ErrorKind::ConnectionReset | ErrorKind::ConnectionAborted | ErrorKind::NotConnected | ErrorKind::AddrInUse | ErrorKind::AddrNotAvailable | ErrorKind::BrokenPipe | ErrorKind::AlreadyExists | ErrorKind::WouldBlock | ErrorKind::InvalidInput | ErrorKind::InvalidData | ErrorKind::TimedOut | ErrorKind::WriteZero | ErrorKind::Interrupted | ErrorKind::Other | ErrorKind::UnexpectedEof | _` error: aborting due to 5 previous errors -- cgit 1.4.1-3-g733a5 From 6cc9cac4bc7a2eca8528bfa78383dac68cc5b7bf Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 8 Mar 2021 10:33:36 -0500 Subject: Add test for `#[non_exhaustive]` enum in `match_wildcard_for_single-variant` --- tests/ui/match_wildcard_for_single_variants.fixed | 12 ++++++++++++ tests/ui/match_wildcard_for_single_variants.rs | 12 ++++++++++++ 2 files changed, 24 insertions(+) (limited to 'tests') diff --git a/tests/ui/match_wildcard_for_single_variants.fixed b/tests/ui/match_wildcard_for_single_variants.fixed index f101e144a13..d99f9af3faf 100644 --- a/tests/ui/match_wildcard_for_single_variants.fixed +++ b/tests/ui/match_wildcard_for_single_variants.fixed @@ -96,4 +96,16 @@ fn main() { Some(_) => 1, _ => 2, }; + + #[non_exhaustive] + enum Bar { + A, + B, + C, + } + match Bar::A { + Bar::A => (), + Bar::B => (), + _ => (), + }; } diff --git a/tests/ui/match_wildcard_for_single_variants.rs b/tests/ui/match_wildcard_for_single_variants.rs index 1ddba87e78f..1752a95de4b 100644 --- a/tests/ui/match_wildcard_for_single_variants.rs +++ b/tests/ui/match_wildcard_for_single_variants.rs @@ -96,4 +96,16 @@ fn main() { Some(_) => 1, _ => 2, }; + + #[non_exhaustive] + enum Bar { + A, + B, + C, + } + match Bar::A { + Bar::A => (), + Bar::B => (), + _ => (), + }; } -- cgit 1.4.1-3-g733a5 From d5a7941ead06e1cb89a0a2cc6ea5c19810daea03 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 16 Mar 2021 12:56:08 -0400 Subject: Fix message for `match_wildcard_for_single_variant` --- clippy_lints/src/matches.rs | 6 +++--- tests/ui/match_wildcard_for_single_variants.stderr | 16 ++++++++-------- tests/ui/wildcard_enum_match_arm.stderr | 10 +++++----- 3 files changed, 16 insertions(+), 16 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index e9926e0bca5..d43cb32ee51 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -1106,7 +1106,7 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) cx, MATCH_WILDCARD_FOR_SINGLE_VARIANTS, wildcard_span, - "match on non-exhaustive enum doesn't explicitly match all known variants", + "wildcard matches only a single variant and will also match any future added variants", "try this", format_suggestion(x), Applicability::MaybeIncorrect, @@ -1115,9 +1115,9 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) let mut suggestions: Vec<_> = variants.iter().cloned().map(format_suggestion).collect(); let message = if adt_def.is_variant_list_non_exhaustive() { suggestions.push("_".into()); - "match on non-exhaustive enum doesn't explicitly match all known variants" + "wildcard matches known variants and will also match future added variants" } else { - "wildcard match will miss any future added variants" + "wildcard match will also match any future added variants" }; span_lint_and_sugg( diff --git a/tests/ui/match_wildcard_for_single_variants.stderr b/tests/ui/match_wildcard_for_single_variants.stderr index e0900c970c3..34538dea8e5 100644 --- a/tests/ui/match_wildcard_for_single_variants.stderr +++ b/tests/ui/match_wildcard_for_single_variants.stderr @@ -1,4 +1,4 @@ -error: match on non-exhaustive enum doesn't explicitly match all known variants +error: wildcard matches only a single variant and will also match any future added variants --> $DIR/match_wildcard_for_single_variants.rs:24:13 | LL | _ => (), @@ -6,43 +6,43 @@ LL | _ => (), | = note: `-D clippy::match-wildcard-for-single-variants` implied by `-D warnings` -error: match on non-exhaustive enum doesn't explicitly match all known variants +error: wildcard matches only a single variant and will also match any future added variants --> $DIR/match_wildcard_for_single_variants.rs:34:9 | LL | _ => {}, | ^ help: try this: `Foo::C` -error: match on non-exhaustive enum doesn't explicitly match all known variants +error: wildcard matches only a single variant and will also match any future added variants --> $DIR/match_wildcard_for_single_variants.rs:44:9 | LL | _ => {}, | ^ help: try this: `Color::Blue` -error: match on non-exhaustive enum doesn't explicitly match all known variants +error: wildcard matches only a single variant and will also match any future added variants --> $DIR/match_wildcard_for_single_variants.rs:52:9 | LL | _ => {}, | ^ help: try this: `Color::Blue` -error: match on non-exhaustive enum doesn't explicitly match all known variants +error: wildcard matches only a single variant and will also match any future added variants --> $DIR/match_wildcard_for_single_variants.rs:58:9 | LL | _ => {}, | ^ help: try this: `Color::Blue` -error: match on non-exhaustive enum doesn't explicitly match all known variants +error: wildcard matches only a single variant and will also match any future added variants --> $DIR/match_wildcard_for_single_variants.rs:75:9 | LL | &_ => (), | ^^ help: try this: `Color::Blue` -error: match on non-exhaustive enum doesn't explicitly match all known variants +error: wildcard matches only a single variant and will also match any future added variants --> $DIR/match_wildcard_for_single_variants.rs:84:9 | LL | _ => (), | ^ help: try this: `C::Blue` -error: match on non-exhaustive enum doesn't explicitly match all known variants +error: wildcard matches only a single variant and will also match any future added variants --> $DIR/match_wildcard_for_single_variants.rs:91:9 | LL | _ => (), diff --git a/tests/ui/wildcard_enum_match_arm.stderr b/tests/ui/wildcard_enum_match_arm.stderr index 8d880b7ce0a..a513a62c748 100644 --- a/tests/ui/wildcard_enum_match_arm.stderr +++ b/tests/ui/wildcard_enum_match_arm.stderr @@ -1,4 +1,4 @@ -error: wildcard match will miss any future added variants +error: wildcard match will also match any future added variants --> $DIR/wildcard_enum_match_arm.rs:39:9 | LL | _ => eprintln!("Not red"), @@ -10,25 +10,25 @@ note: the lint level is defined here LL | #![deny(clippy::wildcard_enum_match_arm)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: wildcard match will miss any future added variants +error: wildcard match will also match any future added variants --> $DIR/wildcard_enum_match_arm.rs:43:9 | LL | _not_red => eprintln!("Not red"), | ^^^^^^^^ help: try this: `_not_red @ Color::Green | _not_red @ Color::Blue | _not_red @ Color::Rgb(..) | _not_red @ Color::Cyan` -error: wildcard match will miss any future added variants +error: wildcard match will also match any future added variants --> $DIR/wildcard_enum_match_arm.rs:47:9 | LL | not_red => format!("{:?}", not_red), | ^^^^^^^ help: try this: `not_red @ Color::Green | not_red @ Color::Blue | not_red @ Color::Rgb(..) | not_red @ Color::Cyan` -error: wildcard match will miss any future added variants +error: wildcard match will also match any future added variants --> $DIR/wildcard_enum_match_arm.rs:63:9 | LL | _ => "No red", | ^ help: try this: `Color::Red | Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan` -error: match on non-exhaustive enum doesn't explicitly match all known variants +error: wildcard matches known variants and will also match future added variants --> $DIR/wildcard_enum_match_arm.rs:80:9 | LL | _ => {}, -- cgit 1.4.1-3-g733a5 From f468d822830b36bb61d2ecd90fe3f1e9e1b90cf4 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 5 Mar 2021 15:57:37 -0500 Subject: Fix `manual_map` suggestion for `if let.. else ... if let.. else` chain --- clippy_lints/src/manual_map.rs | 18 +++++++++++++----- clippy_utils/src/lib.rs | 20 ++++++++++++++++++++ tests/ui/manual_map_option.fixed | 5 +++++ tests/ui/manual_map_option.rs | 9 +++++++++ tests/ui/manual_map_option.stderr | 21 ++++++++++++++++++++- 5 files changed, 67 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs index cd9594737bf..ed157783b72 100644 --- a/clippy_lints/src/manual_map.rs +++ b/clippy_lints/src/manual_map.rs @@ -2,13 +2,13 @@ use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::ty::{can_partially_move_ty, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; -use clippy_utils::{is_allowed, match_def_path, match_var, paths, peel_hir_expr_refs}; +use clippy_utils::{is_allowed, is_else_clause_of_if_let_else, match_def_path, match_var, paths, peel_hir_expr_refs}; use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::Applicability; use rustc_hir::{ def::Res, intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor}, - Arm, BindingAnnotation, Block, Expr, ExprKind, Mutability, Pat, PatKind, Path, QPath, + Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind, Path, QPath, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -51,8 +51,11 @@ impl LateLintPass<'_> for ManualMap { return; } - if let ExprKind::Match(scrutinee, [arm1 @ Arm { guard: None, .. }, arm2 @ Arm { guard: None, .. }], _) = - expr.kind + if let ExprKind::Match( + scrutinee, + [arm1 @ Arm { guard: None, .. }, arm2 @ Arm { guard: None, .. }], + match_kind, + ) = expr.kind { let (scrutinee_ty, ty_ref_count, ty_mutability) = peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee)); @@ -178,7 +181,12 @@ impl LateLintPass<'_> for ManualMap { expr.span, "manual implementation of `Option::map`", "try this", - format!("{}{}.map({})", scrutinee_str, as_ref_str, body_str), + if matches!(match_kind, MatchSource::IfLetDesugar { .. }) && is_else_clause_of_if_let_else(cx.tcx, expr) + { + format!("{{ {}{}.map({}) }}", scrutinee_str, as_ref_str, body_str) + } else { + format!("{}{}.map({})", scrutinee_str, as_ref_str, body_str) + }, app, ); } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 3efa84f6b86..d5a5430546d 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -798,6 +798,26 @@ pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> { } } +/// Checks if the given expression is the else clause in the expression `if let .. {} else {}` +pub fn is_else_clause_of_if_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { + let map = tcx.hir(); + let mut iter = map.parent_iter(expr.hir_id); + let arm_id = match iter.next() { + Some((id, Node::Arm(..))) => id, + _ => return false, + }; + match iter.next() { + Some(( + _, + Node::Expr(Expr { + kind: ExprKind::Match(_, [_, else_arm], kind), + .. + }), + )) => else_arm.hir_id == arm_id && matches!(kind, MatchSource::IfLetDesugar { .. }), + _ => false, + } +} + /// Checks whether the given expression is a constant integer of the given value. /// unlike `is_integer_literal`, this version does const folding pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool { diff --git a/tests/ui/manual_map_option.fixed b/tests/ui/manual_map_option.fixed index 9222aaf6c78..acb6a580ceb 100644 --- a/tests/ui/manual_map_option.fixed +++ b/tests/ui/manual_map_option.fixed @@ -128,4 +128,9 @@ fn main() { None => None, }; } + + // #6847 + if Some(0).is_some() { + Some(0) + } else { Some(0).map(|x| x + 1) }; } diff --git a/tests/ui/manual_map_option.rs b/tests/ui/manual_map_option.rs index 1ccb450619c..3299e617707 100644 --- a/tests/ui/manual_map_option.rs +++ b/tests/ui/manual_map_option.rs @@ -186,4 +186,13 @@ fn main() { None => None, }; } + + // #6847 + if let Some(_) = Some(0) { + Some(0) + } else if let Some(x) = Some(0) { + Some(x + 1) + } else { + None + }; } diff --git a/tests/ui/manual_map_option.stderr b/tests/ui/manual_map_option.stderr index d9f86eecd93..048ccfb9582 100644 --- a/tests/ui/manual_map_option.stderr +++ b/tests/ui/manual_map_option.stderr @@ -172,5 +172,24 @@ LL | | None => None, LL | | }; | |_____^ help: try this: `option_env!("").map(String::from)` -error: aborting due to 19 previous errors +error: redundant pattern matching, consider using `is_some()` + --> $DIR/manual_map_option.rs:191:12 + | +LL | if let Some(_) = Some(0) { + | -------^^^^^^^---------- help: try this: `if Some(0).is_some()` + | + = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` + +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:193:12 + | +LL | } else if let Some(x) = Some(0) { + | ____________^ +LL | | Some(x + 1) +LL | | } else { +LL | | None +LL | | }; + | |_____^ help: try this: `{ Some(0).map(|x| x + 1) }` + +error: aborting due to 21 previous errors -- cgit 1.4.1-3-g733a5 From ea15fb2177b0c33352363c130bc296a0dd1a794a Mon Sep 17 00:00:00 2001 From: Mateusz Gacek <96mateusz.gacek@gmail.com> Date: Wed, 17 Mar 2021 15:52:14 +0100 Subject: wrong_self_convention: `to_` respects `Copy` types More details here: https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv --- clippy_lints/src/methods/mod.rs | 39 +++++++----- clippy_lints/src/methods/wrong_self_convention.rs | 74 +++++++++++++---------- tests/ui/use_self.fixed | 1 + tests/ui/use_self.rs | 1 + tests/ui/use_self.stderr | 46 +++++++------- tests/ui/wrong_self_convention.rs | 32 ++++++++++ tests/ui/wrong_self_convention.stderr | 36 +++++------ tests/ui/wrong_self_conventions_mut.stderr | 4 +- 8 files changed, 143 insertions(+), 90 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index af8fe7abd96..722effc29b2 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -193,14 +193,18 @@ declare_clippy_lint! { /// **What it does:** Checks for methods with certain name prefixes and which /// doesn't match how self is taken. The actual rules are: /// - /// |Prefix |Postfix |`self` taken | - /// |-------|------------|----------------------| - /// |`as_` | none |`&self` or `&mut self`| - /// |`from_`| none | none | - /// |`into_`| none |`self` | - /// |`is_` | none |`&self` or none | - /// |`to_` | `_mut` |`&mut &self` | - /// |`to_` | not `_mut` |`&self` | + /// |Prefix |Postfix |`self` taken | `self` type | + /// |-------|------------|-----------------------|--------------| + /// |`as_` | none |`&self` or `&mut self` | any | + /// |`from_`| none | none | any | + /// |`into_`| none |`self` | any | + /// |`is_` | none |`&self` or none | any | + /// |`to_` | `_mut` |`&mut self` | any | + /// |`to_` | not `_mut` |`self` | `Copy` | + /// |`to_` | not `_mut` |`&self` | not `Copy` | + /// + /// Please find more info here: + /// https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv /// /// **Why is this bad?** Consistency breeds readability. If you follow the /// conventions, your users won't be surprised that they, e.g., need to supply a @@ -1837,10 +1841,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { let item = cx.tcx.hir().expect_item(parent); let self_ty = cx.tcx.type_of(item.def_id); - // if this impl block implements a trait, lint in trait definition instead - if let hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) = item.kind { - return; - } + let implements_trait = matches!(item.kind, hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. })); if_chain! { if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind; @@ -1855,7 +1856,8 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if let Some(first_arg_ty) = first_arg_ty; then { - if cx.access_levels.is_exported(impl_item.hir_id()) { + // if this impl block implements a trait, lint in trait definition instead + if !implements_trait && cx.access_levels.is_exported(impl_item.hir_id()) { // check missing trait implementations for method_config in &TRAIT_METHODS { if name == method_config.method_name && @@ -1891,11 +1893,17 @@ impl<'tcx> LateLintPass<'tcx> for Methods { item.vis.node.is_pub(), self_ty, first_arg_ty, - first_arg.pat.span + first_arg.pat.span, + false ); } } + // if this impl block implements a trait, lint in trait definition instead + if implements_trait { + return; + } + if let hir::ImplItemKind::Fn(_, _) = impl_item.kind { let ret_ty = return_ty(cx, impl_item.hir_id()); @@ -1947,7 +1955,8 @@ impl<'tcx> LateLintPass<'tcx> for Methods { false, self_ty, first_arg_ty, - first_arg_span + first_arg_span, + true ); } } diff --git a/clippy_lints/src/methods/wrong_self_convention.rs b/clippy_lints/src/methods/wrong_self_convention.rs index b728d7d8d08..bece8f251da 100644 --- a/clippy_lints/src/methods/wrong_self_convention.rs +++ b/clippy_lints/src/methods/wrong_self_convention.rs @@ -1,5 +1,6 @@ use crate::methods::SelfKind; use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::ty::is_copy; use rustc_lint::LateContext; use rustc_middle::ty::TyS; use rustc_span::source_map::Span; @@ -9,7 +10,7 @@ use super::WRONG_PUB_SELF_CONVENTION; use super::WRONG_SELF_CONVENTION; #[rustfmt::skip] -const CONVENTIONS: [(&[Convention], &[SelfKind]); 8] = [ +const CONVENTIONS: [(&[Convention], &[SelfKind]); 9] = [ (&[Convention::Eq("new")], &[SelfKind::No]), (&[Convention::StartsWith("as_")], &[SelfKind::Ref, SelfKind::RefMut]), (&[Convention::StartsWith("from_")], &[SelfKind::No]), @@ -17,7 +18,11 @@ const CONVENTIONS: [(&[Convention], &[SelfKind]); 8] = [ (&[Convention::StartsWith("is_")], &[SelfKind::Ref, SelfKind::No]), (&[Convention::Eq("to_mut")], &[SelfKind::RefMut]), (&[Convention::StartsWith("to_"), Convention::EndsWith("_mut")], &[SelfKind::RefMut]), - (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut")], &[SelfKind::Ref]), + + // Conversion using `to_` can use borrowed (non-Copy types) or owned (Copy types). + // Source: https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv + (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(false), Convention::ImplementsTrait(false)], &[SelfKind::Ref]), + (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(true), Convention::ImplementsTrait(false)], &[SelfKind::Value]), ]; enum Convention { @@ -25,16 +30,20 @@ enum Convention { StartsWith(&'static str), EndsWith(&'static str), NotEndsWith(&'static str), + IsSelfTypeCopy(bool), + ImplementsTrait(bool), } impl Convention { #[must_use] - fn check(&self, other: &str) -> bool { + fn check<'tcx>(&self, cx: &LateContext<'tcx>, self_ty: &'tcx TyS<'tcx>, other: &str, is_trait_def: bool) -> bool { match *self { Self::Eq(this) => this == other, Self::StartsWith(this) => other.starts_with(this) && this != other, Self::EndsWith(this) => other.ends_with(this) && this != other, - Self::NotEndsWith(this) => !Self::EndsWith(this).check(other), + Self::NotEndsWith(this) => !Self::EndsWith(this).check(cx, self_ty, other, is_trait_def), + Self::IsSelfTypeCopy(is_true) => is_true == is_copy(cx, self_ty), + Self::ImplementsTrait(is_true) => is_true == is_trait_def, } } } @@ -46,6 +55,10 @@ impl fmt::Display for Convention { Self::StartsWith(this) => this.fmt(f).and_then(|_| '*'.fmt(f)), Self::EndsWith(this) => '*'.fmt(f).and_then(|_| this.fmt(f)), Self::NotEndsWith(this) => '~'.fmt(f).and_then(|_| this.fmt(f)), + Self::IsSelfTypeCopy(is_true) => format!("self type is {} Copy", if is_true { "" } else { "not" }).fmt(f), + Self::ImplementsTrait(is_true) => { + format!("Method {} implement a trait", if is_true { "" } else { "do not" }).fmt(f) + }, } } } @@ -57,45 +70,42 @@ pub(super) fn check<'tcx>( self_ty: &'tcx TyS<'tcx>, first_arg_ty: &'tcx TyS<'tcx>, first_arg_span: Span, + is_trait_item: bool, ) { let lint = if is_pub { WRONG_PUB_SELF_CONVENTION } else { WRONG_SELF_CONVENTION }; - if let Some((conventions, self_kinds)) = &CONVENTIONS - .iter() - .find(|(convs, _)| convs.iter().all(|conv| conv.check(item_name))) - { + if let Some((conventions, self_kinds)) = &CONVENTIONS.iter().find(|(convs, _)| { + convs + .iter() + .all(|conv| conv.check(cx, self_ty, item_name, is_trait_item)) + }) { if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) { let suggestion = { if conventions.len() > 1 { - let special_case = { - // Don't mention `NotEndsWith` when there is also `StartsWith` convention present - if conventions.len() == 2 { - match conventions { - [Convention::StartsWith(starts_with), Convention::NotEndsWith(_)] - | [Convention::NotEndsWith(_), Convention::StartsWith(starts_with)] => { - Some(format!("methods called `{}`", Convention::StartsWith(starts_with))) - }, - _ => None, - } - } else { - None - } - }; - - if let Some(suggestion) = special_case { - suggestion - } else { - let s = conventions + // Don't mention `NotEndsWith` when there is also `StartsWith` convention present + let cut_ends_with_conv = conventions.iter().any(|conv| matches!(conv, Convention::StartsWith(_))) + && conventions .iter() - .map(|c| format!("`{}`", &c.to_string())) - .collect::>() - .join(" and "); + .any(|conv| matches!(conv, Convention::NotEndsWith(_))); + + let s = conventions + .iter() + .filter_map(|conv| { + if (cut_ends_with_conv && matches!(conv, Convention::NotEndsWith(_))) + || matches!(conv, Convention::ImplementsTrait(_)) + { + None + } else { + Some(format!("`{}`", &conv.to_string())) + } + }) + .collect::>() + .join(" and "); - format!("methods called like this: ({})", &s) - } + format!("methods with the following characteristics: ({})", &s) } else { format!("methods called `{}`", &conventions[0]) } diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed index a630936e3b1..1986b54e66c 100644 --- a/tests/ui/use_self.fixed +++ b/tests/ui/use_self.fixed @@ -79,6 +79,7 @@ mod issue2894 { } // This should not be linted + #[allow(clippy::wrong_self_convention)] impl IntoBytes for u8 { fn to_bytes(&self) -> Vec { vec![*self] diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index f3e081dd203..acc18280f1a 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -79,6 +79,7 @@ mod issue2894 { } // This should not be linted + #[allow(clippy::wrong_self_convention)] impl IntoBytes for u8 { fn to_bytes(&self) -> Vec { vec![*self] diff --git a/tests/ui/use_self.stderr b/tests/ui/use_self.stderr index e1410d2e652..a2c95963487 100644 --- a/tests/ui/use_self.stderr +++ b/tests/ui/use_self.stderr @@ -37,139 +37,139 @@ LL | Foo::new() | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:93:24 + --> $DIR/use_self.rs:94:24 | LL | fn bad(foos: &[Foo]) -> impl Iterator { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:93:55 + --> $DIR/use_self.rs:94:55 | LL | fn bad(foos: &[Foo]) -> impl Iterator { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:108:13 + --> $DIR/use_self.rs:109:13 | LL | TS(0) | ^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:143:29 + --> $DIR/use_self.rs:144:29 | LL | fn bar() -> Bar { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:144:21 + --> $DIR/use_self.rs:145:21 | LL | Bar { foo: Foo {} } | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:155:21 + --> $DIR/use_self.rs:156:21 | LL | fn baz() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:156:13 + --> $DIR/use_self.rs:157:13 | LL | Foo {} | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:173:21 + --> $DIR/use_self.rs:174:21 | LL | let _ = Enum::B(42); | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:174:21 + --> $DIR/use_self.rs:175:21 | LL | let _ = Enum::C { field: true }; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:175:21 + --> $DIR/use_self.rs:176:21 | LL | let _ = Enum::A; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:217:13 + --> $DIR/use_self.rs:218:13 | LL | nested::A::fun_1(); | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:218:13 + --> $DIR/use_self.rs:219:13 | LL | nested::A::A; | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:220:13 + --> $DIR/use_self.rs:221:13 | LL | nested::A {}; | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:239:13 + --> $DIR/use_self.rs:240:13 | LL | TestStruct::from_something() | ^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:253:25 + --> $DIR/use_self.rs:254:25 | LL | async fn g() -> S { | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:254:13 + --> $DIR/use_self.rs:255:13 | LL | S {} | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:258:16 + --> $DIR/use_self.rs:259:16 | LL | &p[S::A..S::B] | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:258:22 + --> $DIR/use_self.rs:259:22 | LL | &p[S::A..S::B] | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:281:29 + --> $DIR/use_self.rs:282:29 | LL | fn foo(value: T) -> Foo { | ^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:282:13 + --> $DIR/use_self.rs:283:13 | LL | Foo { value } | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:319:21 + --> $DIR/use_self.rs:320:21 | LL | type From = T::From; | ^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:320:19 + --> $DIR/use_self.rs:321:19 | LL | type To = T::To; | ^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:453:13 + --> $DIR/use_self.rs:454:13 | LL | A::new::(submod::B {}) | ^ help: use the applicable keyword: `Self` diff --git a/tests/ui/wrong_self_convention.rs b/tests/ui/wrong_self_convention.rs index 6cfc0fcb4ca..ba9e19a1722 100644 --- a/tests/ui/wrong_self_convention.rs +++ b/tests/ui/wrong_self_convention.rs @@ -163,3 +163,35 @@ mod issue6307 { fn to_mut(&mut self); } } + +mod issue6727 { + trait ToU64 { + fn to_u64(self) -> u64; + fn to_u64_v2(&self) -> u64; + } + + #[derive(Clone, Copy)] + struct FooCopy; + + impl ToU64 for FooCopy { + fn to_u64(self) -> u64 { + 1 + } + // trigger lint + fn to_u64_v2(&self) -> u64 { + 1 + } + } + + struct FooNoCopy; + + impl ToU64 for FooNoCopy { + // trigger lint + fn to_u64(self) -> u64 { + 2 + } + fn to_u64_v2(&self) -> u64 { + 2 + } + } +} diff --git a/tests/ui/wrong_self_convention.stderr b/tests/ui/wrong_self_convention.stderr index f43fea0d513..6daa334ed48 100644 --- a/tests/ui/wrong_self_convention.stderr +++ b/tests/ui/wrong_self_convention.stderr @@ -39,7 +39,7 @@ LL | fn is_i32(self) {} | = help: consider choosing a less ambiguous name -error: methods called `to_*` usually take self by reference +error: methods with the following characteristics: (`to_*` and `self type is not Copy`) usually take self by reference --> $DIR/wrong_self_convention.rs:42:15 | LL | fn to_i32(self) {} @@ -79,7 +79,7 @@ LL | pub fn is_i64(self) {} | = help: consider choosing a less ambiguous name -error: methods called `to_*` usually take self by reference +error: methods with the following characteristics: (`to_*` and `self type is not Copy`) usually take self by reference --> $DIR/wrong_self_convention.rs:49:19 | LL | pub fn to_i64(self) {} @@ -119,14 +119,6 @@ LL | fn is_i32(self) {} | = help: consider choosing a less ambiguous name -error: methods called `to_*` usually take self by reference - --> $DIR/wrong_self_convention.rs:102:19 - | -LL | fn to_i32(self) {} - | ^^^^ - | - = help: consider choosing a less ambiguous name - error: methods called `from_*` usually take no self --> $DIR/wrong_self_convention.rs:104:21 | @@ -159,14 +151,6 @@ LL | fn is_i32(self); | = help: consider choosing a less ambiguous name -error: methods called `to_*` usually take self by reference - --> $DIR/wrong_self_convention.rs:126:19 - | -LL | fn to_i32(self); - | ^^^^ - | - = help: consider choosing a less ambiguous name - error: methods called `from_*` usually take no self --> $DIR/wrong_self_convention.rs:128:21 | @@ -191,5 +175,21 @@ LL | fn from_i32(self); | = help: consider choosing a less ambiguous name +error: methods with the following characteristics: (`to_*` and `self type is Copy`) usually take self by value + --> $DIR/wrong_self_convention.rs:181:22 + | +LL | fn to_u64_v2(&self) -> u64 { + | ^^^^^ + | + = help: consider choosing a less ambiguous name + +error: methods with the following characteristics: (`to_*` and `self type is not Copy`) usually take self by reference + --> $DIR/wrong_self_convention.rs:190:19 + | +LL | fn to_u64(self) -> u64 { + | ^^^^ + | + = help: consider choosing a less ambiguous name + error: aborting due to 24 previous errors diff --git a/tests/ui/wrong_self_conventions_mut.stderr b/tests/ui/wrong_self_conventions_mut.stderr index 7662b38e67d..8095188758c 100644 --- a/tests/ui/wrong_self_conventions_mut.stderr +++ b/tests/ui/wrong_self_conventions_mut.stderr @@ -1,4 +1,4 @@ -error: methods called `to_*` usually take self by reference +error: methods with the following characteristics: (`to_*` and `self type is not Copy`) usually take self by reference --> $DIR/wrong_self_conventions_mut.rs:15:24 | LL | pub fn to_many(&mut self) -> Option<&mut [T]> { @@ -7,7 +7,7 @@ LL | pub fn to_many(&mut self) -> Option<&mut [T]> { = note: `-D clippy::wrong-self-convention` implied by `-D warnings` = help: consider choosing a less ambiguous name -error: methods called like this: (`to_*` and `*_mut`) usually take self by mutable reference +error: methods with the following characteristics: (`to_*` and `*_mut`) usually take self by mutable reference --> $DIR/wrong_self_conventions_mut.rs:23:28 | LL | pub fn to_many_mut(&self) -> Option<&[T]> { -- cgit 1.4.1-3-g733a5 From b1f89ee02ff05d559a27949601bef628e498b128 Mon Sep 17 00:00:00 2001 From: Mateusz Gacek <96mateusz.gacek@gmail.com> Date: Wed, 17 Mar 2021 20:29:31 +0100 Subject: or_fun_call: trigger on unsafe blocks --- clippy_lints/src/methods/or_fun_call.rs | 13 +++++++++++-- tests/ui/or_fun_call.fixed | 14 ++++++++++++++ tests/ui/or_fun_call.rs | 14 ++++++++++++++ tests/ui/or_fun_call.stderr | 20 +++++++++++++++++++- 4 files changed, 58 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index 1b43802a08e..22df3e028f9 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -6,6 +6,7 @@ use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; +use rustc_hir::{BlockCheckMode, UnsafeSource}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::source_map::Span; @@ -154,7 +155,6 @@ pub(super) fn check<'tcx>( } } } - if args.len() == 2 { match args[1].kind { hir::ExprKind::Call(ref fun, ref or_args) => { @@ -167,7 +167,16 @@ 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); + } + } + } + }, + _ => (), } } } diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 64347cae5da..660245f10c3 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -132,4 +132,18 @@ fn f() -> Option<()> { Some(()) } +mod issue6675 { + unsafe fn foo() { + let mut s = "test".to_owned(); + None.unwrap_or_else(|| s.as_mut_vec()); + } + + fn bar() { + let mut s = "test".to_owned(); + None.unwrap_or_else(|| unsafe { s.as_mut_vec() }); + #[rustfmt::skip] + None.unwrap_or_else(|| unsafe { s.as_mut_vec() }); + } +} + fn main() {} diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 7faab0017b2..c589b170bdd 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -132,4 +132,18 @@ fn f() -> Option<()> { Some(()) } +mod issue6675 { + unsafe fn foo() { + let mut s = "test".to_owned(); + None.unwrap_or(s.as_mut_vec()); + } + + fn bar() { + let mut s = "test".to_owned(); + None.unwrap_or(unsafe { s.as_mut_vec() }); + #[rustfmt::skip] + None.unwrap_or( unsafe { s.as_mut_vec() } ); + } +} + fn main() {} diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index 1e2bfd490e0..d63b3443592 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -114,5 +114,23 @@ error: use of `or` followed by a function call LL | .or(Some(Bar(b, Duration::from_secs(2)))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some(Bar(b, Duration::from_secs(2))))` -error: aborting due to 19 previous errors +error: use of `unwrap_or` followed by a function call + --> $DIR/or_fun_call.rs:138:14 + | +LL | None.unwrap_or(s.as_mut_vec()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| s.as_mut_vec())` + +error: use of `unwrap_or` followed by a function call + --> $DIR/or_fun_call.rs:143:14 + | +LL | None.unwrap_or(unsafe { s.as_mut_vec() }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { s.as_mut_vec() })` + +error: use of `unwrap_or` followed by a function call + --> $DIR/or_fun_call.rs:145:14 + | +LL | None.unwrap_or( unsafe { s.as_mut_vec() } ); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { s.as_mut_vec() })` + +error: aborting due to 22 previous errors -- cgit 1.4.1-3-g733a5 From 032cdfe043c5c9d00530dc18005066a184f3d46e Mon Sep 17 00:00:00 2001 From: Mateusz Gacek <96mateusz.gacek@gmail.com> Date: Thu, 18 Mar 2021 10:27:55 +0100 Subject: Adjust use_self uitest to proper self convention --- tests/ui/use_self.fixed | 7 +++---- tests/ui/use_self.rs | 7 +++---- tests/ui/use_self.stderr | 46 +++++++++++++++++++++++----------------------- 3 files changed, 29 insertions(+), 31 deletions(-) (limited to 'tests') diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed index 1986b54e66c..a6619f35892 100644 --- a/tests/ui/use_self.fixed +++ b/tests/ui/use_self.fixed @@ -75,14 +75,13 @@ mod lifetimes { mod issue2894 { trait IntoBytes { - fn to_bytes(&self) -> Vec; + fn to_bytes(self) -> Vec; } // This should not be linted - #[allow(clippy::wrong_self_convention)] impl IntoBytes for u8 { - fn to_bytes(&self) -> Vec { - vec![*self] + fn to_bytes(self) -> Vec { + vec![self] } } } diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index acc18280f1a..3c41ce500e0 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -75,14 +75,13 @@ mod lifetimes { mod issue2894 { trait IntoBytes { - fn to_bytes(&self) -> Vec; + fn to_bytes(self) -> Vec; } // This should not be linted - #[allow(clippy::wrong_self_convention)] impl IntoBytes for u8 { - fn to_bytes(&self) -> Vec { - vec![*self] + fn to_bytes(self) -> Vec { + vec![self] } } } diff --git a/tests/ui/use_self.stderr b/tests/ui/use_self.stderr index a2c95963487..e1410d2e652 100644 --- a/tests/ui/use_self.stderr +++ b/tests/ui/use_self.stderr @@ -37,139 +37,139 @@ LL | Foo::new() | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:94:24 + --> $DIR/use_self.rs:93:24 | LL | fn bad(foos: &[Foo]) -> impl Iterator { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:94:55 + --> $DIR/use_self.rs:93:55 | LL | fn bad(foos: &[Foo]) -> impl Iterator { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:109:13 + --> $DIR/use_self.rs:108:13 | LL | TS(0) | ^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:144:29 + --> $DIR/use_self.rs:143:29 | LL | fn bar() -> Bar { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:145:21 + --> $DIR/use_self.rs:144:21 | LL | Bar { foo: Foo {} } | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:156:21 + --> $DIR/use_self.rs:155:21 | LL | fn baz() -> Foo { | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:157:13 + --> $DIR/use_self.rs:156:13 | LL | Foo {} | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:174:21 + --> $DIR/use_self.rs:173:21 | LL | let _ = Enum::B(42); | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:175:21 + --> $DIR/use_self.rs:174:21 | LL | let _ = Enum::C { field: true }; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:176:21 + --> $DIR/use_self.rs:175:21 | LL | let _ = Enum::A; | ^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:218:13 + --> $DIR/use_self.rs:217:13 | LL | nested::A::fun_1(); | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:219:13 + --> $DIR/use_self.rs:218:13 | LL | nested::A::A; | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:221:13 + --> $DIR/use_self.rs:220:13 | LL | nested::A {}; | ^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:240:13 + --> $DIR/use_self.rs:239:13 | LL | TestStruct::from_something() | ^^^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:254:25 + --> $DIR/use_self.rs:253:25 | LL | async fn g() -> S { | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:255:13 + --> $DIR/use_self.rs:254:13 | LL | S {} | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:259:16 + --> $DIR/use_self.rs:258:16 | LL | &p[S::A..S::B] | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:259:22 + --> $DIR/use_self.rs:258:22 | LL | &p[S::A..S::B] | ^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:282:29 + --> $DIR/use_self.rs:281:29 | LL | fn foo(value: T) -> Foo { | ^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:283:13 + --> $DIR/use_self.rs:282:13 | LL | Foo { value } | ^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:320:21 + --> $DIR/use_self.rs:319:21 | LL | type From = T::From; | ^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:321:19 + --> $DIR/use_self.rs:320:19 | LL | type To = T::To; | ^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition - --> $DIR/use_self.rs:454:13 + --> $DIR/use_self.rs:453:13 | LL | A::new::(submod::B {}) | ^ help: use the applicable keyword: `Self` -- cgit 1.4.1-3-g733a5 From b42ec5e04d51e2fa16a689ce61128533559f09ff Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Thu, 18 Mar 2021 23:01:42 +0100 Subject: needless_question_mark: don't lint if Some(..) is inside a macro def and the ? is not. The suggestion would fail to apply. Fixes #6921 changelog: needless_question_mark: don't lint if Some(..) is inside a macro def and the ? is not. --- clippy_lints/src/needless_question_mark.rs | 7 ++++++- tests/ui/needless_question_mark.fixed | 25 +++++++++++++++++++++++++ tests/ui/needless_question_mark.rs | 25 +++++++++++++++++++++++++ tests/ui/needless_question_mark.stderr | 13 ++++++++++++- 4 files changed, 68 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/needless_question_mark.rs b/clippy_lints/src/needless_question_mark.rs index 99e85e6683c..9852633b734 100644 --- a/clippy_lints/src/needless_question_mark.rs +++ b/clippy_lints/src/needless_question_mark.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_ok_ctor, is_some_ctor, meets_msrv}; +use clippy_utils::{differing_macro_contexts, is_ok_ctor, is_some_ctor, meets_msrv}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Body, Expr, ExprKind, LangItem, MatchSource, QPath}; @@ -173,6 +173,11 @@ fn is_some_or_ok_call<'a>( // question mark operator let inner_expr = &args[0]; + // if the inner expr is inside macro but the outer one is not, do not lint (#6921) + if differing_macro_contexts(expr.span, inner_expr.span) { + return None; + } + let inner_ty = cx.typeck_results().expr_ty(inner_expr); let outer_ty = cx.typeck_results().expr_ty(expr); diff --git a/tests/ui/needless_question_mark.fixed b/tests/ui/needless_question_mark.fixed index 71fb3565224..fd8433870bb 100644 --- a/tests/ui/needless_question_mark.fixed +++ b/tests/ui/needless_question_mark.fixed @@ -167,3 +167,28 @@ mod question_mark_both { needless_question_mark_result(); } } + +// #6921 if a macro wraps an expr in Some( ) and the ? is in the macro use, +// the suggestion fails to apply; do not lint +macro_rules! some_in_macro { + ($expr:expr) => { + || -> _ { Some($expr) }() + }; +} + +pub fn test1() { + let x = Some(3); + let _x = some_in_macro!(x?); +} + +// this one is ok because both the ? and the Some are both inside the macro def +macro_rules! some_and_qmark_in_macro { + ($expr:expr) => { + || -> Option<_> { Some($expr) }() + }; +} + +pub fn test2() { + let x = Some(3); + let _x = some_and_qmark_in_macro!(x?); +} diff --git a/tests/ui/needless_question_mark.rs b/tests/ui/needless_question_mark.rs index e31f6f48fa7..36d45ac7e03 100644 --- a/tests/ui/needless_question_mark.rs +++ b/tests/ui/needless_question_mark.rs @@ -167,3 +167,28 @@ mod question_mark_both { needless_question_mark_result(); } } + +// #6921 if a macro wraps an expr in Some( ) and the ? is in the macro use, +// the suggestion fails to apply; do not lint +macro_rules! some_in_macro { + ($expr:expr) => { + || -> _ { Some($expr) }() + }; +} + +pub fn test1() { + let x = Some(3); + let _x = some_in_macro!(x?); +} + +// this one is ok because both the ? and the Some are both inside the macro def +macro_rules! some_and_qmark_in_macro { + ($expr:expr) => { + || -> Option<_> { Some(Some($expr)?) }() + }; +} + +pub fn test2() { + let x = Some(3); + let _x = some_and_qmark_in_macro!(x?); +} diff --git a/tests/ui/needless_question_mark.stderr b/tests/ui/needless_question_mark.stderr index 983c56031d8..7cbf1e505ad 100644 --- a/tests/ui/needless_question_mark.stderr +++ b/tests/ui/needless_question_mark.stderr @@ -84,5 +84,16 @@ error: question mark operator is useless here LL | Ok(to.magic?) // should be triggered | ^^^^^^^^^^^^^ help: try: `to.magic` -error: aborting due to 14 previous errors +error: question mark operator is useless here + --> $DIR/needless_question_mark.rs:187:27 + | +LL | || -> Option<_> { Some(Some($expr)?) }() + | ^^^^^^^^^^^^^^^^^^ help: try: `Some($expr)` +... +LL | let _x = some_and_qmark_in_macro!(x?); + | ---------------------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 15 previous errors -- cgit 1.4.1-3-g733a5 From 1f2d01641d6ef2f283265eb603c7d231692b6a89 Mon Sep 17 00:00:00 2001 From: Mateusz Gacek <96mateusz.gacek@gmail.com> Date: Thu, 18 Mar 2021 19:45:13 +0100 Subject: wrong_self_convention: Enhance lint message --- clippy_lints/src/methods/mod.rs | 8 ++-- clippy_lints/src/methods/wrong_self_convention.rs | 19 +++++---- tests/ui/def_id_nocore.stderr | 2 +- tests/ui/wrong_self_convention.stderr | 48 +++++++++++------------ tests/ui/wrong_self_conventions_mut.stderr | 4 +- 5 files changed, 42 insertions(+), 39 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 722effc29b2..a3652c18a78 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2348,10 +2348,10 @@ impl SelfKind { #[must_use] fn description(self) -> &'static str { match self { - Self::Value => "self by value", - Self::Ref => "self by reference", - Self::RefMut => "self by mutable reference", - Self::No => "no self", + Self::Value => "`self` by value", + Self::Ref => "`self` by reference", + Self::RefMut => "`self` by mutable reference", + Self::No => "no `self`", } } } diff --git a/clippy_lints/src/methods/wrong_self_convention.rs b/clippy_lints/src/methods/wrong_self_convention.rs index bece8f251da..59e683aa9a7 100644 --- a/clippy_lints/src/methods/wrong_self_convention.rs +++ b/clippy_lints/src/methods/wrong_self_convention.rs @@ -51,13 +51,16 @@ impl Convention { impl fmt::Display for Convention { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { match *self { - Self::Eq(this) => this.fmt(f), - Self::StartsWith(this) => this.fmt(f).and_then(|_| '*'.fmt(f)), - Self::EndsWith(this) => '*'.fmt(f).and_then(|_| this.fmt(f)), - Self::NotEndsWith(this) => '~'.fmt(f).and_then(|_| this.fmt(f)), - Self::IsSelfTypeCopy(is_true) => format!("self type is {} Copy", if is_true { "" } else { "not" }).fmt(f), + Self::Eq(this) => format!("`{}`", this).fmt(f), + Self::StartsWith(this) => format!("`{}*`", this).fmt(f), + Self::EndsWith(this) => format!("`*{}`", this).fmt(f), + Self::NotEndsWith(this) => format!("`~{}`", this).fmt(f), + Self::IsSelfTypeCopy(is_true) => { + format!("`self` type is{} `Copy`", if is_true { "" } else { " not" }).fmt(f) + }, Self::ImplementsTrait(is_true) => { - format!("Method {} implement a trait", if is_true { "" } else { "do not" }).fmt(f) + let (negation, s_suffix) = if is_true { ("", "s") } else { (" does not", "") }; + format!("Method{} implement{} a trait", negation, s_suffix).fmt(f) }, } } @@ -99,7 +102,7 @@ pub(super) fn check<'tcx>( { None } else { - Some(format!("`{}`", &conv.to_string())) + Some(conv.to_string()) } }) .collect::>() @@ -107,7 +110,7 @@ pub(super) fn check<'tcx>( format!("methods with the following characteristics: ({})", &s) } else { - format!("methods called `{}`", &conventions[0]) + format!("methods called {}", &conventions[0]) } }; diff --git a/tests/ui/def_id_nocore.stderr b/tests/ui/def_id_nocore.stderr index a3e9cc75b08..702684f6b43 100644 --- a/tests/ui/def_id_nocore.stderr +++ b/tests/ui/def_id_nocore.stderr @@ -1,4 +1,4 @@ -error: methods called `as_*` usually take self by reference or self by mutable reference +error: methods called `as_*` usually take `self` by reference or `self` by mutable reference --> $DIR/def_id_nocore.rs:26:19 | LL | pub fn as_ref(self) -> &'static str { diff --git a/tests/ui/wrong_self_convention.stderr b/tests/ui/wrong_self_convention.stderr index 6daa334ed48..1d58a12ac79 100644 --- a/tests/ui/wrong_self_convention.stderr +++ b/tests/ui/wrong_self_convention.stderr @@ -1,4 +1,4 @@ -error: methods called `from_*` usually take no self +error: methods called `from_*` usually take no `self` --> $DIR/wrong_self_convention.rs:18:17 | LL | fn from_i32(self) {} @@ -7,7 +7,7 @@ LL | fn from_i32(self) {} = note: `-D clippy::wrong-self-convention` implied by `-D warnings` = help: consider choosing a less ambiguous name -error: methods called `from_*` usually take no self +error: methods called `from_*` usually take no `self` --> $DIR/wrong_self_convention.rs:24:21 | LL | pub fn from_i64(self) {} @@ -15,7 +15,7 @@ LL | pub fn from_i64(self) {} | = help: consider choosing a less ambiguous name -error: methods called `as_*` usually take self by reference or self by mutable reference +error: methods called `as_*` usually take `self` by reference or `self` by mutable reference --> $DIR/wrong_self_convention.rs:36:15 | LL | fn as_i32(self) {} @@ -23,7 +23,7 @@ LL | fn as_i32(self) {} | = help: consider choosing a less ambiguous name -error: methods called `into_*` usually take self by value +error: methods called `into_*` usually take `self` by value --> $DIR/wrong_self_convention.rs:38:17 | LL | fn into_i32(&self) {} @@ -31,7 +31,7 @@ LL | fn into_i32(&self) {} | = help: consider choosing a less ambiguous name -error: methods called `is_*` usually take self by reference or no self +error: methods called `is_*` usually take `self` by reference or no `self` --> $DIR/wrong_self_convention.rs:40:15 | LL | fn is_i32(self) {} @@ -39,7 +39,7 @@ LL | fn is_i32(self) {} | = help: consider choosing a less ambiguous name -error: methods with the following characteristics: (`to_*` and `self type is not Copy`) usually take self by reference +error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference --> $DIR/wrong_self_convention.rs:42:15 | LL | fn to_i32(self) {} @@ -47,7 +47,7 @@ LL | fn to_i32(self) {} | = help: consider choosing a less ambiguous name -error: methods called `from_*` usually take no self +error: methods called `from_*` usually take no `self` --> $DIR/wrong_self_convention.rs:44:17 | LL | fn from_i32(self) {} @@ -55,7 +55,7 @@ LL | fn from_i32(self) {} | = help: consider choosing a less ambiguous name -error: methods called `as_*` usually take self by reference or self by mutable reference +error: methods called `as_*` usually take `self` by reference or `self` by mutable reference --> $DIR/wrong_self_convention.rs:46:19 | LL | pub fn as_i64(self) {} @@ -63,7 +63,7 @@ LL | pub fn as_i64(self) {} | = help: consider choosing a less ambiguous name -error: methods called `into_*` usually take self by value +error: methods called `into_*` usually take `self` by value --> $DIR/wrong_self_convention.rs:47:21 | LL | pub fn into_i64(&self) {} @@ -71,7 +71,7 @@ LL | pub fn into_i64(&self) {} | = help: consider choosing a less ambiguous name -error: methods called `is_*` usually take self by reference or no self +error: methods called `is_*` usually take `self` by reference or no `self` --> $DIR/wrong_self_convention.rs:48:19 | LL | pub fn is_i64(self) {} @@ -79,7 +79,7 @@ LL | pub fn is_i64(self) {} | = help: consider choosing a less ambiguous name -error: methods with the following characteristics: (`to_*` and `self type is not Copy`) usually take self by reference +error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference --> $DIR/wrong_self_convention.rs:49:19 | LL | pub fn to_i64(self) {} @@ -87,7 +87,7 @@ LL | pub fn to_i64(self) {} | = help: consider choosing a less ambiguous name -error: methods called `from_*` usually take no self +error: methods called `from_*` usually take no `self` --> $DIR/wrong_self_convention.rs:50:21 | LL | pub fn from_i64(self) {} @@ -95,7 +95,7 @@ LL | pub fn from_i64(self) {} | = help: consider choosing a less ambiguous name -error: methods called `as_*` usually take self by reference or self by mutable reference +error: methods called `as_*` usually take `self` by reference or `self` by mutable reference --> $DIR/wrong_self_convention.rs:95:19 | LL | fn as_i32(self) {} @@ -103,7 +103,7 @@ LL | fn as_i32(self) {} | = help: consider choosing a less ambiguous name -error: methods called `into_*` usually take self by value +error: methods called `into_*` usually take `self` by value --> $DIR/wrong_self_convention.rs:98:25 | LL | fn into_i32_ref(&self) {} @@ -111,7 +111,7 @@ LL | fn into_i32_ref(&self) {} | = help: consider choosing a less ambiguous name -error: methods called `is_*` usually take self by reference or no self +error: methods called `is_*` usually take `self` by reference or no `self` --> $DIR/wrong_self_convention.rs:100:19 | LL | fn is_i32(self) {} @@ -119,7 +119,7 @@ LL | fn is_i32(self) {} | = help: consider choosing a less ambiguous name -error: methods called `from_*` usually take no self +error: methods called `from_*` usually take no `self` --> $DIR/wrong_self_convention.rs:104:21 | LL | fn from_i32(self) {} @@ -127,7 +127,7 @@ LL | fn from_i32(self) {} | = help: consider choosing a less ambiguous name -error: methods called `as_*` usually take self by reference or self by mutable reference +error: methods called `as_*` usually take `self` by reference or `self` by mutable reference --> $DIR/wrong_self_convention.rs:119:19 | LL | fn as_i32(self); @@ -135,7 +135,7 @@ LL | fn as_i32(self); | = help: consider choosing a less ambiguous name -error: methods called `into_*` usually take self by value +error: methods called `into_*` usually take `self` by value --> $DIR/wrong_self_convention.rs:122:25 | LL | fn into_i32_ref(&self); @@ -143,7 +143,7 @@ LL | fn into_i32_ref(&self); | = help: consider choosing a less ambiguous name -error: methods called `is_*` usually take self by reference or no self +error: methods called `is_*` usually take `self` by reference or no `self` --> $DIR/wrong_self_convention.rs:124:19 | LL | fn is_i32(self); @@ -151,7 +151,7 @@ LL | fn is_i32(self); | = help: consider choosing a less ambiguous name -error: methods called `from_*` usually take no self +error: methods called `from_*` usually take no `self` --> $DIR/wrong_self_convention.rs:128:21 | LL | fn from_i32(self); @@ -159,7 +159,7 @@ LL | fn from_i32(self); | = help: consider choosing a less ambiguous name -error: methods called `into_*` usually take self by value +error: methods called `into_*` usually take `self` by value --> $DIR/wrong_self_convention.rs:146:25 | LL | fn into_i32_ref(&self); @@ -167,7 +167,7 @@ LL | fn into_i32_ref(&self); | = help: consider choosing a less ambiguous name -error: methods called `from_*` usually take no self +error: methods called `from_*` usually take no `self` --> $DIR/wrong_self_convention.rs:152:21 | LL | fn from_i32(self); @@ -175,7 +175,7 @@ LL | fn from_i32(self); | = help: consider choosing a less ambiguous name -error: methods with the following characteristics: (`to_*` and `self type is Copy`) usually take self by value +error: methods with the following characteristics: (`to_*` and `self` type is `Copy`) usually take `self` by value --> $DIR/wrong_self_convention.rs:181:22 | LL | fn to_u64_v2(&self) -> u64 { @@ -183,7 +183,7 @@ LL | fn to_u64_v2(&self) -> u64 { | = help: consider choosing a less ambiguous name -error: methods with the following characteristics: (`to_*` and `self type is not Copy`) usually take self by reference +error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference --> $DIR/wrong_self_convention.rs:190:19 | LL | fn to_u64(self) -> u64 { diff --git a/tests/ui/wrong_self_conventions_mut.stderr b/tests/ui/wrong_self_conventions_mut.stderr index 8095188758c..6ce37c59491 100644 --- a/tests/ui/wrong_self_conventions_mut.stderr +++ b/tests/ui/wrong_self_conventions_mut.stderr @@ -1,4 +1,4 @@ -error: methods with the following characteristics: (`to_*` and `self type is not Copy`) usually take self by reference +error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference --> $DIR/wrong_self_conventions_mut.rs:15:24 | LL | pub fn to_many(&mut self) -> Option<&mut [T]> { @@ -7,7 +7,7 @@ LL | pub fn to_many(&mut self) -> Option<&mut [T]> { = note: `-D clippy::wrong-self-convention` implied by `-D warnings` = help: consider choosing a less ambiguous name -error: methods with the following characteristics: (`to_*` and `*_mut`) usually take self by mutable reference +error: methods with the following characteristics: (`to_*` and `*_mut`) usually take `self` by mutable reference --> $DIR/wrong_self_conventions_mut.rs:23:28 | LL | pub fn to_many_mut(&self) -> Option<&[T]> { -- cgit 1.4.1-3-g733a5 From 296751f643952f02f6fa721c90bbb81f4183e3f6 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sat, 20 Mar 2021 10:24:10 +0100 Subject: Fix bad suggestion for generics in `new_without_default` lint --- clippy_lints/src/new_without_default.rs | 15 ++++++++----- tests/ui/new_without_default.rs | 15 +++++++++++++ tests/ui/new_without_default.stderr | 38 +++++++++++++++++++++++++++++++-- 3 files changed, 61 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index 502e5e4bf37..3789572ad43 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::paths; +use clippy_utils::source::snippet; use clippy_utils::sugg::DiagnosticBuilderExt; use clippy_utils::{get_trait_def_id, return_ty}; use if_chain::if_chain; @@ -62,7 +63,10 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { #[allow(clippy::too_many_lines)] fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { if let hir::ItemKind::Impl(hir::Impl { - of_trait: None, items, .. + of_trait: None, + ref generics, + items, + .. }) = item.kind { for assoc_item in items { @@ -126,6 +130,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { } } + let generics_sugg = snippet(cx, generics.span, ""); span_lint_hir_and_then( cx, NEW_WITHOUT_DEFAULT, @@ -140,7 +145,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { cx, item.span, "try this", - &create_new_without_default_suggest_msg(self_ty), + &create_new_without_default_suggest_msg(self_ty, &generics_sugg), Applicability::MaybeIncorrect, ); }, @@ -155,12 +160,12 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { } } -fn create_new_without_default_suggest_msg(ty: Ty<'_>) -> String { +fn create_new_without_default_suggest_msg(ty: Ty<'_>, generics_sugg: &str) -> String { #[rustfmt::skip] format!( -"impl Default for {} {{ +"impl{} Default for {} {{ fn default() -> Self {{ Self::new() }} -}}", ty) +}}", generics_sugg, ty) } diff --git a/tests/ui/new_without_default.rs b/tests/ui/new_without_default.rs index 3b6041823d8..64659b63f46 100644 --- a/tests/ui/new_without_default.rs +++ b/tests/ui/new_without_default.rs @@ -159,4 +159,19 @@ impl NewNotEqualToDerive { } } +// see #6933 +pub struct FooGenerics(std::marker::PhantomData); +impl FooGenerics { + pub fn new() -> Self { + Self(Default::default()) + } +} + +pub struct BarGenerics(std::marker::PhantomData); +impl BarGenerics { + pub fn new() -> Self { + Self(Default::default()) + } +} + fn main() {} diff --git a/tests/ui/new_without_default.stderr b/tests/ui/new_without_default.stderr index e529e441eb7..973836f75a9 100644 --- a/tests/ui/new_without_default.stderr +++ b/tests/ui/new_without_default.stderr @@ -43,7 +43,7 @@ LL | | } | help: try this | -LL | impl Default for LtKo<'c> { +LL | impl<'c> Default for LtKo<'c> { LL | fn default() -> Self { LL | Self::new() LL | } @@ -67,5 +67,39 @@ LL | } LL | } | -error: aborting due to 4 previous errors +error: you should consider adding a `Default` implementation for `FooGenerics` + --> $DIR/new_without_default.rs:165:5 + | +LL | / pub fn new() -> Self { +LL | | Self(Default::default()) +LL | | } + | |_____^ + | +help: try this + | +LL | impl Default for FooGenerics { +LL | fn default() -> Self { +LL | Self::new() +LL | } +LL | } + | + +error: you should consider adding a `Default` implementation for `BarGenerics` + --> $DIR/new_without_default.rs:172:5 + | +LL | / pub fn new() -> Self { +LL | | Self(Default::default()) +LL | | } + | |_____^ + | +help: try this + | +LL | impl Default for BarGenerics { +LL | fn default() -> Self { +LL | Self::new() +LL | } +LL | } + | + +error: aborting due to 6 previous errors -- cgit 1.4.1-3-g733a5 From 00a2d7ad7e1a041e47f618a019702bfb37eca680 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sat, 20 Mar 2021 16:11:19 +0100 Subject: Fix bad suggestion that needs curly braces for `match_single_binding` lint --- clippy_lints/src/matches.rs | 14 +++++++++++++- tests/ui/match_single_binding.fixed | 29 +++++++++++++++++++++++++++++ tests/ui/match_single_binding.rs | 29 +++++++++++++++++++++++++++++ tests/ui/match_single_binding.stderr | 33 ++++++++++++++++++++++++++++++++- 4 files changed, 103 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index d43cb32ee51..1a8ec9c6ec3 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -1353,6 +1353,7 @@ fn find_bool_lit(ex: &ExprKind<'_>, desugared: bool) -> Option { } } +#[allow(clippy::too_many_lines)] fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], expr: &Expr<'_>) { if in_macro(expr.span) || arms.len() != 1 || is_refutable(cx, arms[0].pat) { return; @@ -1427,7 +1428,18 @@ fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[A indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); cbrace_start = format!("{{\n{}", indent); } - }; + } + // If the parent is already an arm, and the body is another match statement, + // we need curly braces around suggestion + let parent_node_id = cx.tcx.hir().get_parent_node(expr.hir_id); + if let Node::Arm(arm) = &cx.tcx.hir().get(parent_node_id) { + if let ExprKind::Match(..) = arm.body.kind { + cbrace_end = format!("\n{}}}", indent); + // Fix body indent due to the match + indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); + cbrace_start = format!("{{\n{}", indent); + } + } ( expr.span, format!( diff --git a/tests/ui/match_single_binding.fixed b/tests/ui/match_single_binding.fixed index 526e94b10bd..4709b5b0157 100644 --- a/tests/ui/match_single_binding.fixed +++ b/tests/ui/match_single_binding.fixed @@ -115,4 +115,33 @@ fn main() { // => _ => println!("Not an array index start"), } + // Lint (additional curly braces needed, see #6572) + struct AppendIter + where + I: Iterator, + { + inner: Option<(I, ::Item)>, + } + + #[allow(dead_code)] + fn size_hint(iter: &AppendIter) -> (usize, Option) { + match &iter.inner { + Some((iter, _item)) => { + let (min, max) = iter.size_hint(); + (min.saturating_add(1), max.and_then(|max| max.checked_add(1))) + }, + None => (0, Some(0)), + } + } + // Lint (no additional curly braces needed) + let opt = Some((5, 2)); + let get_tup = || -> (i32, i32) { (1, 2) }; + match opt { + #[rustfmt::skip] + Some((first, _second)) => { + let (a, b) = get_tup(); + println!("a {:?} and b {:?}", a, b); + }, + None => println!("nothing"), + } } diff --git a/tests/ui/match_single_binding.rs b/tests/ui/match_single_binding.rs index 6a2ca7c5e93..6a6b3e8e8a9 100644 --- a/tests/ui/match_single_binding.rs +++ b/tests/ui/match_single_binding.rs @@ -132,4 +132,33 @@ fn main() { // => _ => println!("Not an array index start"), } + // Lint (additional curly braces needed, see #6572) + struct AppendIter + where + I: Iterator, + { + inner: Option<(I, ::Item)>, + } + + #[allow(dead_code)] + fn size_hint(iter: &AppendIter) -> (usize, Option) { + match &iter.inner { + Some((iter, _item)) => match iter.size_hint() { + (min, max) => (min.saturating_add(1), max.and_then(|max| max.checked_add(1))), + }, + None => (0, Some(0)), + } + } + // Lint (no additional curly braces needed) + let opt = Some((5, 2)); + let get_tup = || -> (i32, i32) { (1, 2) }; + match opt { + #[rustfmt::skip] + Some((first, _second)) => { + match get_tup() { + (a, b) => println!("a {:?} and b {:?}", a, b), + } + }, + None => println!("nothing"), + } } diff --git a/tests/ui/match_single_binding.stderr b/tests/ui/match_single_binding.stderr index cbbf5d29c02..73cc867dd9f 100644 --- a/tests/ui/match_single_binding.stderr +++ b/tests/ui/match_single_binding.stderr @@ -178,5 +178,36 @@ LL | | _ => println!("Single branch"), LL | | } | |_____^ help: consider using the match body instead: `println!("Single branch");` -error: aborting due to 12 previous errors +error: this match could be written as a `let` statement + --> $DIR/match_single_binding.rs:146:36 + | +LL | Some((iter, _item)) => match iter.size_hint() { + | ____________________________________^ +LL | | (min, max) => (min.saturating_add(1), max.and_then(|max| max.checked_add(1))), +LL | | }, + | |_____________^ + | +help: consider using `let` statement + | +LL | Some((iter, _item)) => { +LL | let (min, max) = iter.size_hint(); +LL | (min.saturating_add(1), max.and_then(|max| max.checked_add(1))) +LL | }, + | + +error: this match could be written as a `let` statement + --> $DIR/match_single_binding.rs:158:13 + | +LL | / match get_tup() { +LL | | (a, b) => println!("a {:?} and b {:?}", a, b), +LL | | } + | |_____________^ + | +help: consider using `let` statement + | +LL | let (a, b) = get_tup(); +LL | println!("a {:?} and b {:?}", a, b); + | + +error: aborting due to 14 previous errors -- cgit 1.4.1-3-g733a5 From 7d45d8a29a55aad636c00108854c15be134870d3 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sat, 20 Mar 2021 16:31:39 +0100 Subject: Split `match_single_binding` tests in 2 files (too many lines for CI) --- tests/ui/match_single_binding.fixed | 29 --------------------------- tests/ui/match_single_binding.rs | 29 --------------------------- tests/ui/match_single_binding.stderr | 33 +------------------------------ tests/ui/match_single_binding2.fixed | 37 +++++++++++++++++++++++++++++++++++ tests/ui/match_single_binding2.rs | 37 +++++++++++++++++++++++++++++++++++ tests/ui/match_single_binding2.stderr | 34 ++++++++++++++++++++++++++++++++ 6 files changed, 109 insertions(+), 90 deletions(-) create mode 100644 tests/ui/match_single_binding2.fixed create mode 100644 tests/ui/match_single_binding2.rs create mode 100644 tests/ui/match_single_binding2.stderr (limited to 'tests') diff --git a/tests/ui/match_single_binding.fixed b/tests/ui/match_single_binding.fixed index 4709b5b0157..526e94b10bd 100644 --- a/tests/ui/match_single_binding.fixed +++ b/tests/ui/match_single_binding.fixed @@ -115,33 +115,4 @@ fn main() { // => _ => println!("Not an array index start"), } - // Lint (additional curly braces needed, see #6572) - struct AppendIter - where - I: Iterator, - { - inner: Option<(I, ::Item)>, - } - - #[allow(dead_code)] - fn size_hint(iter: &AppendIter) -> (usize, Option) { - match &iter.inner { - Some((iter, _item)) => { - let (min, max) = iter.size_hint(); - (min.saturating_add(1), max.and_then(|max| max.checked_add(1))) - }, - None => (0, Some(0)), - } - } - // Lint (no additional curly braces needed) - let opt = Some((5, 2)); - let get_tup = || -> (i32, i32) { (1, 2) }; - match opt { - #[rustfmt::skip] - Some((first, _second)) => { - let (a, b) = get_tup(); - println!("a {:?} and b {:?}", a, b); - }, - None => println!("nothing"), - } } diff --git a/tests/ui/match_single_binding.rs b/tests/ui/match_single_binding.rs index 6a6b3e8e8a9..6a2ca7c5e93 100644 --- a/tests/ui/match_single_binding.rs +++ b/tests/ui/match_single_binding.rs @@ -132,33 +132,4 @@ fn main() { // => _ => println!("Not an array index start"), } - // Lint (additional curly braces needed, see #6572) - struct AppendIter - where - I: Iterator, - { - inner: Option<(I, ::Item)>, - } - - #[allow(dead_code)] - fn size_hint(iter: &AppendIter) -> (usize, Option) { - match &iter.inner { - Some((iter, _item)) => match iter.size_hint() { - (min, max) => (min.saturating_add(1), max.and_then(|max| max.checked_add(1))), - }, - None => (0, Some(0)), - } - } - // Lint (no additional curly braces needed) - let opt = Some((5, 2)); - let get_tup = || -> (i32, i32) { (1, 2) }; - match opt { - #[rustfmt::skip] - Some((first, _second)) => { - match get_tup() { - (a, b) => println!("a {:?} and b {:?}", a, b), - } - }, - None => println!("nothing"), - } } diff --git a/tests/ui/match_single_binding.stderr b/tests/ui/match_single_binding.stderr index 73cc867dd9f..cbbf5d29c02 100644 --- a/tests/ui/match_single_binding.stderr +++ b/tests/ui/match_single_binding.stderr @@ -178,36 +178,5 @@ LL | | _ => println!("Single branch"), LL | | } | |_____^ help: consider using the match body instead: `println!("Single branch");` -error: this match could be written as a `let` statement - --> $DIR/match_single_binding.rs:146:36 - | -LL | Some((iter, _item)) => match iter.size_hint() { - | ____________________________________^ -LL | | (min, max) => (min.saturating_add(1), max.and_then(|max| max.checked_add(1))), -LL | | }, - | |_____________^ - | -help: consider using `let` statement - | -LL | Some((iter, _item)) => { -LL | let (min, max) = iter.size_hint(); -LL | (min.saturating_add(1), max.and_then(|max| max.checked_add(1))) -LL | }, - | - -error: this match could be written as a `let` statement - --> $DIR/match_single_binding.rs:158:13 - | -LL | / match get_tup() { -LL | | (a, b) => println!("a {:?} and b {:?}", a, b), -LL | | } - | |_____________^ - | -help: consider using `let` statement - | -LL | let (a, b) = get_tup(); -LL | println!("a {:?} and b {:?}", a, b); - | - -error: aborting due to 14 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/match_single_binding2.fixed b/tests/ui/match_single_binding2.fixed new file mode 100644 index 00000000000..e73a85b73d7 --- /dev/null +++ b/tests/ui/match_single_binding2.fixed @@ -0,0 +1,37 @@ +// run-rustfix + +#![warn(clippy::match_single_binding)] +#![allow(unused_variables)] + +fn main() { + // Lint (additional curly braces needed, see #6572) + struct AppendIter + where + I: Iterator, + { + inner: Option<(I, ::Item)>, + } + + #[allow(dead_code)] + fn size_hint(iter: &AppendIter) -> (usize, Option) { + match &iter.inner { + Some((iter, _item)) => { + let (min, max) = iter.size_hint(); + (min.saturating_add(1), max.and_then(|max| max.checked_add(1))) + }, + None => (0, Some(0)), + } + } + + // Lint (no additional curly braces needed) + let opt = Some((5, 2)); + let get_tup = || -> (i32, i32) { (1, 2) }; + match opt { + #[rustfmt::skip] + Some((first, _second)) => { + let (a, b) = get_tup(); + println!("a {:?} and b {:?}", a, b); + }, + None => println!("nothing"), + } +} diff --git a/tests/ui/match_single_binding2.rs b/tests/ui/match_single_binding2.rs new file mode 100644 index 00000000000..7362cb390e5 --- /dev/null +++ b/tests/ui/match_single_binding2.rs @@ -0,0 +1,37 @@ +// run-rustfix + +#![warn(clippy::match_single_binding)] +#![allow(unused_variables)] + +fn main() { + // Lint (additional curly braces needed, see #6572) + struct AppendIter + where + I: Iterator, + { + inner: Option<(I, ::Item)>, + } + + #[allow(dead_code)] + fn size_hint(iter: &AppendIter) -> (usize, Option) { + match &iter.inner { + Some((iter, _item)) => match iter.size_hint() { + (min, max) => (min.saturating_add(1), max.and_then(|max| max.checked_add(1))), + }, + None => (0, Some(0)), + } + } + + // Lint (no additional curly braces needed) + let opt = Some((5, 2)); + let get_tup = || -> (i32, i32) { (1, 2) }; + match opt { + #[rustfmt::skip] + Some((first, _second)) => { + match get_tup() { + (a, b) => println!("a {:?} and b {:?}", a, b), + } + }, + None => println!("nothing"), + } +} diff --git a/tests/ui/match_single_binding2.stderr b/tests/ui/match_single_binding2.stderr new file mode 100644 index 00000000000..bc18d191aa3 --- /dev/null +++ b/tests/ui/match_single_binding2.stderr @@ -0,0 +1,34 @@ +error: this match could be written as a `let` statement + --> $DIR/match_single_binding2.rs:18:36 + | +LL | Some((iter, _item)) => match iter.size_hint() { + | ____________________________________^ +LL | | (min, max) => (min.saturating_add(1), max.and_then(|max| max.checked_add(1))), +LL | | }, + | |_____________^ + | + = note: `-D clippy::match-single-binding` implied by `-D warnings` +help: consider using `let` statement + | +LL | Some((iter, _item)) => { +LL | let (min, max) = iter.size_hint(); +LL | (min.saturating_add(1), max.and_then(|max| max.checked_add(1))) +LL | }, + | + +error: this match could be written as a `let` statement + --> $DIR/match_single_binding2.rs:31:13 + | +LL | / match get_tup() { +LL | | (a, b) => println!("a {:?} and b {:?}", a, b), +LL | | } + | |_____________^ + | +help: consider using `let` statement + | +LL | let (a, b) = get_tup(); +LL | println!("a {:?} and b {:?}", a, b); + | + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From 3ddaabcbc9cf223f6a2f8f645b2092166f911eab Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 21 Mar 2021 09:50:35 +0100 Subject: Fix suggestion with generics for `field_reassign_with_default` lint --- clippy_lints/src/default.rs | 19 +++++++++++++++++++ tests/ui/field_reassign_with_default.rs | 18 ++++++++++++++++++ tests/ui/field_reassign_with_default.stderr | 26 +++++++++++++++++++++++++- 3 files changed, 62 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index d046e894792..568a174445c 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -104,6 +104,7 @@ impl LateLintPass<'_> for Default { } } + #[allow(clippy::too_many_lines)] fn check_block<'tcx>(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) { // start from the `let mut _ = _::default();` and look at all the following // statements, see if they re-assign the fields of the binding @@ -197,6 +198,24 @@ impl LateLintPass<'_> for Default { .collect::>() .join(", "); + // give correct suggestion if generics are involved (see #6944) + let binding_type = if_chain! { + if let ty::Adt(adt_def, substs) = binding_type.kind(); + if !substs.is_empty(); + let adt_def_ty_name = cx.tcx.item_name(adt_def.did); + let generic_args = substs.iter().collect::>(); + let tys_str = generic_args + .iter() + .map(ToString::to_string) + .collect::>() + .join(", "); + then { + format!("{}::<{}>", adt_def_ty_name, &tys_str) + } else { + binding_type.to_string() + } + }; + let sugg = if ext_with_default { if field_list.is_empty() { format!("{}::default()", binding_type) diff --git a/tests/ui/field_reassign_with_default.rs b/tests/ui/field_reassign_with_default.rs index 9fc208f5332..1368c5d7984 100644 --- a/tests/ui/field_reassign_with_default.rs +++ b/tests/ui/field_reassign_with_default.rs @@ -136,6 +136,13 @@ fn main() { // Don't lint in external macros field_reassign_with_default!(); + + // be sure suggestion is correct with generics + let mut a: Wrapper = Default::default(); + a.i = true; + + let mut a: WrapperMulti = Default::default(); + a.i = 42; } mod m { @@ -145,3 +152,14 @@ mod m { b: u64, } } + +#[derive(Default)] +struct Wrapper { + i: T, +} + +#[derive(Default)] +struct WrapperMulti { + i: T, + j: U, +} diff --git a/tests/ui/field_reassign_with_default.stderr b/tests/ui/field_reassign_with_default.stderr index 2f0f28f7bb7..dd7c0360bb1 100644 --- a/tests/ui/field_reassign_with_default.stderr +++ b/tests/ui/field_reassign_with_default.stderr @@ -83,5 +83,29 @@ note: consider initializing the variable with `C { i: vec![1], ..Default::defaul LL | let mut a: C = C::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 7 previous errors +error: field assignment outside of initializer for an instance created with Default::default() + --> $DIR/field_reassign_with_default.rs:142:5 + | +LL | a.i = true; + | ^^^^^^^^^^^ + | +note: consider initializing the variable with `Wrapper:: { i: true }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:141:5 + | +LL | let mut a: Wrapper = Default::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: field assignment outside of initializer for an instance created with Default::default() + --> $DIR/field_reassign_with_default.rs:145:5 + | +LL | a.i = 42; + | ^^^^^^^^^ + | +note: consider initializing the variable with `WrapperMulti:: { i: 42, ..Default::default() }` and removing relevant reassignments + --> $DIR/field_reassign_with_default.rs:144:5 + | +LL | let mut a: WrapperMulti = Default::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 9 previous errors -- cgit 1.4.1-3-g733a5 From 2ffee89b752212d4e7d72ece16636d7c43b11c63 Mon Sep 17 00:00:00 2001 From: Mateusz Gacek <96mateusz.gacek@gmail.com> Date: Sat, 20 Mar 2021 14:30:45 +0100 Subject: search_is_some: check also when search is none --- clippy_lints/src/methods/mod.rs | 57 ++++++++++++--- clippy_lints/src/methods/search_is_some.rs | 107 +++++++++++++++++++++-------- tests/ui/search_is_some.rs | 37 +++++++++- tests/ui/search_is_some.stderr | 44 ++++++++++-- tests/ui/search_is_some_fixable.fixed | 35 +++++++++- tests/ui/search_is_some_fixable.rs | 35 +++++++++- tests/ui/search_is_some_fixable.stderr | 92 ++++++++++++++++++++++++- 7 files changed, 359 insertions(+), 48 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 46deb20f97d..5be28354769 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -588,26 +588,31 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** Checks for an iterator or string search (such as `find()`, - /// `position()`, or `rposition()`) followed by a call to `is_some()`. + /// `position()`, or `rposition()`) followed by a call to `is_some()` or `is_none()`. /// - /// **Why is this bad?** Readability, this can be written more concisely as - /// `_.any(_)` or `_.contains(_)`. + /// **Why is this bad?** Readability, this can be written more concisely as: + /// * `_.any(_)`, or `_.contains(_)` for `is_some()`, + /// * `!_.any(_)`, or `!_.contains(_)` for `is_none()`. /// /// **Known problems:** None. /// /// **Example:** /// ```rust - /// # let vec = vec![1]; + /// let vec = vec![1]; /// vec.iter().find(|x| **x == 0).is_some(); + /// + /// let _ = "hello world".find("world").is_none(); /// ``` /// Could be written as /// ```rust - /// # let vec = vec![1]; + /// let vec = vec![1]; /// vec.iter().any(|x| *x == 0); + /// + /// let _ = !"hello world".contains("world"); /// ``` pub SEARCH_IS_SOME, complexity, - "using an iterator or string search followed by `is_some()`, which is more succinctly expressed as a call to `any()` or `contains()`" + "using an iterator or string search followed by `is_some()` or `is_none()`, which is more succinctly expressed as a call to `any()` or `contains()` (with negation in case of `is_none()`)" } declare_clippy_lint! { @@ -1720,12 +1725,42 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["flat_map", "filter_map"] => filter_map_flat_map::check(cx, expr, arg_lists[1], arg_lists[0]), ["flat_map", ..] => flat_map_identity::check(cx, expr, arg_lists[0], method_spans[0]), ["flatten", "map"] => map_flatten::check(cx, expr, arg_lists[1]), - ["is_some", "find"] => search_is_some::check(cx, expr, "find", arg_lists[1], arg_lists[0], method_spans[1]), - ["is_some", "position"] => { - search_is_some::check(cx, expr, "position", arg_lists[1], arg_lists[0], method_spans[1]) + [option_check_method, "find"] if "is_some" == *option_check_method || "is_none" == *option_check_method => { + search_is_some::check( + cx, + expr, + "find", + option_check_method, + arg_lists[1], + arg_lists[0], + method_spans[1], + ) }, - ["is_some", "rposition"] => { - search_is_some::check(cx, expr, "rposition", arg_lists[1], arg_lists[0], method_spans[1]) + [option_check_method, "position"] + if "is_some" == *option_check_method || "is_none" == *option_check_method => + { + search_is_some::check( + cx, + expr, + "position", + option_check_method, + arg_lists[1], + arg_lists[0], + method_spans[1], + ) + }, + [option_check_method, "rposition"] + if "is_some" == *option_check_method || "is_none" == *option_check_method => + { + search_is_some::check( + cx, + expr, + "rposition", + option_check_method, + arg_lists[1], + arg_lists[0], + method_spans[1], + ) }, ["extend", ..] => string_extend_chars::check(cx, expr, arg_lists[0]), ["count", "into_iter"] => iter_count::check(cx, expr, &arg_lists[1], "into_iter"), diff --git a/clippy_lints/src/methods/search_is_some.rs b/clippy_lints/src/methods/search_is_some.rs index 13546dc1779..de7d168295f 100644 --- a/clippy_lints/src/methods/search_is_some.rs +++ b/clippy_lints/src/methods/search_is_some.rs @@ -14,11 +14,13 @@ use rustc_span::symbol::sym; use super::SEARCH_IS_SOME; /// lint searching an Iterator followed by `is_some()` -/// or calling `find()` on a string followed by `is_some()` +/// or calling `find()` on a string followed by `is_some()` or `is_none()` +#[allow(clippy::too_many_lines)] pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, search_method: &str, + option_check_method: &str, search_args: &'tcx [hir::Expr<'_>], is_some_args: &'tcx [hir::Expr<'_>], method_span: Span, @@ -26,10 +28,9 @@ pub(super) fn check<'tcx>( // lint if caller of search is an Iterator if is_trait_method(cx, &is_some_args[0], sym::Iterator) { let msg = format!( - "called `is_some()` after searching an `Iterator` with `{}`", - search_method + "called `{}()` after searching an `Iterator` with `{}`", + option_check_method, search_method ); - let hint = "this is more succinctly expressed by calling `any()`"; let search_snippet = snippet(cx, search_args[1].span, ".."); if search_snippet.lines().count() <= 1 { // suggest `any(|x| ..)` instead of `any(|&x| ..)` for `find(|&x| ..).is_some()` @@ -53,20 +54,49 @@ pub(super) fn check<'tcx>( } }; // add note if not multi-line - span_lint_and_sugg( - cx, - SEARCH_IS_SOME, - method_span.with_hi(expr.span.hi()), - &msg, - "use `any()` instead", - format!( - "any({})", - any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) - ), - Applicability::MachineApplicable, - ); + match option_check_method { + "is_some" => { + span_lint_and_sugg( + cx, + SEARCH_IS_SOME, + method_span.with_hi(expr.span.hi()), + &msg, + "use `any()` instead", + format!( + "any({})", + any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) + ), + Applicability::MachineApplicable, + ); + }, + "is_none" => { + let iter = snippet(cx, search_args[0].span, ".."); + span_lint_and_sugg( + cx, + SEARCH_IS_SOME, + expr.span, + &msg, + "use `!_.any()` instead", + format!( + "!{}.any({})", + iter, + any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) + ), + Applicability::MachineApplicable, + ); + }, + _ => (), + } } else { - span_lint_and_help(cx, SEARCH_IS_SOME, expr.span, &msg, None, hint); + let hint = format!( + "this is more succinctly expressed by calling `any()`{}", + if option_check_method == "is_none" { + " with negation" + } else { + "" + } + ); + span_lint_and_help(cx, SEARCH_IS_SOME, expr.span, &msg, None, &hint); } } // lint if `find()` is called by `String` or `&str` @@ -83,18 +113,37 @@ pub(super) fn check<'tcx>( if is_string_or_str_slice(&search_args[0]); if is_string_or_str_slice(&search_args[1]); then { - let msg = "called `is_some()` after calling `find()` on a string"; - let mut applicability = Applicability::MachineApplicable; - let find_arg = snippet_with_applicability(cx, search_args[1].span, "..", &mut applicability); - span_lint_and_sugg( - cx, - SEARCH_IS_SOME, - method_span.with_hi(expr.span.hi()), - msg, - "use `contains()` instead", - format!("contains({})", find_arg), - applicability, - ); + let msg = format!("called `{}()` after calling `find()` on a string", option_check_method); + match option_check_method { + "is_some" => { + let mut applicability = Applicability::MachineApplicable; + let find_arg = snippet_with_applicability(cx, search_args[1].span, "..", &mut applicability); + span_lint_and_sugg( + cx, + SEARCH_IS_SOME, + method_span.with_hi(expr.span.hi()), + &msg, + "use `contains()` instead", + format!("contains({})", find_arg), + applicability, + ); + }, + "is_none" => { + let string = snippet(cx, search_args[0].span, ".."); + let mut applicability = Applicability::MachineApplicable; + let find_arg = snippet_with_applicability(cx, search_args[1].span, "..", &mut applicability); + span_lint_and_sugg( + cx, + SEARCH_IS_SOME, + expr.span, + &msg, + "use `!_.contains()` instead", + format!("!{}.contains({})", string, find_arg), + applicability, + ); + }, + _ => (), + } } } } diff --git a/tests/ui/search_is_some.rs b/tests/ui/search_is_some.rs index f0dc3b3d06b..72bc6ef35d3 100644 --- a/tests/ui/search_is_some.rs +++ b/tests/ui/search_is_some.rs @@ -1,8 +1,9 @@ // aux-build:option_helpers.rs +#![warn(clippy::search_is_some)] +#![allow(dead_code)] extern crate option_helpers; use option_helpers::IteratorFalsePositives; -#[warn(clippy::search_is_some)] #[rustfmt::skip] fn main() { let v = vec![3, 2, 1, 0, -1, -2, -3]; @@ -36,3 +37,37 @@ fn main() { // `Pattern` that is not a string let _ = "hello world".find(|c: char| c == 'o' || c == 'l').is_some(); } + +#[rustfmt::skip] +fn is_none() { + let v = vec![3, 2, 1, 0, -1, -2, -3]; + let y = &&42; + + + // Check `find().is_none()`, multi-line case. + let _ = v.iter().find(|&x| { + *x < 0 + } + ).is_none(); + + // Check `position().is_none()`, multi-line case. + let _ = v.iter().position(|&x| { + x < 0 + } + ).is_none(); + + // Check `rposition().is_none()`, multi-line case. + let _ = v.iter().rposition(|&x| { + x < 0 + } + ).is_none(); + + // Check that we don't lint if the caller is not an `Iterator` or string + let falsepos = IteratorFalsePositives { foo: 0 }; + let _ = falsepos.find().is_none(); + let _ = falsepos.position().is_none(); + let _ = falsepos.rposition().is_none(); + // check that we don't lint if `find()` is called with + // `Pattern` that is not a string + let _ = "hello world".find(|c: char| c == 'o' || c == 'l').is_none(); +} diff --git a/tests/ui/search_is_some.stderr b/tests/ui/search_is_some.stderr index c601f568c60..f3c758e451e 100644 --- a/tests/ui/search_is_some.stderr +++ b/tests/ui/search_is_some.stderr @@ -1,5 +1,5 @@ error: called `is_some()` after searching an `Iterator` with `find` - --> $DIR/search_is_some.rs:13:13 + --> $DIR/search_is_some.rs:14:13 | LL | let _ = v.iter().find(|&x| { | _____________^ @@ -12,7 +12,7 @@ LL | | ).is_some(); = help: this is more succinctly expressed by calling `any()` error: called `is_some()` after searching an `Iterator` with `position` - --> $DIR/search_is_some.rs:19:13 + --> $DIR/search_is_some.rs:20:13 | LL | let _ = v.iter().position(|&x| { | _____________^ @@ -24,7 +24,7 @@ LL | | ).is_some(); = help: this is more succinctly expressed by calling `any()` error: called `is_some()` after searching an `Iterator` with `rposition` - --> $DIR/search_is_some.rs:25:13 + --> $DIR/search_is_some.rs:26:13 | LL | let _ = v.iter().rposition(|&x| { | _____________^ @@ -35,5 +35,41 @@ LL | | ).is_some(); | = help: this is more succinctly expressed by calling `any()` -error: aborting due to 3 previous errors +error: called `is_none()` after searching an `Iterator` with `find` + --> $DIR/search_is_some.rs:48:13 + | +LL | let _ = v.iter().find(|&x| { + | _____________^ +LL | | *x < 0 +LL | | } +LL | | ).is_none(); + | |______________________________^ + | + = help: this is more succinctly expressed by calling `any()` with negation + +error: called `is_none()` after searching an `Iterator` with `position` + --> $DIR/search_is_some.rs:54:13 + | +LL | let _ = v.iter().position(|&x| { + | _____________^ +LL | | x < 0 +LL | | } +LL | | ).is_none(); + | |______________________________^ + | + = help: this is more succinctly expressed by calling `any()` with negation + +error: called `is_none()` after searching an `Iterator` with `rposition` + --> $DIR/search_is_some.rs:60:13 + | +LL | let _ = v.iter().rposition(|&x| { + | _____________^ +LL | | x < 0 +LL | | } +LL | | ).is_none(); + | |______________________________^ + | + = help: this is more succinctly expressed by calling `any()` with negation + +error: aborting due to 6 previous errors diff --git a/tests/ui/search_is_some_fixable.fixed b/tests/ui/search_is_some_fixable.fixed index dc3f290e562..62ff16f67f4 100644 --- a/tests/ui/search_is_some_fixable.fixed +++ b/tests/ui/search_is_some_fixable.fixed @@ -1,5 +1,5 @@ // run-rustfix - +#![allow(dead_code)] #![warn(clippy::search_is_some)] fn main() { @@ -33,3 +33,36 @@ fn main() { let _ = s1[2..].contains(&s2); let _ = s1[2..].contains(&s2[2..]); } + +fn is_none() { + let v = vec![3, 2, 1, 0, -1, -2, -3]; + let y = &&42; + + // Check `find().is_none()`, single-line case. + let _ = !v.iter().any(|x| *x < 0); + let _ = !(0..1).any(|x| **y == x); // one dereference less + let _ = !(0..1).any(|x| x == 0); + let _ = !v.iter().any(|x| *x == 0); + + // Check `position().is_none()`, single-line case. + let _ = !v.iter().any(|&x| x < 0); + + // Check `rposition().is_none()`, single-line case. + let _ = !v.iter().any(|&x| x < 0); + + let s1 = String::from("hello world"); + let s2 = String::from("world"); + + // caller of `find()` is a `&`static str` + let _ = !"hello world".contains("world"); + let _ = !"hello world".contains(&s2); + let _ = !"hello world".contains(&s2[2..]); + // caller of `find()` is a `String` + let _ = !s1.contains("world"); + let _ = !s1.contains(&s2); + let _ = !s1.contains(&s2[2..]); + // caller of `find()` is slice of `String` + let _ = !s1[2..].contains("world"); + let _ = !s1[2..].contains(&s2); + let _ = !s1[2..].contains(&s2[2..]); +} diff --git a/tests/ui/search_is_some_fixable.rs b/tests/ui/search_is_some_fixable.rs index 146cf5adf1b..8407f716647 100644 --- a/tests/ui/search_is_some_fixable.rs +++ b/tests/ui/search_is_some_fixable.rs @@ -1,5 +1,5 @@ // run-rustfix - +#![allow(dead_code)] #![warn(clippy::search_is_some)] fn main() { @@ -33,3 +33,36 @@ fn main() { let _ = s1[2..].find(&s2).is_some(); let _ = s1[2..].find(&s2[2..]).is_some(); } + +fn is_none() { + let v = vec![3, 2, 1, 0, -1, -2, -3]; + let y = &&42; + + // Check `find().is_none()`, single-line case. + let _ = v.iter().find(|&x| *x < 0).is_none(); + let _ = (0..1).find(|x| **y == *x).is_none(); // one dereference less + let _ = (0..1).find(|x| *x == 0).is_none(); + let _ = v.iter().find(|x| **x == 0).is_none(); + + // Check `position().is_none()`, single-line case. + let _ = v.iter().position(|&x| x < 0).is_none(); + + // Check `rposition().is_none()`, single-line case. + let _ = v.iter().rposition(|&x| x < 0).is_none(); + + let s1 = String::from("hello world"); + let s2 = String::from("world"); + + // caller of `find()` is a `&`static str` + let _ = "hello world".find("world").is_none(); + let _ = "hello world".find(&s2).is_none(); + let _ = "hello world".find(&s2[2..]).is_none(); + // caller of `find()` is a `String` + let _ = s1.find("world").is_none(); + let _ = s1.find(&s2).is_none(); + let _ = s1.find(&s2[2..]).is_none(); + // caller of `find()` is slice of `String` + let _ = s1[2..].find("world").is_none(); + let _ = s1[2..].find(&s2).is_none(); + let _ = s1[2..].find(&s2[2..]).is_none(); +} diff --git a/tests/ui/search_is_some_fixable.stderr b/tests/ui/search_is_some_fixable.stderr index 23c1d9a901b..bd1b6955a97 100644 --- a/tests/ui/search_is_some_fixable.stderr +++ b/tests/ui/search_is_some_fixable.stderr @@ -90,5 +90,95 @@ error: called `is_some()` after calling `find()` on a string LL | let _ = s1[2..].find(&s2[2..]).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])` -error: aborting due to 15 previous errors +error: called `is_none()` after searching an `Iterator` with `find` + --> $DIR/search_is_some_fixable.rs:42:13 + | +LL | let _ = v.iter().find(|&x| *x < 0).is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| *x < 0)` + +error: called `is_none()` after searching an `Iterator` with `find` + --> $DIR/search_is_some_fixable.rs:43:13 + | +LL | let _ = (0..1).find(|x| **y == *x).is_none(); // one dereference less + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(0..1).any(|x| **y == x)` + +error: called `is_none()` after searching an `Iterator` with `find` + --> $DIR/search_is_some_fixable.rs:44:13 + | +LL | let _ = (0..1).find(|x| *x == 0).is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(0..1).any(|x| x == 0)` + +error: called `is_none()` after searching an `Iterator` with `find` + --> $DIR/search_is_some_fixable.rs:45:13 + | +LL | let _ = v.iter().find(|x| **x == 0).is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| *x == 0)` + +error: called `is_none()` after searching an `Iterator` with `position` + --> $DIR/search_is_some_fixable.rs:48:13 + | +LL | let _ = v.iter().position(|&x| x < 0).is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|&x| x < 0)` + +error: called `is_none()` after searching an `Iterator` with `rposition` + --> $DIR/search_is_some_fixable.rs:51:13 + | +LL | let _ = v.iter().rposition(|&x| x < 0).is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|&x| x < 0)` + +error: called `is_none()` after calling `find()` on a string + --> $DIR/search_is_some_fixable.rs:57:13 + | +LL | let _ = "hello world".find("world").is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains("world")` + +error: called `is_none()` after calling `find()` on a string + --> $DIR/search_is_some_fixable.rs:58:13 + | +LL | let _ = "hello world".find(&s2).is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains(&s2)` + +error: called `is_none()` after calling `find()` on a string + --> $DIR/search_is_some_fixable.rs:59:13 + | +LL | let _ = "hello world".find(&s2[2..]).is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains(&s2[2..])` + +error: called `is_none()` after calling `find()` on a string + --> $DIR/search_is_some_fixable.rs:61:13 + | +LL | let _ = s1.find("world").is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains("world")` + +error: called `is_none()` after calling `find()` on a string + --> $DIR/search_is_some_fixable.rs:62:13 + | +LL | let _ = s1.find(&s2).is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains(&s2)` + +error: called `is_none()` after calling `find()` on a string + --> $DIR/search_is_some_fixable.rs:63:13 + | +LL | let _ = s1.find(&s2[2..]).is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains(&s2[2..])` + +error: called `is_none()` after calling `find()` on a string + --> $DIR/search_is_some_fixable.rs:65:13 + | +LL | let _ = s1[2..].find("world").is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains("world")` + +error: called `is_none()` after calling `find()` on a string + --> $DIR/search_is_some_fixable.rs:66:13 + | +LL | let _ = s1[2..].find(&s2).is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains(&s2)` + +error: called `is_none()` after calling `find()` on a string + --> $DIR/search_is_some_fixable.rs:67:13 + | +LL | let _ = s1[2..].find(&s2[2..]).is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains(&s2[2..])` + +error: aborting due to 30 previous errors -- cgit 1.4.1-3-g733a5 From 45e775697ea4376224275e0c568fbefed6645fee Mon Sep 17 00:00:00 2001 From: Jamie Quigley Date: Mon, 22 Mar 2021 19:34:20 +0000 Subject: Ignore str::len() in or_fun_call lint. --- clippy_lints/src/methods/or_fun_call.rs | 2 +- tests/ui/or_fun_call.fixed | 3 +++ tests/ui/or_fun_call.rs | 3 +++ tests/ui/or_fun_call.stderr | 6 +++--- 4 files changed, 10 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index e6b473dbafa..20861ff8686 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -91,7 +91,7 @@ pub(super) fn check<'tcx>( let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs(); match ty.kind() { - ty::Slice(_) | ty::Array(_, _) => return, + ty::Slice(_) | ty::Array(_, _) | ty::Str => return, _ => (), } diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 660245f10c3..4390ff7dc30 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -120,6 +120,9 @@ fn test_or_with_ctors() { let slice = &["foo"][..]; let _ = opt.ok_or(slice.len()); + + let string = "foo"; + let _ = opt.ok_or(string.len()); } // Issue 4514 - early return diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index c589b170bdd..75908c974cc 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -120,6 +120,9 @@ fn test_or_with_ctors() { let slice = &["foo"][..]; let _ = opt.ok_or(slice.len()); + + let string = "foo"; + let _ = opt.ok_or(string.len()); } // Issue 4514 - early return diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index d63b3443592..9905029ce91 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -115,19 +115,19 @@ LL | .or(Some(Bar(b, Duration::from_secs(2)))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some(Bar(b, Duration::from_secs(2))))` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:138:14 + --> $DIR/or_fun_call.rs:141:14 | LL | None.unwrap_or(s.as_mut_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| s.as_mut_vec())` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:143:14 + --> $DIR/or_fun_call.rs:146:14 | LL | None.unwrap_or(unsafe { s.as_mut_vec() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { s.as_mut_vec() })` error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:145:14 + --> $DIR/or_fun_call.rs:148:14 | LL | None.unwrap_or( unsafe { s.as_mut_vec() } ); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { s.as_mut_vec() })` -- cgit 1.4.1-3-g733a5 From 3b8e85a5dccd47c7284b30ad29d3144d5c152235 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Thu, 25 Mar 2021 00:15:15 +0900 Subject: fix false positive in manual_flatten --- clippy_lints/src/loops/manual_flatten.rs | 10 +++- tests/ui/manual_flatten.rs | 22 +++++++++ tests/ui/manual_flatten.stderr | 85 +++++++++++++++++++++++++++----- 3 files changed, 105 insertions(+), 12 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/loops/manual_flatten.rs b/clippy_lints/src/loops/manual_flatten.rs index be87f67d300..8d2b9cccba4 100644 --- a/clippy_lints/src/loops/manual_flatten.rs +++ b/clippy_lints/src/loops/manual_flatten.rs @@ -6,6 +6,7 @@ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, StmtKind}; use rustc_lint::LateContext; +use rustc_middle::ty; use rustc_span::source_map::Span; /// Check for unnecessary `if let` usage in a for loop where only the `Some` or `Ok` variant of the @@ -54,6 +55,13 @@ pub(super) fn check<'tcx>( // Prepare the help message let mut applicability = Applicability::MaybeIncorrect; let arg_snippet = make_iterator_snippet(cx, arg, &mut applicability); + let copied = match cx.typeck_results().expr_ty(match_expr).kind() { + ty::Ref(_, inner, _) => match inner.kind() { + ty::Ref(..) => ".copied()", + _ => "" + } + _ => "" + }; span_lint_and_then( cx, @@ -61,7 +69,7 @@ pub(super) fn check<'tcx>( span, &msg, |diag| { - let sugg = format!("{}.flatten()", arg_snippet); + let sugg = format!("{}{}.flatten()", arg_snippet, copied); diag.span_suggestion( arg.span, "try", diff --git a/tests/ui/manual_flatten.rs b/tests/ui/manual_flatten.rs index cff68eca933..b5bd35a6878 100644 --- a/tests/ui/manual_flatten.rs +++ b/tests/ui/manual_flatten.rs @@ -1,4 +1,5 @@ #![warn(clippy::manual_flatten)] +#![allow(clippy::useless_vec)] fn main() { // Test for loop over implicitly adjusted `Iterator` with `if let` expression @@ -69,6 +70,27 @@ fn main() { } } + let vec_of_ref = vec![&Some(1)]; + for n in &vec_of_ref { + if let Some(n) = n { + println!("{:?}", n); + } + } + + let vec_of_ref = &vec_of_ref; + for n in vec_of_ref { + if let Some(n) = n { + println!("{:?}", n); + } + } + + let slice_of_ref = &[&Some(1)]; + for n in slice_of_ref { + if let Some(n) = n { + println!("{:?}", n); + } + } + // Using manual flatten should not trigger the lint for n in vec![Some(1), Some(2), Some(3)].iter().flatten() { println!("{}", n); diff --git a/tests/ui/manual_flatten.stderr b/tests/ui/manual_flatten.stderr index 855dd9130e2..be5f8a1d818 100644 --- a/tests/ui/manual_flatten.stderr +++ b/tests/ui/manual_flatten.stderr @@ -1,5 +1,5 @@ error: unnecessary `if let` since only the `Some` variant of the iterator element is used - --> $DIR/manual_flatten.rs:6:5 + --> $DIR/manual_flatten.rs:7:5 | LL | for n in x { | ^ - help: try: `x.into_iter().flatten()` @@ -13,7 +13,7 @@ LL | | } | = note: `-D clippy::manual-flatten` implied by `-D warnings` help: ...and remove the `if let` statement in the for loop - --> $DIR/manual_flatten.rs:7:9 + --> $DIR/manual_flatten.rs:8:9 | LL | / if let Some(y) = n { LL | | println!("{}", y); @@ -21,7 +21,7 @@ LL | | } | |_________^ error: unnecessary `if let` since only the `Ok` variant of the iterator element is used - --> $DIR/manual_flatten.rs:14:5 + --> $DIR/manual_flatten.rs:15:5 | LL | for n in y.clone() { | ^ --------- help: try: `y.clone().into_iter().flatten()` @@ -34,7 +34,7 @@ LL | | } | |_____^ | help: ...and remove the `if let` statement in the for loop - --> $DIR/manual_flatten.rs:15:9 + --> $DIR/manual_flatten.rs:16:9 | LL | / if let Ok(n) = n { LL | | println!("{}", n); @@ -42,7 +42,7 @@ LL | | }; | |_________^ error: unnecessary `if let` since only the `Ok` variant of the iterator element is used - --> $DIR/manual_flatten.rs:21:5 + --> $DIR/manual_flatten.rs:22:5 | LL | for n in &y { | ^ -- help: try: `y.iter().flatten()` @@ -55,7 +55,7 @@ LL | | } | |_____^ | help: ...and remove the `if let` statement in the for loop - --> $DIR/manual_flatten.rs:22:9 + --> $DIR/manual_flatten.rs:23:9 | LL | / if let Ok(n) = n { LL | | println!("{}", n); @@ -63,7 +63,7 @@ LL | | } | |_________^ error: unnecessary `if let` since only the `Ok` variant of the iterator element is used - --> $DIR/manual_flatten.rs:31:5 + --> $DIR/manual_flatten.rs:32:5 | LL | for n in z { | ^ - help: try: `z.into_iter().flatten()` @@ -76,7 +76,7 @@ LL | | } | |_____^ | help: ...and remove the `if let` statement in the for loop - --> $DIR/manual_flatten.rs:32:9 + --> $DIR/manual_flatten.rs:33:9 | LL | / if let Ok(n) = n { LL | | println!("{}", n); @@ -84,7 +84,7 @@ LL | | } | |_________^ error: unnecessary `if let` since only the `Some` variant of the iterator element is used - --> $DIR/manual_flatten.rs:40:5 + --> $DIR/manual_flatten.rs:41:5 | LL | for n in z { | ^ - help: try: `z.flatten()` @@ -97,12 +97,75 @@ LL | | } | |_____^ | help: ...and remove the `if let` statement in the for loop - --> $DIR/manual_flatten.rs:41:9 + --> $DIR/manual_flatten.rs:42:9 | LL | / if let Some(m) = n { LL | | println!("{}", m); LL | | } | |_________^ -error: aborting due to 5 previous errors +error: unnecessary `if let` since only the `Some` variant of the iterator element is used + --> $DIR/manual_flatten.rs:74:5 + | +LL | for n in &vec_of_ref { + | ^ ----------- help: try: `vec_of_ref.iter().copied().flatten()` + | _____| + | | +LL | | if let Some(n) = n { +LL | | println!("{:?}", n); +LL | | } +LL | | } + | |_____^ + | +help: ...and remove the `if let` statement in the for loop + --> $DIR/manual_flatten.rs:75:9 + | +LL | / if let Some(n) = n { +LL | | println!("{:?}", n); +LL | | } + | |_________^ + +error: unnecessary `if let` since only the `Some` variant of the iterator element is used + --> $DIR/manual_flatten.rs:81:5 + | +LL | for n in vec_of_ref { + | ^ ---------- help: try: `vec_of_ref.into_iter().copied().flatten()` + | _____| + | | +LL | | if let Some(n) = n { +LL | | println!("{:?}", n); +LL | | } +LL | | } + | |_____^ + | +help: ...and remove the `if let` statement in the for loop + --> $DIR/manual_flatten.rs:82:9 + | +LL | / if let Some(n) = n { +LL | | println!("{:?}", n); +LL | | } + | |_________^ + +error: unnecessary `if let` since only the `Some` variant of the iterator element is used + --> $DIR/manual_flatten.rs:88:5 + | +LL | for n in slice_of_ref { + | ^ ------------ help: try: `slice_of_ref.into_iter().copied().flatten()` + | _____| + | | +LL | | if let Some(n) = n { +LL | | println!("{:?}", n); +LL | | } +LL | | } + | |_____^ + | +help: ...and remove the `if let` statement in the for loop + --> $DIR/manual_flatten.rs:89:9 + | +LL | / if let Some(n) = n { +LL | | println!("{:?}", n); +LL | | } + | |_________^ + +error: aborting due to 8 previous errors -- cgit 1.4.1-3-g733a5 From 99b8a671981eb9bf2c624027940bf9ae4cd23087 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 22 Mar 2021 21:38:14 -0400 Subject: Fix false positive with `new_ret_no_self` when returning `Self` with different generic arguments --- clippy_lints/src/methods/mod.rs | 14 +++++++++++--- clippy_utils/src/ty.rs | 11 ++++++++++- tests/ui/new_ret_no_self.rs | 10 ++++++++++ 3 files changed, 31 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index d77f9c03cae..b6be77b03e5 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -61,7 +61,7 @@ mod zst_offset; use bind_instead_of_map::BindInsteadOfMap; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; -use clippy_utils::ty::{contains_ty, implements_trait, is_copy, is_type_diagnostic_item}; +use clippy_utils::ty::{contains_adt, contains_ty, implements_trait, is_copy, is_type_diagnostic_item}; use clippy_utils::{contains_return, get_trait_def_id, in_macro, iter_input_pats, method_calls, paths, return_ty}; use if_chain::if_chain; use rustc_hir as hir; @@ -1916,7 +1916,11 @@ impl<'tcx> LateLintPass<'tcx> for Methods { let ret_ty = return_ty(cx, impl_item.hir_id()); // walk the return type and check for Self (this does not check associated types) - if contains_ty(ret_ty, self_ty) { + if let Some(self_adt) = self_ty.ty_adt_def() { + if contains_adt(ret_ty, self_adt) { + return; + } + } else if contains_ty(ret_ty, self_ty) { return; } @@ -1926,7 +1930,11 @@ impl<'tcx> LateLintPass<'tcx> for Methods { for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) { if let ty::PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() { // walk the associated type and check for Self - if contains_ty(projection_predicate.ty, self_ty) { + if let Some(self_adt) = self_ty.ty_adt_def() { + if contains_adt(projection_predicate.ty, self_adt) { + return; + } + } else if contains_ty(projection_predicate.ty, self_ty) { return; } } diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index f4a1ae67da3..b9724efb04f 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -11,7 +11,7 @@ use rustc_hir::{TyKind, Unsafety}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; -use rustc_middle::ty::{self, IntTy, Ty, TypeFoldable, UintTy}; +use rustc_middle::ty::{self, AdtDef, IntTy, Ty, TypeFoldable, UintTy}; use rustc_span::sym; use rustc_span::symbol::Symbol; use rustc_span::DUMMY_SP; @@ -43,6 +43,15 @@ pub fn contains_ty(ty: Ty<'_>, other_ty: Ty<'_>) -> bool { }) } +/// Walks into `ty` and returns `true` if any inner type is any instance of the given abstract data +/// type.` +pub fn contains_adt(ty: Ty<'_>, adt: &AdtDef) -> bool { + ty.walk().any(|inner| match inner.unpack() { + GenericArgKind::Type(inner_ty) => inner_ty.ty_adt_def() == Some(adt), + GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, + }) +} + /// Returns true if ty has `iter` or `iter_mut` methods pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option { // FIXME: instead of this hard-coded list, we should check if `::iter` diff --git a/tests/ui/new_ret_no_self.rs b/tests/ui/new_ret_no_self.rs index e82873629a5..2f315ffe298 100644 --- a/tests/ui/new_ret_no_self.rs +++ b/tests/ui/new_ret_no_self.rs @@ -340,3 +340,13 @@ mod issue5435 { } } } + +// issue #1724 +struct RetOtherSelf(T); +struct RetOtherSelfWrapper(T); + +impl RetOtherSelf { + fn new(t: T) -> RetOtherSelf> { + RetOtherSelf(RetOtherSelfWrapper(t)) + } +} -- cgit 1.4.1-3-g733a5 From e9ebc27525757f967131ecb3570c1438f8de4efb Mon Sep 17 00:00:00 2001 From: Elliot Bobrow Date: Tue, 23 Mar 2021 17:10:27 -0700 Subject: check for `.to_string().into_bytes()` in string_lit_to_bytes apply changes from review --- clippy_lints/src/strings.rs | 29 +++++++++++++++++++++++++++++ tests/ui/string_lit_as_bytes.fixed | 6 ++++++ tests/ui/string_lit_as_bytes.rs | 6 ++++++ tests/ui/string_lit_as_bytes.stderr | 18 +++++++++++++++--- 4 files changed, 56 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 760f5e3b432..99ca7ef77a5 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -284,6 +284,35 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { } } } + + if_chain! { + if let ExprKind::MethodCall(path, _, [recv], _) = &e.kind; + if path.ident.name == sym!(into_bytes); + if let ExprKind::MethodCall(path, _, [recv], _) = &recv.kind; + if matches!(&*path.ident.name.as_str(), "to_owned" | "to_string"); + if let ExprKind::Lit(lit) = &recv.kind; + if let LitKind::Str(lit_content, _) = &lit.node; + + if lit_content.as_str().is_ascii(); + if lit_content.as_str().len() <= MAX_LENGTH_BYTE_STRING_LIT; + if !recv.span.from_expansion(); + then { + let mut applicability = Applicability::MachineApplicable; + + span_lint_and_sugg( + cx, + STRING_LIT_AS_BYTES, + e.span, + "calling `into_bytes()` on a string literal", + "consider using a byte string literal instead", + format!( + "b{}.to_vec()", + snippet_with_applicability(cx, recv.span, r#""..""#, &mut applicability) + ), + applicability, + ); + } + } } } diff --git a/tests/ui/string_lit_as_bytes.fixed b/tests/ui/string_lit_as_bytes.fixed index ccf8f61c4a9..dd22bfa5c53 100644 --- a/tests/ui/string_lit_as_bytes.fixed +++ b/tests/ui/string_lit_as_bytes.fixed @@ -8,10 +8,16 @@ fn str_lit_as_bytes() { let bs = br###"raw string with 3# plus " ""###; + let bs = b"lit to string".to_vec(); + let bs = b"lit to owned".to_vec(); + // no warning, because these cannot be written as byte string literals: let ubs = "☃".as_bytes(); let ubs = "hello there! this is a very long string".as_bytes(); + let ubs = "☃".to_string().into_bytes(); + let ubs = "this is also too long and shouldn't be fixed".to_string().into_bytes(); + let strify = stringify!(foobar).as_bytes(); let current_version = env!("CARGO_PKG_VERSION").as_bytes(); diff --git a/tests/ui/string_lit_as_bytes.rs b/tests/ui/string_lit_as_bytes.rs index 178df08e249..d2a710ed6b8 100644 --- a/tests/ui/string_lit_as_bytes.rs +++ b/tests/ui/string_lit_as_bytes.rs @@ -8,10 +8,16 @@ fn str_lit_as_bytes() { let bs = r###"raw string with 3# plus " ""###.as_bytes(); + let bs = "lit to string".to_string().into_bytes(); + let bs = "lit to owned".to_owned().into_bytes(); + // no warning, because these cannot be written as byte string literals: let ubs = "☃".as_bytes(); let ubs = "hello there! this is a very long string".as_bytes(); + let ubs = "☃".to_string().into_bytes(); + let ubs = "this is also too long and shouldn't be fixed".to_string().into_bytes(); + let strify = stringify!(foobar).as_bytes(); let current_version = env!("CARGO_PKG_VERSION").as_bytes(); diff --git a/tests/ui/string_lit_as_bytes.stderr b/tests/ui/string_lit_as_bytes.stderr index 99c512354d5..e0ddb070b50 100644 --- a/tests/ui/string_lit_as_bytes.stderr +++ b/tests/ui/string_lit_as_bytes.stderr @@ -12,17 +12,29 @@ error: calling `as_bytes()` on a string literal LL | let bs = r###"raw string with 3# plus " ""###.as_bytes(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a byte string literal instead: `br###"raw string with 3# plus " ""###` +error: calling `into_bytes()` on a string literal + --> $DIR/string_lit_as_bytes.rs:11:14 + | +LL | let bs = "lit to string".to_string().into_bytes(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a byte string literal instead: `b"lit to string".to_vec()` + +error: calling `into_bytes()` on a string literal + --> $DIR/string_lit_as_bytes.rs:12:14 + | +LL | let bs = "lit to owned".to_owned().into_bytes(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a byte string literal instead: `b"lit to owned".to_vec()` + error: calling `as_bytes()` on `include_str!(..)` - --> $DIR/string_lit_as_bytes.rs:19:22 + --> $DIR/string_lit_as_bytes.rs:25:22 | LL | let includestr = include_str!("entry_unfixable.rs").as_bytes(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `include_bytes!(..)` instead: `include_bytes!("entry_unfixable.rs")` error: calling `as_bytes()` on a string literal - --> $DIR/string_lit_as_bytes.rs:21:13 + --> $DIR/string_lit_as_bytes.rs:27:13 | LL | let _ = "string with newline/t/n".as_bytes(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a byte string literal instead: `b"string with newline/t/n"` -error: aborting due to 4 previous errors +error: aborting due to 6 previous errors -- cgit 1.4.1-3-g733a5 From 0ff68bb151ee8e93c0a6aff9f3cda3ac3f757d2f Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 25 Mar 2021 22:30:14 -0400 Subject: Improve `redundant_slicing` Fix bad suggestion when a reborrow might be required Fix bad suggestion when the value being sliced is a macro call Don't lint inside of a macro due to the previous context sensitive changes --- clippy_lints/src/redundant_slicing.rs | 37 ++++++++++++++++++++++++++--------- tests/ui/redundant_slicing.rs | 29 +++++++++++++++++++++++---- tests/ui/redundant_slicing.stderr | 32 +++++++++++++++++++++++------- 3 files changed, 78 insertions(+), 20 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/redundant_slicing.rs b/clippy_lints/src/redundant_slicing.rs index 6da7b5fbcc8..9c6cd7b4fa6 100644 --- a/clippy_lints/src/redundant_slicing.rs +++ b/clippy_lints/src/redundant_slicing.rs @@ -1,11 +1,12 @@ 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::is_type_lang_item; +use clippy_utils::{get_parent_expr, in_macro}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, LangItem}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::{lint::in_external_macro, ty::TyS}; +use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TyS; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -40,26 +41,44 @@ declare_lint_pass!(RedundantSlicing => [REDUNDANT_SLICING]); impl LateLintPass<'_> for RedundantSlicing { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if in_external_macro(cx.sess(), expr.span) { + if in_macro(expr.span) { return; } + let ctxt = expr.span.ctxt(); if_chain! { - if let ExprKind::AddrOf(_, _, addressee) = expr.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, mutability, addressee) = expr.kind; + if addressee.span.ctxt() == ctxt; if let ExprKind::Index(indexed, range) = addressee.kind; if is_type_lang_item(cx, cx.typeck_results().expr_ty_adjusted(range), LangItem::RangeFull); if TyS::same_type(cx.typeck_results().expr_ty(expr), cx.typeck_results().expr_ty(indexed)); then { let mut app = Applicability::MachineApplicable; - let hint = snippet_with_applicability(cx, indexed.span, "..", &mut app).into_owned(); + let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0; + + let (reborrow_str, help_str) = if mutability == Mutability::Mut { + // The slice was used to reborrow the mutable reference. + ("&mut *", "reborrow the original value instead") + } else if matches!( + get_parent_expr(cx, expr), + Some(Expr { + kind: ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _), + .. + }) + ) { + // The slice was used to make a temporary reference. + ("&*", "reborrow the original value instead") + } else { + ("", "use the original value instead") + }; span_lint_and_sugg( cx, REDUNDANT_SLICING, expr.span, "redundant slicing of the whole range", - "use the original slice instead", - hint, + help_str, + format!("{}{}", reborrow_str, snip), app, ); } diff --git a/tests/ui/redundant_slicing.rs b/tests/ui/redundant_slicing.rs index 922b8b4ce57..554b6ba36ae 100644 --- a/tests/ui/redundant_slicing.rs +++ b/tests/ui/redundant_slicing.rs @@ -2,10 +2,31 @@ #![warn(clippy::redundant_slicing)] fn main() { - let x: &[u32] = &[0]; - let err = &x[..]; + let slice: &[u32] = &[0]; + let _ = &slice[..]; let v = vec![0]; - let ok = &v[..]; - let err = &(&v[..])[..]; + let _ = &v[..]; // Changes the type + let _ = &(&v[..])[..]; // Outer borrow is redundant + + static S: &[u8] = &[0, 1, 2]; + let err = &mut &S[..]; // Should reborrow instead of slice + + let mut vec = vec![0]; + let mut_slice = &mut *vec; + let _ = &mut mut_slice[..]; // Should reborrow instead of slice + + macro_rules! m { + ($e:expr) => { + $e + }; + } + let _ = &m!(slice)[..]; + + macro_rules! m2 { + ($e:expr) => { + &$e[..] + }; + } + let _ = m2!(slice); // Don't lint in a macro } diff --git a/tests/ui/redundant_slicing.stderr b/tests/ui/redundant_slicing.stderr index 9efd6484ad0..bbd10eafbbe 100644 --- a/tests/ui/redundant_slicing.stderr +++ b/tests/ui/redundant_slicing.stderr @@ -1,16 +1,34 @@ error: redundant slicing of the whole range - --> $DIR/redundant_slicing.rs:6:15 + --> $DIR/redundant_slicing.rs:6:13 | -LL | let err = &x[..]; - | ^^^^^^ help: use the original slice instead: `x` +LL | let _ = &slice[..]; + | ^^^^^^^^^^ help: use the original value instead: `slice` | = note: `-D clippy::redundant-slicing` implied by `-D warnings` error: redundant slicing of the whole range - --> $DIR/redundant_slicing.rs:10:15 + --> $DIR/redundant_slicing.rs:10:13 | -LL | let err = &(&v[..])[..]; - | ^^^^^^^^^^^^^ help: use the original slice instead: `(&v[..])` +LL | let _ = &(&v[..])[..]; // Outer borrow is redundant + | ^^^^^^^^^^^^^ help: use the original value instead: `(&v[..])` -error: aborting due to 2 previous errors +error: redundant slicing of the whole range + --> $DIR/redundant_slicing.rs:13:20 + | +LL | let err = &mut &S[..]; // Should reborrow instead of slice + | ^^^^^^ help: reborrow the original value instead: `&*S` + +error: redundant slicing of the whole range + --> $DIR/redundant_slicing.rs:17:13 + | +LL | let _ = &mut mut_slice[..]; // Should reborrow instead of slice + | ^^^^^^^^^^^^^^^^^^ help: reborrow the original value instead: `&mut *mut_slice` + +error: redundant slicing of the whole range + --> $DIR/redundant_slicing.rs:24:13 + | +LL | let _ = &m!(slice)[..]; + | ^^^^^^^^^^^^^^ help: use the original value instead: `slice` + +error: aborting due to 5 previous errors -- cgit 1.4.1-3-g733a5 From 494bc8a30cf154eede2f22178c9a7ebc404302e7 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Fri, 19 Mar 2021 18:14:48 +0900 Subject: Fix FN that types lints don't work with const or static --- clippy_lints/src/types/mod.rs | 8 ++++++++ tests/ui/dlist.rs | 3 +++ tests/ui/dlist.stderr | 32 ++++++++++++++++++++++++-------- tests/ui/option_option.rs | 3 +++ tests/ui/option_option.stderr | 38 +++++++++++++++++++++++++------------- tests/ui/vec_box_sized.fixed | 2 ++ tests/ui/vec_box_sized.rs | 2 ++ tests/ui/vec_box_sized.stderr | 26 +++++++++++++++++++------- 8 files changed, 86 insertions(+), 28 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index d5f2b3d013e..1df964db38f 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -249,6 +249,14 @@ impl<'tcx> LateLintPass<'tcx> for Types { self.check_fn_decl(cx, decl); } + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + match item.kind { + ItemKind::Static(ref ty, _, _) | ItemKind::Const(ref ty, _) => self.check_ty(cx, ty, false), + // functions, enums, structs, impls and traits are covered + _ => (), + } + } + fn check_field_def(&mut self, cx: &LateContext<'_>, field: &hir::FieldDef<'_>) { self.check_ty(cx, &field.ty, false); } diff --git a/tests/ui/dlist.rs b/tests/ui/dlist.rs index 2940d2d2901..2c3b25cd45e 100644 --- a/tests/ui/dlist.rs +++ b/tests/ui/dlist.rs @@ -5,6 +5,9 @@ extern crate alloc; use alloc::collections::linked_list::LinkedList; +const C: LinkedList = LinkedList::new(); +static S: LinkedList = LinkedList::new(); + trait Foo { type Baz = LinkedList; fn foo(_: LinkedList); diff --git a/tests/ui/dlist.stderr b/tests/ui/dlist.stderr index 234db33ba12..425407dc334 100644 --- a/tests/ui/dlist.stderr +++ b/tests/ui/dlist.stderr @@ -1,14 +1,30 @@ error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? - --> $DIR/dlist.rs:9:16 + --> $DIR/dlist.rs:8:10 + | +LL | const C: LinkedList = LinkedList::new(); + | ^^^^^^^^^^^^^^^ + | + = note: `-D clippy::linkedlist` implied by `-D warnings` + = help: a `VecDeque` might work + +error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? + --> $DIR/dlist.rs:9:11 + | +LL | static S: LinkedList = LinkedList::new(); + | ^^^^^^^^^^^^^^^ + | + = help: a `VecDeque` might work + +error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? + --> $DIR/dlist.rs:12:16 | LL | type Baz = LinkedList; | ^^^^^^^^^^^^^^ | - = note: `-D clippy::linkedlist` implied by `-D warnings` = help: a `VecDeque` might work error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? - --> $DIR/dlist.rs:10:15 + --> $DIR/dlist.rs:13:15 | LL | fn foo(_: LinkedList); | ^^^^^^^^^^^^^^ @@ -16,7 +32,7 @@ LL | fn foo(_: LinkedList); = help: a `VecDeque` might work error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? - --> $DIR/dlist.rs:11:23 + --> $DIR/dlist.rs:14:23 | LL | const BAR: Option>; | ^^^^^^^^^^^^^^ @@ -24,7 +40,7 @@ LL | const BAR: Option>; = help: a `VecDeque` might work error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? - --> $DIR/dlist.rs:22:15 + --> $DIR/dlist.rs:25:15 | LL | fn foo(_: LinkedList) {} | ^^^^^^^^^^^^^^ @@ -32,7 +48,7 @@ LL | fn foo(_: LinkedList) {} = help: a `VecDeque` might work error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? - --> $DIR/dlist.rs:25:39 + --> $DIR/dlist.rs:28:39 | LL | pub fn test(my_favourite_linked_list: LinkedList) { | ^^^^^^^^^^^^^^ @@ -40,12 +56,12 @@ LL | pub fn test(my_favourite_linked_list: LinkedList) { = help: a `VecDeque` might work error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? - --> $DIR/dlist.rs:29:29 + --> $DIR/dlist.rs:32:29 | LL | pub fn test_ret() -> Option> { | ^^^^^^^^^^^^^^ | = help: a `VecDeque` might work -error: aborting due to 6 previous errors +error: aborting due to 8 previous errors diff --git a/tests/ui/option_option.rs b/tests/ui/option_option.rs index 6859ba8e5bb..2faab9e035d 100644 --- a/tests/ui/option_option.rs +++ b/tests/ui/option_option.rs @@ -1,6 +1,9 @@ #![deny(clippy::option_option)] #![allow(clippy::unnecessary_wraps)] +const C: Option> = None; +static S: Option> = None; + fn input(_: Option>) {} fn output() -> Option> { diff --git a/tests/ui/option_option.stderr b/tests/ui/option_option.stderr index ad7f081c713..a925bb35b04 100644 --- a/tests/ui/option_option.stderr +++ b/tests/ui/option_option.stderr @@ -1,8 +1,8 @@ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:4:13 + --> $DIR/option_option.rs:4:10 | -LL | fn input(_: Option>) {} - | ^^^^^^^^^^^^^^^^^^ +LL | const C: Option> = None; + | ^^^^^^^^^^^^^^^^^^^ | note: the lint level is defined here --> $DIR/option_option.rs:1:9 @@ -11,58 +11,70 @@ LL | #![deny(clippy::option_option)] | ^^^^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:6:16 + --> $DIR/option_option.rs:5:11 + | +LL | static S: Option> = None; + | ^^^^^^^^^^^^^^^^^^^ + +error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases + --> $DIR/option_option.rs:7:13 + | +LL | fn input(_: Option>) {} + | ^^^^^^^^^^^^^^^^^^ + +error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases + --> $DIR/option_option.rs:9:16 | LL | fn output() -> Option> { | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:10:27 + --> $DIR/option_option.rs:13:27 | LL | fn output_nested() -> Vec>> { | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:15:30 + --> $DIR/option_option.rs:18:30 | LL | fn output_nested_nested() -> Option>> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:20:8 + --> $DIR/option_option.rs:23:8 | LL | x: Option>, | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:24:23 + --> $DIR/option_option.rs:27:23 | LL | fn struct_fn() -> Option> { | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:30:22 + --> $DIR/option_option.rs:33:22 | LL | fn trait_fn() -> Option>; | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:34:11 + --> $DIR/option_option.rs:37:11 | LL | Tuple(Option>), | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:35:17 + --> $DIR/option_option.rs:38:17 | LL | Struct { x: Option> }, | ^^^^^^^^^^^^^^^^^^ error: consider using `Option` instead of `Option>` or a custom enum if you need to distinguish all 3 cases - --> $DIR/option_option.rs:76:14 + --> $DIR/option_option.rs:79:14 | LL | foo: Option>>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 10 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/vec_box_sized.fixed b/tests/ui/vec_box_sized.fixed index 4fa28b525c3..a40d91fdb18 100644 --- a/tests/ui/vec_box_sized.fixed +++ b/tests/ui/vec_box_sized.fixed @@ -9,6 +9,8 @@ struct BigStruct([i32; 10000]); /// The following should trigger the lint mod should_trigger { use super::SizedStruct; + const C: Vec = Vec::new(); + static S: Vec = Vec::new(); struct StructWithVecBox { sized_type: Vec, diff --git a/tests/ui/vec_box_sized.rs b/tests/ui/vec_box_sized.rs index 7dc735cd90b..843bbb64e71 100644 --- a/tests/ui/vec_box_sized.rs +++ b/tests/ui/vec_box_sized.rs @@ -9,6 +9,8 @@ struct BigStruct([i32; 10000]); /// The following should trigger the lint mod should_trigger { use super::SizedStruct; + const C: Vec> = Vec::new(); + static S: Vec> = Vec::new(); struct StructWithVecBox { sized_type: Vec>, diff --git a/tests/ui/vec_box_sized.stderr b/tests/ui/vec_box_sized.stderr index 83435a40aa1..c518267f041 100644 --- a/tests/ui/vec_box_sized.stderr +++ b/tests/ui/vec_box_sized.stderr @@ -1,28 +1,40 @@ error: `Vec` is already on the heap, the boxing is unnecessary - --> $DIR/vec_box_sized.rs:14:21 + --> $DIR/vec_box_sized.rs:12:14 | -LL | sized_type: Vec>, - | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Vec` +LL | const C: Vec> = Vec::new(); + | ^^^^^^^^^^^^^ help: try: `Vec` | = note: `-D clippy::vec-box` implied by `-D warnings` error: `Vec` is already on the heap, the boxing is unnecessary - --> $DIR/vec_box_sized.rs:17:14 + --> $DIR/vec_box_sized.rs:13:15 + | +LL | static S: Vec> = Vec::new(); + | ^^^^^^^^^^^^^ help: try: `Vec` + +error: `Vec` is already on the heap, the boxing is unnecessary + --> $DIR/vec_box_sized.rs:16:21 + | +LL | sized_type: Vec>, + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Vec` + +error: `Vec` is already on the heap, the boxing is unnecessary + --> $DIR/vec_box_sized.rs:19:14 | LL | struct A(Vec>); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `Vec` error: `Vec` is already on the heap, the boxing is unnecessary - --> $DIR/vec_box_sized.rs:18:18 + --> $DIR/vec_box_sized.rs:20:18 | LL | struct B(Vec>>); | ^^^^^^^^^^^^^^^ help: try: `Vec` error: `Vec` is already on the heap, the boxing is unnecessary - --> $DIR/vec_box_sized.rs:46:23 + --> $DIR/vec_box_sized.rs:48:23 | LL | pub fn f() -> Vec> { | ^^^^^^^^^^^ help: try: `Vec` -error: aborting due to 4 previous errors +error: aborting due to 6 previous errors -- cgit 1.4.1-3-g733a5 From bd1201a2635d757244dbdab09026d53ad52da6a1 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Fri, 19 Mar 2021 18:21:33 +0900 Subject: Fix inconsistent test names --- tests/ui/complex_types.rs | 60 -------------------------- tests/ui/complex_types.stderr | 94 ----------------------------------------- tests/ui/dlist.rs | 43 ------------------- tests/ui/dlist.stderr | 67 ----------------------------- tests/ui/linkedlist.rs | 43 +++++++++++++++++++ tests/ui/linkedlist.stderr | 67 +++++++++++++++++++++++++++++ tests/ui/type_complexity.rs | 60 ++++++++++++++++++++++++++ tests/ui/type_complexity.stderr | 94 +++++++++++++++++++++++++++++++++++++++++ 8 files changed, 264 insertions(+), 264 deletions(-) delete mode 100644 tests/ui/complex_types.rs delete mode 100644 tests/ui/complex_types.stderr delete mode 100644 tests/ui/dlist.rs delete mode 100644 tests/ui/dlist.stderr create mode 100644 tests/ui/linkedlist.rs create mode 100644 tests/ui/linkedlist.stderr create mode 100644 tests/ui/type_complexity.rs create mode 100644 tests/ui/type_complexity.stderr (limited to 'tests') diff --git a/tests/ui/complex_types.rs b/tests/ui/complex_types.rs deleted file mode 100644 index 383bbb49dbe..00000000000 --- a/tests/ui/complex_types.rs +++ /dev/null @@ -1,60 +0,0 @@ -#![warn(clippy::all)] -#![allow(unused, clippy::needless_pass_by_value, clippy::vec_box)] -#![feature(associated_type_defaults)] - -type Alias = Vec>>; // no warning here - -const CST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); -static ST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); - -struct S { - f: Vec>>, -} - -struct Ts(Vec>>); - -enum E { - Tuple(Vec>>), - Struct { f: Vec>> }, -} - -impl S { - const A: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); - fn impl_method(&self, p: Vec>>) {} -} - -trait T { - const A: Vec>>; - type B = Vec>>; - fn method(&self, p: Vec>>); - fn def_method(&self, p: Vec>>) {} -} - -fn test1() -> Vec>> { - vec![] -} - -fn test2(_x: Vec>>) {} - -fn test3() { - let _y: Vec>> = vec![]; -} - -#[repr(C)] -struct D { - // should not warn, since we don't have control over the signature (#3222) - test4: extern "C" fn( - itself: &D, - a: usize, - b: usize, - c: usize, - d: usize, - e: usize, - f: usize, - g: usize, - h: usize, - i: usize, - ), -} - -fn main() {} diff --git a/tests/ui/complex_types.stderr b/tests/ui/complex_types.stderr deleted file mode 100644 index 7fcbb4bce88..00000000000 --- a/tests/ui/complex_types.stderr +++ /dev/null @@ -1,94 +0,0 @@ -error: very complex type used. Consider factoring parts into `type` definitions - --> $DIR/complex_types.rs:7:12 - | -LL | const CST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::type-complexity` implied by `-D warnings` - -error: very complex type used. Consider factoring parts into `type` definitions - --> $DIR/complex_types.rs:8:12 - | -LL | static ST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: very complex type used. Consider factoring parts into `type` definitions - --> $DIR/complex_types.rs:11:8 - | -LL | f: Vec>>, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: very complex type used. Consider factoring parts into `type` definitions - --> $DIR/complex_types.rs:14:11 - | -LL | struct Ts(Vec>>); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: very complex type used. Consider factoring parts into `type` definitions - --> $DIR/complex_types.rs:17:11 - | -LL | Tuple(Vec>>), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: very complex type used. Consider factoring parts into `type` definitions - --> $DIR/complex_types.rs:18:17 - | -LL | Struct { f: Vec>> }, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: very complex type used. Consider factoring parts into `type` definitions - --> $DIR/complex_types.rs:22:14 - | -LL | const A: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: very complex type used. Consider factoring parts into `type` definitions - --> $DIR/complex_types.rs:23:30 - | -LL | fn impl_method(&self, p: Vec>>) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: very complex type used. Consider factoring parts into `type` definitions - --> $DIR/complex_types.rs:27:14 - | -LL | const A: Vec>>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: very complex type used. Consider factoring parts into `type` definitions - --> $DIR/complex_types.rs:28:14 - | -LL | type B = Vec>>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: very complex type used. Consider factoring parts into `type` definitions - --> $DIR/complex_types.rs:29:25 - | -LL | fn method(&self, p: Vec>>); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: very complex type used. Consider factoring parts into `type` definitions - --> $DIR/complex_types.rs:30:29 - | -LL | fn def_method(&self, p: Vec>>) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: very complex type used. Consider factoring parts into `type` definitions - --> $DIR/complex_types.rs:33:15 - | -LL | fn test1() -> Vec>> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: very complex type used. Consider factoring parts into `type` definitions - --> $DIR/complex_types.rs:37:14 - | -LL | fn test2(_x: Vec>>) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: very complex type used. Consider factoring parts into `type` definitions - --> $DIR/complex_types.rs:40:13 - | -LL | let _y: Vec>> = vec![]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 15 previous errors - diff --git a/tests/ui/dlist.rs b/tests/ui/dlist.rs deleted file mode 100644 index 2c3b25cd45e..00000000000 --- a/tests/ui/dlist.rs +++ /dev/null @@ -1,43 +0,0 @@ -#![feature(associated_type_defaults)] -#![warn(clippy::linkedlist)] -#![allow(dead_code, clippy::needless_pass_by_value)] - -extern crate alloc; -use alloc::collections::linked_list::LinkedList; - -const C: LinkedList = LinkedList::new(); -static S: LinkedList = LinkedList::new(); - -trait Foo { - type Baz = LinkedList; - fn foo(_: LinkedList); - const BAR: Option>; -} - -// Ok, we don’t want to warn for implementations; see issue #605. -impl Foo for LinkedList { - fn foo(_: LinkedList) {} - const BAR: Option> = None; -} - -struct Bar; -impl Bar { - fn foo(_: LinkedList) {} -} - -pub fn test(my_favourite_linked_list: LinkedList) { - println!("{:?}", my_favourite_linked_list) -} - -pub fn test_ret() -> Option> { - unimplemented!(); -} - -pub fn test_local_not_linted() { - let _: LinkedList; -} - -fn main() { - test(LinkedList::new()); - test_local_not_linted(); -} diff --git a/tests/ui/dlist.stderr b/tests/ui/dlist.stderr deleted file mode 100644 index 425407dc334..00000000000 --- a/tests/ui/dlist.stderr +++ /dev/null @@ -1,67 +0,0 @@ -error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? - --> $DIR/dlist.rs:8:10 - | -LL | const C: LinkedList = LinkedList::new(); - | ^^^^^^^^^^^^^^^ - | - = note: `-D clippy::linkedlist` implied by `-D warnings` - = help: a `VecDeque` might work - -error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? - --> $DIR/dlist.rs:9:11 - | -LL | static S: LinkedList = LinkedList::new(); - | ^^^^^^^^^^^^^^^ - | - = help: a `VecDeque` might work - -error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? - --> $DIR/dlist.rs:12:16 - | -LL | type Baz = LinkedList; - | ^^^^^^^^^^^^^^ - | - = help: a `VecDeque` might work - -error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? - --> $DIR/dlist.rs:13:15 - | -LL | fn foo(_: LinkedList); - | ^^^^^^^^^^^^^^ - | - = help: a `VecDeque` might work - -error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? - --> $DIR/dlist.rs:14:23 - | -LL | const BAR: Option>; - | ^^^^^^^^^^^^^^ - | - = help: a `VecDeque` might work - -error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? - --> $DIR/dlist.rs:25:15 - | -LL | fn foo(_: LinkedList) {} - | ^^^^^^^^^^^^^^ - | - = help: a `VecDeque` might work - -error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? - --> $DIR/dlist.rs:28:39 - | -LL | pub fn test(my_favourite_linked_list: LinkedList) { - | ^^^^^^^^^^^^^^ - | - = help: a `VecDeque` might work - -error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? - --> $DIR/dlist.rs:32:29 - | -LL | pub fn test_ret() -> Option> { - | ^^^^^^^^^^^^^^ - | - = help: a `VecDeque` might work - -error: aborting due to 8 previous errors - diff --git a/tests/ui/linkedlist.rs b/tests/ui/linkedlist.rs new file mode 100644 index 00000000000..2c3b25cd45e --- /dev/null +++ b/tests/ui/linkedlist.rs @@ -0,0 +1,43 @@ +#![feature(associated_type_defaults)] +#![warn(clippy::linkedlist)] +#![allow(dead_code, clippy::needless_pass_by_value)] + +extern crate alloc; +use alloc::collections::linked_list::LinkedList; + +const C: LinkedList = LinkedList::new(); +static S: LinkedList = LinkedList::new(); + +trait Foo { + type Baz = LinkedList; + fn foo(_: LinkedList); + const BAR: Option>; +} + +// Ok, we don’t want to warn for implementations; see issue #605. +impl Foo for LinkedList { + fn foo(_: LinkedList) {} + const BAR: Option> = None; +} + +struct Bar; +impl Bar { + fn foo(_: LinkedList) {} +} + +pub fn test(my_favourite_linked_list: LinkedList) { + println!("{:?}", my_favourite_linked_list) +} + +pub fn test_ret() -> Option> { + unimplemented!(); +} + +pub fn test_local_not_linted() { + let _: LinkedList; +} + +fn main() { + test(LinkedList::new()); + test_local_not_linted(); +} diff --git a/tests/ui/linkedlist.stderr b/tests/ui/linkedlist.stderr new file mode 100644 index 00000000000..38ae71714d6 --- /dev/null +++ b/tests/ui/linkedlist.stderr @@ -0,0 +1,67 @@ +error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? + --> $DIR/linkedlist.rs:8:10 + | +LL | const C: LinkedList = LinkedList::new(); + | ^^^^^^^^^^^^^^^ + | + = note: `-D clippy::linkedlist` implied by `-D warnings` + = help: a `VecDeque` might work + +error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? + --> $DIR/linkedlist.rs:9:11 + | +LL | static S: LinkedList = LinkedList::new(); + | ^^^^^^^^^^^^^^^ + | + = help: a `VecDeque` might work + +error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? + --> $DIR/linkedlist.rs:12:16 + | +LL | type Baz = LinkedList; + | ^^^^^^^^^^^^^^ + | + = help: a `VecDeque` might work + +error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? + --> $DIR/linkedlist.rs:13:15 + | +LL | fn foo(_: LinkedList); + | ^^^^^^^^^^^^^^ + | + = help: a `VecDeque` might work + +error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? + --> $DIR/linkedlist.rs:14:23 + | +LL | const BAR: Option>; + | ^^^^^^^^^^^^^^ + | + = help: a `VecDeque` might work + +error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? + --> $DIR/linkedlist.rs:25:15 + | +LL | fn foo(_: LinkedList) {} + | ^^^^^^^^^^^^^^ + | + = help: a `VecDeque` might work + +error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? + --> $DIR/linkedlist.rs:28:39 + | +LL | pub fn test(my_favourite_linked_list: LinkedList) { + | ^^^^^^^^^^^^^^ + | + = help: a `VecDeque` might work + +error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? + --> $DIR/linkedlist.rs:32:29 + | +LL | pub fn test_ret() -> Option> { + | ^^^^^^^^^^^^^^ + | + = help: a `VecDeque` might work + +error: aborting due to 8 previous errors + diff --git a/tests/ui/type_complexity.rs b/tests/ui/type_complexity.rs new file mode 100644 index 00000000000..383bbb49dbe --- /dev/null +++ b/tests/ui/type_complexity.rs @@ -0,0 +1,60 @@ +#![warn(clippy::all)] +#![allow(unused, clippy::needless_pass_by_value, clippy::vec_box)] +#![feature(associated_type_defaults)] + +type Alias = Vec>>; // no warning here + +const CST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); +static ST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); + +struct S { + f: Vec>>, +} + +struct Ts(Vec>>); + +enum E { + Tuple(Vec>>), + Struct { f: Vec>> }, +} + +impl S { + const A: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); + fn impl_method(&self, p: Vec>>) {} +} + +trait T { + const A: Vec>>; + type B = Vec>>; + fn method(&self, p: Vec>>); + fn def_method(&self, p: Vec>>) {} +} + +fn test1() -> Vec>> { + vec![] +} + +fn test2(_x: Vec>>) {} + +fn test3() { + let _y: Vec>> = vec![]; +} + +#[repr(C)] +struct D { + // should not warn, since we don't have control over the signature (#3222) + test4: extern "C" fn( + itself: &D, + a: usize, + b: usize, + c: usize, + d: usize, + e: usize, + f: usize, + g: usize, + h: usize, + i: usize, + ), +} + +fn main() {} diff --git a/tests/ui/type_complexity.stderr b/tests/ui/type_complexity.stderr new file mode 100644 index 00000000000..7879233fdf2 --- /dev/null +++ b/tests/ui/type_complexity.stderr @@ -0,0 +1,94 @@ +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/type_complexity.rs:7:12 + | +LL | const CST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::type-complexity` implied by `-D warnings` + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/type_complexity.rs:8:12 + | +LL | static ST: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/type_complexity.rs:11:8 + | +LL | f: Vec>>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/type_complexity.rs:14:11 + | +LL | struct Ts(Vec>>); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/type_complexity.rs:17:11 + | +LL | Tuple(Vec>>), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/type_complexity.rs:18:17 + | +LL | Struct { f: Vec>> }, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/type_complexity.rs:22:14 + | +LL | const A: (u32, (u32, (u32, (u32, u32)))) = (0, (0, (0, (0, 0)))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/type_complexity.rs:23:30 + | +LL | fn impl_method(&self, p: Vec>>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/type_complexity.rs:27:14 + | +LL | const A: Vec>>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/type_complexity.rs:28:14 + | +LL | type B = Vec>>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/type_complexity.rs:29:25 + | +LL | fn method(&self, p: Vec>>); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/type_complexity.rs:30:29 + | +LL | fn def_method(&self, p: Vec>>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/type_complexity.rs:33:15 + | +LL | fn test1() -> Vec>> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/type_complexity.rs:37:14 + | +LL | fn test2(_x: Vec>>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: very complex type used. Consider factoring parts into `type` definitions + --> $DIR/type_complexity.rs:40:13 + | +LL | let _y: Vec>> = vec![]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 15 previous errors + -- cgit 1.4.1-3-g733a5 From 1a1adad81d0fd7d0c64d0a72575a6b4db69c035a Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 26 Mar 2021 10:56:05 +0100 Subject: Add MSRV option to unnested_or_patterns lint --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/unnested_or_patterns.rs | 44 ++++++++++++++++++++++++++------ clippy_lints/src/utils/conf.rs | 2 +- tests/ui/min_rust_version_attr.rs | 6 +++++ tests/ui/min_rust_version_attr.stderr | 8 +++--- 5 files changed, 48 insertions(+), 14 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3a9236d8735..f013613119c 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1079,6 +1079,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box missing_const_for_fn::MissingConstForFn::new(msrv)); store.register_late_pass(move || box needless_question_mark::NeedlessQuestionMark::new(msrv)); store.register_late_pass(move || box casts::Casts::new(msrv)); + store.register_early_pass(move || box unnested_or_patterns::UnnestedOrPatterns::new(msrv)); store.register_late_pass(|| box size_of_in_element_count::SizeOfInElementCount); store.register_late_pass(|| box map_clone::MapClone); @@ -1254,7 +1255,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(move || box non_expressive_names::NonExpressiveNames { single_char_binding_names_threshold, }); - store.register_early_pass(|| box unnested_or_patterns::UnnestedOrPatterns); store.register_late_pass(|| box macro_use::MacroUseImports::default()); store.register_late_pass(|| box map_identity::MapIdentity); store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch); diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index 2b9479365c6..9376a2cf66a 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -1,15 +1,19 @@ #![allow(clippy::wildcard_imports, clippy::enum_glob_use)] -use clippy_utils::ast_utils::{eq_field_pat, eq_id, eq_pat, eq_path}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::over; +use clippy_utils::{ + ast_utils::{eq_field_pat, eq_id, eq_pat, eq_path}, + meets_msrv, +}; use rustc_ast::mut_visit::*; use rustc_ast::ptr::P; use rustc_ast::{self as ast, Pat, PatKind, PatKind::*, DUMMY_NODE_ID}; use rustc_ast_pretty::pprust; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::DUMMY_SP; use std::cell::Cell; @@ -50,26 +54,50 @@ declare_clippy_lint! { "unnested or-patterns, e.g., `Foo(Bar) | Foo(Baz) instead of `Foo(Bar | Baz)`" } -declare_lint_pass!(UnnestedOrPatterns => [UNNESTED_OR_PATTERNS]); +const UNNESTED_OR_PATTERNS_MSRV: RustcVersion = RustcVersion::new(1, 53, 0); + +#[derive(Clone, Copy)] +pub struct UnnestedOrPatterns { + msrv: Option, +} + +impl UnnestedOrPatterns { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(UnnestedOrPatterns => [UNNESTED_OR_PATTERNS]); impl EarlyLintPass for UnnestedOrPatterns { fn check_arm(&mut self, cx: &EarlyContext<'_>, a: &ast::Arm) { - lint_unnested_or_patterns(cx, &a.pat); + if meets_msrv(self.msrv.as_ref(), &UNNESTED_OR_PATTERNS_MSRV) { + lint_unnested_or_patterns(cx, &a.pat); + } } fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { - if let ast::ExprKind::Let(pat, _) = &e.kind { - lint_unnested_or_patterns(cx, pat); + if meets_msrv(self.msrv.as_ref(), &UNNESTED_OR_PATTERNS_MSRV) { + if let ast::ExprKind::Let(pat, _) = &e.kind { + lint_unnested_or_patterns(cx, pat); + } } } fn check_param(&mut self, cx: &EarlyContext<'_>, p: &ast::Param) { - lint_unnested_or_patterns(cx, &p.pat); + if meets_msrv(self.msrv.as_ref(), &UNNESTED_OR_PATTERNS_MSRV) { + lint_unnested_or_patterns(cx, &p.pat); + } } fn check_local(&mut self, cx: &EarlyContext<'_>, l: &ast::Local) { - lint_unnested_or_patterns(cx, &l.pat); + if meets_msrv(self.msrv.as_ref(), &UNNESTED_OR_PATTERNS_MSRV) { + lint_unnested_or_patterns(cx, &l.pat); + } } + + extract_msrv_attr!(EarlyContext); } fn lint_unnested_or_patterns(cx: &EarlyContext<'_>, pat: &Pat) { diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 9139a0966c5..b372f5b0e72 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -106,7 +106,7 @@ macro_rules! define_Conf { pub use self::helpers::Conf; define_Conf! { - /// Lint: REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN. The minimum rust version that the project supports + /// Lint: REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS. The minimum rust version that the project supports (msrv, "msrv": Option, None), /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses (blacklisted_names, "blacklisted_names": Vec, ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()), diff --git a/tests/ui/min_rust_version_attr.rs b/tests/ui/min_rust_version_attr.rs index 0f47f1cbc40..49ace1ca128 100644 --- a/tests/ui/min_rust_version_attr.rs +++ b/tests/ui/min_rust_version_attr.rs @@ -123,6 +123,11 @@ fn missing_const_for_fn() -> i32 { 1 } +fn unnest_or_patterns() { + struct TS(u8, u8); + if let TS(0, x) | TS(1, x) = TS(0, 0) {} +} + fn main() { filter_map_next(); checked_conversion(); @@ -138,6 +143,7 @@ fn main() { replace_with_default(); map_unwrap_or(); missing_const_for_fn(); + unnest_or_patterns(); } mod meets_msrv { diff --git a/tests/ui/min_rust_version_attr.stderr b/tests/ui/min_rust_version_attr.stderr index e3e3b335cbe..8d3575c2da8 100644 --- a/tests/ui/min_rust_version_attr.stderr +++ b/tests/ui/min_rust_version_attr.stderr @@ -1,12 +1,12 @@ error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:150:24 + --> $DIR/min_rust_version_attr.rs:156:24 | LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); | ^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::manual-strip` implied by `-D warnings` note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:149:9 + --> $DIR/min_rust_version_attr.rs:155:9 | LL | if s.starts_with("hello, ") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,13 +17,13 @@ LL | assert_eq!(.to_uppercase(), "WORLD!"); | error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:162:24 + --> $DIR/min_rust_version_attr.rs:168:24 | LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); | ^^^^^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:161:9 + --> $DIR/min_rust_version_attr.rs:167:9 | LL | if s.starts_with("hello, ") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From ca7e95501c13f386956728bb66ec099a0d89e8e5 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Sat, 27 Mar 2021 00:52:49 +0100 Subject: upper_case_acronyms: add ui and ui-toml tests for private/public enums --- clippy_lints/src/upper_case_acronyms.rs | 5 ++--- .../upper_case_acronyms.rs | 16 ++++++++++++++++ .../upper_case_acronyms.stderr | 14 +++++++++++++- tests/ui/upper_case_acronyms.rs | 14 ++++++++++++++ tests/ui/upper_case_acronyms.stderr | 8 +++++++- 5 files changed, 52 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/upper_case_acronyms.rs b/clippy_lints/src/upper_case_acronyms.rs index f8023d07437..4ac2ec55b98 100644 --- a/clippy_lints/src/upper_case_acronyms.rs +++ b/clippy_lints/src/upper_case_acronyms.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use if_chain::if_chain; use itertools::Itertools; -use rustc_ast::ast::{Item, ItemKind, Variant, VisibilityKind}; +use rustc_ast::ast::{Item, ItemKind, VisibilityKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -109,7 +108,7 @@ impl EarlyLintPass for UpperCaseAcronyms { } else if let ItemKind::Enum(ref enumdef, _) = it.kind { // check enum variants seperately because again we only want to lint on private enums and // the fn check_variant does not know about the vis of the enum of its variants - &enumdef + enumdef .variants .iter() .for_each(|variant| check_ident(cx, &variant.ident, self.upper_case_acronyms_aggressive)); diff --git a/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.rs b/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.rs index c6659edacc3..1a5cf1b1947 100644 --- a/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.rs +++ b/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.rs @@ -25,4 +25,20 @@ pub struct MIXEDCapital; pub struct FULLCAPITAL; +// enum variants should not be linted if the num is pub +pub enum ParseError { + FULLCAPITAL(u8), + MIXEDCapital(String), + Utf8(std::string::FromUtf8Error), + Parse(T, String), +} + +// private, do lint here +enum ParseErrorPrivate { + WASD(u8), + WASDMixed(String), + Utf8(std::string::FromUtf8Error), + Parse(T, String), +} + fn main() {} diff --git a/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.stderr b/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.stderr index 38e30683d57..02f29bbefe1 100644 --- a/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.stderr +++ b/tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.stderr @@ -66,5 +66,17 @@ error: name `GCCLLVMSomething` contains a capitalized acronym LL | struct GCCLLVMSomething; | ^^^^^^^^^^^^^^^^ help: consider making the acronym lowercase, except the initial letter: `GccllvmSomething` -error: aborting due to 11 previous errors +error: name `WASD` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:38:5 + | +LL | WASD(u8), + | ^^^^ help: consider making the acronym lowercase, except the initial letter: `Wasd` + +error: name `WASDMixed` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:39:5 + | +LL | WASDMixed(String), + | ^^^^^^^^^ help: consider making the acronym lowercase, except the initial letter: `WasdMixed` + +error: aborting due to 13 previous errors diff --git a/tests/ui/upper_case_acronyms.rs b/tests/ui/upper_case_acronyms.rs index 8c09c6f5b23..48bb9e54b12 100644 --- a/tests/ui/upper_case_acronyms.rs +++ b/tests/ui/upper_case_acronyms.rs @@ -24,4 +24,18 @@ struct GCCLLVMSomething; pub struct NOWARNINGHERE; pub struct ALSONoWarningHERE; +// enum variants should not be linted if the num is pub +pub enum ParseError { + YDB(u8), + Utf8(std::string::FromUtf8Error), + Parse(T, String), +} + +// private, do lint here +enum ParseErrorPrivate { + WASD(u8), + Utf8(std::string::FromUtf8Error), + Parse(T, String), +} + fn main() {} diff --git a/tests/ui/upper_case_acronyms.stderr b/tests/ui/upper_case_acronyms.stderr index bbe38991e52..250b196a99e 100644 --- a/tests/ui/upper_case_acronyms.stderr +++ b/tests/ui/upper_case_acronyms.stderr @@ -48,5 +48,11 @@ error: name `FIN` contains a capitalized acronym LL | FIN, | ^^^ help: consider making the acronym lowercase, except the initial letter: `Fin` -error: aborting due to 8 previous errors +error: name `WASD` contains a capitalized acronym + --> $DIR/upper_case_acronyms.rs:36:5 + | +LL | WASD(u8), + | ^^^^ help: consider making the acronym lowercase, except the initial letter: `Wasd` + +error: aborting due to 9 previous errors -- cgit 1.4.1-3-g733a5 From 12985afbcad28eb67c0b5c5903ff21b6fa922b13 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 26 Mar 2021 16:27:19 -0400 Subject: `len_without_is_empty` improvements Check the return type of `len`. Only integral types, or an `Option` or `Result` wrapping one. Ensure the return type of `is_empty` matches. e.g. `Option` -> `Option` --- clippy_lints/src/len_zero.rs | 78 +++++++++++++++++++++++----- tests/ui/len_without_is_empty.rs | 99 +++++++++++++++++++++++++++++++++++- tests/ui/len_without_is_empty.stderr | 79 ++++++++++++++++++++++++---- 3 files changed, 231 insertions(+), 25 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 717f2ea84f4..fb522be2f1a 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -10,9 +10,12 @@ use rustc_hir::{ ItemKind, Mutability, Node, TraitItemRef, TyKind, }; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, AssocKind, FnSig}; +use rustc_middle::ty::{self, AssocKind, FnSig, Ty, TyS}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::{Span, Spanned, Symbol}; +use rustc_span::{ + source_map::{Span, Spanned, Symbol}, + symbol::sym, +}; declare_clippy_lint! { /// **What it does:** Checks for getting the length of something via `.len()` @@ -137,6 +140,7 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { if let Some(local_id) = ty_id.as_local(); let ty_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_id); if !is_allowed(cx, LEN_WITHOUT_IS_EMPTY, ty_hir_id); + if let Some(output) = parse_len_output(cx, cx.tcx.fn_sig(item.def_id).skip_binder()); then { let (name, kind) = match cx.tcx.hir().find(ty_hir_id) { Some(Node::ForeignItem(x)) => (x.ident.name, "extern type"), @@ -148,7 +152,7 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { } _ => return, }; - check_for_is_empty(cx, sig.span, sig.decl.implicit_self, ty_id, name, kind) + check_for_is_empty(cx, sig.span, sig.decl.implicit_self, output, ty_id, name, kind) } } } @@ -231,10 +235,62 @@ fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items } } +#[derive(Debug, Clone, Copy)] +enum LenOutput<'tcx> { + Integral, + Option(DefId), + Result(DefId, Ty<'tcx>), +} +fn parse_len_output(cx: &LateContext<'_>, sig: FnSig<'tcx>) -> Option> { + match *sig.output().kind() { + ty::Int(_) | ty::Uint(_) => Some(LenOutput::Integral), + ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::option_type, adt.did) => { + subs.type_at(0).is_integral().then(|| LenOutput::Option(adt.did)) + }, + ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::result_type, adt.did) => subs + .type_at(0) + .is_integral() + .then(|| LenOutput::Result(adt.did, subs.type_at(1))), + _ => None, + } +} + +impl LenOutput<'_> { + fn matches_is_empty_output(self, ty: Ty<'_>) -> bool { + match (self, ty.kind()) { + (_, &ty::Bool) => true, + (Self::Option(id), &ty::Adt(adt, subs)) if id == adt.did => subs.type_at(0).is_bool(), + (Self::Result(id, err_ty), &ty::Adt(adt, subs)) if id == adt.did => { + subs.type_at(0).is_bool() && TyS::same_type(subs.type_at(1), err_ty) + }, + _ => false, + } + } + + fn expected_sig(self, self_kind: ImplicitSelfKind) -> String { + let self_ref = match self_kind { + ImplicitSelfKind::ImmRef => "&", + ImplicitSelfKind::MutRef => "&mut ", + _ => "", + }; + match self { + Self::Integral => format!("expected signature: `({}self) -> bool`", self_ref), + Self::Option(_) => format!( + "expected signature: `({}self) -> bool` or `({}self) -> Option", + self_ref, self_ref + ), + Self::Result(..) => format!( + "expected signature: `({}self) -> bool` or `({}self) -> Result", + self_ref, self_ref + ), + } + } +} + /// Checks if the given signature matches the expectations for `is_empty` -fn check_is_empty_sig(cx: &LateContext<'_>, sig: FnSig<'_>, self_kind: ImplicitSelfKind) -> bool { +fn check_is_empty_sig(sig: FnSig<'_>, self_kind: ImplicitSelfKind, len_output: LenOutput<'_>) -> bool { match &**sig.inputs_and_output { - [arg, res] if *res == cx.tcx.types.bool => { + [arg, res] if len_output.matches_is_empty_output(res) => { matches!( (arg.kind(), self_kind), (ty::Ref(_, _, Mutability::Not), ImplicitSelfKind::ImmRef) @@ -250,6 +306,7 @@ fn check_for_is_empty( cx: &LateContext<'_>, span: Span, self_kind: ImplicitSelfKind, + output: LenOutput<'_>, impl_ty: DefId, item_name: Symbol, item_kind: &str, @@ -289,7 +346,7 @@ fn check_for_is_empty( }, Some(is_empty) if !(is_empty.fn_has_self_parameter - && check_is_empty_sig(cx, cx.tcx.fn_sig(is_empty.def_id).skip_binder(), self_kind)) => + && check_is_empty_sig(cx.tcx.fn_sig(is_empty.def_id).skip_binder(), self_kind, output)) => { ( format!( @@ -309,14 +366,7 @@ fn check_for_is_empty( db.span_note(span, "`is_empty` defined here"); } if let Some(self_kind) = self_kind { - db.note(&format!( - "expected signature: `({}self) -> bool`", - match self_kind { - ImplicitSelfKind::ImmRef => "&", - ImplicitSelfKind::MutRef => "&mut ", - _ => "", - } - )); + db.note(&output.expected_sig(self_kind)); } }); } diff --git a/tests/ui/len_without_is_empty.rs b/tests/ui/len_without_is_empty.rs index 6b3636a482e..b9d66347c27 100644 --- a/tests/ui/len_without_is_empty.rs +++ b/tests/ui/len_without_is_empty.rs @@ -1,3 +1,5 @@ +// edition:2018 + #![warn(clippy::len_without_is_empty)] #![allow(dead_code, unused)] @@ -172,9 +174,9 @@ pub trait DependsOnFoo: Foo { fn len(&mut self) -> usize; } +// issue #1562 pub struct MultipleImpls; -// issue #1562 impl MultipleImpls { pub fn len(&self) -> usize { 1 @@ -187,4 +189,99 @@ impl MultipleImpls { } } +// issue #6958 +pub struct OptionalLen; + +impl OptionalLen { + pub fn len(&self) -> Option { + Some(0) + } + + pub fn is_empty(&self) -> Option { + Some(true) + } +} + +pub struct OptionalLen2; +impl OptionalLen2 { + pub fn len(&self) -> Option { + Some(0) + } + + pub fn is_empty(&self) -> bool { + true + } +} + +pub struct OptionalLen3; +impl OptionalLen3 { + pub fn len(&self) -> usize { + 0 + } + + // should lint, len is not an option + pub fn is_empty(&self) -> Option { + None + } +} + +pub struct ResultLen; +impl ResultLen { + pub fn len(&self) -> Result { + Ok(0) + } + + // Differing result types + pub fn is_empty(&self) -> Option { + Some(true) + } +} + +pub struct ResultLen2; +impl ResultLen2 { + pub fn len(&self) -> Result { + Ok(0) + } + + pub fn is_empty(&self) -> Result { + Ok(true) + } +} + +pub struct ResultLen3; +impl ResultLen3 { + pub fn len(&self) -> Result { + Ok(0) + } + + // Non-fallible result is ok. + pub fn is_empty(&self) -> bool { + true + } +} + +pub struct OddLenSig; +impl OddLenSig { + // don't lint + pub fn len(&self) -> bool { + true + } +} + +// issue #6958 +pub struct AsyncLen; +impl AsyncLen { + async fn async_task(&self) -> bool { + true + } + + pub async fn len(&self) -> usize { + if self.async_task().await { 0 } else { 1 } + } + + pub async fn is_empty(&self) -> bool { + self.len().await == 0 + } +} + fn main() {} diff --git a/tests/ui/len_without_is_empty.stderr b/tests/ui/len_without_is_empty.stderr index f106506faf4..ff7f5562308 100644 --- a/tests/ui/len_without_is_empty.stderr +++ b/tests/ui/len_without_is_empty.stderr @@ -1,5 +1,5 @@ error: struct `PubOne` has a public `len` method, but no `is_empty` method - --> $DIR/len_without_is_empty.rs:7:5 + --> $DIR/len_without_is_empty.rs:9:5 | LL | pub fn len(&self) -> isize { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | pub fn len(&self) -> isize { = note: `-D clippy::len-without-is-empty` implied by `-D warnings` error: trait `PubTraitsToo` has a `len` method but no (possibly inherited) `is_empty` method - --> $DIR/len_without_is_empty.rs:55:1 + --> $DIR/len_without_is_empty.rs:57:1 | LL | / pub trait PubTraitsToo { LL | | fn len(&self) -> isize; @@ -15,50 +15,109 @@ LL | | } | |_^ error: struct `HasIsEmpty` has a public `len` method, but a private `is_empty` method - --> $DIR/len_without_is_empty.rs:68:5 + --> $DIR/len_without_is_empty.rs:70:5 | LL | pub fn len(&self) -> isize { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: `is_empty` defined here - --> $DIR/len_without_is_empty.rs:72:5 + --> $DIR/len_without_is_empty.rs:74:5 | LL | fn is_empty(&self) -> bool { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: struct `HasWrongIsEmpty` has a public `len` method, but the `is_empty` method has an unexpected signature - --> $DIR/len_without_is_empty.rs:80:5 + --> $DIR/len_without_is_empty.rs:82:5 | LL | pub fn len(&self) -> isize { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: `is_empty` defined here - --> $DIR/len_without_is_empty.rs:84:5 + --> $DIR/len_without_is_empty.rs:86:5 | LL | pub fn is_empty(&self, x: u32) -> bool { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: expected signature: `(&self) -> bool` error: struct `MismatchedSelf` has a public `len` method, but the `is_empty` method has an unexpected signature - --> $DIR/len_without_is_empty.rs:92:5 + --> $DIR/len_without_is_empty.rs:94:5 | LL | pub fn len(self) -> isize { | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: `is_empty` defined here - --> $DIR/len_without_is_empty.rs:96:5 + --> $DIR/len_without_is_empty.rs:98:5 | LL | pub fn is_empty(&self) -> bool { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: expected signature: `(self) -> bool` error: trait `DependsOnFoo` has a `len` method but no (possibly inherited) `is_empty` method - --> $DIR/len_without_is_empty.rs:171:1 + --> $DIR/len_without_is_empty.rs:173:1 | LL | / pub trait DependsOnFoo: Foo { LL | | fn len(&mut self) -> usize; LL | | } | |_^ -error: aborting due to 6 previous errors +error: struct `OptionalLen3` has a public `len` method, but the `is_empty` method has an unexpected signature + --> $DIR/len_without_is_empty.rs:218:5 + | +LL | pub fn len(&self) -> usize { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: `is_empty` defined here + --> $DIR/len_without_is_empty.rs:223:5 + | +LL | pub fn is_empty(&self) -> Option { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: expected signature: `(&self) -> bool` + +error: struct `ResultLen` has a public `len` method, but the `is_empty` method has an unexpected signature + --> $DIR/len_without_is_empty.rs:230:5 + | +LL | pub fn len(&self) -> Result { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: `is_empty` defined here + --> $DIR/len_without_is_empty.rs:235:5 + | +LL | pub fn is_empty(&self) -> Option { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: expected signature: `(&self) -> bool` or `(&self) -> Result + +error: this returns a `Result<_, ()> + --> $DIR/len_without_is_empty.rs:230:5 + | +LL | pub fn len(&self) -> Result { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::result-unit-err` implied by `-D warnings` + = help: use a custom Error type instead + +error: this returns a `Result<_, ()> + --> $DIR/len_without_is_empty.rs:242:5 + | +LL | pub fn len(&self) -> Result { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a custom Error type instead + +error: this returns a `Result<_, ()> + --> $DIR/len_without_is_empty.rs:246:5 + | +LL | pub fn is_empty(&self) -> Result { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a custom Error type instead + +error: this returns a `Result<_, ()> + --> $DIR/len_without_is_empty.rs:253:5 + | +LL | pub fn len(&self) -> Result { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use a custom Error type instead + +error: aborting due to 12 previous errors -- cgit 1.4.1-3-g733a5 From e006c77d611e20692beb3f319b0fec8cb6f09dc3 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Sun, 28 Mar 2021 00:04:44 +0100 Subject: redundant_pattern_matching: look inside Refs look inside refs and detect if let &None = ... Fixes https://github.com/rust-lang/rust-clippy/issues/5396 changelog: redundant_pattern_matching: look inside Refs to fix FNs with "if let &None = .. " --- clippy_lints/src/matches.rs | 8 +++++++- tests/ui/match_ref_pats.stderr | 16 +++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 3680429fed7..425124b78f4 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -1723,7 +1723,13 @@ mod redundant_pattern_match { arms: &[Arm<'_>], keyword: &'static str, ) { - let good_method = match arms[0].pat.kind { + // also look inside refs + let mut kind = &arms[0].pat.kind; + // if we have &None for example, peel it so we can detect "if let None = x" + if let PatKind::Ref(inner, _mutability) = kind { + kind = &inner.kind; + } + let good_method = match kind { PatKind::TupleStruct(ref path, ref patterns, _) if patterns.len() == 1 => { if let PatKind::Wild = patterns[0].kind { if match_qpath(path, &paths::RESULT_OK) { diff --git a/tests/ui/match_ref_pats.stderr b/tests/ui/match_ref_pats.stderr index 52cb4a14b72..67474e65cde 100644 --- a/tests/ui/match_ref_pats.stderr +++ b/tests/ui/match_ref_pats.stderr @@ -46,6 +46,14 @@ LL | Some(v) => println!("{:?}", v), LL | None => println!("none"), | +error: redundant pattern matching, consider using `is_none()` + --> $DIR/match_ref_pats.rs:35:12 + | +LL | if let &None = a { + | -------^^^^^---- help: try this: `if a.is_none()` + | + = 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 | @@ -59,6 +67,12 @@ help: instead of prefixing all patterns with `&`, you can dereference the expres LL | if let None = *a { | ^^^^ ^^ +error: redundant pattern matching, consider using `is_none()` + --> $DIR/match_ref_pats.rs:40: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 | @@ -87,5 +101,5 @@ LL | match *foo_variant!(0) { LL | Foo::A => println!("A"), | -error: aborting due to 6 previous errors +error: aborting due to 8 previous errors -- cgit 1.4.1-3-g733a5 From 1768efa33344022c550f3cfcd30ede168bcf3143 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 14 Mar 2021 19:35:35 +0100 Subject: Fix FP in `single_component_path_imports` lint --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/single_component_path_imports.rs | 90 ++++++++++++++++++---- .../ui/single_component_path_imports_self_after.rs | 16 ++++ .../single_component_path_imports_self_before.rs | 17 ++++ 4 files changed, 109 insertions(+), 16 deletions(-) create mode 100644 tests/ui/single_component_path_imports_self_after.rs create mode 100644 tests/ui/single_component_path_imports_self_before.rs (limited to 'tests') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f013613119c..97557fefe3e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1232,7 +1232,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box as_conversions::AsConversions); store.register_late_pass(|| box let_underscore::LetUnderscore); store.register_late_pass(|| box atomic_ordering::AtomicOrdering); - store.register_early_pass(|| box single_component_path_imports::SingleComponentPathImports); + store.register_early_pass(|| box single_component_path_imports::SingleComponentPathImports::default()); let max_fn_params_bools = conf.max_fn_params_bools; let max_struct_bools = conf.max_struct_bools; store.register_early_pass(move || box excessive_bools::ExcessiveBools::new(max_struct_bools, max_fn_params_bools)); diff --git a/clippy_lints/src/single_component_path_imports.rs b/clippy_lints/src/single_component_path_imports.rs index c9d72aabb6a..ee74ef97bcd 100644 --- a/clippy_lints/src/single_component_path_imports.rs +++ b/clippy_lints/src/single_component_path_imports.rs @@ -1,11 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::in_macro; use if_chain::if_chain; -use rustc_ast::{Item, ItemKind, UseTreeKind}; +use rustc_ast::{Crate, Item, ItemKind, UseTreeKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::edition::Edition; +use rustc_span::symbol::kw; +use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// **What it does:** Checking for imports with single component use path. @@ -35,29 +37,87 @@ declare_clippy_lint! { "imports with single component path are redundant" } -declare_lint_pass!(SingleComponentPathImports => [SINGLE_COMPONENT_PATH_IMPORTS]); +#[derive(Default)] +pub struct SingleComponentPathImports { + /// keep track of imports reused with `self` keyword, + /// such as `self::crypto_hash` in the example below + /// + /// ```rust,ignore + /// use self::crypto_hash::{Algorithm, Hasher}; + /// ``` + imports_reused_with_self: Vec, + /// keep track of single use statements + /// such as `crypto_hash` in the example below + /// + /// ```rust,ignore + /// use crypto_hash; + /// ``` + single_use_usages: Vec<(Symbol, Span)>, +} + +impl_lint_pass!(SingleComponentPathImports => [SINGLE_COMPONENT_PATH_IMPORTS]); impl EarlyLintPass for SingleComponentPathImports { - fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - if_chain! { - if !in_macro(item.span); - if cx.sess.opts.edition >= Edition::Edition2018; - if !item.vis.kind.is_pub(); - if let ItemKind::Use(use_tree) = &item.kind; - if let segments = &use_tree.prefix.segments; - if segments.len() == 1; - if let UseTreeKind::Simple(None, _, _) = use_tree.kind; - then { + fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) { + if cx.sess.opts.edition < Edition::Edition2018 { + return; + } + for item in &krate.items { + self.track_uses(&item); + } + for single_use in &self.single_use_usages { + if !self.imports_reused_with_self.contains(&single_use.0) { span_lint_and_sugg( cx, SINGLE_COMPONENT_PATH_IMPORTS, - item.span, + single_use.1, "this import is redundant", "remove it entirely", String::new(), - Applicability::MachineApplicable + Applicability::MachineApplicable, ); } } } } + +impl SingleComponentPathImports { + fn track_uses(&mut self, item: &Item) { + if_chain! { + if !in_macro(item.span); + if !item.vis.kind.is_pub(); + if let ItemKind::Use(use_tree) = &item.kind; + if let segments = &use_tree.prefix.segments; + + then { + // keep track of `use some_module;` usages + if segments.len() == 1 { + if let UseTreeKind::Simple(None, _, _) = use_tree.kind { + let ident = &segments[0].ident; + self.single_use_usages.push((ident.name, item.span)); + } + return; + } + + // keep track of `use self::some_module` usages + if segments[0].ident.name == kw::SelfLower { + // simple case such as `use self::module::SomeStruct` + if segments.len() > 1 { + self.imports_reused_with_self.push(segments[1].ident.name); + return; + } + + // nested case such as `use self::{module1::Struct1, module2::Struct2}` + if let UseTreeKind::Nested(trees) = &use_tree.kind { + for tree in trees { + let segments = &tree.0.prefix.segments; + if !segments.is_empty() { + self.imports_reused_with_self.push(segments[0].ident.name); + } + } + } + } + } + } + } +} diff --git a/tests/ui/single_component_path_imports_self_after.rs b/tests/ui/single_component_path_imports_self_after.rs new file mode 100644 index 00000000000..94319ade0ac --- /dev/null +++ b/tests/ui/single_component_path_imports_self_after.rs @@ -0,0 +1,16 @@ +// edition:2018 +#![warn(clippy::single_component_path_imports)] +#![allow(unused_imports)] + +use self::regex::{Regex as xeger, RegexSet as tesxeger}; +pub use self::{ + regex::{Regex, RegexSet}, + some_mod::SomeType, +}; +use regex; + +mod some_mod { + pub struct SomeType; +} + +fn main() {} diff --git a/tests/ui/single_component_path_imports_self_before.rs b/tests/ui/single_component_path_imports_self_before.rs new file mode 100644 index 00000000000..c7437b23456 --- /dev/null +++ b/tests/ui/single_component_path_imports_self_before.rs @@ -0,0 +1,17 @@ +// edition:2018 +#![warn(clippy::single_component_path_imports)] +#![allow(unused_imports)] + +use regex; + +use self::regex::{Regex as xeger, RegexSet as tesxeger}; +pub use self::{ + regex::{Regex, RegexSet}, + some_mod::SomeType, +}; + +mod some_mod { + pub struct SomeType; +} + +fn main() {} -- cgit 1.4.1-3-g733a5 From 6985d13a9783bb7181b6c43e83465e7630e48ec2 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 28 Mar 2021 09:35:44 +0200 Subject: Take into account sub modules --- clippy_lints/src/single_component_path_imports.rs | 24 ++++++++++++++--------- tests/ui/single_component_path_imports.fixed | 6 ++++++ tests/ui/single_component_path_imports.rs | 6 ++++++ tests/ui/single_component_path_imports.stderr | 8 +++++++- 4 files changed, 34 insertions(+), 10 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/single_component_path_imports.rs b/clippy_lints/src/single_component_path_imports.rs index ee74ef97bcd..901f7642a12 100644 --- a/clippy_lints/src/single_component_path_imports.rs +++ b/clippy_lints/src/single_component_path_imports.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::in_macro; -use if_chain::if_chain; -use rustc_ast::{Crate, Item, ItemKind, UseTreeKind}; +use rustc_ast::{Crate, Item, ItemKind, ModKind, UseTreeKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -83,13 +82,19 @@ impl EarlyLintPass for SingleComponentPathImports { impl SingleComponentPathImports { fn track_uses(&mut self, item: &Item) { - if_chain! { - if !in_macro(item.span); - if !item.vis.kind.is_pub(); - if let ItemKind::Use(use_tree) = &item.kind; - if let segments = &use_tree.prefix.segments; + if in_macro(item.span) || item.vis.kind.is_pub() { + return; + } + + match &item.kind { + ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) => { + for item in items.iter() { + self.track_uses(&item); + } + }, + ItemKind::Use(use_tree) => { + let segments = &use_tree.prefix.segments; - then { // keep track of `use some_module;` usages if segments.len() == 1 { if let UseTreeKind::Simple(None, _, _) = use_tree.kind { @@ -117,7 +122,8 @@ impl SingleComponentPathImports { } } } - } + }, + _ => {}, } } } diff --git a/tests/ui/single_component_path_imports.fixed b/tests/ui/single_component_path_imports.fixed index a7a8499b58f..226d2b315d7 100644 --- a/tests/ui/single_component_path_imports.fixed +++ b/tests/ui/single_component_path_imports.fixed @@ -19,3 +19,9 @@ fn main() { // False positive #5154, shouldn't trigger lint. m!(); } + +mod hello_mod { + + #[allow(dead_code)] + fn hello_mod() {} +} diff --git a/tests/ui/single_component_path_imports.rs b/tests/ui/single_component_path_imports.rs index 9a427e90ad3..88bf7f1fc5a 100644 --- a/tests/ui/single_component_path_imports.rs +++ b/tests/ui/single_component_path_imports.rs @@ -19,3 +19,9 @@ fn main() { // False positive #5154, shouldn't trigger lint. m!(); } + +mod hello_mod { + use regex; + #[allow(dead_code)] + fn hello_mod() {} +} diff --git a/tests/ui/single_component_path_imports.stderr b/tests/ui/single_component_path_imports.stderr index 519ada0169a..646e6d3647a 100644 --- a/tests/ui/single_component_path_imports.stderr +++ b/tests/ui/single_component_path_imports.stderr @@ -6,5 +6,11 @@ LL | use regex; | = note: `-D clippy::single-component-path-imports` implied by `-D warnings` -error: aborting due to previous error +error: this import is redundant + --> $DIR/single_component_path_imports.rs:24:5 + | +LL | use regex; + | ^^^^^^^^^^ help: remove it entirely + +error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From 879fa5c9721c89c27be6a9db5f51d935a51f549b Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 27 Mar 2021 21:48:10 -0400 Subject: Improve `expl_impl_clone_on_copy` Check to see if the generic constraints are the same as if using derive --- clippy_lints/src/derive.rs | 87 ++++++++++++++++++++++++---------------------- tests/ui/derive.rs | 21 +++++++++-- tests/ui/derive.stderr | 30 +++++++++++++--- 3 files changed, 89 insertions(+), 49 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 834136f910d..9fad85f8ef4 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_then}; use clippy_utils::paths; -use clippy_utils::ty::is_copy; +use clippy_utils::ty::{implements_trait, is_copy}; use clippy_utils::{get_trait_def_id, is_allowed, is_automatically_derived, match_def_path}; use if_chain::if_chain; use rustc_hir::def_id::DefId; @@ -12,7 +12,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Span; +use rustc_span::{def_id::LOCAL_CRATE, source_map::Span}; declare_clippy_lint! { /// **What it does:** Checks for deriving `Hash` but implementing `PartialEq` @@ -293,48 +293,53 @@ fn check_ord_partial_ord<'tcx>( /// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint. fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &TraitRef<'_>, ty: Ty<'tcx>) { - if cx - .tcx - .lang_items() - .clone_trait() - .map_or(false, |id| Some(id) == trait_ref.trait_def_id()) - { - if !is_copy(cx, ty) { + let clone_id = match cx.tcx.lang_items().clone_trait() { + Some(id) if trait_ref.trait_def_id() == Some(id) => id, + _ => return, + }; + let copy_id = match cx.tcx.lang_items().copy_trait() { + Some(id) => id, + None => return, + }; + let (ty_adt, ty_subs) = match *ty.kind() { + // Unions can't derive clone. + ty::Adt(adt, subs) if !adt.is_union() => (adt, subs), + _ => return, + }; + // If the current self type doesn't implement Copy (due to generic constraints), search to see if + // there's a Copy impl for any instance of the adt. + if !is_copy(cx, ty) { + if ty_subs.non_erasable_generics().next().is_some() { + let has_copy_impl = cx + .tcx + .all_local_trait_impls(LOCAL_CRATE) + .get(©_id) + .map_or(false, |impls| { + impls + .iter() + .any(|&id| matches!(cx.tcx.type_of(id).kind(), ty::Adt(adt, _) if ty_adt.did == adt.did)) + }); + if !has_copy_impl { + return; + } + } else { return; } - - match *ty.kind() { - ty::Adt(def, _) if def.is_union() => return, - - // Some types are not Clone by default but could be cloned “by hand” if necessary - ty::Adt(def, substs) => { - for variant in &def.variants { - for field in &variant.fields { - if let ty::FnDef(..) = field.ty(cx.tcx, substs).kind() { - return; - } - } - for subst in substs { - if let ty::subst::GenericArgKind::Type(subst) = subst.unpack() { - if let ty::Param(_) = subst.kind() { - return; - } - } - } - } - }, - _ => (), - } - - span_lint_and_note( - cx, - EXPL_IMPL_CLONE_ON_COPY, - item.span, - "you are implementing `Clone` explicitly on a `Copy` type", - Some(item.span), - "consider deriving `Clone` or removing `Copy`", - ); } + // Derive constrains all generic types to requiring Clone. Check if any type is not constrained for + // this impl. + if ty_subs.types().any(|ty| !implements_trait(cx, ty, clone_id, &[])) { + return; + } + + span_lint_and_note( + cx, + EXPL_IMPL_CLONE_ON_COPY, + item.span, + "you are implementing `Clone` explicitly on a `Copy` type", + Some(item.span), + "consider deriving `Clone` or removing `Copy`", + ); } /// Implementation of the `UNSAFE_DERIVE_DESERIALIZE` lint. diff --git a/tests/ui/derive.rs b/tests/ui/derive.rs index 8fcb0e8b28d..4e46bf13991 100644 --- a/tests/ui/derive.rs +++ b/tests/ui/derive.rs @@ -35,7 +35,6 @@ impl<'a> Clone for Lt<'a> { } } -// Ok, `Clone` cannot be derived because of the big array #[derive(Copy)] struct BigArray { a: [u8; 65], @@ -47,7 +46,6 @@ impl Clone for BigArray { } } -// Ok, function pointers are not always Clone #[derive(Copy)] struct FnPtr { a: fn() -> !, @@ -59,7 +57,7 @@ impl Clone for FnPtr { } } -// Ok, generics +// Ok, Clone trait impl doesn't have constrained generics. #[derive(Copy)] struct Generic { a: T, @@ -71,4 +69,21 @@ impl Clone for Generic { } } +#[derive(Copy)] +struct Generic2(T); +impl Clone for Generic2 { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} + +// Ok, Clone trait impl doesn't have constrained generics. +#[derive(Copy)] +struct GenericRef<'a, T, U>(T, &'a U); +impl Clone for GenericRef<'_, T, U> { + fn clone(&self) -> Self { + Self(self.0.clone(), self.1) + } +} + fn main() {} diff --git a/tests/ui/derive.stderr b/tests/ui/derive.stderr index 1328a9b3107..82a70ceecc3 100644 --- a/tests/ui/derive.stderr +++ b/tests/ui/derive.stderr @@ -40,7 +40,7 @@ LL | | } | |_^ error: you are implementing `Clone` explicitly on a `Copy` type - --> $DIR/derive.rs:44:1 + --> $DIR/derive.rs:43:1 | LL | / impl Clone for BigArray { LL | | fn clone(&self) -> Self { @@ -50,7 +50,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> $DIR/derive.rs:44:1 + --> $DIR/derive.rs:43:1 | LL | / impl Clone for BigArray { LL | | fn clone(&self) -> Self { @@ -60,7 +60,7 @@ LL | | } | |_^ error: you are implementing `Clone` explicitly on a `Copy` type - --> $DIR/derive.rs:56:1 + --> $DIR/derive.rs:54:1 | LL | / impl Clone for FnPtr { LL | | fn clone(&self) -> Self { @@ -70,7 +70,7 @@ LL | | } | |_^ | note: consider deriving `Clone` or removing `Copy` - --> $DIR/derive.rs:56:1 + --> $DIR/derive.rs:54:1 | LL | / impl Clone for FnPtr { LL | | fn clone(&self) -> Self { @@ -79,5 +79,25 @@ LL | | } LL | | } | |_^ -error: aborting due to 4 previous errors +error: you are implementing `Clone` explicitly on a `Copy` type + --> $DIR/derive.rs:74:1 + | +LL | / impl Clone for Generic2 { +LL | | fn clone(&self) -> Self { +LL | | Self(self.0.clone()) +LL | | } +LL | | } + | |_^ + | +note: consider deriving `Clone` or removing `Copy` + --> $DIR/derive.rs:74:1 + | +LL | / impl Clone for Generic2 { +LL | | fn clone(&self) -> Self { +LL | | Self(self.0.clone()) +LL | | } +LL | | } + | |_^ + +error: aborting due to 5 previous errors -- cgit 1.4.1-3-g733a5 From 1f95940c24c63b0d196b4f889a68baed2b123086 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Mon, 29 Mar 2021 16:19:52 +0900 Subject: Fix inconsistent test name --- tests/ui/doc_panics.rs | 114 ------------------------------------- tests/ui/doc_panics.stderr | 82 -------------------------- tests/ui/missing_panics_doc.rs | 114 +++++++++++++++++++++++++++++++++++++ tests/ui/missing_panics_doc.stderr | 82 ++++++++++++++++++++++++++ 4 files changed, 196 insertions(+), 196 deletions(-) delete mode 100644 tests/ui/doc_panics.rs delete mode 100644 tests/ui/doc_panics.stderr create mode 100644 tests/ui/missing_panics_doc.rs create mode 100644 tests/ui/missing_panics_doc.stderr (limited to 'tests') diff --git a/tests/ui/doc_panics.rs b/tests/ui/doc_panics.rs deleted file mode 100644 index 17e72353f80..00000000000 --- a/tests/ui/doc_panics.rs +++ /dev/null @@ -1,114 +0,0 @@ -#![warn(clippy::missing_panics_doc)] -#![allow(clippy::option_map_unit_fn)] - -fn main() {} - -/// This needs to be documented -pub fn unwrap() { - let result = Err("Hi"); - result.unwrap() -} - -/// This needs to be documented -pub fn panic() { - panic!("This function panics") -} - -/// This needs to be documented -pub fn todo() { - todo!() -} - -/// This needs to be documented -pub fn inner_body(opt: Option) { - opt.map(|x| { - if x == 10 { - panic!() - } - }); -} - -/// This needs to be documented -pub fn unreachable_and_panic() { - if true { unreachable!() } else { panic!() } -} - -/// This is documented -/// -/// # Panics -/// -/// Panics if `result` if an error -pub fn unwrap_documented() { - let result = Err("Hi"); - result.unwrap() -} - -/// This is documented -/// -/// # Panics -/// -/// Panics just because -pub fn panic_documented() { - panic!("This function panics") -} - -/// This is documented -/// -/// # Panics -/// -/// Panics if `opt` is Just(10) -pub fn inner_body_documented(opt: Option) { - opt.map(|x| { - if x == 10 { - panic!() - } - }); -} - -/// This is documented -/// -/// # Panics -/// -/// We still need to do this part -pub fn todo_documented() { - todo!() -} - -/// This is documented -/// -/// # Panics -/// -/// We still need to do this part -pub fn unreachable_amd_panic_documented() { - if true { unreachable!() } else { panic!() } -} - -/// This is okay because it is private -fn unwrap_private() { - let result = Err("Hi"); - result.unwrap() -} - -/// This is okay because it is private -fn panic_private() { - panic!("This function panics") -} - -/// This is okay because it is private -fn todo_private() { - todo!() -} - -/// This is okay because it is private -fn inner_body_private(opt: Option) { - opt.map(|x| { - if x == 10 { - panic!() - } - }); -} - -/// This is okay because unreachable -pub fn unreachable() { - unreachable!("This function panics") -} diff --git a/tests/ui/doc_panics.stderr b/tests/ui/doc_panics.stderr deleted file mode 100644 index 2fa88a2f6ec..00000000000 --- a/tests/ui/doc_panics.stderr +++ /dev/null @@ -1,82 +0,0 @@ -error: docs for function which may panic missing `# Panics` section - --> $DIR/doc_panics.rs:7:1 - | -LL | / pub fn unwrap() { -LL | | let result = Err("Hi"); -LL | | result.unwrap() -LL | | } - | |_^ - | - = note: `-D clippy::missing-panics-doc` implied by `-D warnings` -note: first possible panic found here - --> $DIR/doc_panics.rs:9:5 - | -LL | result.unwrap() - | ^^^^^^^^^^^^^^^ - -error: docs for function which may panic missing `# Panics` section - --> $DIR/doc_panics.rs:13:1 - | -LL | / pub fn panic() { -LL | | panic!("This function panics") -LL | | } - | |_^ - | -note: first possible panic found here - --> $DIR/doc_panics.rs:14:5 - | -LL | panic!("This function panics") - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: docs for function which may panic missing `# Panics` section - --> $DIR/doc_panics.rs:18:1 - | -LL | / pub fn todo() { -LL | | todo!() -LL | | } - | |_^ - | -note: first possible panic found here - --> $DIR/doc_panics.rs:19:5 - | -LL | todo!() - | ^^^^^^^ - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: docs for function which may panic missing `# Panics` section - --> $DIR/doc_panics.rs:23:1 - | -LL | / pub fn inner_body(opt: Option) { -LL | | opt.map(|x| { -LL | | if x == 10 { -LL | | panic!() -LL | | } -LL | | }); -LL | | } - | |_^ - | -note: first possible panic found here - --> $DIR/doc_panics.rs:26:13 - | -LL | panic!() - | ^^^^^^^^ - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: docs for function which may panic missing `# Panics` section - --> $DIR/doc_panics.rs:32:1 - | -LL | / pub fn unreachable_and_panic() { -LL | | if true { unreachable!() } else { panic!() } -LL | | } - | |_^ - | -note: first possible panic found here - --> $DIR/doc_panics.rs:33:39 - | -LL | if true { unreachable!() } else { panic!() } - | ^^^^^^^^ - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 5 previous errors - diff --git a/tests/ui/missing_panics_doc.rs b/tests/ui/missing_panics_doc.rs new file mode 100644 index 00000000000..17e72353f80 --- /dev/null +++ b/tests/ui/missing_panics_doc.rs @@ -0,0 +1,114 @@ +#![warn(clippy::missing_panics_doc)] +#![allow(clippy::option_map_unit_fn)] + +fn main() {} + +/// This needs to be documented +pub fn unwrap() { + let result = Err("Hi"); + result.unwrap() +} + +/// This needs to be documented +pub fn panic() { + panic!("This function panics") +} + +/// This needs to be documented +pub fn todo() { + todo!() +} + +/// This needs to be documented +pub fn inner_body(opt: Option) { + opt.map(|x| { + if x == 10 { + panic!() + } + }); +} + +/// This needs to be documented +pub fn unreachable_and_panic() { + if true { unreachable!() } else { panic!() } +} + +/// This is documented +/// +/// # Panics +/// +/// Panics if `result` if an error +pub fn unwrap_documented() { + let result = Err("Hi"); + result.unwrap() +} + +/// This is documented +/// +/// # Panics +/// +/// Panics just because +pub fn panic_documented() { + panic!("This function panics") +} + +/// This is documented +/// +/// # Panics +/// +/// Panics if `opt` is Just(10) +pub fn inner_body_documented(opt: Option) { + opt.map(|x| { + if x == 10 { + panic!() + } + }); +} + +/// This is documented +/// +/// # Panics +/// +/// We still need to do this part +pub fn todo_documented() { + todo!() +} + +/// This is documented +/// +/// # Panics +/// +/// We still need to do this part +pub fn unreachable_amd_panic_documented() { + if true { unreachable!() } else { panic!() } +} + +/// This is okay because it is private +fn unwrap_private() { + let result = Err("Hi"); + result.unwrap() +} + +/// This is okay because it is private +fn panic_private() { + panic!("This function panics") +} + +/// This is okay because it is private +fn todo_private() { + todo!() +} + +/// This is okay because it is private +fn inner_body_private(opt: Option) { + opt.map(|x| { + if x == 10 { + panic!() + } + }); +} + +/// This is okay because unreachable +pub fn unreachable() { + unreachable!("This function panics") +} diff --git a/tests/ui/missing_panics_doc.stderr b/tests/ui/missing_panics_doc.stderr new file mode 100644 index 00000000000..37da6bfd92d --- /dev/null +++ b/tests/ui/missing_panics_doc.stderr @@ -0,0 +1,82 @@ +error: docs for function which may panic missing `# Panics` section + --> $DIR/missing_panics_doc.rs:7:1 + | +LL | / pub fn unwrap() { +LL | | let result = Err("Hi"); +LL | | result.unwrap() +LL | | } + | |_^ + | + = note: `-D clippy::missing-panics-doc` implied by `-D warnings` +note: first possible panic found here + --> $DIR/missing_panics_doc.rs:9:5 + | +LL | result.unwrap() + | ^^^^^^^^^^^^^^^ + +error: docs for function which may panic missing `# Panics` section + --> $DIR/missing_panics_doc.rs:13:1 + | +LL | / pub fn panic() { +LL | | panic!("This function panics") +LL | | } + | |_^ + | +note: first possible panic found here + --> $DIR/missing_panics_doc.rs:14:5 + | +LL | panic!("This function panics") + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: docs for function which may panic missing `# Panics` section + --> $DIR/missing_panics_doc.rs:18:1 + | +LL | / pub fn todo() { +LL | | todo!() +LL | | } + | |_^ + | +note: first possible panic found here + --> $DIR/missing_panics_doc.rs:19:5 + | +LL | todo!() + | ^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: docs for function which may panic missing `# Panics` section + --> $DIR/missing_panics_doc.rs:23:1 + | +LL | / pub fn inner_body(opt: Option) { +LL | | opt.map(|x| { +LL | | if x == 10 { +LL | | panic!() +LL | | } +LL | | }); +LL | | } + | |_^ + | +note: first possible panic found here + --> $DIR/missing_panics_doc.rs:26:13 + | +LL | panic!() + | ^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: docs for function which may panic missing `# Panics` section + --> $DIR/missing_panics_doc.rs:32:1 + | +LL | / pub fn unreachable_and_panic() { +LL | | if true { unreachable!() } else { panic!() } +LL | | } + | |_^ + | +note: first possible panic found here + --> $DIR/missing_panics_doc.rs:33:39 + | +LL | if true { unreachable!() } else { panic!() } + | ^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 5 previous errors + -- cgit 1.4.1-3-g733a5 From 31afdfc12bbe1bd2681ac17bfb07134ffd3cf061 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Mon, 29 Mar 2021 17:19:05 +0900 Subject: missing_panics_doc: Ignore usage of debug_assert family --- clippy_lints/src/doc.rs | 6 ++++++ tests/ui/missing_panics_doc.rs | 8 ++++++++ 2 files changed, 14 insertions(+) (limited to 'tests') diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 14338ac8faf..69800f9d331 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -715,6 +715,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> { if let Some(path_def_id) = path.res.opt_def_id(); if match_panic_def_id(self.cx, path_def_id); if is_expn_of(expr.span, "unreachable").is_none(); + if !is_expn_of_debug_assertions(expr.span); then { self.panic_span = Some(expr.span); } @@ -738,3 +739,8 @@ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> { NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) } } + +fn is_expn_of_debug_assertions(span: Span) -> bool { + const MACRO_NAMES: &[&str] = &["debug_assert", "debug_assert_eq", "debug_assert_ne"]; + MACRO_NAMES.iter().any(|name| is_expn_of(span, name).is_some()) +} diff --git a/tests/ui/missing_panics_doc.rs b/tests/ui/missing_panics_doc.rs index 17e72353f80..3fe35c75799 100644 --- a/tests/ui/missing_panics_doc.rs +++ b/tests/ui/missing_panics_doc.rs @@ -112,3 +112,11 @@ fn inner_body_private(opt: Option) { pub fn unreachable() { unreachable!("This function panics") } + +/// #6970. +/// This is okay because it is expansion of `debug_assert` family. +pub fn debug_assertions() { + debug_assert!(false); + debug_assert_eq!(1, 2); + debug_assert_ne!(1, 2); +} -- cgit 1.4.1-3-g733a5 From d2657769a22f15098343f9272f52a01145698786 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 29 Mar 2021 13:29:58 -0400 Subject: Improve `clone_on_copy` Lint on `_.clone().method()` when method takes self by value Set applicability correctly Correct suggestion when the cloned value is a macro call. e.g. `m!(x).clone()` Don't lint when not using the `Clone` trait --- clippy_lints/src/methods/clone_on_copy.rs | 118 +++++++++++++++++------------- tests/ui/clone_on_copy.fixed | 34 ++++++++- tests/ui/clone_on_copy.rs | 34 ++++++++- tests/ui/clone_on_copy.stderr | 30 ++++++-- tests/ui/clone_on_copy_mut.rs | 18 ----- 5 files changed, 157 insertions(+), 77 deletions(-) delete mode 100644 tests/ui/clone_on_copy_mut.rs (limited to 'tests') diff --git a/clippy_lints/src/methods/clone_on_copy.rs b/clippy_lints/src/methods/clone_on_copy.rs index edb6649b87b..ce2e8fa8b10 100644 --- a/clippy_lints/src/methods/clone_on_copy.rs +++ b/clippy_lints/src/methods/clone_on_copy.rs @@ -1,10 +1,12 @@ -use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::get_parent_node; +use clippy_utils::source::snippet_with_context; use clippy_utils::sugg; use clippy_utils::ty::is_copy; use rustc_errors::Applicability; -use rustc_hir as hir; +use rustc_hir::{BindingAnnotation, Expr, ExprKind, MatchSource, Node, PatKind}; use rustc_lint::LateContext; -use rustc_middle::ty; +use rustc_middle::ty::{self, adjustment::Adjust}; use rustc_span::symbol::{sym, Symbol}; use std::iter; @@ -12,12 +14,26 @@ use super::CLONE_DOUBLE_REF; use super::CLONE_ON_COPY; /// Checks for the `CLONE_ON_COPY` lint. -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Symbol, args: &[hir::Expr<'_>]) { - if !(args.len() == 1 && method_name == sym::clone) { +#[allow(clippy::too_many_lines)] +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, args: &[Expr<'_>]) { + let arg = match args { + [arg] if method_name == sym::clone => arg, + _ => return, + }; + if cx + .typeck_results() + .type_dependent_def_id(expr.hir_id) + .and_then(|id| cx.tcx.trait_of_item(id)) + .zip(cx.tcx.lang_items().clone_trait()) + .map_or(true, |(x, y)| x != y) + { return; } - let arg = &args[0]; - let arg_ty = cx.typeck_results().expr_ty_adjusted(&args[0]); + let arg_adjustments = cx.typeck_results().expr_adjustments(arg); + let arg_ty = arg_adjustments + .last() + .map_or_else(|| cx.typeck_results().expr_ty(arg), |a| a.target); + let ty = cx.typeck_results().expr_ty(expr); if let ty::Ref(_, inner, _) = arg_ty.kind() { if let ty::Ref(_, innermost, _) = inner.kind() { @@ -61,57 +77,57 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Sym } if is_copy(cx, ty) { - let snip; - if let Some(snippet) = sugg::Sugg::hir_opt(cx, arg) { - let parent = cx.tcx.hir().get_parent_node(expr.hir_id); - match &cx.tcx.hir().get(parent) { - hir::Node::Expr(parent) => match parent.kind { - // &*x is a nop, &x.clone() is not - hir::ExprKind::AddrOf(..) => return, - // (*x).func() is useless, x.clone().func() can work in case func borrows mutably - hir::ExprKind::MethodCall(_, _, parent_args, _) if expr.hir_id == parent_args[0].hir_id => { - return; - }, - - _ => {}, - }, - hir::Node::Stmt(stmt) => { - if let hir::StmtKind::Local(ref loc) = stmt.kind { - if let hir::PatKind::Ref(..) = loc.pat.kind { - // let ref y = *x borrows x, let ref y = x.clone() does not - return; - } - } - }, - _ => {}, + let parent_is_suffix_expr = match get_parent_node(cx.tcx, expr.hir_id) { + Some(Node::Expr(parent)) => match parent.kind { + // &*x is a nop, &x.clone() is not + ExprKind::AddrOf(..) => return, + // (*x).func() is useless, x.clone().func() can work in case func borrows self + ExprKind::MethodCall(_, _, [self_arg, ..], _) + if expr.hir_id == self_arg.hir_id && ty != cx.typeck_results().expr_ty_adjusted(expr) => + { + return; + } + ExprKind::MethodCall(_, _, [self_arg, ..], _) if expr.hir_id == self_arg.hir_id => true, + ExprKind::Match(_, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar) + | ExprKind::Field(..) + | ExprKind::Index(..) => true, + _ => false, + }, + // local binding capturing a reference + Some(Node::Local(l)) + if matches!( + l.pat.kind, + PatKind::Binding(BindingAnnotation::Ref | BindingAnnotation::RefMut, ..) + ) => + { + return; } + _ => false, + }; - // x.clone() might have dereferenced x, possibly through Deref impls - if cx.typeck_results().expr_ty(arg) == ty { - snip = Some(("try removing the `clone` call", format!("{}", snippet))); - } else { - let deref_count = cx - .typeck_results() - .expr_adjustments(arg) - .iter() - .filter(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(_))) - .count(); - let derefs: String = iter::repeat('*').take(deref_count).collect(); - snip = Some(("try dereferencing it", format!("{}{}", derefs, snippet))); - } + let mut app = Applicability::MachineApplicable; + let snip = snippet_with_context(cx, arg.span, expr.span.ctxt(), "_", &mut app).0; + + let deref_count = arg_adjustments + .iter() + .take_while(|adj| matches!(adj.kind, Adjust::Deref(_))) + .count(); + let (help, sugg) = if deref_count == 0 { + ("try removing the `clone` call", snip.into()) + } else if parent_is_suffix_expr { + ("try dereferencing it", format!("({}{})", "*".repeat(deref_count), snip)) } else { - snip = None; - } - span_lint_and_then( + ("try dereferencing it", format!("{}{}", "*".repeat(deref_count), snip)) + }; + + span_lint_and_sugg( cx, CLONE_ON_COPY, expr.span, &format!("using `clone` on type `{}` which implements the `Copy` trait", ty), - |diag| { - if let Some((text, snip)) = snip { - diag.span_suggestion(expr.span, text, snip, Applicability::MachineApplicable); - } - }, + help, + sugg, + app, ); } } diff --git a/tests/ui/clone_on_copy.fixed b/tests/ui/clone_on_copy.fixed index d924625132e..8d43f64768d 100644 --- a/tests/ui/clone_on_copy.fixed +++ b/tests/ui/clone_on_copy.fixed @@ -6,7 +6,8 @@ clippy::deref_addrof, clippy::no_effect, clippy::unnecessary_operation, - clippy::vec_init_then_push + clippy::vec_init_then_push, + clippy::toplevel_ref_arg )] use std::cell::RefCell; @@ -29,6 +30,37 @@ fn clone_on_copy() { let rc = RefCell::new(0); *rc.borrow(); + let x = 0u32; + x.rotate_left(1); + + #[derive(Clone, Copy)] + struct Foo; + impl Foo { + fn clone(&self) -> u32 { + 0 + } + } + Foo.clone(); // ok, this is not the clone trait + + macro_rules! m { + ($e:expr) => {{ $e }}; + } + m!(42); + + struct Wrap([u32; 2]); + impl core::ops::Deref for Wrap { + type Target = [u32; 2]; + fn deref(&self) -> &[u32; 2] { + &self.0 + } + } + let x = Wrap([0, 0]); + (*x)[0]; + + let x = 42; + let ref y = x.clone(); // ok, binds by reference + let ref mut y = x.clone(); // ok, binds by reference + // Issue #4348 let mut x = 43; let _ = &x.clone(); // ok, getting a ref diff --git a/tests/ui/clone_on_copy.rs b/tests/ui/clone_on_copy.rs index 97f49467244..f15501f7184 100644 --- a/tests/ui/clone_on_copy.rs +++ b/tests/ui/clone_on_copy.rs @@ -6,7 +6,8 @@ clippy::deref_addrof, clippy::no_effect, clippy::unnecessary_operation, - clippy::vec_init_then_push + clippy::vec_init_then_push, + clippy::toplevel_ref_arg )] use std::cell::RefCell; @@ -29,6 +30,37 @@ fn clone_on_copy() { let rc = RefCell::new(0); rc.borrow().clone(); + let x = 0u32; + x.clone().rotate_left(1); + + #[derive(Clone, Copy)] + struct Foo; + impl Foo { + fn clone(&self) -> u32 { + 0 + } + } + Foo.clone(); // ok, this is not the clone trait + + macro_rules! m { + ($e:expr) => {{ $e }}; + } + m!(42).clone(); + + struct Wrap([u32; 2]); + impl core::ops::Deref for Wrap { + type Target = [u32; 2]; + fn deref(&self) -> &[u32; 2] { + &self.0 + } + } + let x = Wrap([0, 0]); + x.clone()[0]; + + let x = 42; + let ref y = x.clone(); // ok, binds by reference + let ref mut y = x.clone(); // ok, binds by reference + // Issue #4348 let mut x = 43; let _ = &x.clone(); // ok, getting a ref diff --git a/tests/ui/clone_on_copy.stderr b/tests/ui/clone_on_copy.stderr index 7a706884fb0..e7d28b4320b 100644 --- a/tests/ui/clone_on_copy.stderr +++ b/tests/ui/clone_on_copy.stderr @@ -1,5 +1,5 @@ error: using `clone` on type `i32` which implements the `Copy` trait - --> $DIR/clone_on_copy.rs:23:5 + --> $DIR/clone_on_copy.rs:24:5 | LL | 42.clone(); | ^^^^^^^^^^ help: try removing the `clone` call: `42` @@ -7,28 +7,46 @@ LL | 42.clone(); = note: `-D clippy::clone-on-copy` implied by `-D warnings` error: using `clone` on type `i32` which implements the `Copy` trait - --> $DIR/clone_on_copy.rs:27:5 + --> $DIR/clone_on_copy.rs:28:5 | LL | (&42).clone(); | ^^^^^^^^^^^^^ help: try dereferencing it: `*(&42)` error: using `clone` on type `i32` which implements the `Copy` trait - --> $DIR/clone_on_copy.rs:30:5 + --> $DIR/clone_on_copy.rs:31:5 | LL | rc.borrow().clone(); | ^^^^^^^^^^^^^^^^^^^ help: try dereferencing it: `*rc.borrow()` +error: using `clone` on type `u32` which implements the `Copy` trait + --> $DIR/clone_on_copy.rs:34:5 + | +LL | x.clone().rotate_left(1); + | ^^^^^^^^^ help: try removing the `clone` call: `x` + +error: using `clone` on type `i32` which implements the `Copy` trait + --> $DIR/clone_on_copy.rs:48:5 + | +LL | m!(42).clone(); + | ^^^^^^^^^^^^^^ help: try removing the `clone` call: `m!(42)` + +error: using `clone` on type `[u32; 2]` which implements the `Copy` trait + --> $DIR/clone_on_copy.rs:58:5 + | +LL | x.clone()[0]; + | ^^^^^^^^^ help: try dereferencing it: `(*x)` + error: using `clone` on type `char` which implements the `Copy` trait - --> $DIR/clone_on_copy.rs:36:14 + --> $DIR/clone_on_copy.rs:68:14 | LL | is_ascii('z'.clone()); | ^^^^^^^^^^^ help: try removing the `clone` call: `'z'` error: using `clone` on type `i32` which implements the `Copy` trait - --> $DIR/clone_on_copy.rs:40:14 + --> $DIR/clone_on_copy.rs:72:14 | LL | vec.push(42.clone()); | ^^^^^^^^^^ help: try removing the `clone` call: `42` -error: aborting due to 5 previous errors +error: aborting due to 8 previous errors diff --git a/tests/ui/clone_on_copy_mut.rs b/tests/ui/clone_on_copy_mut.rs deleted file mode 100644 index 5bfa256623b..00000000000 --- a/tests/ui/clone_on_copy_mut.rs +++ /dev/null @@ -1,18 +0,0 @@ -pub fn dec_read_dec(i: &mut i32) -> i32 { - *i -= 1; - let ret = *i; - *i -= 1; - ret -} - -pub fn minus_1(i: &i32) -> i32 { - dec_read_dec(&mut i.clone()) -} - -fn main() { - let mut i = 10; - assert_eq!(minus_1(&i), 9); - assert_eq!(i, 10); - assert_eq!(dec_read_dec(&mut i), 9); - assert_eq!(i, 8); -} -- cgit 1.4.1-3-g733a5 From 6966c78be74ec7dea5cbbb18f1ec10771bf4b728 Mon Sep 17 00:00:00 2001 From: Mateusz Gacek <96mateusz.gacek@gmail.com> Date: Mon, 29 Mar 2021 12:51:23 -0700 Subject: wrong_self_convention: fix FP inside trait impl for `to_*` method When the `to_*` method takes `&self` and it is a trait implementation, we don't trigger the lint. --- clippy_lints/src/methods/mod.rs | 10 ++++++- clippy_lints/src/methods/wrong_self_convention.rs | 32 ++++++++++++++++++----- tests/ui/wrong_self_convention.rs | 9 ++----- tests/ui/wrong_self_convention.stderr | 4 +-- tests/ui/wrong_self_convention2.rs | 32 +++++++++++++++++++++++ tests/ui/wrong_self_convention2.stderr | 11 ++++++++ 6 files changed, 81 insertions(+), 17 deletions(-) create mode 100644 tests/ui/wrong_self_convention2.rs create mode 100644 tests/ui/wrong_self_convention2.stderr (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index fccdee07877..95592523928 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -205,6 +205,13 @@ declare_clippy_lint! { /// |`to_` | not `_mut` |`self` | `Copy` | /// |`to_` | not `_mut` |`&self` | not `Copy` | /// + /// Note: Clippy doesn't trigger methods with `to_` prefix in: + /// - Traits definition. + /// Clippy can not tell if a type that implements a trait is `Copy` or not. + /// - Traits implementation, when `&self` is taken. + /// The method signature is controlled by the trait and often `&self` is required for all types that implement the trait + /// (see e.g. the `std::string::ToString` trait). + /// /// Please find more info here: /// https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv /// @@ -1850,7 +1857,6 @@ impl<'tcx> LateLintPass<'tcx> for Methods { let self_ty = cx.tcx.type_of(item.def_id); let implements_trait = matches!(item.kind, hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. })); - if_chain! { if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind; if let Some(first_arg) = iter_input_pats(&sig.decl, cx.tcx.hir().body(id)).next(); @@ -1902,6 +1908,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { self_ty, first_arg_ty, first_arg.pat.span, + implements_trait, false ); } @@ -1972,6 +1979,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { self_ty, first_arg_ty, first_arg_span, + false, true ); } diff --git a/clippy_lints/src/methods/wrong_self_convention.rs b/clippy_lints/src/methods/wrong_self_convention.rs index 59e683aa9a7..1e0de249a91 100644 --- a/clippy_lints/src/methods/wrong_self_convention.rs +++ b/clippy_lints/src/methods/wrong_self_convention.rs @@ -21,8 +21,10 @@ const CONVENTIONS: [(&[Convention], &[SelfKind]); 9] = [ // Conversion using `to_` can use borrowed (non-Copy types) or owned (Copy types). // Source: https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv - (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(false), Convention::ImplementsTrait(false)], &[SelfKind::Ref]), - (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(true), Convention::ImplementsTrait(false)], &[SelfKind::Value]), + (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(false), + Convention::IsTraitItem(false)], &[SelfKind::Ref]), + (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(true), + Convention::IsTraitItem(false), Convention::ImplementsTrait(false)], &[SelfKind::Value]), ]; enum Convention { @@ -32,18 +34,27 @@ enum Convention { NotEndsWith(&'static str), IsSelfTypeCopy(bool), ImplementsTrait(bool), + IsTraitItem(bool), } impl Convention { #[must_use] - fn check<'tcx>(&self, cx: &LateContext<'tcx>, self_ty: &'tcx TyS<'tcx>, other: &str, is_trait_def: bool) -> bool { + fn check<'tcx>( + &self, + cx: &LateContext<'tcx>, + self_ty: &'tcx TyS<'tcx>, + other: &str, + implements_trait: bool, + is_trait_item: bool, + ) -> bool { match *self { Self::Eq(this) => this == other, Self::StartsWith(this) => other.starts_with(this) && this != other, Self::EndsWith(this) => other.ends_with(this) && this != other, - Self::NotEndsWith(this) => !Self::EndsWith(this).check(cx, self_ty, other, is_trait_def), + Self::NotEndsWith(this) => !Self::EndsWith(this).check(cx, self_ty, other, implements_trait, is_trait_item), Self::IsSelfTypeCopy(is_true) => is_true == is_copy(cx, self_ty), - Self::ImplementsTrait(is_true) => is_true == is_trait_def, + Self::ImplementsTrait(is_true) => is_true == implements_trait, + Self::IsTraitItem(is_true) => is_true == is_trait_item, } } } @@ -60,12 +71,17 @@ impl fmt::Display for Convention { }, Self::ImplementsTrait(is_true) => { let (negation, s_suffix) = if is_true { ("", "s") } else { (" does not", "") }; - format!("Method{} implement{} a trait", negation, s_suffix).fmt(f) + format!("method{} implement{} a trait", negation, s_suffix).fmt(f) + }, + Self::IsTraitItem(is_true) => { + let suffix = if is_true { " is" } else { " is not" }; + format!("method{} a trait item", suffix).fmt(f) }, } } } +#[allow(clippy::too_many_arguments)] pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, item_name: &str, @@ -73,6 +89,7 @@ pub(super) fn check<'tcx>( self_ty: &'tcx TyS<'tcx>, first_arg_ty: &'tcx TyS<'tcx>, first_arg_span: Span, + implements_trait: bool, is_trait_item: bool, ) { let lint = if is_pub { @@ -83,7 +100,7 @@ pub(super) fn check<'tcx>( if let Some((conventions, self_kinds)) = &CONVENTIONS.iter().find(|(convs, _)| { convs .iter() - .all(|conv| conv.check(cx, self_ty, item_name, is_trait_item)) + .all(|conv| conv.check(cx, self_ty, item_name, implements_trait, is_trait_item)) }) { if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) { let suggestion = { @@ -99,6 +116,7 @@ pub(super) fn check<'tcx>( .filter_map(|conv| { if (cut_ends_with_conv && matches!(conv, Convention::NotEndsWith(_))) || matches!(conv, Convention::ImplementsTrait(_)) + || matches!(conv, Convention::IsTraitItem(_)) { None } else { diff --git a/tests/ui/wrong_self_convention.rs b/tests/ui/wrong_self_convention.rs index ba9e19a1722..cdfbdb8b0db 100644 --- a/tests/ui/wrong_self_convention.rs +++ b/tests/ui/wrong_self_convention.rs @@ -165,15 +165,10 @@ mod issue6307 { } mod issue6727 { - trait ToU64 { - fn to_u64(self) -> u64; - fn to_u64_v2(&self) -> u64; - } - #[derive(Clone, Copy)] struct FooCopy; - impl ToU64 for FooCopy { + impl FooCopy { fn to_u64(self) -> u64 { 1 } @@ -185,7 +180,7 @@ mod issue6727 { struct FooNoCopy; - impl ToU64 for FooNoCopy { + impl FooNoCopy { // trigger lint fn to_u64(self) -> u64 { 2 diff --git a/tests/ui/wrong_self_convention.stderr b/tests/ui/wrong_self_convention.stderr index 1d58a12ac79..29f5ba82695 100644 --- a/tests/ui/wrong_self_convention.stderr +++ b/tests/ui/wrong_self_convention.stderr @@ -176,7 +176,7 @@ LL | fn from_i32(self); = help: consider choosing a less ambiguous name error: methods with the following characteristics: (`to_*` and `self` type is `Copy`) usually take `self` by value - --> $DIR/wrong_self_convention.rs:181:22 + --> $DIR/wrong_self_convention.rs:176:22 | LL | fn to_u64_v2(&self) -> u64 { | ^^^^^ @@ -184,7 +184,7 @@ LL | fn to_u64_v2(&self) -> u64 { = help: consider choosing a less ambiguous name error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference - --> $DIR/wrong_self_convention.rs:190:19 + --> $DIR/wrong_self_convention.rs:185:19 | LL | fn to_u64(self) -> u64 { | ^^^^ diff --git a/tests/ui/wrong_self_convention2.rs b/tests/ui/wrong_self_convention2.rs new file mode 100644 index 00000000000..8b42aa59e13 --- /dev/null +++ b/tests/ui/wrong_self_convention2.rs @@ -0,0 +1,32 @@ +// edition:2018 +#![warn(clippy::wrong_self_convention)] +#![warn(clippy::wrong_pub_self_convention)] +#![allow(dead_code)] + +fn main() {} + +mod issue6983 { + pub struct Thing; + pub trait Trait { + fn to_thing(&self) -> Thing; + } + + impl Trait for u8 { + // don't trigger, e.g. `ToString` from `std` requires `&self` + fn to_thing(&self) -> Thing { + Thing + } + } + + trait ToU64 { + fn to_u64(self) -> u64; + } + + struct FooNoCopy; + // trigger lint + impl ToU64 for FooNoCopy { + fn to_u64(self) -> u64 { + 2 + } + } +} diff --git a/tests/ui/wrong_self_convention2.stderr b/tests/ui/wrong_self_convention2.stderr new file mode 100644 index 00000000000..0ca1a390974 --- /dev/null +++ b/tests/ui/wrong_self_convention2.stderr @@ -0,0 +1,11 @@ +error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference + --> $DIR/wrong_self_convention2.rs:28:19 + | +LL | fn to_u64(self) -> u64 { + | ^^^^ + | + = note: `-D clippy::wrong-self-convention` implied by `-D warnings` + = help: consider choosing a less ambiguous name + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From fa689f865e4096a1788c38fe9fef0c05dc15b9be Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 29 Mar 2021 20:39:28 -0400 Subject: Fix `manual_map` at the end of an if chain --- clippy_lints/src/manual_map.rs | 5 ++-- clippy_utils/src/lib.rs | 23 ++++++++++------ tests/ui/manual_map_option.fixed | 7 ++++- tests/ui/manual_map_option.rs | 9 +++++++ tests/ui/manual_map_option.stderr | 55 +++++++++++++++++++++------------------ 5 files changed, 61 insertions(+), 38 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs index ed157783b72..d6ef3aa1e77 100644 --- a/clippy_lints/src/manual_map.rs +++ b/clippy_lints/src/manual_map.rs @@ -2,7 +2,7 @@ use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::ty::{can_partially_move_ty, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; -use clippy_utils::{is_allowed, is_else_clause_of_if_let_else, match_def_path, match_var, paths, peel_hir_expr_refs}; +use clippy_utils::{is_allowed, is_else_clause, match_def_path, match_var, paths, peel_hir_expr_refs}; use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::Applicability; use rustc_hir::{ @@ -181,8 +181,7 @@ impl LateLintPass<'_> for ManualMap { expr.span, "manual implementation of `Option::map`", "try this", - if matches!(match_kind, MatchSource::IfLetDesugar { .. }) && is_else_clause_of_if_let_else(cx.tcx, expr) - { + if matches!(match_kind, MatchSource::IfLetDesugar { .. }) && is_else_clause(cx.tcx, expr) { format!("{{ {}{}.map({}) }}", scrutinee_str, as_ref_str, body_str) } else { format!("{}{}.map({})", scrutinee_str, as_ref_str, body_str) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 9e4dcb600ed..d6364625e70 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -797,22 +797,29 @@ pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> { } } -/// Checks if the given expression is the else clause in the expression `if let .. {} else {}` -pub fn is_else_clause_of_if_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { +/// Checks if the given expression is the else clause of either an `if` or `if let` expression. +pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { let map = tcx.hir(); let mut iter = map.parent_iter(expr.hir_id); - let arm_id = match iter.next() { - Some((id, Node::Arm(..))) => id, - _ => return false, - }; match iter.next() { + Some((arm_id, Node::Arm(..))) => matches!( + iter.next(), + Some(( + _, + Node::Expr(Expr { + kind: ExprKind::Match(_, [_, else_arm], MatchSource::IfLetDesugar { .. }), + .. + }) + )) + if else_arm.hir_id == arm_id + ), Some(( _, Node::Expr(Expr { - kind: ExprKind::Match(_, [_, else_arm], kind), + kind: ExprKind::If(_, _, Some(else_expr)), .. }), - )) => else_arm.hir_id == arm_id && matches!(kind, MatchSource::IfLetDesugar { .. }), + )) => else_expr.hir_id == expr.hir_id, _ => false, } } diff --git a/tests/ui/manual_map_option.fixed b/tests/ui/manual_map_option.fixed index acb6a580ceb..5e26958041d 100644 --- a/tests/ui/manual_map_option.fixed +++ b/tests/ui/manual_map_option.fixed @@ -7,6 +7,7 @@ clippy::map_identity, clippy::unit_arg, clippy::match_ref_pats, + clippy::redundant_pattern_matching, dead_code )] @@ -130,7 +131,11 @@ fn main() { } // #6847 - if Some(0).is_some() { + if let Some(_) = Some(0) { + Some(0) + } else { Some(0).map(|x| x + 1) }; + + if true { Some(0) } else { Some(0).map(|x| x + 1) }; } diff --git a/tests/ui/manual_map_option.rs b/tests/ui/manual_map_option.rs index 3299e617707..33eb8156105 100644 --- a/tests/ui/manual_map_option.rs +++ b/tests/ui/manual_map_option.rs @@ -7,6 +7,7 @@ clippy::map_identity, clippy::unit_arg, clippy::match_ref_pats, + clippy::redundant_pattern_matching, dead_code )] @@ -195,4 +196,12 @@ fn main() { } else { None }; + + if true { + Some(0) + } else if let Some(x) = Some(0) { + Some(x + 1) + } else { + None + }; } diff --git a/tests/ui/manual_map_option.stderr b/tests/ui/manual_map_option.stderr index 048ccfb9582..cdc2c0e62a9 100644 --- a/tests/ui/manual_map_option.stderr +++ b/tests/ui/manual_map_option.stderr @@ -1,5 +1,5 @@ error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:14:5 + --> $DIR/manual_map_option.rs:15:5 | LL | / match Some(0) { LL | | Some(_) => Some(2), @@ -10,7 +10,7 @@ LL | | }; = note: `-D clippy::manual-map` implied by `-D warnings` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:19:5 + --> $DIR/manual_map_option.rs:20:5 | LL | / match Some(0) { LL | | Some(x) => Some(x + 1), @@ -19,7 +19,7 @@ LL | | }; | |_____^ help: try this: `Some(0).map(|x| x + 1)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:24:5 + --> $DIR/manual_map_option.rs:25:5 | LL | / match Some("") { LL | | Some(x) => Some(x.is_empty()), @@ -28,7 +28,7 @@ LL | | }; | |_____^ help: try this: `Some("").map(|x| x.is_empty())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:29:5 + --> $DIR/manual_map_option.rs:30:5 | LL | / if let Some(x) = Some(0) { LL | | Some(!x) @@ -38,7 +38,7 @@ LL | | }; | |_____^ help: try this: `Some(0).map(|x| !x)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:36:5 + --> $DIR/manual_map_option.rs:37:5 | LL | / match Some(0) { LL | | Some(x) => { Some(std::convert::identity(x)) } @@ -47,7 +47,7 @@ LL | | }; | |_____^ help: try this: `Some(0).map(std::convert::identity)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:41:5 + --> $DIR/manual_map_option.rs:42:5 | LL | / match Some(&String::new()) { LL | | Some(x) => Some(str::len(x)), @@ -56,7 +56,7 @@ LL | | }; | |_____^ help: try this: `Some(&String::new()).map(|x| str::len(x))` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:51:5 + --> $DIR/manual_map_option.rs:52:5 | LL | / match &Some([0, 1]) { LL | | Some(x) => Some(x[0]), @@ -65,7 +65,7 @@ LL | | }; | |_____^ help: try this: `Some([0, 1]).as_ref().map(|x| x[0])` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:56:5 + --> $DIR/manual_map_option.rs:57:5 | LL | / match &Some(0) { LL | | &Some(x) => Some(x * 2), @@ -74,7 +74,7 @@ LL | | }; | |_____^ help: try this: `Some(0).map(|x| x * 2)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:61:5 + --> $DIR/manual_map_option.rs:62:5 | LL | / match Some(String::new()) { LL | | Some(ref x) => Some(x.is_empty()), @@ -83,7 +83,7 @@ LL | | }; | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.is_empty())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:66:5 + --> $DIR/manual_map_option.rs:67:5 | LL | / match &&Some(String::new()) { LL | | Some(x) => Some(x.len()), @@ -92,7 +92,7 @@ LL | | }; | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.len())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:71:5 + --> $DIR/manual_map_option.rs:72:5 | LL | / match &&Some(0) { LL | | &&Some(x) => Some(x + x), @@ -101,7 +101,7 @@ LL | | }; | |_____^ help: try this: `Some(0).map(|x| x + x)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:84:9 + --> $DIR/manual_map_option.rs:85:9 | LL | / match &mut Some(String::new()) { LL | | Some(x) => Some(x.push_str("")), @@ -110,7 +110,7 @@ LL | | }; | |_________^ help: try this: `Some(String::new()).as_mut().map(|x| x.push_str(""))` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:90:5 + --> $DIR/manual_map_option.rs:91:5 | LL | / match &mut Some(String::new()) { LL | | Some(ref x) => Some(x.len()), @@ -119,7 +119,7 @@ LL | | }; | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.len())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:95:5 + --> $DIR/manual_map_option.rs:96:5 | LL | / match &mut &Some(String::new()) { LL | | Some(x) => Some(x.is_empty()), @@ -128,7 +128,7 @@ LL | | }; | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.is_empty())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:100:5 + --> $DIR/manual_map_option.rs:101:5 | LL | / match Some((0, 1, 2)) { LL | | Some((x, y, z)) => Some(x + y + z), @@ -137,7 +137,7 @@ LL | | }; | |_____^ help: try this: `Some((0, 1, 2)).map(|(x, y, z)| x + y + z)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:105:5 + --> $DIR/manual_map_option.rs:106:5 | LL | / match Some([1, 2, 3]) { LL | | Some([first, ..]) => Some(first), @@ -146,7 +146,7 @@ LL | | }; | |_____^ help: try this: `Some([1, 2, 3]).map(|[first, ..]| first)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:110:5 + --> $DIR/manual_map_option.rs:111:5 | LL | / match &Some((String::new(), "test")) { LL | | Some((x, y)) => Some((y, x)), @@ -155,7 +155,7 @@ LL | | }; | |_____^ help: try this: `Some((String::new(), "test")).as_ref().map(|(x, y)| (y, x))` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:168:5 + --> $DIR/manual_map_option.rs:169:5 | LL | / match Some(0) { LL | | Some(x) => Some(vec![x]), @@ -164,7 +164,7 @@ LL | | }; | |_____^ help: try this: `Some(0).map(|x| vec![x])` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:173:5 + --> $DIR/manual_map_option.rs:174:5 | LL | / match option_env!("") { LL | | Some(x) => Some(String::from(x)), @@ -172,16 +172,19 @@ LL | | None => None, LL | | }; | |_____^ help: try this: `option_env!("").map(String::from)` -error: redundant pattern matching, consider using `is_some()` - --> $DIR/manual_map_option.rs:191:12 - | -LL | if let Some(_) = Some(0) { - | -------^^^^^^^---------- help: try this: `if Some(0).is_some()` +error: manual implementation of `Option::map` + --> $DIR/manual_map_option.rs:194:12 | - = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` +LL | } else if let Some(x) = Some(0) { + | ____________^ +LL | | Some(x + 1) +LL | | } else { +LL | | None +LL | | }; + | |_____^ help: try this: `{ Some(0).map(|x| x + 1) }` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:193:12 + --> $DIR/manual_map_option.rs:202:12 | LL | } else if let Some(x) = Some(0) { | ____________^ -- cgit 1.4.1-3-g733a5 From cc7f1daab2e8104d8ae5952e28f22dcac920d244 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 25 Mar 2021 22:48:27 -0400 Subject: Don't lint `manual_map` in const functions --- clippy_lints/src/manual_map.rs | 10 +++++----- tests/ui/manual_map_option.fixed | 8 ++++++++ tests/ui/manual_map_option.rs | 8 ++++++++ 3 files changed, 21 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs index d6ef3aa1e77..8c9e3af62f4 100644 --- a/clippy_lints/src/manual_map.rs +++ b/clippy_lints/src/manual_map.rs @@ -2,7 +2,7 @@ use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::ty::{can_partially_move_ty, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; -use clippy_utils::{is_allowed, is_else_clause, match_def_path, match_var, paths, peel_hir_expr_refs}; +use clippy_utils::{in_constant, is_allowed, is_else_clause, match_def_path, match_var, paths, peel_hir_expr_refs}; use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::Applicability; use rustc_hir::{ @@ -47,16 +47,16 @@ declare_lint_pass!(ManualMap => [MANUAL_MAP]); impl LateLintPass<'_> for ManualMap { #[allow(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if in_external_macro(cx.sess(), expr.span) { - return; - } - if let ExprKind::Match( scrutinee, [arm1 @ Arm { guard: None, .. }, arm2 @ Arm { guard: None, .. }], match_kind, ) = expr.kind { + if in_external_macro(cx.sess(), expr.span) || in_constant(cx, expr.hir_id) { + return; + } + let (scrutinee_ty, ty_ref_count, ty_mutability) = peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee)); if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::option_type) diff --git a/tests/ui/manual_map_option.fixed b/tests/ui/manual_map_option.fixed index 5e26958041d..ee015845777 100644 --- a/tests/ui/manual_map_option.fixed +++ b/tests/ui/manual_map_option.fixed @@ -138,4 +138,12 @@ fn main() { if true { Some(0) } else { Some(0).map(|x| x + 1) }; + + // #6967 + const fn f4() { + match Some(0) { + Some(x) => Some(x + 1), + None => None, + }; + } } diff --git a/tests/ui/manual_map_option.rs b/tests/ui/manual_map_option.rs index 33eb8156105..29509bddfd9 100644 --- a/tests/ui/manual_map_option.rs +++ b/tests/ui/manual_map_option.rs @@ -204,4 +204,12 @@ fn main() { } else { None }; + + // #6967 + const fn f4() { + match Some(0) { + Some(x) => Some(x + 1), + None => None, + }; + } } -- cgit 1.4.1-3-g733a5 From 7fcd15571201d55951cf518a564069ff3ed115aa Mon Sep 17 00:00:00 2001 From: Elliot Bobrow Date: Mon, 29 Mar 2021 11:18:59 -0700 Subject: Add non_octal_unix_permissions lint --- CHANGELOG.md | 1 + README.md | 2 +- clippy_lints/src/lib.rs | 5 ++ clippy_lints/src/non_octal_unix_permissions.rs | 106 +++++++++++++++++++++++++ clippy_utils/src/paths.rs | 3 + tests/ui/non_octal_unix_permissions.fixed | 33 ++++++++ tests/ui/non_octal_unix_permissions.rs | 33 ++++++++ tests/ui/non_octal_unix_permissions.stderr | 28 +++++++ 8 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/non_octal_unix_permissions.rs create mode 100644 tests/ui/non_octal_unix_permissions.fixed create mode 100644 tests/ui/non_octal_unix_permissions.rs create mode 100644 tests/ui/non_octal_unix_permissions.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 57996802182..681ecd6832a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2382,6 +2382,7 @@ Released 2018-09-13 [`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 [`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 [`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 [`not_unsafe_ptr_arg_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#not_unsafe_ptr_arg_deref diff --git a/README.md b/README.md index 63057609bb6..8c0c16c443d 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are over 400 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are over 450 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category. diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f013613119c..237c0f48881 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -297,6 +297,7 @@ mod new_without_default; mod no_effect; mod non_copy_const; mod non_expressive_names; +mod non_octal_unix_permissions; mod open_options; mod option_env_unwrap; mod option_if_let_else; @@ -873,6 +874,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &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, &open_options::NONSENSICAL_OPEN_OPTIONS, &option_env_unwrap::OPTION_ENV_UNWRAP, &option_if_let_else::OPTION_IF_LET_ELSE, @@ -1057,6 +1059,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box implicit_saturating_sub::ImplicitSaturatingSub); store.register_late_pass(|| box default_numeric_fallback::DefaultNumericFallback); store.register_late_pass(|| box inconsistent_struct_constructor::InconsistentStructConstructor); + store.register_late_pass(|| box non_octal_unix_permissions::NonOctalUnixPermissions); let msrv = conf.msrv.as_ref().and_then(|s| { parse_msrv(s, None, None).or_else(|| { @@ -1647,6 +1650,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST), LintId::of(&non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), LintId::of(&non_expressive_names::MANY_SINGLE_CHAR_NAMES), + 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), @@ -1987,6 +1991,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&misc::FLOAT_CMP), LintId::of(&misc::MODULO_ONE), LintId::of(&mut_key::MUTABLE_KEY_TYPE), + 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::MUT_FROM_REF), diff --git a/clippy_lints/src/non_octal_unix_permissions.rs b/clippy_lints/src/non_octal_unix_permissions.rs new file mode 100644 index 00000000000..6d45e7bc6cf --- /dev/null +++ b/clippy_lints/src/non_octal_unix_permissions.rs @@ -0,0 +1,106 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::{snippet_opt, snippet_with_applicability}; +use clippy_utils::ty::match_type; +use clippy_utils::{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_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for non-octal values used to set Unix file permissions. + /// + /// **Why is this bad?** They will be converted into octal, creating potentially + /// unintended file permissions. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust,ignore + /// use std::fs::OpenOptions; + /// use std::os::unix::fs::OpenOptionsExt; + /// + /// let mut options = OpenOptions::new(); + /// options.mode(644); + /// ``` + /// Use instead: + /// ```rust,ignore + /// use std::fs::OpenOptions; + /// use std::os::unix::fs::OpenOptionsExt; + /// + /// let mut options = OpenOptions::new(); + /// options.mode(0o644); + /// ``` + pub NON_OCTAL_UNIX_PERMISSIONS, + correctness, + "use of non-octal value to set unix file permissions, which will be translated into octal" +} + +declare_lint_pass!(NonOctalUnixPermissions => [NON_OCTAL_UNIX_PERMISSIONS]); + +impl LateLintPass<'_> for NonOctalUnixPermissions { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + match &expr.kind { + ExprKind::MethodCall(path, _, [func, param], _) => { + let obj_ty = cx.typeck_results().expr_ty(&func).peel_refs(); + + if_chain! { + if (path.ident.name == sym!(mode) + && (match_type(cx, obj_ty, &paths::OPEN_OPTIONS) + || match_type(cx, obj_ty, &paths::DIR_BUILDER))) + || (path.ident.name == sym!(set_mode) && match_type(cx, obj_ty, &paths::PERMISSIONS)); + if let ExprKind::Lit(_) = param.kind; + + then { + let snip = match snippet_opt(cx, param.span) { + Some(s) => s, + _ => return, + }; + + if !snip.starts_with("0o") { + show_error(cx, param); + } + } + } + }, + ExprKind::Call(ref func, [param]) => { + if_chain! { + if let ExprKind::Path(ref path) = func.kind; + if let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id(); + if match_def_path(cx, def_id, &paths::PERMISSIONS_FROM_MODE); + if let ExprKind::Lit(_) = param.kind; + + then { + let snip = match snippet_opt(cx, param.span) { + Some(s) => s, + _ => return, + }; + + if !snip.starts_with("0o") { + show_error(cx, param); + } + } + } + }, + _ => {}, + }; + } +} + +fn show_error(cx: &LateContext<'_>, param: &Expr<'_>) { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + NON_OCTAL_UNIX_PERMISSIONS, + param.span, + "using a non-octal value to set unix file permissions", + "consider using an octal literal instead", + format!( + "0o{}", + snippet_with_applicability(cx, param.span, "0o..", &mut applicability,), + ), + applicability, + ); +} diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 11a446e42a4..7c83a9fe4e2 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -26,6 +26,7 @@ pub const DEFAULT_TRAIT: [&str; 3] = ["core", "default", "Default"]; pub const DEFAULT_TRAIT_METHOD: [&str; 4] = ["core", "default", "Default", "default"]; pub const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"]; pub const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"]; +pub const DIR_BUILDER: [&str; 3] = ["std", "fs", "DirBuilder"]; pub const DISPLAY_FMT_METHOD: [&str; 4] = ["core", "fmt", "Display", "fmt"]; pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"]; pub const DOUBLE_ENDED_ITERATOR: [&str; 4] = ["core", "iter", "traits", "DoubleEndedIterator"]; @@ -95,6 +96,8 @@ pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 2] = ["parking_lot", "RwLockWri pub const PATH_BUF: [&str; 3] = ["std", "path", "PathBuf"]; pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"]; pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"]; +pub const PERMISSIONS: [&str; 3] = ["std", "fs", "Permissions"]; +pub const PERMISSIONS_FROM_MODE: [&str; 7] = ["std", "sys", "unix", "ext", "fs", "PermissionsExt", "from_mode"]; 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"]; diff --git a/tests/ui/non_octal_unix_permissions.fixed b/tests/ui/non_octal_unix_permissions.fixed new file mode 100644 index 00000000000..a9b2dcfb085 --- /dev/null +++ b/tests/ui/non_octal_unix_permissions.fixed @@ -0,0 +1,33 @@ +// ignore-windows +// run-rustfix +#![warn(clippy::non_octal_unix_permissions)] +use std::fs::{DirBuilder, File, OpenOptions, Permissions}; +use std::os::unix::fs::{DirBuilderExt, OpenOptionsExt, PermissionsExt}; + +fn main() { + let permissions = 0o760; + + // OpenOptionsExt::mode + let mut options = OpenOptions::new(); + options.mode(0o440); + options.mode(0o400); + options.mode(permissions); + + // PermissionsExt::from_mode + let _permissions = Permissions::from_mode(0o647); + let _permissions = Permissions::from_mode(0o000); + let _permissions = Permissions::from_mode(permissions); + + // PermissionsExt::set_mode + let f = File::create("foo.txt").unwrap(); + let metadata = f.metadata().unwrap(); + let mut permissions = metadata.permissions(); + + permissions.set_mode(0o644); + permissions.set_mode(0o704); + + // DirBuilderExt::mode + let mut builder = DirBuilder::new(); + builder.mode(0o755); + builder.mode(0o406); +} diff --git a/tests/ui/non_octal_unix_permissions.rs b/tests/ui/non_octal_unix_permissions.rs new file mode 100644 index 00000000000..7d2922f494e --- /dev/null +++ b/tests/ui/non_octal_unix_permissions.rs @@ -0,0 +1,33 @@ +// ignore-windows +// run-rustfix +#![warn(clippy::non_octal_unix_permissions)] +use std::fs::{DirBuilder, File, OpenOptions, Permissions}; +use std::os::unix::fs::{DirBuilderExt, OpenOptionsExt, PermissionsExt}; + +fn main() { + let permissions = 0o760; + + // OpenOptionsExt::mode + let mut options = OpenOptions::new(); + options.mode(440); + options.mode(0o400); + options.mode(permissions); + + // PermissionsExt::from_mode + let _permissions = Permissions::from_mode(647); + let _permissions = Permissions::from_mode(0o000); + let _permissions = Permissions::from_mode(permissions); + + // PermissionsExt::set_mode + let f = File::create("foo.txt").unwrap(); + let metadata = f.metadata().unwrap(); + let mut permissions = metadata.permissions(); + + permissions.set_mode(644); + permissions.set_mode(0o704); + + // DirBuilderExt::mode + let mut builder = DirBuilder::new(); + builder.mode(755); + builder.mode(0o406); +} diff --git a/tests/ui/non_octal_unix_permissions.stderr b/tests/ui/non_octal_unix_permissions.stderr new file mode 100644 index 00000000000..32845d06594 --- /dev/null +++ b/tests/ui/non_octal_unix_permissions.stderr @@ -0,0 +1,28 @@ +error: using a non-octal value to set unix file permissions + --> $DIR/non_octal_unix_permissions.rs:12:18 + | +LL | options.mode(440); + | ^^^ help: consider using an octal literal instead: `0o440` + | + = note: `-D clippy::non-octal-unix-permissions` implied by `-D warnings` + +error: using a non-octal value to set unix file permissions + --> $DIR/non_octal_unix_permissions.rs:17:47 + | +LL | let _permissions = Permissions::from_mode(647); + | ^^^ help: consider using an octal literal instead: `0o647` + +error: using a non-octal value to set unix file permissions + --> $DIR/non_octal_unix_permissions.rs:26:26 + | +LL | permissions.set_mode(644); + | ^^^ help: consider using an octal literal instead: `0o644` + +error: using a non-octal value to set unix file permissions + --> $DIR/non_octal_unix_permissions.rs:31:18 + | +LL | builder.mode(755); + | ^^^ help: consider using an octal literal instead: `0o755` + +error: aborting due to 4 previous errors + -- cgit 1.4.1-3-g733a5 From 45164de59fb09d8aec57ffe87a7ea7619daa2781 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Wed, 31 Mar 2021 11:18:48 +0900 Subject: result_unit_err: Fix typo --- clippy_lints/src/functions/result_unit_err.rs | 4 ++-- tests/ui/len_without_is_empty.stderr | 16 ++++++++-------- tests/ui/result_unit_error.stderr | 20 ++++++++++---------- 3 files changed, 20 insertions(+), 20 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/functions/result_unit_err.rs b/clippy_lints/src/functions/result_unit_err.rs index f71bfc690f6..3d1092ce21f 100644 --- a/clippy_lints/src/functions/result_unit_err.rs +++ b/clippy_lints/src/functions/result_unit_err.rs @@ -57,9 +57,9 @@ fn check_result_unit_err(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, item_span cx, RESULT_UNIT_ERR, fn_header_span, - "this returns a `Result<_, ()>", + "this returns a `Result<_, ()>`", None, - "use a custom Error type instead", + "use a custom `Error` type instead", ); } } diff --git a/tests/ui/len_without_is_empty.stderr b/tests/ui/len_without_is_empty.stderr index ff7f5562308..3282709bcd6 100644 --- a/tests/ui/len_without_is_empty.stderr +++ b/tests/ui/len_without_is_empty.stderr @@ -86,38 +86,38 @@ LL | pub fn is_empty(&self) -> Option { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: expected signature: `(&self) -> bool` or `(&self) -> Result -error: this returns a `Result<_, ()> +error: this returns a `Result<_, ()>` --> $DIR/len_without_is_empty.rs:230:5 | LL | pub fn len(&self) -> Result { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::result-unit-err` implied by `-D warnings` - = help: use a custom Error type instead + = help: use a custom `Error` type instead -error: this returns a `Result<_, ()> +error: this returns a `Result<_, ()>` --> $DIR/len_without_is_empty.rs:242:5 | LL | pub fn len(&self) -> Result { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: use a custom Error type instead + = help: use a custom `Error` type instead -error: this returns a `Result<_, ()> +error: this returns a `Result<_, ()>` --> $DIR/len_without_is_empty.rs:246:5 | LL | pub fn is_empty(&self) -> Result { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: use a custom Error type instead + = help: use a custom `Error` type instead -error: this returns a `Result<_, ()> +error: this returns a `Result<_, ()>` --> $DIR/len_without_is_empty.rs:253:5 | LL | pub fn len(&self) -> Result { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: use a custom Error type instead + = help: use a custom `Error` type instead error: aborting due to 12 previous errors diff --git a/tests/ui/result_unit_error.stderr b/tests/ui/result_unit_error.stderr index 41d8b0a7cb7..8c7573eabda 100644 --- a/tests/ui/result_unit_error.stderr +++ b/tests/ui/result_unit_error.stderr @@ -1,43 +1,43 @@ -error: this returns a `Result<_, ()> +error: this returns a `Result<_, ()>` --> $DIR/result_unit_error.rs:3:1 | LL | pub fn returns_unit_error() -> Result { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::result-unit-err` implied by `-D warnings` - = help: use a custom Error type instead + = help: use a custom `Error` type instead -error: this returns a `Result<_, ()> +error: this returns a `Result<_, ()>` --> $DIR/result_unit_error.rs:12:5 | LL | fn get_that_error(&self) -> Result; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: use a custom Error type instead + = help: use a custom `Error` type instead -error: this returns a `Result<_, ()> +error: this returns a `Result<_, ()>` --> $DIR/result_unit_error.rs:14:5 | LL | fn get_this_one_too(&self) -> Result { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: use a custom Error type instead + = help: use a custom `Error` type instead -error: this returns a `Result<_, ()> +error: this returns a `Result<_, ()>` --> $DIR/result_unit_error.rs:32:5 | LL | pub fn unit_error(&self) -> Result { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: use a custom Error type instead + = help: use a custom `Error` type instead -error: this returns a `Result<_, ()> +error: this returns a `Result<_, ()>` --> $DIR/result_unit_error.rs:41:5 | LL | pub fn should_lint() -> ResInv<(), usize> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: use a custom Error type instead + = help: use a custom `Error` type instead error: aborting due to 5 previous errors -- cgit 1.4.1-3-g733a5 From 8abab5561caf0243799fba29a53e175948977ec2 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Sat, 27 Mar 2021 08:50:19 +0200 Subject: Fix hidden variant suggestion on single variant Fixes #6984 --- clippy_lints/src/lib.rs | 1 - clippy_lints/src/manual_non_exhaustive.rs | 14 +++----------- clippy_lints/src/matches.rs | 7 ++++++- clippy_lints/src/missing_doc.rs | 11 ++--------- clippy_utils/src/attrs.rs | 12 +++++++++++- tests/ui/match_wildcard_for_single_variants.fixed | 16 ++++++++++++++++ tests/ui/match_wildcard_for_single_variants.rs | 16 ++++++++++++++++ 7 files changed, 54 insertions(+), 23 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 153972671fd..c060429edea 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; diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index 95261580b8e..7f7aeaf2be8 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -1,9 +1,9 @@ +use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::meets_msrv; use clippy_utils::source::snippet_opt; use if_chain::if_chain; -use rustc_ast::ast::{Attribute, FieldDef, Item, ItemKind, Variant, VariantData, VisibilityKind}; -use rustc_attr as attr; +use rustc_ast::ast::{FieldDef, Item, ItemKind, Variant, VariantData, VisibilityKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_semver::RustcVersion; @@ -102,15 +102,7 @@ fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants fn is_non_exhaustive_marker(variant: &Variant) -> bool { matches!(variant.data, VariantData::Unit(_)) && variant.ident.as_str().starts_with('_') - && variant.attrs.iter().any(|a| is_doc_hidden(a)) - } - - fn is_doc_hidden(attr: &Attribute) -> bool { - attr.has_name(sym::doc) - && match attr.meta_item_list() { - Some(l) => attr::list_contains_name(&l, sym::hidden), - None => false, - } + && is_doc_hidden(&variant.attrs) } if_chain! { diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 425124b78f4..052bbb48887 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -983,6 +983,11 @@ impl CommonPrefixSearcher<'a> { } } +fn is_doc_hidden(cx: &LateContext<'_>, variant_def: &VariantDef) -> bool { + let attrs = cx.tcx.get_attrs(variant_def.def_id); + clippy_utils::attrs::is_doc_hidden(attrs) +} + #[allow(clippy::too_many_lines)] fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { let ty = cx.typeck_results().expr_ty(ex).peel_refs(); @@ -1102,7 +1107,7 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) match missing_variants.as_slice() { [] => (), - [x] if !adt_def.is_variant_list_non_exhaustive() => span_lint_and_sugg( + [x] if !adt_def.is_variant_list_non_exhaustive() && !is_doc_hidden(cx, x) => span_lint_and_sugg( cx, MATCH_WILDCARD_FOR_SINGLE_VARIANTS, wildcard_span, diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index ff87828c2e7..d731a9fe223 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -5,10 +5,10 @@ // [`missing_doc`]: https://github.com/rust-lang/rust/blob/cf9cf7c923eb01146971429044f216a3ca905e06/compiler/rustc_lint/src/builtin.rs#L415 // +use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::span_lint; use if_chain::if_chain; use rustc_ast::ast::{self, MetaItem, MetaItemKind}; -use rustc_ast::attr; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty; @@ -111,14 +111,7 @@ impl_lint_pass!(MissingDoc => [MISSING_DOCS_IN_PRIVATE_ITEMS]); impl<'tcx> LateLintPass<'tcx> for MissingDoc { fn enter_lint_attrs(&mut self, _: &LateContext<'tcx>, attrs: &'tcx [ast::Attribute]) { - let doc_hidden = self.doc_hidden() - || attrs.iter().any(|attr| { - attr.has_name(sym::doc) - && match attr.meta_item_list() { - None => false, - Some(l) => attr::list_contains_name(&l[..], sym::hidden), - } - }); + let doc_hidden = self.doc_hidden() || is_doc_hidden(attrs); self.doc_hidden_stack.push(doc_hidden); } diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index 8d28421d70d..7ec8452bf4c 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -1,4 +1,4 @@ -use rustc_ast::ast; +use rustc_ast::{ast, attr}; use rustc_errors::Applicability; use rustc_session::Session; use rustc_span::sym; @@ -148,3 +148,13 @@ pub fn get_unique_inner_attr(sess: &Session, attrs: &[ast::Attribute], name: &'s pub fn is_proc_macro(sess: &Session, attrs: &[ast::Attribute]) -> bool { attrs.iter().any(|attr| sess.is_proc_macro_attr(attr)) } + +/// Return true if the attributes contain `#[doc(hidden)]` +pub fn is_doc_hidden(attrs: &[ast::Attribute]) -> bool { + #[allow(clippy::filter_map)] + attrs + .iter() + .filter(|attr| attr.has_name(sym::doc)) + .flat_map(ast::Attribute::meta_item_list) + .any(|l| attr::list_contains_name(&l, sym::hidden)) +} diff --git a/tests/ui/match_wildcard_for_single_variants.fixed b/tests/ui/match_wildcard_for_single_variants.fixed index d99f9af3faf..31b743f7a18 100644 --- a/tests/ui/match_wildcard_for_single_variants.fixed +++ b/tests/ui/match_wildcard_for_single_variants.fixed @@ -108,4 +108,20 @@ fn main() { Bar::B => (), _ => (), }; + + //#6984 + { + #![allow(clippy::manual_non_exhaustive)] + pub enum Enum { + A, + B, + #[doc(hidden)] + __Private, + } + match Enum::A { + Enum::A => (), + Enum::B => (), + _ => (), + } + } } diff --git a/tests/ui/match_wildcard_for_single_variants.rs b/tests/ui/match_wildcard_for_single_variants.rs index 1752a95de4b..d19e6ab7eb2 100644 --- a/tests/ui/match_wildcard_for_single_variants.rs +++ b/tests/ui/match_wildcard_for_single_variants.rs @@ -108,4 +108,20 @@ fn main() { Bar::B => (), _ => (), }; + + //#6984 + { + #![allow(clippy::manual_non_exhaustive)] + pub enum Enum { + A, + B, + #[doc(hidden)] + __Private, + } + match Enum::A { + Enum::A => (), + Enum::B => (), + _ => (), + } + } } -- cgit 1.4.1-3-g733a5 From 5029dc805fa1b8c58988cad119a45a6d51bcdaad Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Tue, 9 Feb 2021 00:24:23 +0900 Subject: New Lint: excessive_for_each --- CHANGELOG.md | 1 + clippy_lints/src/iter_for_each.rs | 0 clippy_lints/src/lib.rs | 3 + clippy_lints/src/methods/excessive_for_each.rs | 149 +++++++++++++++++++++++++ clippy_lints/src/methods/mod.rs | 29 +++++ tests/ui/excessive_for_each.rs | 96 ++++++++++++++++ tests/ui/excessive_for_each.stderr | 123 ++++++++++++++++++++ 7 files changed, 401 insertions(+) create mode 100644 clippy_lints/src/iter_for_each.rs create mode 100644 clippy_lints/src/methods/excessive_for_each.rs create mode 100644 tests/ui/excessive_for_each.rs create mode 100644 tests/ui/excessive_for_each.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 681ecd6832a..c35ab6c94bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2192,6 +2192,7 @@ Released 2018-09-13 [`eq_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#eq_op [`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_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_for_each [`excessive_precision`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision [`exhaustive_enums`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_enums [`exhaustive_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_structs diff --git a/clippy_lints/src/iter_for_each.rs b/clippy_lints/src/iter_for_each.rs new file mode 100644 index 00000000000..e69de29bb2d diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d37e229fb57..8fb3bfdafc9 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -781,6 +781,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::CLONE_DOUBLE_REF, &methods::CLONE_ON_COPY, &methods::CLONE_ON_REF_PTR, + &methods::EXCESSIVE_FOR_EACH, &methods::EXPECT_FUN_CALL, &methods::EXPECT_USED, &methods::FILETYPE_IS_FILE, @@ -1581,6 +1582,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::CHARS_NEXT_CMP), LintId::of(&methods::CLONE_DOUBLE_REF), LintId::of(&methods::CLONE_ON_COPY), + LintId::of(&methods::EXCESSIVE_FOR_EACH), LintId::of(&methods::EXPECT_FUN_CALL), LintId::of(&methods::FILTER_MAP_IDENTITY), LintId::of(&methods::FILTER_NEXT), @@ -1797,6 +1799,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::BYTES_NTH), LintId::of(&methods::CHARS_LAST_CMP), LintId::of(&methods::CHARS_NEXT_CMP), + LintId::of(&methods::EXCESSIVE_FOR_EACH), LintId::of(&methods::FROM_ITER_INSTEAD_OF_COLLECT), LintId::of(&methods::INTO_ITER_ON_REF), LintId::of(&methods::ITER_CLONED_COLLECT), diff --git a/clippy_lints/src/methods/excessive_for_each.rs b/clippy_lints/src/methods/excessive_for_each.rs new file mode 100644 index 00000000000..36f92d5b95f --- /dev/null +++ b/clippy_lints/src/methods/excessive_for_each.rs @@ -0,0 +1,149 @@ +use rustc_errors::Applicability; +use rustc_hir::{ + intravisit::{walk_expr, NestedVisitorMap, Visitor}, + Expr, ExprKind, +}; +use rustc_lint::LateContext; +use rustc_middle::{hir::map::Map, ty, ty::Ty}; +use rustc_span::source_map::Span; + +use crate::utils::{match_trait_method, match_type, paths, snippet, span_lint_and_then}; + +use if_chain::if_chain; + +pub(super) fn lint(cx: &LateContext<'_>, expr: &'tcx Expr<'_>, args: &[&[Expr<'_>]]) { + if args.len() < 2 { + return; + } + + let for_each_args = args[0]; + if for_each_args.len() < 2 { + return; + } + let for_each_receiver = &for_each_args[0]; + let for_each_arg = &for_each_args[1]; + let iter_receiver = &args[1][0]; + + if_chain! { + if match_trait_method(cx, expr, &paths::ITERATOR); + if !match_trait_method(cx, for_each_receiver, &paths::ITERATOR); + if is_target_ty(cx, cx.typeck_results().expr_ty(iter_receiver)); + if let ExprKind::Closure(_, _, body_id, ..) = for_each_arg.kind; + then { + let body = cx.tcx.hir().body(body_id); + let mut ret_span_collector = RetSpanCollector::new(); + ret_span_collector.visit_expr(&body.value); + + let label = "'outer"; + let loop_label = if ret_span_collector.need_label { + format!("{}: ", label) + } else { + "".to_string() + }; + let sugg = + format!("{}for {} in {} {{ .. }}", loop_label, snippet(cx, body.params[0].pat.span, ""), snippet(cx, for_each_receiver.span, "")); + + let mut notes = vec![]; + for (span, need_label) in ret_span_collector.spans { + let cont_label = if need_label { + format!(" {}", label) + } else { + "".to_string() + }; + let note = format!("change `return` to `continue{}` in the loop body", cont_label); + notes.push((span, note)); + } + + span_lint_and_then(cx, + super::EXCESSIVE_FOR_EACH, + expr.span, + "excessive use of `for_each`", + |diag| { + diag.span_suggestion(expr.span, "try", sugg, Applicability::HasPlaceholders); + for note in notes { + diag.span_note(note.0, ¬e.1); + } + } + ); + } + } +} + +type PathSegment = &'static [&'static str]; + +const TARGET_ITER_RECEIVER_TY: &[PathSegment] = &[ + &paths::VEC, + &paths::VEC_DEQUE, + &paths::LINKED_LIST, + &paths::HASHMAP, + &paths::BTREEMAP, + &paths::HASHSET, + &paths::BTREESET, + &paths::BINARY_HEAP, +]; + +fn is_target_ty(cx: &LateContext<'_>, expr_ty: Ty<'_>) -> bool { + let expr_ty = expr_ty.peel_refs(); + for target in TARGET_ITER_RECEIVER_TY { + if match_type(cx, expr_ty, target) { + return true; + } + } + + if_chain! { + if matches!(expr_ty.kind(), ty::Slice(_) | ty::Array(..)); + then { + return true; + } + } + + false +} + +/// Collect spans of `return` in the closure body. +struct RetSpanCollector { + spans: Vec<(Span, bool)>, + loop_depth: u16, + need_label: bool, +} + +impl RetSpanCollector { + fn new() -> Self { + Self { + spans: Vec::new(), + loop_depth: 0, + need_label: false, + } + } +} + +impl<'tcx> Visitor<'tcx> for RetSpanCollector { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &Expr<'_>) { + match expr.kind { + ExprKind::Ret(..) => { + if self.loop_depth > 0 && !self.need_label { + self.need_label = true + } + + self.spans.push((expr.span, self.loop_depth > 0)) + }, + + ExprKind::Loop(..) => { + self.loop_depth += 1; + walk_expr(self, expr); + self.loop_depth -= 1; + return; + }, + + _ => {}, + } + + walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index fccdee07877..058140fddb8 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -974,6 +974,33 @@ declare_clippy_lint! { "using `.skip(x).next()` on an iterator" } +declare_clippy_lint! { + /// **What it does:** Checks for use of `obj.method().for_each(closure)` if obj doesn't + /// implelement `Iterator` and `method()` returns `Impl Iterator` type. + /// + /// **Why is this bad?** Excessive use of `for_each` reduces redability, using `for` loop is + /// clearer and more concise. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let v = vec![0, 1, 2]; + /// v.iter().for_each(|elem| println!("{}", elem)); + /// ``` + /// Use instead: + /// ```rust + /// let v = vec![0, 1, 2]; + /// for elem in v.iter() { + /// println!("{}", elem); + /// } + /// ``` + pub EXCESSIVE_FOR_EACH, + style, + "using `.iter().for_each(|x| {..})` when using `for` loop would work instead" +} + declare_clippy_lint! { /// **What it does:** Checks for use of `.get().unwrap()` (or /// `.get_mut().unwrap`) on a standard library type which implements `Index` @@ -1661,6 +1688,7 @@ impl_lint_pass!(Methods => [ ITER_NTH_ZERO, BYTES_NTH, ITER_SKIP_NEXT, + EXCESSIVE_FOR_EACH, GET_UNWRAP, STRING_EXTEND_CHARS, ITER_CLONED_COLLECT, @@ -1807,6 +1835,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["to_os_string", ..] => implicit_clone::check(cx, expr, sym::OsStr), ["to_path_buf", ..] => implicit_clone::check(cx, expr, sym::Path), ["to_vec", ..] => implicit_clone::check(cx, expr, sym::slice), + ["for_each", ..] => excessive_for_each::lint(cx, expr, &arg_lists), _ => {}, } diff --git a/tests/ui/excessive_for_each.rs b/tests/ui/excessive_for_each.rs new file mode 100644 index 00000000000..12c87782d97 --- /dev/null +++ b/tests/ui/excessive_for_each.rs @@ -0,0 +1,96 @@ +#![warn(clippy::excessive_for_each)] +#![allow(clippy::needless_return)] + +use std::collections::*; + +fn main() { + // Should trigger this lint: Vec. + let vec: Vec = Vec::new(); + vec.iter().for_each(|v| println!("{}", v)); + + // Should trigger this lint: &Vec. + let vec_ref = &vec; + vec_ref.iter().for_each(|v| println!("{}", v)); + + // Should trigger this lint: VecDeque. + let vec_deq: VecDeque = VecDeque::new(); + vec_deq.iter().for_each(|v| println!("{}", v)); + + // Should trigger this lint: LinkedList. + let list: LinkedList = LinkedList::new(); + list.iter().for_each(|v| println!("{}", v)); + + // Should trigger this lint: HashMap. + let mut hash_map: HashMap = HashMap::new(); + hash_map.iter().for_each(|(k, v)| println!("{}: {}", k, v)); + hash_map.iter_mut().for_each(|(k, v)| println!("{}: {}", k, v)); + hash_map.keys().for_each(|k| println!("{}", k)); + hash_map.values().for_each(|v| println!("{}", v)); + + // Should trigger this lint: HashSet. + let hash_set: HashSet = HashSet::new(); + hash_set.iter().for_each(|v| println!("{}", v)); + + // Should trigger this lint: BTreeSet. + let btree_set: BTreeSet = BTreeSet::new(); + btree_set.iter().for_each(|v| println!("{}", v)); + + // Should trigger this lint: BinaryHeap. + let binary_heap: BinaryHeap = BinaryHeap::new(); + binary_heap.iter().for_each(|v| println!("{}", v)); + + // Should trigger this lint: Array. + let s = [1, 2, 3]; + s.iter().for_each(|v| println!("{}", v)); + + // Should trigger this lint. Slice. + vec.as_slice().iter().for_each(|v| println!("{}", v)); + + // Should trigger this lint with notes that say "change `return` to `continue`". + vec.iter().for_each(|v| { + if *v == 10 { + return; + } else { + println!("{}", v); + } + }); + + // Should trigger this lint with notes that say "change `return` to `continue 'outer`". + vec.iter().for_each(|v| { + for i in 0..*v { + if i == 10 { + return; + } else { + println!("{}", v); + } + } + if *v == 20 { + return; + } else { + println!("{}", v); + } + }); + + // Should NOT trigger this lint in case `for_each` follows long iterator chain. + vec.iter().chain(vec.iter()).for_each(|v| println!("{}", v)); + + // Should NOT trigger this lint in case a `for_each` argument is not closure. + fn print(x: &i32) { + println!("{}", x); + } + vec.iter().for_each(print); + + // Should NOT trigger this lint in case the receiver of `iter` is a user defined type. + let my_collection = MyCollection { v: vec![] }; + my_collection.iter().for_each(|v| println!("{}", v)); +} + +struct MyCollection { + v: Vec, +} + +impl MyCollection { + fn iter(&self) -> impl Iterator { + self.v.iter() + } +} diff --git a/tests/ui/excessive_for_each.stderr b/tests/ui/excessive_for_each.stderr new file mode 100644 index 00000000000..026b14a5899 --- /dev/null +++ b/tests/ui/excessive_for_each.stderr @@ -0,0 +1,123 @@ +error: excessive use of `for_each` + --> $DIR/excessive_for_each.rs:9:5 + | +LL | vec.iter().for_each(|v| println!("{}", v)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in vec.iter() { .. }` + | + = note: `-D clippy::excessive-for-each` implied by `-D warnings` + +error: excessive use of `for_each` + --> $DIR/excessive_for_each.rs:13:5 + | +LL | vec_ref.iter().for_each(|v| println!("{}", v)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in vec_ref.iter() { .. }` + +error: excessive use of `for_each` + --> $DIR/excessive_for_each.rs:17:5 + | +LL | vec_deq.iter().for_each(|v| println!("{}", v)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in vec_deq.iter() { .. }` + +error: excessive use of `for_each` + --> $DIR/excessive_for_each.rs:21:5 + | +LL | list.iter().for_each(|v| println!("{}", v)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in list.iter() { .. }` + +error: excessive use of `for_each` + --> $DIR/excessive_for_each.rs:25:5 + | +LL | hash_map.iter().for_each(|(k, v)| println!("{}: {}", k, v)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for (k, v) in hash_map.iter() { .. }` + +error: excessive use of `for_each` + --> $DIR/excessive_for_each.rs:26:5 + | +LL | hash_map.iter_mut().for_each(|(k, v)| println!("{}: {}", k, v)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for (k, v) in hash_map.iter_mut() { .. }` + +error: excessive use of `for_each` + --> $DIR/excessive_for_each.rs:27:5 + | +LL | hash_map.keys().for_each(|k| println!("{}", k)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for k in hash_map.keys() { .. }` + +error: excessive use of `for_each` + --> $DIR/excessive_for_each.rs:28:5 + | +LL | hash_map.values().for_each(|v| println!("{}", v)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in hash_map.values() { .. }` + +error: excessive use of `for_each` + --> $DIR/excessive_for_each.rs:32:5 + | +LL | hash_set.iter().for_each(|v| println!("{}", v)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in hash_set.iter() { .. }` + +error: excessive use of `for_each` + --> $DIR/excessive_for_each.rs:36:5 + | +LL | btree_set.iter().for_each(|v| println!("{}", v)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in btree_set.iter() { .. }` + +error: excessive use of `for_each` + --> $DIR/excessive_for_each.rs:40:5 + | +LL | binary_heap.iter().for_each(|v| println!("{}", v)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in binary_heap.iter() { .. }` + +error: excessive use of `for_each` + --> $DIR/excessive_for_each.rs:44:5 + | +LL | s.iter().for_each(|v| println!("{}", v)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in s.iter() { .. }` + +error: excessive use of `for_each` + --> $DIR/excessive_for_each.rs:47:5 + | +LL | vec.as_slice().iter().for_each(|v| println!("{}", v)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in vec.as_slice().iter() { .. }` + +error: excessive use of `for_each` + --> $DIR/excessive_for_each.rs:50:5 + | +LL | / vec.iter().for_each(|v| { +LL | | if *v == 10 { +LL | | return; +LL | | } else { +LL | | println!("{}", v); +LL | | } +LL | | }); + | |______^ help: try: `for v in vec.iter() { .. }` + | +note: change `return` to `continue` in the loop body + --> $DIR/excessive_for_each.rs:52:13 + | +LL | return; + | ^^^^^^ + +error: excessive use of `for_each` + --> $DIR/excessive_for_each.rs:59:5 + | +LL | / vec.iter().for_each(|v| { +LL | | for i in 0..*v { +LL | | if i == 10 { +LL | | return; +... | +LL | | } +LL | | }); + | |______^ help: try: `'outer: for v in vec.iter() { .. }` + | +note: change `return` to `continue 'outer` in the loop body + --> $DIR/excessive_for_each.rs:62:17 + | +LL | return; + | ^^^^^^ +note: change `return` to `continue` in the loop body + --> $DIR/excessive_for_each.rs:68:13 + | +LL | return; + | ^^^^^^ + +error: aborting due to 15 previous errors + -- cgit 1.4.1-3-g733a5 From 5bb0f16552e8eab45025c7f4581b8ae5edca3219 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Wed, 10 Feb 2021 00:53:53 +0900 Subject: Trigger the lint iff exposure's body is ExprKind::Block. --- clippy_lints/src/methods/excessive_for_each.rs | 3 +- tests/ui/excessive_for_each.rs | 56 +++++++++--- tests/ui/excessive_for_each.stderr | 114 +++++++++++++++---------- 3 files changed, 115 insertions(+), 58 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/excessive_for_each.rs b/clippy_lints/src/methods/excessive_for_each.rs index 6b3a11044f0..f3e9e2400f1 100644 --- a/clippy_lints/src/methods/excessive_for_each.rs +++ b/clippy_lints/src/methods/excessive_for_each.rs @@ -28,8 +28,9 @@ pub(super) fn lint(cx: &LateContext<'_>, expr: &'tcx Expr<'_>, args: &[&[Expr<'_ if match_trait_method(cx, expr, &paths::ITERATOR); if is_target_ty(cx, cx.typeck_results().expr_ty(iter_receiver)); if let ExprKind::Closure(_, _, body_id, ..) = for_each_arg.kind; + let body = cx.tcx.hir().body(body_id); + if let ExprKind::Block(..) = body.value.kind; then { - let body = cx.tcx.hir().body(body_id); let mut ret_span_collector = RetSpanCollector::new(); ret_span_collector.visit_expr(&body.value); diff --git a/tests/ui/excessive_for_each.rs b/tests/ui/excessive_for_each.rs index 12c87782d97..1c8e450398e 100644 --- a/tests/ui/excessive_for_each.rs +++ b/tests/ui/excessive_for_each.rs @@ -6,45 +6,72 @@ use std::collections::*; fn main() { // Should trigger this lint: Vec. let vec: Vec = Vec::new(); - vec.iter().for_each(|v| println!("{}", v)); + let mut acc = 0; + vec.iter().for_each(|v| { + acc += v; + }); // Should trigger this lint: &Vec. let vec_ref = &vec; - vec_ref.iter().for_each(|v| println!("{}", v)); + vec_ref.iter().for_each(|v| { + acc += v; + }); // Should trigger this lint: VecDeque. let vec_deq: VecDeque = VecDeque::new(); - vec_deq.iter().for_each(|v| println!("{}", v)); + vec_deq.iter().for_each(|v| { + acc += v; + }); // Should trigger this lint: LinkedList. let list: LinkedList = LinkedList::new(); - list.iter().for_each(|v| println!("{}", v)); + list.iter().for_each(|v| { + acc += v; + }); // Should trigger this lint: HashMap. let mut hash_map: HashMap = HashMap::new(); - hash_map.iter().for_each(|(k, v)| println!("{}: {}", k, v)); - hash_map.iter_mut().for_each(|(k, v)| println!("{}: {}", k, v)); - hash_map.keys().for_each(|k| println!("{}", k)); - hash_map.values().for_each(|v| println!("{}", v)); + hash_map.iter().for_each(|(k, v)| { + acc += k + v; + }); + hash_map.iter_mut().for_each(|(k, v)| { + acc += *k + *v; + }); + hash_map.keys().for_each(|k| { + acc += k; + }); + hash_map.values().for_each(|v| { + acc += v; + }); // Should trigger this lint: HashSet. let hash_set: HashSet = HashSet::new(); - hash_set.iter().for_each(|v| println!("{}", v)); + hash_set.iter().for_each(|v| { + acc += v; + }); // Should trigger this lint: BTreeSet. let btree_set: BTreeSet = BTreeSet::new(); - btree_set.iter().for_each(|v| println!("{}", v)); + btree_set.iter().for_each(|v| { + acc += v; + }); // Should trigger this lint: BinaryHeap. let binary_heap: BinaryHeap = BinaryHeap::new(); - binary_heap.iter().for_each(|v| println!("{}", v)); + binary_heap.iter().for_each(|v| { + acc += v; + }); // Should trigger this lint: Array. let s = [1, 2, 3]; - s.iter().for_each(|v| println!("{}", v)); + s.iter().for_each(|v| { + acc += v; + }); // Should trigger this lint. Slice. - vec.as_slice().iter().for_each(|v| println!("{}", v)); + vec.as_slice().iter().for_each(|v| { + acc += v; + }); // Should trigger this lint with notes that say "change `return` to `continue`". vec.iter().for_each(|v| { @@ -83,6 +110,9 @@ fn main() { // Should NOT trigger this lint in case the receiver of `iter` is a user defined type. let my_collection = MyCollection { v: vec![] }; my_collection.iter().for_each(|v| println!("{}", v)); + + // Should NOT trigger this lint in case the closure body is not a `ExprKind::Block`. + vec.iter().for_each(|x| acc += x); } struct MyCollection { diff --git a/tests/ui/excessive_for_each.stderr b/tests/ui/excessive_for_each.stderr index 026b14a5899..c4b66e3a034 100644 --- a/tests/ui/excessive_for_each.stderr +++ b/tests/ui/excessive_for_each.stderr @@ -1,85 +1,111 @@ error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:9:5 + --> $DIR/excessive_for_each.rs:10:5 | -LL | vec.iter().for_each(|v| println!("{}", v)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in vec.iter() { .. }` +LL | / vec.iter().for_each(|v| { +LL | | acc += v; +LL | | }); + | |______^ help: try: `for v in vec.iter() { .. }` | = note: `-D clippy::excessive-for-each` implied by `-D warnings` error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:13:5 + --> $DIR/excessive_for_each.rs:16:5 | -LL | vec_ref.iter().for_each(|v| println!("{}", v)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in vec_ref.iter() { .. }` +LL | / vec_ref.iter().for_each(|v| { +LL | | acc += v; +LL | | }); + | |______^ help: try: `for v in vec_ref.iter() { .. }` error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:17:5 + --> $DIR/excessive_for_each.rs:22:5 | -LL | vec_deq.iter().for_each(|v| println!("{}", v)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in vec_deq.iter() { .. }` +LL | / vec_deq.iter().for_each(|v| { +LL | | acc += v; +LL | | }); + | |______^ help: try: `for v in vec_deq.iter() { .. }` error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:21:5 + --> $DIR/excessive_for_each.rs:28:5 | -LL | list.iter().for_each(|v| println!("{}", v)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in list.iter() { .. }` +LL | / list.iter().for_each(|v| { +LL | | acc += v; +LL | | }); + | |______^ help: try: `for v in list.iter() { .. }` error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:25:5 + --> $DIR/excessive_for_each.rs:34:5 | -LL | hash_map.iter().for_each(|(k, v)| println!("{}: {}", k, v)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for (k, v) in hash_map.iter() { .. }` +LL | / hash_map.iter().for_each(|(k, v)| { +LL | | acc += k + v; +LL | | }); + | |______^ help: try: `for (k, v) in hash_map.iter() { .. }` error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:26:5 + --> $DIR/excessive_for_each.rs:37:5 | -LL | hash_map.iter_mut().for_each(|(k, v)| println!("{}: {}", k, v)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for (k, v) in hash_map.iter_mut() { .. }` +LL | / hash_map.iter_mut().for_each(|(k, v)| { +LL | | acc += *k + *v; +LL | | }); + | |______^ help: try: `for (k, v) in hash_map.iter_mut() { .. }` error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:27:5 + --> $DIR/excessive_for_each.rs:40:5 | -LL | hash_map.keys().for_each(|k| println!("{}", k)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for k in hash_map.keys() { .. }` +LL | / hash_map.keys().for_each(|k| { +LL | | acc += k; +LL | | }); + | |______^ help: try: `for k in hash_map.keys() { .. }` error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:28:5 + --> $DIR/excessive_for_each.rs:43:5 | -LL | hash_map.values().for_each(|v| println!("{}", v)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in hash_map.values() { .. }` +LL | / hash_map.values().for_each(|v| { +LL | | acc += v; +LL | | }); + | |______^ help: try: `for v in hash_map.values() { .. }` error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:32:5 + --> $DIR/excessive_for_each.rs:49:5 | -LL | hash_set.iter().for_each(|v| println!("{}", v)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in hash_set.iter() { .. }` +LL | / hash_set.iter().for_each(|v| { +LL | | acc += v; +LL | | }); + | |______^ help: try: `for v in hash_set.iter() { .. }` error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:36:5 + --> $DIR/excessive_for_each.rs:55:5 | -LL | btree_set.iter().for_each(|v| println!("{}", v)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in btree_set.iter() { .. }` +LL | / btree_set.iter().for_each(|v| { +LL | | acc += v; +LL | | }); + | |______^ help: try: `for v in btree_set.iter() { .. }` error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:40:5 + --> $DIR/excessive_for_each.rs:61:5 | -LL | binary_heap.iter().for_each(|v| println!("{}", v)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in binary_heap.iter() { .. }` +LL | / binary_heap.iter().for_each(|v| { +LL | | acc += v; +LL | | }); + | |______^ help: try: `for v in binary_heap.iter() { .. }` error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:44:5 + --> $DIR/excessive_for_each.rs:67:5 | -LL | s.iter().for_each(|v| println!("{}", v)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in s.iter() { .. }` +LL | / s.iter().for_each(|v| { +LL | | acc += v; +LL | | }); + | |______^ help: try: `for v in s.iter() { .. }` error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:47:5 + --> $DIR/excessive_for_each.rs:72:5 | -LL | vec.as_slice().iter().for_each(|v| println!("{}", v)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in vec.as_slice().iter() { .. }` +LL | / vec.as_slice().iter().for_each(|v| { +LL | | acc += v; +LL | | }); + | |______^ help: try: `for v in vec.as_slice().iter() { .. }` error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:50:5 + --> $DIR/excessive_for_each.rs:77:5 | LL | / vec.iter().for_each(|v| { LL | | if *v == 10 { @@ -91,13 +117,13 @@ LL | | }); | |______^ help: try: `for v in vec.iter() { .. }` | note: change `return` to `continue` in the loop body - --> $DIR/excessive_for_each.rs:52:13 + --> $DIR/excessive_for_each.rs:79:13 | LL | return; | ^^^^^^ error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:59:5 + --> $DIR/excessive_for_each.rs:86:5 | LL | / vec.iter().for_each(|v| { LL | | for i in 0..*v { @@ -109,12 +135,12 @@ LL | | }); | |______^ help: try: `'outer: for v in vec.iter() { .. }` | note: change `return` to `continue 'outer` in the loop body - --> $DIR/excessive_for_each.rs:62:17 + --> $DIR/excessive_for_each.rs:89:17 | LL | return; | ^^^^^^ note: change `return` to `continue` in the loop body - --> $DIR/excessive_for_each.rs:68:13 + --> $DIR/excessive_for_each.rs:95:13 | LL | return; | ^^^^^^ -- cgit 1.4.1-3-g733a5 From 90cbbb2da3989a437a36c075396331f81a0e3b30 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Thu, 11 Feb 2021 12:50:20 +0900 Subject: Avoid to suggest using label --- clippy_lints/src/methods/excessive_for_each.rs | 51 ++++++++++++-------------- tests/ui/excessive_for_each.rs | 2 +- tests/ui/excessive_for_each.stderr | 25 +------------ 3 files changed, 26 insertions(+), 52 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/excessive_for_each.rs b/clippy_lints/src/methods/excessive_for_each.rs index f3e9e2400f1..e1440e68327 100644 --- a/clippy_lints/src/methods/excessive_for_each.rs +++ b/clippy_lints/src/methods/excessive_for_each.rs @@ -31,26 +31,20 @@ pub(super) fn lint(cx: &LateContext<'_>, expr: &'tcx Expr<'_>, args: &[&[Expr<'_ let body = cx.tcx.hir().body(body_id); if let ExprKind::Block(..) = body.value.kind; then { - let mut ret_span_collector = RetSpanCollector::new(); - ret_span_collector.visit_expr(&body.value); - - let label = "'outer"; - let loop_label = if ret_span_collector.need_label { - format!("{}: ", label) - } else { - "".to_string() - }; + let mut ret_collector = RetCollector::new(); + ret_collector.visit_expr(&body.value); + + // Skip the lint if `return` is used in `Loop` to avoid a suggest using `'label`. + if ret_collector.ret_in_loop { + return; + } + let sugg = - format!("{}for {} in {} {{ .. }}", loop_label, snippet(cx, body.params[0].pat.span, ""), snippet(cx, for_each_receiver.span, "")); + format!("for {} in {} {{ .. }}", snippet(cx, body.params[0].pat.span, ""), snippet(cx, for_each_receiver.span, "")); let mut notes = vec![]; - for (span, need_label) in ret_span_collector.spans { - let cont_label = if need_label { - format!(" {}", label) - } else { - "".to_string() - }; - let note = format!("change `return` to `continue{}` in the loop body", cont_label); + for span in ret_collector.spans { + let note = format!("change `return` to `continue` in the loop body"); notes.push((span, note)); } @@ -100,34 +94,37 @@ fn is_target_ty(cx: &LateContext<'_>, expr_ty: Ty<'_>) -> bool { false } -/// Collect spans of `return` in the closure body. -struct RetSpanCollector { - spans: Vec<(Span, bool)>, +/// This type plays two roles. +/// 1. Collect spans of `return` in the closure body. +/// 2. Detect use of `return` in `Loop` in the closure body. +struct RetCollector { + spans: Vec, + ret_in_loop: bool, + loop_depth: u16, - need_label: bool, } -impl RetSpanCollector { +impl RetCollector { fn new() -> Self { Self { spans: Vec::new(), + ret_in_loop: false, loop_depth: 0, - need_label: false, } } } -impl<'tcx> Visitor<'tcx> for RetSpanCollector { +impl<'tcx> Visitor<'tcx> for RetCollector { type Map = Map<'tcx>; fn visit_expr(&mut self, expr: &Expr<'_>) { match expr.kind { ExprKind::Ret(..) => { - if self.loop_depth > 0 && !self.need_label { - self.need_label = true + if self.loop_depth > 0 && !self.ret_in_loop { + self.ret_in_loop = true } - self.spans.push((expr.span, self.loop_depth > 0)) + self.spans.push(expr.span) }, ExprKind::Loop(..) => { diff --git a/tests/ui/excessive_for_each.rs b/tests/ui/excessive_for_each.rs index 1c8e450398e..0800bef71e9 100644 --- a/tests/ui/excessive_for_each.rs +++ b/tests/ui/excessive_for_each.rs @@ -82,7 +82,7 @@ fn main() { } }); - // Should trigger this lint with notes that say "change `return` to `continue 'outer`". + // Should NOT trigger this lint in case `return` is used in `Loop` of the closure. vec.iter().for_each(|v| { for i in 0..*v { if i == 10 { diff --git a/tests/ui/excessive_for_each.stderr b/tests/ui/excessive_for_each.stderr index c4b66e3a034..f5799484e03 100644 --- a/tests/ui/excessive_for_each.stderr +++ b/tests/ui/excessive_for_each.stderr @@ -122,28 +122,5 @@ note: change `return` to `continue` in the loop body LL | return; | ^^^^^^ -error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:86:5 - | -LL | / vec.iter().for_each(|v| { -LL | | for i in 0..*v { -LL | | if i == 10 { -LL | | return; -... | -LL | | } -LL | | }); - | |______^ help: try: `'outer: for v in vec.iter() { .. }` - | -note: change `return` to `continue 'outer` in the loop body - --> $DIR/excessive_for_each.rs:89:17 - | -LL | return; - | ^^^^^^ -note: change `return` to `continue` in the loop body - --> $DIR/excessive_for_each.rs:95:13 - | -LL | return; - | ^^^^^^ - -error: aborting due to 15 previous errors +error: aborting due to 14 previous errors -- cgit 1.4.1-3-g733a5 From 527fbbef486bab32a95209b638b097a14ff41db0 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Sat, 13 Mar 2021 00:42:43 +0900 Subject: Refactor excessive_for_each --- CHANGELOG.md | 2 +- clippy_lints/src/iter_for_each.rs | 0 clippy_lints/src/lib.rs | 6 +- clippy_lints/src/methods/excessive_for_each.rs | 122 ------------------ clippy_lints/src/methods/mod.rs | 29 ----- clippy_lints/src/needless_for_each.rs | 170 +++++++++++++++++++++++++ tests/ui/excessive_for_each.rs | 126 ------------------ tests/ui/excessive_for_each.stderr | 126 ------------------ tests/ui/needless_for_each_fixable.fixed | 99 ++++++++++++++ tests/ui/needless_for_each_fixable.rs | 99 ++++++++++++++ tests/ui/needless_for_each_fixable.stderr | 108 ++++++++++++++++ tests/ui/needless_for_each_unfixable.rs | 14 ++ tests/ui/needless_for_each_unfixable.stderr | 30 +++++ 13 files changed, 525 insertions(+), 406 deletions(-) delete mode 100644 clippy_lints/src/iter_for_each.rs delete mode 100644 clippy_lints/src/methods/excessive_for_each.rs create mode 100644 clippy_lints/src/needless_for_each.rs delete mode 100644 tests/ui/excessive_for_each.rs delete mode 100644 tests/ui/excessive_for_each.stderr create mode 100644 tests/ui/needless_for_each_fixable.fixed create mode 100644 tests/ui/needless_for_each_fixable.rs create mode 100644 tests/ui/needless_for_each_fixable.stderr create mode 100644 tests/ui/needless_for_each_unfixable.rs create mode 100644 tests/ui/needless_for_each_unfixable.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index c35ab6c94bf..a2193f0eab9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2192,7 +2192,6 @@ Released 2018-09-13 [`eq_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#eq_op [`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_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_for_each [`excessive_precision`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision [`exhaustive_enums`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_enums [`exhaustive_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_structs @@ -2370,6 +2369,7 @@ Released 2018-09-13 [`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect [`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue [`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main +[`needless_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_for_each [`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes [`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value [`needless_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark diff --git a/clippy_lints/src/iter_for_each.rs b/clippy_lints/src/iter_for_each.rs deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 2f2ccdb310a..8c9247d9781 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -291,6 +291,7 @@ mod needless_bool; mod needless_borrow; mod needless_borrowed_ref; mod needless_continue; +mod needless_for_each; mod needless_pass_by_value; mod needless_question_mark; mod needless_update; @@ -781,7 +782,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &methods::CLONE_DOUBLE_REF, &methods::CLONE_ON_COPY, &methods::CLONE_ON_REF_PTR, - &methods::EXCESSIVE_FOR_EACH, &methods::EXPECT_FUN_CALL, &methods::EXPECT_USED, &methods::FILETYPE_IS_FILE, @@ -868,6 +868,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &needless_borrow::NEEDLESS_BORROW, &needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE, &needless_continue::NEEDLESS_CONTINUE, + &needless_for_each::NEEDLESS_FOR_EACH, &needless_pass_by_value::NEEDLESS_PASS_BY_VALUE, &needless_question_mark::NEEDLESS_QUESTION_MARK, &needless_update::NEEDLESS_UPDATE, @@ -1046,6 +1047,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box ptr_eq::PtrEq); store.register_late_pass(|| box needless_bool::NeedlessBool); store.register_late_pass(|| box needless_bool::BoolComparison); + store.register_late_pass(|| box needless_for_each::NeedlessForEach); store.register_late_pass(|| box approx_const::ApproxConstant); store.register_late_pass(|| box misc::MiscLints); store.register_late_pass(|| box eta_reduction::EtaReduction); @@ -1314,7 +1316,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&matches::WILDCARD_ENUM_MATCH_ARM), LintId::of(&mem_forget::MEM_FORGET), LintId::of(&methods::CLONE_ON_REF_PTR), - LintId::of(&methods::EXCESSIVE_FOR_EACH), LintId::of(&methods::EXPECT_USED), LintId::of(&methods::FILETYPE_IS_FILE), LintId::of(&methods::GET_UNWRAP), @@ -1325,6 +1326,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS), LintId::of(&missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS), LintId::of(&modulo_arithmetic::MODULO_ARITHMETIC), + LintId::of(&needless_for_each::NEEDLESS_FOR_EACH), LintId::of(&panic_in_result_fn::PANIC_IN_RESULT_FN), LintId::of(&panic_unimplemented::PANIC), LintId::of(&panic_unimplemented::TODO), diff --git a/clippy_lints/src/methods/excessive_for_each.rs b/clippy_lints/src/methods/excessive_for_each.rs deleted file mode 100644 index bcb7cf1491f..00000000000 --- a/clippy_lints/src/methods/excessive_for_each.rs +++ /dev/null @@ -1,122 +0,0 @@ -use rustc_errors::Applicability; -use rustc_hir::{ - intravisit::{walk_expr, NestedVisitorMap, Visitor}, - Expr, ExprKind, -}; -use rustc_lint::LateContext; -use rustc_middle::hir::map::Map; -use rustc_span::source_map::Span; - -use if_chain::if_chain; - -use crate::utils::{has_iter_method, match_trait_method, paths, snippet, span_lint_and_then}; - -use super::EXCESSIVE_FOR_EACH; - -pub(super) fn lint(cx: &LateContext<'_>, expr: &'tcx Expr<'_>, args: &[&[Expr<'_>]]) { - if args.len() < 2 { - return; - } - - let for_each_args = args[0]; - if for_each_args.len() < 2 { - return; - } - let for_each_receiver = &for_each_args[0]; - let for_each_arg = &for_each_args[1]; - let iter_receiver = &args[1][0]; - - if_chain! { - if has_iter_method(cx, cx.typeck_results().expr_ty(iter_receiver)).is_some(); - if match_trait_method(cx, expr, &paths::ITERATOR); - if let ExprKind::Closure(_, _, body_id, ..) = for_each_arg.kind; - let body = cx.tcx.hir().body(body_id); - if let ExprKind::Block(..) = body.value.kind; - then { - let mut ret_collector = RetCollector::new(); - ret_collector.visit_expr(&body.value); - - // Skip the lint if `return` is used in `Loop` in order not to suggest using `'label`. - if ret_collector.ret_in_loop { - return; - } - - let sugg = format!( - "for {} in {} {{ .. }}", - snippet(cx, body.params[0].pat.span, ".."), - snippet(cx, for_each_receiver.span, "..") - ); - - span_lint_and_then( - cx, - EXCESSIVE_FOR_EACH, - expr.span, - "excessive use of `for_each`", - |diag| { - diag.span_suggestion(expr.span, "try", sugg, Applicability::HasPlaceholders); - for span in ret_collector.spans { - diag.span_note(span, "change `return` to `continue` in the loop body"); - } - } - ) - } - } -} - -/// This type plays two roles. -/// 1. Collect spans of `return` in the closure body. -/// 2. Detect use of `return` in `Loop` in the closure body. -/// -/// NOTE: The functionality of this type is similar to -/// [`crate::utilts::visitors::find_all_ret_expressions`], but we can't use -/// `find_all_ret_expressions` instead of this type. The reasons are: -/// 1. `find_all_ret_expressions` passes the argument of `ExprKind::Ret` to a callback, but what we -/// need here is `ExprKind::Ret` itself. -/// 2. We can't trace current loop depth with `find_all_ret_expressions`. -struct RetCollector { - spans: Vec, - ret_in_loop: bool, - - loop_depth: u16, -} - -impl RetCollector { - fn new() -> Self { - Self { - spans: Vec::new(), - ret_in_loop: false, - loop_depth: 0, - } - } -} - -impl<'tcx> Visitor<'tcx> for RetCollector { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, expr: &Expr<'_>) { - match expr.kind { - ExprKind::Ret(..) => { - if self.loop_depth > 0 && !self.ret_in_loop { - self.ret_in_loop = true - } - - self.spans.push(expr.span) - }, - - ExprKind::Loop(..) => { - self.loop_depth += 1; - walk_expr(self, expr); - self.loop_depth -= 1; - return; - }, - - _ => {}, - } - - walk_expr(self, expr); - } - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } -} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index f0c99528fe1..fccdee07877 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -974,33 +974,6 @@ declare_clippy_lint! { "using `.skip(x).next()` on an iterator" } -declare_clippy_lint! { - /// **What it does:** Checks for use of `.method(..).for_each(closure)` if the reciever of `.method(..)` doesn't - /// implement `Iterator` and the return type of `.method(..)` implements `Iterator`. - /// - /// **Why is this bad?** Excessive use of `for_each` reduces redability, using `for` loop is - /// clearer and more concise. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// - /// ```rust - /// let v = vec![0, 1, 2]; - /// v.iter().for_each(|elem| println!("{}", elem)); - /// ``` - /// Use instead: - /// ```rust - /// let v = vec![0, 1, 2]; - /// for elem in v.iter() { - /// println!("{}", elem); - /// } - /// ``` - pub EXCESSIVE_FOR_EACH, - restriction, - "using `.iter().for_each(|x| {..})` when using `for` loop would work instead" -} - declare_clippy_lint! { /// **What it does:** Checks for use of `.get().unwrap()` (or /// `.get_mut().unwrap`) on a standard library type which implements `Index` @@ -1688,7 +1661,6 @@ impl_lint_pass!(Methods => [ ITER_NTH_ZERO, BYTES_NTH, ITER_SKIP_NEXT, - EXCESSIVE_FOR_EACH, GET_UNWRAP, STRING_EXTEND_CHARS, ITER_CLONED_COLLECT, @@ -1835,7 +1807,6 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["to_os_string", ..] => implicit_clone::check(cx, expr, sym::OsStr), ["to_path_buf", ..] => implicit_clone::check(cx, expr, sym::Path), ["to_vec", ..] => implicit_clone::check(cx, expr, sym::slice), - ["for_each", ..] => excessive_for_each::lint(cx, expr, &arg_lists), _ => {}, } diff --git a/clippy_lints/src/needless_for_each.rs b/clippy_lints/src/needless_for_each.rs new file mode 100644 index 00000000000..a7b0a1ca082 --- /dev/null +++ b/clippy_lints/src/needless_for_each.rs @@ -0,0 +1,170 @@ +use rustc_errors::Applicability; +use rustc_hir::{ + intravisit::{walk_expr, NestedVisitorMap, Visitor}, + Expr, ExprKind, Stmt, StmtKind, +}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{source_map::Span, sym, Symbol}; + +use if_chain::if_chain; + +use crate::utils::{ + has_iter_method, is_diagnostic_assoc_item, method_calls, snippet_with_applicability, span_lint_and_then, +}; + +declare_clippy_lint! { + /// **What it does:** Checks for usage of `for_each` that would be more simply written as a + /// `for` loop. + /// + /// **Why is this bad?** `for_each` may be used after applying iterator transformers like + /// `filter` for better readability and performance. It may also be used to fit a simple + /// operation on one line. + /// But when none of these apply, a simple `for` loop is more idiomatic. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let v = vec![0, 1, 2]; + /// v.iter().for_each(|elem| { + /// println!("{}", elem); + /// }) + /// ``` + /// Use instead: + /// ```rust + /// let v = vec![0, 1, 2]; + /// for elem in v.iter() { + /// println!("{}", elem); + /// } + /// ``` + pub NEEDLESS_FOR_EACH, + restriction, + "using `for_each` where a `for` loop would be simpler" +} + +declare_lint_pass!(NeedlessForEach => [NEEDLESS_FOR_EACH]); + +impl LateLintPass<'_> for NeedlessForEach { + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { + let expr = match stmt.kind { + StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr, + StmtKind::Local(local) if local.init.is_some() => local.init.unwrap(), + _ => return, + }; + + // Max depth is set to 3 because we need to check the method chain length is just two. + let (method_names, arg_lists, _) = method_calls(expr, 3); + + if_chain! { + // This assures the length of this method chain is two. + if let [for_each_args, iter_args] = arg_lists.as_slice(); + if let Some(for_each_sym) = method_names.first(); + if *for_each_sym == Symbol::intern("for_each"); + if let Some(did) = cx.typeck_results().type_dependent_def_id(expr.hir_id); + if is_diagnostic_assoc_item(cx, did, sym::Iterator); + // Checks the type of the first method receiver is NOT a user defined type. + if has_iter_method(cx, cx.typeck_results().expr_ty(&iter_args[0])).is_some(); + if let ExprKind::Closure(_, _, body_id, ..) = for_each_args[1].kind; + let body = cx.tcx.hir().body(body_id); + // Skip the lint if the body is not block because this is simpler than `for` loop. + // e.g. `v.iter().for_each(f)` is simpler and clearer than using `for` loop. + if let ExprKind::Block(..) = body.value.kind; + then { + let mut ret_collector = RetCollector::default(); + ret_collector.visit_expr(&body.value); + + // Skip the lint if `return` is used in `Loop` in order not to suggest using `'label`. + if ret_collector.ret_in_loop { + return; + } + + // We can't use `Applicability::MachineApplicable` when the closure contains `return` + // because `Diagnostic::multipart_suggestion` doesn't work with multiple overlapped + // spans. + let mut applicability = if ret_collector.spans.is_empty() { + Applicability::MachineApplicable + } else { + Applicability::MaybeIncorrect + }; + + let mut suggs = vec![]; + suggs.push((stmt.span, format!( + "for {} in {} {}", + snippet_with_applicability(cx, body.params[0].pat.span, "..", &mut applicability), + snippet_with_applicability(cx, for_each_args[0].span, "..", &mut applicability), + snippet_with_applicability(cx, body.value.span, "..", &mut applicability), + ))); + + for span in &ret_collector.spans { + suggs.push((*span, "return".to_string())); + } + + span_lint_and_then( + cx, + NEEDLESS_FOR_EACH, + stmt.span, + "needless use of `for_each`", + |diag| { + diag.multipart_suggestion("try", suggs, applicability); + // `Diagnostic::multipart_suggestion` ignores the second and subsequent overlapped spans, + // so `span_note` is needed here even though `suggs` includes the replacements. + for span in ret_collector.spans { + diag.span_note(span, "replace `return` with `continue`"); + } + } + ) + } + } + } +} + +/// This type plays two roles. +/// 1. Collect spans of `return` in the closure body. +/// 2. Detect use of `return` in `Loop` in the closure body. +/// +/// NOTE: The functionality of this type is similar to +/// [`crate::utilts::visitors::find_all_ret_expressions`], but we can't use +/// `find_all_ret_expressions` instead of this type. The reasons are: +/// 1. `find_all_ret_expressions` passes the argument of `ExprKind::Ret` to a callback, but what we +/// need here is `ExprKind::Ret` itself. +/// 2. We can't trace current loop depth with `find_all_ret_expressions`. +#[derive(Default)] +struct RetCollector { + spans: Vec, + ret_in_loop: bool, + loop_depth: u16, +} + +impl<'tcx> Visitor<'tcx> for RetCollector { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, expr: &Expr<'_>) { + match expr.kind { + ExprKind::Ret(..) => { + if self.loop_depth > 0 && !self.ret_in_loop { + self.ret_in_loop = true + } + + self.spans.push(expr.span) + }, + + ExprKind::Loop(..) => { + self.loop_depth += 1; + walk_expr(self, expr); + self.loop_depth -= 1; + return; + }, + + _ => {}, + } + + walk_expr(self, expr); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } +} diff --git a/tests/ui/excessive_for_each.rs b/tests/ui/excessive_for_each.rs deleted file mode 100644 index 0800bef71e9..00000000000 --- a/tests/ui/excessive_for_each.rs +++ /dev/null @@ -1,126 +0,0 @@ -#![warn(clippy::excessive_for_each)] -#![allow(clippy::needless_return)] - -use std::collections::*; - -fn main() { - // Should trigger this lint: Vec. - let vec: Vec = Vec::new(); - let mut acc = 0; - vec.iter().for_each(|v| { - acc += v; - }); - - // Should trigger this lint: &Vec. - let vec_ref = &vec; - vec_ref.iter().for_each(|v| { - acc += v; - }); - - // Should trigger this lint: VecDeque. - let vec_deq: VecDeque = VecDeque::new(); - vec_deq.iter().for_each(|v| { - acc += v; - }); - - // Should trigger this lint: LinkedList. - let list: LinkedList = LinkedList::new(); - list.iter().for_each(|v| { - acc += v; - }); - - // Should trigger this lint: HashMap. - let mut hash_map: HashMap = HashMap::new(); - hash_map.iter().for_each(|(k, v)| { - acc += k + v; - }); - hash_map.iter_mut().for_each(|(k, v)| { - acc += *k + *v; - }); - hash_map.keys().for_each(|k| { - acc += k; - }); - hash_map.values().for_each(|v| { - acc += v; - }); - - // Should trigger this lint: HashSet. - let hash_set: HashSet = HashSet::new(); - hash_set.iter().for_each(|v| { - acc += v; - }); - - // Should trigger this lint: BTreeSet. - let btree_set: BTreeSet = BTreeSet::new(); - btree_set.iter().for_each(|v| { - acc += v; - }); - - // Should trigger this lint: BinaryHeap. - let binary_heap: BinaryHeap = BinaryHeap::new(); - binary_heap.iter().for_each(|v| { - acc += v; - }); - - // Should trigger this lint: Array. - let s = [1, 2, 3]; - s.iter().for_each(|v| { - acc += v; - }); - - // Should trigger this lint. Slice. - vec.as_slice().iter().for_each(|v| { - acc += v; - }); - - // Should trigger this lint with notes that say "change `return` to `continue`". - vec.iter().for_each(|v| { - if *v == 10 { - return; - } else { - println!("{}", v); - } - }); - - // Should NOT trigger this lint in case `return` is used in `Loop` of the closure. - vec.iter().for_each(|v| { - for i in 0..*v { - if i == 10 { - return; - } else { - println!("{}", v); - } - } - if *v == 20 { - return; - } else { - println!("{}", v); - } - }); - - // Should NOT trigger this lint in case `for_each` follows long iterator chain. - vec.iter().chain(vec.iter()).for_each(|v| println!("{}", v)); - - // Should NOT trigger this lint in case a `for_each` argument is not closure. - fn print(x: &i32) { - println!("{}", x); - } - vec.iter().for_each(print); - - // Should NOT trigger this lint in case the receiver of `iter` is a user defined type. - let my_collection = MyCollection { v: vec![] }; - my_collection.iter().for_each(|v| println!("{}", v)); - - // Should NOT trigger this lint in case the closure body is not a `ExprKind::Block`. - vec.iter().for_each(|x| acc += x); -} - -struct MyCollection { - v: Vec, -} - -impl MyCollection { - fn iter(&self) -> impl Iterator { - self.v.iter() - } -} diff --git a/tests/ui/excessive_for_each.stderr b/tests/ui/excessive_for_each.stderr deleted file mode 100644 index f5799484e03..00000000000 --- a/tests/ui/excessive_for_each.stderr +++ /dev/null @@ -1,126 +0,0 @@ -error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:10:5 - | -LL | / vec.iter().for_each(|v| { -LL | | acc += v; -LL | | }); - | |______^ help: try: `for v in vec.iter() { .. }` - | - = note: `-D clippy::excessive-for-each` implied by `-D warnings` - -error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:16:5 - | -LL | / vec_ref.iter().for_each(|v| { -LL | | acc += v; -LL | | }); - | |______^ help: try: `for v in vec_ref.iter() { .. }` - -error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:22:5 - | -LL | / vec_deq.iter().for_each(|v| { -LL | | acc += v; -LL | | }); - | |______^ help: try: `for v in vec_deq.iter() { .. }` - -error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:28:5 - | -LL | / list.iter().for_each(|v| { -LL | | acc += v; -LL | | }); - | |______^ help: try: `for v in list.iter() { .. }` - -error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:34:5 - | -LL | / hash_map.iter().for_each(|(k, v)| { -LL | | acc += k + v; -LL | | }); - | |______^ help: try: `for (k, v) in hash_map.iter() { .. }` - -error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:37:5 - | -LL | / hash_map.iter_mut().for_each(|(k, v)| { -LL | | acc += *k + *v; -LL | | }); - | |______^ help: try: `for (k, v) in hash_map.iter_mut() { .. }` - -error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:40:5 - | -LL | / hash_map.keys().for_each(|k| { -LL | | acc += k; -LL | | }); - | |______^ help: try: `for k in hash_map.keys() { .. }` - -error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:43:5 - | -LL | / hash_map.values().for_each(|v| { -LL | | acc += v; -LL | | }); - | |______^ help: try: `for v in hash_map.values() { .. }` - -error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:49:5 - | -LL | / hash_set.iter().for_each(|v| { -LL | | acc += v; -LL | | }); - | |______^ help: try: `for v in hash_set.iter() { .. }` - -error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:55:5 - | -LL | / btree_set.iter().for_each(|v| { -LL | | acc += v; -LL | | }); - | |______^ help: try: `for v in btree_set.iter() { .. }` - -error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:61:5 - | -LL | / binary_heap.iter().for_each(|v| { -LL | | acc += v; -LL | | }); - | |______^ help: try: `for v in binary_heap.iter() { .. }` - -error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:67:5 - | -LL | / s.iter().for_each(|v| { -LL | | acc += v; -LL | | }); - | |______^ help: try: `for v in s.iter() { .. }` - -error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:72:5 - | -LL | / vec.as_slice().iter().for_each(|v| { -LL | | acc += v; -LL | | }); - | |______^ help: try: `for v in vec.as_slice().iter() { .. }` - -error: excessive use of `for_each` - --> $DIR/excessive_for_each.rs:77:5 - | -LL | / vec.iter().for_each(|v| { -LL | | if *v == 10 { -LL | | return; -LL | | } else { -LL | | println!("{}", v); -LL | | } -LL | | }); - | |______^ help: try: `for v in vec.iter() { .. }` - | -note: change `return` to `continue` in the loop body - --> $DIR/excessive_for_each.rs:79:13 - | -LL | return; - | ^^^^^^ - -error: aborting due to 14 previous errors - diff --git a/tests/ui/needless_for_each_fixable.fixed b/tests/ui/needless_for_each_fixable.fixed new file mode 100644 index 00000000000..0caa95a9f53 --- /dev/null +++ b/tests/ui/needless_for_each_fixable.fixed @@ -0,0 +1,99 @@ +// run-rustfix +#![warn(clippy::needless_for_each)] +#![allow(unused, clippy::needless_return, clippy::match_single_binding)] + +use std::collections::HashMap; + +fn should_lint() { + let v: Vec = Vec::new(); + let mut acc = 0; + for elem in v.iter() { + acc += elem; + } + for elem in v.into_iter() { + acc += elem; + } + + let mut hash_map: HashMap = HashMap::new(); + for (k, v) in hash_map.iter() { + acc += k + v; + } + for (k, v) in hash_map.iter_mut() { + acc += *k + *v; + } + for k in hash_map.keys() { + acc += k; + } + for v in hash_map.values() { + acc += v; + } + + fn my_vec() -> Vec { + Vec::new() + } + for elem in my_vec().iter() { + acc += elem; + } +} + +fn should_not_lint() { + let v: Vec = Vec::new(); + let mut acc = 0; + + // `for_each` argument is not closure. + fn print(x: &i32) { + println!("{}", x); + } + v.iter().for_each(print); + + // `for_each` follows long iterator chain. + v.iter().chain(v.iter()).for_each(|v| println!("{}", v)); + v.as_slice().iter().for_each(|v| { + acc += v; + }); + + // `return` is used in `Loop` of the closure. + v.iter().for_each(|v| { + for i in 0..*v { + if i == 10 { + return; + } else { + println!("{}", v); + } + } + if *v == 20 { + return; + } else { + println!("{}", v); + } + }); + + // User defined type. + struct MyStruct { + v: Vec, + } + impl MyStruct { + fn iter(&self) -> impl Iterator { + self.v.iter() + } + } + let s = MyStruct { v: Vec::new() }; + s.iter().for_each(|elem| { + acc += elem; + }); + + // Previously transformed iterator variable. + let it = v.iter(); + it.chain(v.iter()).for_each(|elem| { + acc += elem; + }); + + // `for_each` is not directly in a statement. + match 1 { + _ => v.iter().for_each(|elem| { + acc += elem; + }), + } +} + +fn main() {} diff --git a/tests/ui/needless_for_each_fixable.rs b/tests/ui/needless_for_each_fixable.rs new file mode 100644 index 00000000000..a04243de27a --- /dev/null +++ b/tests/ui/needless_for_each_fixable.rs @@ -0,0 +1,99 @@ +// run-rustfix +#![warn(clippy::needless_for_each)] +#![allow(unused, clippy::needless_return, clippy::match_single_binding)] + +use std::collections::HashMap; + +fn should_lint() { + let v: Vec = Vec::new(); + let mut acc = 0; + v.iter().for_each(|elem| { + acc += elem; + }); + v.into_iter().for_each(|elem| { + acc += elem; + }); + + let mut hash_map: HashMap = HashMap::new(); + hash_map.iter().for_each(|(k, v)| { + acc += k + v; + }); + hash_map.iter_mut().for_each(|(k, v)| { + acc += *k + *v; + }); + hash_map.keys().for_each(|k| { + acc += k; + }); + hash_map.values().for_each(|v| { + acc += v; + }); + + fn my_vec() -> Vec { + Vec::new() + } + my_vec().iter().for_each(|elem| { + acc += elem; + }); +} + +fn should_not_lint() { + let v: Vec = Vec::new(); + let mut acc = 0; + + // `for_each` argument is not closure. + fn print(x: &i32) { + println!("{}", x); + } + v.iter().for_each(print); + + // `for_each` follows long iterator chain. + v.iter().chain(v.iter()).for_each(|v| println!("{}", v)); + v.as_slice().iter().for_each(|v| { + acc += v; + }); + + // `return` is used in `Loop` of the closure. + v.iter().for_each(|v| { + for i in 0..*v { + if i == 10 { + return; + } else { + println!("{}", v); + } + } + if *v == 20 { + return; + } else { + println!("{}", v); + } + }); + + // User defined type. + struct MyStruct { + v: Vec, + } + impl MyStruct { + fn iter(&self) -> impl Iterator { + self.v.iter() + } + } + let s = MyStruct { v: Vec::new() }; + s.iter().for_each(|elem| { + acc += elem; + }); + + // Previously transformed iterator variable. + let it = v.iter(); + it.chain(v.iter()).for_each(|elem| { + acc += elem; + }); + + // `for_each` is not directly in a statement. + match 1 { + _ => v.iter().for_each(|elem| { + acc += elem; + }), + } +} + +fn main() {} diff --git a/tests/ui/needless_for_each_fixable.stderr b/tests/ui/needless_for_each_fixable.stderr new file mode 100644 index 00000000000..214e357a208 --- /dev/null +++ b/tests/ui/needless_for_each_fixable.stderr @@ -0,0 +1,108 @@ +error: needless use of `for_each` + --> $DIR/needless_for_each_fixable.rs:10:5 + | +LL | / v.iter().for_each(|elem| { +LL | | acc += elem; +LL | | }); + | |_______^ + | + = note: `-D clippy::needless-for-each` implied by `-D warnings` +help: try + | +LL | for elem in v.iter() { +LL | acc += elem; +LL | } + | + +error: needless use of `for_each` + --> $DIR/needless_for_each_fixable.rs:13:5 + | +LL | / v.into_iter().for_each(|elem| { +LL | | acc += elem; +LL | | }); + | |_______^ + | +help: try + | +LL | for elem in v.into_iter() { +LL | acc += elem; +LL | } + | + +error: needless use of `for_each` + --> $DIR/needless_for_each_fixable.rs:18:5 + | +LL | / hash_map.iter().for_each(|(k, v)| { +LL | | acc += k + v; +LL | | }); + | |_______^ + | +help: try + | +LL | for (k, v) in hash_map.iter() { +LL | acc += k + v; +LL | } + | + +error: needless use of `for_each` + --> $DIR/needless_for_each_fixable.rs:21:5 + | +LL | / hash_map.iter_mut().for_each(|(k, v)| { +LL | | acc += *k + *v; +LL | | }); + | |_______^ + | +help: try + | +LL | for (k, v) in hash_map.iter_mut() { +LL | acc += *k + *v; +LL | } + | + +error: needless use of `for_each` + --> $DIR/needless_for_each_fixable.rs:24:5 + | +LL | / hash_map.keys().for_each(|k| { +LL | | acc += k; +LL | | }); + | |_______^ + | +help: try + | +LL | for k in hash_map.keys() { +LL | acc += k; +LL | } + | + +error: needless use of `for_each` + --> $DIR/needless_for_each_fixable.rs:27:5 + | +LL | / hash_map.values().for_each(|v| { +LL | | acc += v; +LL | | }); + | |_______^ + | +help: try + | +LL | for v in hash_map.values() { +LL | acc += v; +LL | } + | + +error: needless use of `for_each` + --> $DIR/needless_for_each_fixable.rs:34:5 + | +LL | / my_vec().iter().for_each(|elem| { +LL | | acc += elem; +LL | | }); + | |_______^ + | +help: try + | +LL | for elem in my_vec().iter() { +LL | acc += elem; +LL | } + | + +error: aborting due to 7 previous errors + diff --git a/tests/ui/needless_for_each_unfixable.rs b/tests/ui/needless_for_each_unfixable.rs new file mode 100644 index 00000000000..d765d7dab65 --- /dev/null +++ b/tests/ui/needless_for_each_unfixable.rs @@ -0,0 +1,14 @@ +#![warn(clippy::needless_for_each)] +#![allow(clippy::needless_return)] + +fn main() { + let v: Vec = Vec::new(); + // This is unfixable because the closure includes `return`. + v.iter().for_each(|v| { + if *v == 10 { + return; + } else { + println!("{}", v); + } + }); +} diff --git a/tests/ui/needless_for_each_unfixable.stderr b/tests/ui/needless_for_each_unfixable.stderr new file mode 100644 index 00000000000..58d107062bc --- /dev/null +++ b/tests/ui/needless_for_each_unfixable.stderr @@ -0,0 +1,30 @@ +error: needless use of `for_each` + --> $DIR/needless_for_each_unfixable.rs:7:5 + | +LL | / v.iter().for_each(|v| { +LL | | if *v == 10 { +LL | | return; +LL | | } else { +LL | | println!("{}", v); +LL | | } +LL | | }); + | |_______^ + | + = note: `-D clippy::needless-for-each` implied by `-D warnings` +note: replace `return` with `continue` + --> $DIR/needless_for_each_unfixable.rs:9:13 + | +LL | return; + | ^^^^^^ +help: try + | +LL | for v in v.iter() { +LL | if *v == 10 { +LL | return; +LL | } else { +LL | println!("{}", v); +LL | } + ... + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From f2cc995bcfdc86a564b4040585f97f012be9454b Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Sat, 13 Mar 2021 15:11:39 +0900 Subject: Remove method_calls --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/needless_for_each.rs | 40 +++++++++++++++++-------------- tests/ui/needless_for_each_fixable.fixed | 39 ++++++++++++++++++------------ tests/ui/needless_for_each_fixable.rs | 39 ++++++++++++++++++------------ tests/ui/needless_for_each_fixable.stderr | 27 ++++++++++++++++----- 5 files changed, 92 insertions(+), 55 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 8c9247d9781..11716afe11c 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1326,7 +1326,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS), LintId::of(&missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS), LintId::of(&modulo_arithmetic::MODULO_ARITHMETIC), - LintId::of(&needless_for_each::NEEDLESS_FOR_EACH), LintId::of(&panic_in_result_fn::PANIC_IN_RESULT_FN), LintId::of(&panic_unimplemented::PANIC), LintId::of(&panic_unimplemented::TODO), @@ -1409,6 +1408,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&misc_early::UNSEPARATED_LITERAL_SUFFIX), LintId::of(&mut_mut::MUT_MUT), 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::SIMILAR_NAMES), LintId::of(&option_if_let_else::OPTION_IF_LET_ELSE), diff --git a/clippy_lints/src/needless_for_each.rs b/clippy_lints/src/needless_for_each.rs index a7b0a1ca082..f60b09898ab 100644 --- a/clippy_lints/src/needless_for_each.rs +++ b/clippy_lints/src/needless_for_each.rs @@ -10,9 +10,7 @@ use rustc_span::{source_map::Span, sym, Symbol}; use if_chain::if_chain; -use crate::utils::{ - has_iter_method, is_diagnostic_assoc_item, method_calls, snippet_with_applicability, span_lint_and_then, -}; +use crate::utils::{has_iter_method, is_trait_method, snippet_with_applicability, span_lint_and_then}; declare_clippy_lint! { /// **What it does:** Checks for usage of `for_each` that would be more simply written as a @@ -41,7 +39,7 @@ declare_clippy_lint! { /// } /// ``` pub NEEDLESS_FOR_EACH, - restriction, + pedantic, "using `for_each` where a `for` loop would be simpler" } @@ -55,22 +53,28 @@ impl LateLintPass<'_> for NeedlessForEach { _ => return, }; - // Max depth is set to 3 because we need to check the method chain length is just two. - let (method_names, arg_lists, _) = method_calls(expr, 3); - if_chain! { - // This assures the length of this method chain is two. - if let [for_each_args, iter_args] = arg_lists.as_slice(); - if let Some(for_each_sym) = method_names.first(); - if *for_each_sym == Symbol::intern("for_each"); - if let Some(did) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if is_diagnostic_assoc_item(cx, did, sym::Iterator); - // Checks the type of the first method receiver is NOT a user defined type. - if has_iter_method(cx, cx.typeck_results().expr_ty(&iter_args[0])).is_some(); - if let ExprKind::Closure(_, _, body_id, ..) = for_each_args[1].kind; - let body = cx.tcx.hir().body(body_id); + // Check the method name is `for_each`. + if let ExprKind::MethodCall(method_name, _, for_each_args, _) = expr.kind; + if method_name.ident.name == Symbol::intern("for_each"); + // Check `for_each` is an associated function of `Iterator`. + if is_trait_method(cx, expr, sym::Iterator); + // Checks the receiver of `for_each` is also a method call. + if let Some(for_each_receiver) = for_each_args.get(0); + if let ExprKind::MethodCall(_, _, iter_args, _) = for_each_receiver.kind; + // Skip the lint if the call chain is too long. e.g. `v.field.iter().for_each()` or + // `v.foo().iter().for_each()` must be skipped. + if let Some(iter_receiver) = iter_args.get(0); + if matches!( + iter_receiver.kind, + ExprKind::Array(..) | ExprKind::Call(..) | ExprKind::Path(..) + ); + // Checks the type of the `iter` method receiver is NOT a user defined type. + if has_iter_method(cx, cx.typeck_results().expr_ty(&iter_receiver)).is_some(); // Skip the lint if the body is not block because this is simpler than `for` loop. // e.g. `v.iter().for_each(f)` is simpler and clearer than using `for` loop. + if let ExprKind::Closure(_, _, body_id, ..) = for_each_args[1].kind; + let body = cx.tcx.hir().body(body_id); if let ExprKind::Block(..) = body.value.kind; then { let mut ret_collector = RetCollector::default(); @@ -99,7 +103,7 @@ impl LateLintPass<'_> for NeedlessForEach { ))); for span in &ret_collector.spans { - suggs.push((*span, "return".to_string())); + suggs.push((*span, "continue".to_string())); } span_lint_and_then( diff --git a/tests/ui/needless_for_each_fixable.fixed b/tests/ui/needless_for_each_fixable.fixed index 0caa95a9f53..a4d4937a19a 100644 --- a/tests/ui/needless_for_each_fixable.fixed +++ b/tests/ui/needless_for_each_fixable.fixed @@ -14,6 +14,10 @@ fn should_lint() { acc += elem; } + for elem in [1, 2, 3].iter() { + acc += elem; + } + let mut hash_map: HashMap = HashMap::new(); for (k, v) in hash_map.iter() { acc += k + v; @@ -46,11 +50,30 @@ fn should_not_lint() { } v.iter().for_each(print); + // User defined type. + struct MyStruct { + v: Vec, + } + impl MyStruct { + fn iter(&self) -> impl Iterator { + self.v.iter() + } + } + let s = MyStruct { v: Vec::new() }; + s.iter().for_each(|elem| { + acc += elem; + }); + // `for_each` follows long iterator chain. - v.iter().chain(v.iter()).for_each(|v| println!("{}", v)); + v.iter().chain(v.iter()).for_each(|v| { + acc += v; + }); v.as_slice().iter().for_each(|v| { acc += v; }); + s.v.iter().for_each(|v| { + acc += v; + }); // `return` is used in `Loop` of the closure. v.iter().for_each(|v| { @@ -68,20 +91,6 @@ fn should_not_lint() { } }); - // User defined type. - struct MyStruct { - v: Vec, - } - impl MyStruct { - fn iter(&self) -> impl Iterator { - self.v.iter() - } - } - let s = MyStruct { v: Vec::new() }; - s.iter().for_each(|elem| { - acc += elem; - }); - // Previously transformed iterator variable. let it = v.iter(); it.chain(v.iter()).for_each(|elem| { diff --git a/tests/ui/needless_for_each_fixable.rs b/tests/ui/needless_for_each_fixable.rs index a04243de27a..b374128f253 100644 --- a/tests/ui/needless_for_each_fixable.rs +++ b/tests/ui/needless_for_each_fixable.rs @@ -14,6 +14,10 @@ fn should_lint() { acc += elem; }); + [1, 2, 3].iter().for_each(|elem| { + acc += elem; + }); + let mut hash_map: HashMap = HashMap::new(); hash_map.iter().for_each(|(k, v)| { acc += k + v; @@ -46,11 +50,30 @@ fn should_not_lint() { } v.iter().for_each(print); + // User defined type. + struct MyStruct { + v: Vec, + } + impl MyStruct { + fn iter(&self) -> impl Iterator { + self.v.iter() + } + } + let s = MyStruct { v: Vec::new() }; + s.iter().for_each(|elem| { + acc += elem; + }); + // `for_each` follows long iterator chain. - v.iter().chain(v.iter()).for_each(|v| println!("{}", v)); + v.iter().chain(v.iter()).for_each(|v| { + acc += v; + }); v.as_slice().iter().for_each(|v| { acc += v; }); + s.v.iter().for_each(|v| { + acc += v; + }); // `return` is used in `Loop` of the closure. v.iter().for_each(|v| { @@ -68,20 +91,6 @@ fn should_not_lint() { } }); - // User defined type. - struct MyStruct { - v: Vec, - } - impl MyStruct { - fn iter(&self) -> impl Iterator { - self.v.iter() - } - } - let s = MyStruct { v: Vec::new() }; - s.iter().for_each(|elem| { - acc += elem; - }); - // Previously transformed iterator variable. let it = v.iter(); it.chain(v.iter()).for_each(|elem| { diff --git a/tests/ui/needless_for_each_fixable.stderr b/tests/ui/needless_for_each_fixable.stderr index 214e357a208..483a5e6d61d 100644 --- a/tests/ui/needless_for_each_fixable.stderr +++ b/tests/ui/needless_for_each_fixable.stderr @@ -30,7 +30,22 @@ LL | } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:18:5 + --> $DIR/needless_for_each_fixable.rs:17:5 + | +LL | / [1, 2, 3].iter().for_each(|elem| { +LL | | acc += elem; +LL | | }); + | |_______^ + | +help: try + | +LL | for elem in [1, 2, 3].iter() { +LL | acc += elem; +LL | } + | + +error: needless use of `for_each` + --> $DIR/needless_for_each_fixable.rs:22:5 | LL | / hash_map.iter().for_each(|(k, v)| { LL | | acc += k + v; @@ -45,7 +60,7 @@ LL | } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:21:5 + --> $DIR/needless_for_each_fixable.rs:25:5 | LL | / hash_map.iter_mut().for_each(|(k, v)| { LL | | acc += *k + *v; @@ -60,7 +75,7 @@ LL | } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:24:5 + --> $DIR/needless_for_each_fixable.rs:28:5 | LL | / hash_map.keys().for_each(|k| { LL | | acc += k; @@ -75,7 +90,7 @@ LL | } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:27:5 + --> $DIR/needless_for_each_fixable.rs:31:5 | LL | / hash_map.values().for_each(|v| { LL | | acc += v; @@ -90,7 +105,7 @@ LL | } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:34:5 + --> $DIR/needless_for_each_fixable.rs:38:5 | LL | / my_vec().iter().for_each(|elem| { LL | | acc += elem; @@ -104,5 +119,5 @@ LL | acc += elem; LL | } | -error: aborting due to 7 previous errors +error: aborting due to 8 previous errors -- cgit 1.4.1-3-g733a5 From 1109dc8838b8af0ad7e2b8eb3a7039c907188082 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Sat, 13 Mar 2021 15:23:57 +0900 Subject: Fix codes that make dogfood fail --- tests/lint_message_convention.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/lint_message_convention.rs b/tests/lint_message_convention.rs index 3f754c255b7..2f8989c8e11 100644 --- a/tests/lint_message_convention.rs +++ b/tests/lint_message_convention.rs @@ -89,14 +89,14 @@ fn lint_message_convention() { .filter(|message| !message.bad_lines.is_empty()) .collect(); - bad_tests.iter().for_each(|message| { + for message in &bad_tests { eprintln!( "error: the test '{}' contained the following nonconforming lines :", message.path.display() ); message.bad_lines.iter().for_each(|line| eprintln!("{}", line)); eprintln!("\n\n"); - }); + } eprintln!( "\n\n\nLint message should not start with a capital letter and should not have punctuation at the end of the message unless multiple sentences are needed." -- cgit 1.4.1-3-g733a5 From bf1e3f7a9f53dd783ddf500ee45e8c0629dd5e67 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Sun, 14 Mar 2021 10:22:28 +0900 Subject: Skip needless_for_each if an input stmt is local --- clippy_lints/src/needless_for_each.rs | 50 +++++++++++++---------------- tests/ui/needless_for_each_fixable.fixed | 5 +++ tests/ui/needless_for_each_fixable.rs | 5 +++ tests/ui/needless_for_each_unfixable.stderr | 9 +++--- 4 files changed, 37 insertions(+), 32 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/needless_for_each.rs b/clippy_lints/src/needless_for_each.rs index f60b09898ab..727937354d6 100644 --- a/clippy_lints/src/needless_for_each.rs +++ b/clippy_lints/src/needless_for_each.rs @@ -49,31 +49,28 @@ impl LateLintPass<'_> for NeedlessForEach { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { let expr = match stmt.kind { StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr, - StmtKind::Local(local) if local.init.is_some() => local.init.unwrap(), _ => return, }; if_chain! { // Check the method name is `for_each`. - if let ExprKind::MethodCall(method_name, _, for_each_args, _) = expr.kind; + if let ExprKind::MethodCall(method_name, _, [for_each_recv, for_each_arg], _) = expr.kind; if method_name.ident.name == Symbol::intern("for_each"); // Check `for_each` is an associated function of `Iterator`. if is_trait_method(cx, expr, sym::Iterator); // Checks the receiver of `for_each` is also a method call. - if let Some(for_each_receiver) = for_each_args.get(0); - if let ExprKind::MethodCall(_, _, iter_args, _) = for_each_receiver.kind; + if let ExprKind::MethodCall(_, _, [iter_recv], _) = for_each_recv.kind; // Skip the lint if the call chain is too long. e.g. `v.field.iter().for_each()` or // `v.foo().iter().for_each()` must be skipped. - if let Some(iter_receiver) = iter_args.get(0); if matches!( - iter_receiver.kind, + iter_recv.kind, ExprKind::Array(..) | ExprKind::Call(..) | ExprKind::Path(..) ); // Checks the type of the `iter` method receiver is NOT a user defined type. - if has_iter_method(cx, cx.typeck_results().expr_ty(&iter_receiver)).is_some(); + if has_iter_method(cx, cx.typeck_results().expr_ty(&iter_recv)).is_some(); // Skip the lint if the body is not block because this is simpler than `for` loop. // e.g. `v.iter().for_each(f)` is simpler and clearer than using `for` loop. - if let ExprKind::Closure(_, _, body_id, ..) = for_each_args[1].kind; + if let ExprKind::Closure(_, _, body_id, ..) = for_each_arg.kind; let body = cx.tcx.hir().body(body_id); if let ExprKind::Block(..) = body.value.kind; then { @@ -85,26 +82,27 @@ impl LateLintPass<'_> for NeedlessForEach { return; } - // We can't use `Applicability::MachineApplicable` when the closure contains `return` - // because `Diagnostic::multipart_suggestion` doesn't work with multiple overlapped - // spans. - let mut applicability = if ret_collector.spans.is_empty() { - Applicability::MachineApplicable + let (mut applicability, ret_suggs) = if ret_collector.spans.is_empty() { + (Applicability::MachineApplicable, None) } else { - Applicability::MaybeIncorrect + ( + Applicability::MaybeIncorrect, + Some( + ret_collector + .spans + .into_iter() + .map(|span| (span, "continue".to_string())) + .collect(), + ), + ) }; - let mut suggs = vec![]; - suggs.push((stmt.span, format!( + let sugg = format!( "for {} in {} {}", snippet_with_applicability(cx, body.params[0].pat.span, "..", &mut applicability), - snippet_with_applicability(cx, for_each_args[0].span, "..", &mut applicability), + snippet_with_applicability(cx, for_each_recv.span, "..", &mut applicability), snippet_with_applicability(cx, body.value.span, "..", &mut applicability), - ))); - - for span in &ret_collector.spans { - suggs.push((*span, "continue".to_string())); - } + ); span_lint_and_then( cx, @@ -112,11 +110,9 @@ impl LateLintPass<'_> for NeedlessForEach { stmt.span, "needless use of `for_each`", |diag| { - diag.multipart_suggestion("try", suggs, applicability); - // `Diagnostic::multipart_suggestion` ignores the second and subsequent overlapped spans, - // so `span_note` is needed here even though `suggs` includes the replacements. - for span in ret_collector.spans { - diag.span_note(span, "replace `return` with `continue`"); + diag.span_suggestion(stmt.span, "try", sugg, applicability); + if let Some(ret_suggs) = ret_suggs { + diag.multipart_suggestion("try replacing `return` with `continue`", ret_suggs, applicability); } } ) diff --git a/tests/ui/needless_for_each_fixable.fixed b/tests/ui/needless_for_each_fixable.fixed index a4d4937a19a..f00f9ee4c33 100644 --- a/tests/ui/needless_for_each_fixable.fixed +++ b/tests/ui/needless_for_each_fixable.fixed @@ -103,6 +103,11 @@ fn should_not_lint() { acc += elem; }), } + + // `for_each` is in a let bingind. + let _ = v.iter().for_each(|elem| { + acc += elem; + }); } fn main() {} diff --git a/tests/ui/needless_for_each_fixable.rs b/tests/ui/needless_for_each_fixable.rs index b374128f253..1bd400d348b 100644 --- a/tests/ui/needless_for_each_fixable.rs +++ b/tests/ui/needless_for_each_fixable.rs @@ -103,6 +103,11 @@ fn should_not_lint() { acc += elem; }), } + + // `for_each` is in a let bingind. + let _ = v.iter().for_each(|elem| { + acc += elem; + }); } fn main() {} diff --git a/tests/ui/needless_for_each_unfixable.stderr b/tests/ui/needless_for_each_unfixable.stderr index 58d107062bc..bbb63fd8deb 100644 --- a/tests/ui/needless_for_each_unfixable.stderr +++ b/tests/ui/needless_for_each_unfixable.stderr @@ -11,11 +11,6 @@ LL | | }); | |_______^ | = note: `-D clippy::needless-for-each` implied by `-D warnings` -note: replace `return` with `continue` - --> $DIR/needless_for_each_unfixable.rs:9:13 - | -LL | return; - | ^^^^^^ help: try | LL | for v in v.iter() { @@ -25,6 +20,10 @@ LL | } else { LL | println!("{}", v); LL | } ... +help: try replacing `return` with `continue` + | +LL | continue; + | ^^^^^^^^ error: aborting due to previous error -- cgit 1.4.1-3-g733a5 From e61f9782c805f246394f870902140bf32c897b7e Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Mon, 15 Mar 2021 12:01:39 +0900 Subject: Tweak a suggestion message of needless_for_each --- clippy_lints/src/needless_for_each.rs | 21 +++++++++------------ tests/ui/needless_for_each_unfixable.stderr | 2 +- 2 files changed, 10 insertions(+), 13 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/needless_for_each.rs b/clippy_lints/src/needless_for_each.rs index 727937354d6..2ea871990f1 100644 --- a/clippy_lints/src/needless_for_each.rs +++ b/clippy_lints/src/needless_for_each.rs @@ -10,7 +10,10 @@ use rustc_span::{source_map::Span, sym, Symbol}; use if_chain::if_chain; -use crate::utils::{has_iter_method, is_trait_method, snippet_with_applicability, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_trait_method; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::has_iter_method; declare_clippy_lint! { /// **What it does:** Checks for usage of `for_each` that would be more simply written as a @@ -104,18 +107,12 @@ impl LateLintPass<'_> for NeedlessForEach { snippet_with_applicability(cx, body.value.span, "..", &mut applicability), ); - span_lint_and_then( - cx, - NEEDLESS_FOR_EACH, - stmt.span, - "needless use of `for_each`", - |diag| { - diag.span_suggestion(stmt.span, "try", sugg, applicability); - if let Some(ret_suggs) = ret_suggs { - diag.multipart_suggestion("try replacing `return` with `continue`", ret_suggs, applicability); - } + span_lint_and_then(cx, NEEDLESS_FOR_EACH, stmt.span, "needless use of `for_each`", |diag| { + diag.span_suggestion(stmt.span, "try", sugg, applicability); + if let Some(ret_suggs) = ret_suggs { + diag.multipart_suggestion("...and replace `return` with `continue`", ret_suggs, applicability); } - ) + }) } } } diff --git a/tests/ui/needless_for_each_unfixable.stderr b/tests/ui/needless_for_each_unfixable.stderr index bbb63fd8deb..8c4507d2328 100644 --- a/tests/ui/needless_for_each_unfixable.stderr +++ b/tests/ui/needless_for_each_unfixable.stderr @@ -20,7 +20,7 @@ LL | } else { LL | println!("{}", v); LL | } ... -help: try replacing `return` with `continue` +help: ...and replace `return` with `continue` | LL | continue; | ^^^^^^^^ -- cgit 1.4.1-3-g733a5 From 56fbbf7b8fc7c99a6c54dfff6847770a4ed27eb1 Mon Sep 17 00:00:00 2001 From: Eric Loren Date: Sat, 14 Nov 2020 19:47:17 -0500 Subject: Suggest `flatten` instead of `is_some` -> `unwrap` --- CHANGELOG.md | 1 + README.md | 2 +- clippy_lints/src/lib.rs | 3 + clippy_lints/src/methods/filter_map.rs | 213 ++++++++++++++++++++++----------- clippy_lints/src/methods/mod.rs | 27 ++++- tests/ui/option_filter_map.fixed | 23 ++++ tests/ui/option_filter_map.rs | 25 ++++ tests/ui/option_filter_map.stderr | 56 +++++++++ 8 files changed, 280 insertions(+), 70 deletions(-) create mode 100644 tests/ui/option_filter_map.fixed create mode 100644 tests/ui/option_filter_map.rs create mode 100644 tests/ui/option_filter_map.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 57996802182..47af42e14ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2389,6 +2389,7 @@ Released 2018-09-13 [`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref [`option_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref [`option_env_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_env_unwrap +[`option_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_filter_map [`option_if_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_if_let_else [`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none [`option_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn diff --git a/README.md b/README.md index 63057609bb6..8c0c16c443d 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are over 400 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are over 450 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category. diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f013613119c..82abd4803db 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -804,6 +804,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &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, @@ -1596,6 +1597,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), @@ -1891,6 +1893,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&methods::MANUAL_FILTER_MAP), LintId::of(&methods::MANUAL_FIND_MAP), 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::SUSPICIOUS_MAP), diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs index 2cb476acb2b..68f8480dc51 100644 --- a/clippy_lints/src/methods/filter_map.rs +++ b/clippy_lints/src/methods/filter_map.rs @@ -1,87 +1,166 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet; -use clippy_utils::{is_trait_method, path_to_local_id, SpanlessEq}; +use clippy_utils::source::{indent_of, reindent_multiline, snippet}; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{is_trait_method, path_to_local_id, remove_blocks, SpanlessEq}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::{Expr, ExprKind, PatKind, UnOp}; +use rustc_hir::def::Res; +use rustc_hir::{Expr, ExprKind, PatKind, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::ty::TyS; -use rustc_span::symbol::sym; +use rustc_span::source_map::Span; +use rustc_span::symbol::{sym, Symbol}; +use std::borrow::Cow; use super::MANUAL_FILTER_MAP; use super::MANUAL_FIND_MAP; +use super::OPTION_FILTER_MAP; + +fn is_method<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Symbol) -> bool { + match &expr.kind { + hir::ExprKind::Path(QPath::TypeRelative(_, ref mname)) => mname.ident.name == method_name, + hir::ExprKind::Path(QPath::Resolved(_, segments)) => { + segments.segments.last().unwrap().ident.name == method_name + }, + hir::ExprKind::Closure(_, _, c, _, _) => { + let body = cx.tcx.hir().body(*c); + let closure_expr = remove_blocks(&body.value); + let arg_id = body.params[0].pat.hir_id; + match closure_expr.kind { + hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, _, ref args, _) => { + if_chain! { + if ident.name == method_name; + if let hir::ExprKind::Path(path) = &args[0].kind; + if let Res::Local(ref local) = cx.qpath_res(path, args[0].hir_id); + then { + return arg_id == *local + } + } + false + }, + _ => false, + } + }, + _ => false, + } +} + +fn is_option_filter_map<'tcx>( + cx: &LateContext<'tcx>, + filter_arg: &'tcx hir::Expr<'_>, + map_arg: &'tcx hir::Expr<'_>, +) -> bool { + is_method(cx, map_arg, sym::unwrap) && is_method(cx, filter_arg, sym!(is_some)) +} + +/// lint use of `filter().map()` for `Iterators` +fn lint_filter_some_map_unwrap<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'_>, + filter_recv: &'tcx hir::Expr<'_>, + filter_arg: &'tcx hir::Expr<'_>, + map_arg: &'tcx hir::Expr<'_>, + target_span: Span, + methods_span: Span, +) { + let iterator = is_trait_method(cx, expr, sym::Iterator); + let option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&filter_recv), sym::option_type); + if (iterator || option) && is_option_filter_map(cx, filter_arg, map_arg) { + let msg = "`filter` for `Some` followed by `unwrap`"; + let help = "consider using `flatten` instead"; + let sugg = format!( + "{}", + reindent_multiline(Cow::Borrowed("flatten()"), true, indent_of(cx, target_span),) + ); + span_lint_and_sugg( + cx, + OPTION_FILTER_MAP, + methods_span, + msg, + help, + sugg, + Applicability::MachineApplicable, + ); + } +} /// lint use of `filter().map()` or `find().map()` for `Iterators` -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, is_find: bool) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, is_find: bool, target_span: Span) { if_chain! { - if let ExprKind::MethodCall(_, _, [map_recv, map_arg], map_span) = expr.kind; - if let ExprKind::MethodCall(_, _, [_, filter_arg], filter_span) = map_recv.kind; - if is_trait_method(cx, map_recv, sym::Iterator); + if let ExprKind::MethodCall(_, _, [map_recv, map_arg], map_span) = expr.kind; + if let ExprKind::MethodCall(_, _, [filter_recv, filter_arg], filter_span) = map_recv.kind; + then { + lint_filter_some_map_unwrap(cx, expr, filter_recv, filter_arg, + map_arg, target_span, filter_span.to(map_span)); + if_chain! { + if is_trait_method(cx, map_recv, sym::Iterator); - // filter(|x| ...is_some())... - if let ExprKind::Closure(_, _, filter_body_id, ..) = filter_arg.kind; - let filter_body = cx.tcx.hir().body(filter_body_id); - if let [filter_param] = filter_body.params; - // optional ref pattern: `filter(|&x| ..)` - let (filter_pat, is_filter_param_ref) = if let PatKind::Ref(ref_pat, _) = filter_param.pat.kind { - (ref_pat, true) - } else { - (filter_param.pat, false) - }; - // closure ends with is_some() or is_ok() - if let PatKind::Binding(_, filter_param_id, _, None) = filter_pat.kind; - if let ExprKind::MethodCall(path, _, [filter_arg], _) = filter_body.value.kind; - if let Some(opt_ty) = cx.typeck_results().expr_ty(filter_arg).ty_adt_def(); - if let Some(is_result) = if cx.tcx.is_diagnostic_item(sym::option_type, opt_ty.did) { - Some(false) - } else if cx.tcx.is_diagnostic_item(sym::result_type, opt_ty.did) { - Some(true) - } else { - None - }; - if path.ident.name.as_str() == if is_result { "is_ok" } else { "is_some" }; + // filter(|x| ...is_some())... + if let ExprKind::Closure(_, _, filter_body_id, ..) = filter_arg.kind; + let filter_body = cx.tcx.hir().body(filter_body_id); + if let [filter_param] = filter_body.params; + // optional ref pattern: `filter(|&x| ..)` + let (filter_pat, is_filter_param_ref) = if let PatKind::Ref(ref_pat, _) = filter_param.pat.kind { + (ref_pat, true) + } else { + (filter_param.pat, false) + }; + // closure ends with is_some() or is_ok() + if let PatKind::Binding(_, filter_param_id, _, None) = filter_pat.kind; + if let ExprKind::MethodCall(path, _, [filter_arg], _) = filter_body.value.kind; + if let Some(opt_ty) = cx.typeck_results().expr_ty(filter_arg).ty_adt_def(); + if let Some(is_result) = if cx.tcx.is_diagnostic_item(sym::option_type, opt_ty.did) { + Some(false) + } else if cx.tcx.is_diagnostic_item(sym::result_type, opt_ty.did) { + Some(true) + } else { + None + }; + if path.ident.name.as_str() == if is_result { "is_ok" } else { "is_some" }; - // ...map(|x| ...unwrap()) - if let ExprKind::Closure(_, _, map_body_id, ..) = map_arg.kind; - let map_body = cx.tcx.hir().body(map_body_id); - if let [map_param] = map_body.params; - if let PatKind::Binding(_, map_param_id, map_param_ident, None) = map_param.pat.kind; - // closure ends with expect() or unwrap() - if let ExprKind::MethodCall(seg, _, [map_arg, ..], _) = map_body.value.kind; - if matches!(seg.ident.name, sym::expect | sym::unwrap | sym::unwrap_or); + // ...map(|x| ...unwrap()) + if let ExprKind::Closure(_, _, map_body_id, ..) = map_arg.kind; + let map_body = cx.tcx.hir().body(map_body_id); + if let [map_param] = map_body.params; + if let PatKind::Binding(_, map_param_id, map_param_ident, None) = map_param.pat.kind; + // closure ends with expect() or unwrap() + if let ExprKind::MethodCall(seg, _, [map_arg, ..], _) = map_body.value.kind; + if matches!(seg.ident.name, sym::expect | sym::unwrap | sym::unwrap_or); - let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| { - // in `filter(|x| ..)`, replace `*x` with `x` - let a_path = if_chain! { - if !is_filter_param_ref; - if let ExprKind::Unary(UnOp::Deref, expr_path) = a.kind; - then { expr_path } else { a } - }; - // let the filter closure arg and the map closure arg be equal - if_chain! { - if path_to_local_id(a_path, filter_param_id); - if path_to_local_id(b, map_param_id); - if TyS::same_type(cx.typeck_results().expr_ty_adjusted(a), cx.typeck_results().expr_ty_adjusted(b)); - then { - return true; + let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| { + // in `filter(|x| ..)`, replace `*x` with `x` + let a_path = if_chain! { + if !is_filter_param_ref; + if let ExprKind::Unary(UnOp::Deref, expr_path) = a.kind; + then { expr_path } else { a } + }; + // let the filter closure arg and the map closure arg be equal + if_chain! { + if path_to_local_id(a_path, filter_param_id); + if path_to_local_id(b, map_param_id); + if TyS::same_type(cx.typeck_results().expr_ty_adjusted(a), cx.typeck_results().expr_ty_adjusted(b)); + then { + return true; + } } - } - false - }; - if SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg); - then { - let span = filter_span.to(map_span); - let (filter_name, lint) = if is_find { - ("find", MANUAL_FIND_MAP) - } else { - ("filter", MANUAL_FILTER_MAP) + false }; - let msg = format!("`{}(..).map(..)` can be simplified as `{0}_map(..)`", filter_name); - let to_opt = if is_result { ".ok()" } else { "" }; - let sugg = format!("{}_map(|{}| {}{})", filter_name, map_param_ident, - snippet(cx, map_arg.span, ".."), to_opt); - span_lint_and_sugg(cx, lint, span, &msg, "try", sugg, Applicability::MachineApplicable); + if SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg); + then { + let span = filter_span.to(map_span); + let (filter_name, lint) = if is_find { + ("find", MANUAL_FIND_MAP) + } else { + ("filter", MANUAL_FILTER_MAP) + }; + let msg = format!("`{}(..).map(..)` can be simplified as `{0}_map(..)`", filter_name); + let to_opt = if is_result { ".ok()" } else { "" }; + let sugg = format!("{}_map(|{}| {}{})", filter_name, map_param_ident, + snippet(cx, map_arg.span, ".."), to_opt); + span_lint_and_sugg(cx, lint, span, &msg, "try", sugg, Applicability::MachineApplicable); + } + } } } } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index fccdee07877..8a04fd0060d 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -896,6 +896,28 @@ declare_clippy_lint! { "using `Iterator::step_by(0)`, which will panic at runtime" } +declare_clippy_lint! { + /// **What it does:** Checks for indirect collection of populated `Option` + /// + /// **Why is this bad?** `Option` is like a collection of 0-1 things, so `flatten` + /// automatically does this without suspicious-looking `unwrap` calls. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let _ = std::iter::empty::>().filter(Option::is_some).map(Option::unwrap); + /// ``` + /// Use instead: + /// ```rust + /// let _ = std::iter::empty::>().flatten(); + /// ``` + pub OPTION_FILTER_MAP, + complexity, + "filtering `Option` for `Some` then force-unwrapping, which can be one type-safe operation" +} + declare_clippy_lint! { /// **What it does:** Checks for the use of `iter.nth(0)`. /// @@ -1651,6 +1673,7 @@ impl_lint_pass!(Methods => [ FILTER_MAP_IDENTITY, MANUAL_FILTER_MAP, MANUAL_FIND_MAP, + OPTION_FILTER_MAP, FILTER_MAP_NEXT, FLAT_MAP_IDENTITY, MAP_FLATTEN, @@ -1720,10 +1743,10 @@ impl<'tcx> LateLintPass<'tcx> for Methods { ["next", "filter"] => filter_next::check(cx, expr, arg_lists[1]), ["next", "skip_while"] => skip_while_next::check(cx, expr, arg_lists[1]), ["next", "iter"] => iter_next_slice::check(cx, expr, arg_lists[1]), - ["map", "filter"] => filter_map::check(cx, expr, false), + ["map", "filter"] => filter_map::check(cx, expr, false, method_spans[0]), ["map", "filter_map"] => filter_map_map::check(cx, expr), ["next", "filter_map"] => filter_map_next::check(cx, expr, arg_lists[1], self.msrv.as_ref()), - ["map", "find"] => filter_map::check(cx, expr, true), + ["map", "find"] => filter_map::check(cx, expr, true, method_spans[0]), ["flat_map", "filter"] => filter_flat_map::check(cx, expr), ["flat_map", "filter_map"] => filter_map_flat_map::check(cx, expr), ["flat_map", ..] => flat_map_identity::check(cx, expr, arg_lists[0], method_spans[0]), diff --git a/tests/ui/option_filter_map.fixed b/tests/ui/option_filter_map.fixed new file mode 100644 index 00000000000..f9d1825ade0 --- /dev/null +++ b/tests/ui/option_filter_map.fixed @@ -0,0 +1,23 @@ +#![warn(clippy::option_filter_map)] +// run-rustfix +fn odds_out(x: i32) -> Option { + if x % 2 == 0 { Some(x) } else { None } +} + +fn main() { + let _ = Some(Some(1)).flatten(); + let _ = Some(Some(1)).flatten(); + let _ = Some(1).map(odds_out).flatten(); + let _ = Some(1).map(odds_out).flatten(); + + let _ = vec![Some(1)].into_iter().flatten(); + let _ = vec![Some(1)].into_iter().flatten(); + let _ = vec![1] + .into_iter() + .map(odds_out) + .flatten(); + let _ = vec![1] + .into_iter() + .map(odds_out) + .flatten(); +} diff --git a/tests/ui/option_filter_map.rs b/tests/ui/option_filter_map.rs new file mode 100644 index 00000000000..588e1ccccce --- /dev/null +++ b/tests/ui/option_filter_map.rs @@ -0,0 +1,25 @@ +#![warn(clippy::option_filter_map)] +// run-rustfix +fn odds_out(x: i32) -> Option { + if x % 2 == 0 { Some(x) } else { None } +} + +fn main() { + let _ = Some(Some(1)).filter(Option::is_some).map(Option::unwrap); + let _ = Some(Some(1)).filter(|o| o.is_some()).map(|o| o.unwrap()); + let _ = Some(1).map(odds_out).filter(Option::is_some).map(Option::unwrap); + let _ = Some(1).map(odds_out).filter(|o| o.is_some()).map(|o| o.unwrap()); + + let _ = vec![Some(1)].into_iter().filter(Option::is_some).map(Option::unwrap); + let _ = vec![Some(1)].into_iter().filter(|o| o.is_some()).map(|o| o.unwrap()); + let _ = vec![1] + .into_iter() + .map(odds_out) + .filter(Option::is_some) + .map(Option::unwrap); + let _ = vec![1] + .into_iter() + .map(odds_out) + .filter(|o| o.is_some()) + .map(|o| o.unwrap()); +} diff --git a/tests/ui/option_filter_map.stderr b/tests/ui/option_filter_map.stderr new file mode 100644 index 00000000000..31a82969d5a --- /dev/null +++ b/tests/ui/option_filter_map.stderr @@ -0,0 +1,56 @@ +error: `filter` for `Some` followed by `unwrap` + --> $DIR/option_filter_map.rs:8:27 + | +LL | let _ = Some(Some(1)).filter(Option::is_some).map(Option::unwrap); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` + | + = note: `-D clippy::option-filter-map` implied by `-D warnings` + +error: `filter` for `Some` followed by `unwrap` + --> $DIR/option_filter_map.rs:9:27 + | +LL | let _ = Some(Some(1)).filter(|o| o.is_some()).map(|o| o.unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` + +error: `filter` for `Some` followed by `unwrap` + --> $DIR/option_filter_map.rs:10:35 + | +LL | let _ = Some(1).map(odds_out).filter(Option::is_some).map(Option::unwrap); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` + +error: `filter` for `Some` followed by `unwrap` + --> $DIR/option_filter_map.rs:11:35 + | +LL | let _ = Some(1).map(odds_out).filter(|o| o.is_some()).map(|o| o.unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` + +error: `filter` for `Some` followed by `unwrap` + --> $DIR/option_filter_map.rs:13:39 + | +LL | let _ = vec![Some(1)].into_iter().filter(Option::is_some).map(Option::unwrap); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` + +error: `filter` for `Some` followed by `unwrap` + --> $DIR/option_filter_map.rs:14:39 + | +LL | let _ = vec![Some(1)].into_iter().filter(|o| o.is_some()).map(|o| o.unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()` + +error: `filter` for `Some` followed by `unwrap` + --> $DIR/option_filter_map.rs:18:10 + | +LL | .filter(Option::is_some) + | __________^ +LL | | .map(Option::unwrap); + | |____________________________^ help: consider using `flatten` instead: `flatten()` + +error: `filter` for `Some` followed by `unwrap` + --> $DIR/option_filter_map.rs:23:10 + | +LL | .filter(|o| o.is_some()) + | __________^ +LL | | .map(|o| o.unwrap()); + | |____________________________^ help: consider using `flatten` instead: `flatten()` + +error: aborting due to 8 previous errors + -- cgit 1.4.1-3-g733a5 From 5f887d09b87d7c6a9c3a4f6afa62a20847dd2509 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 30 Mar 2021 13:35:30 -0500 Subject: Add if_chain lints --- clippy_lints/src/lib.rs | 4 + clippy_lints/src/utils/internal_lints.rs | 177 ++++++++++++++++++++++++++++++- tests/ui-internal/if_chain_style.rs | 92 ++++++++++++++++ tests/ui-internal/if_chain_style.stderr | 85 +++++++++++++++ 4 files changed, 353 insertions(+), 5 deletions(-) create mode 100644 tests/ui-internal/if_chain_style.rs create mode 100644 tests/ui-internal/if_chain_style.stderr (limited to 'tests') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3e0a3e3ef99..f5941977dd1 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -550,6 +550,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: #[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, @@ -1026,6 +1028,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box utils::inspector::DeepCodeInspector); store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls); store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new()); + store.register_late_pass(|| box utils::internal_lints::IfChainStyle); store.register_late_pass(|| box utils::internal_lints::InvalidPaths); store.register_late_pass(|| box utils::internal_lints::InterningDefinedSymbol::default()); store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default()); @@ -1442,6 +1445,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index c496ff1fb24..266b88beeec 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -1,8 +1,10 @@ use crate::consts::{constant_simple, Constant}; -use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet; use clippy_utils::ty::match_type; -use clippy_utils::{is_expn_of, match_def_path, match_qpath, method_calls, path_to_res, paths, run_lints, SpanlessEq}; +use clippy_utils::{ + is_else_clause, is_expn_of, match_def_path, match_qpath, method_calls, path_to_res, paths, run_lints, SpanlessEq, +}; use if_chain::if_chain; use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, ModKind, NodeId}; use rustc_ast::visit::FnKind; @@ -14,15 +16,17 @@ use rustc_hir::def_id::DefId; use rustc_hir::hir_id::CRATE_HIR_ID; use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; use rustc_hir::{ - BinOpKind, Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Node, Path, StmtKind, Ty, TyKind, UnOp, + BinOpKind, Block, Crate, Expr, ExprKind, HirId, Item, Local, MatchSource, MutTy, Mutability, Node, Path, Stmt, + StmtKind, Ty, TyKind, UnOp, }; -use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::mir::interpret::ConstValue; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; -use rustc_span::source_map::{Span, Spanned}; +use rustc_span::source_map::Spanned; use rustc_span::symbol::{Symbol, SymbolStr}; +use rustc_span::{BytePos, Span}; use rustc_typeck::hir_ty_to_ty; use std::borrow::{Borrow, Cow}; @@ -297,6 +301,13 @@ declare_clippy_lint! { "unnecessary conversion between Symbol and string" } +declare_clippy_lint! { + /// Finds unidiomatic usage of `if_chain!` + pub IF_CHAIN_STYLE, + internal, + "non-idiomatic `if_chain!` usage" +} + declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]); impl EarlyLintPass for ClippyLintsInternal { @@ -1063,3 +1074,159 @@ impl<'tcx> SymbolStrExpr<'tcx> { } } } + +declare_lint_pass!(IfChainStyle => [IF_CHAIN_STYLE]); + +impl<'tcx> LateLintPass<'tcx> for IfChainStyle { + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { + let (local, after, if_chain_span) = if_chain! { + if let [Stmt { kind: StmtKind::Local(local), .. }, after @ ..] = block.stmts; + if let Some(if_chain_span) = is_expn_of(block.span, "if_chain"); + then { (local, after, if_chain_span) } else { return } + }; + if is_first_if_chain_expr(cx, block.hir_id, if_chain_span) { + span_lint( + cx, + IF_CHAIN_STYLE, + if_chain_local_span(cx, local, if_chain_span), + "`let` expression should be above the `if_chain!`", + ); + } else if local.span.ctxt() == block.span.ctxt() && is_if_chain_then(after, block.expr, if_chain_span) { + span_lint( + cx, + IF_CHAIN_STYLE, + if_chain_local_span(cx, local, if_chain_span), + "`let` expression should be inside `then { .. }`", + ) + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + let (cond, then, els) = match expr.kind { + ExprKind::If(cond, then, els) => (Some(cond), then, els.is_some()), + ExprKind::Match( + _, + [arm, ..], + MatchSource::IfLetDesugar { + contains_else_clause: els, + }, + ) => (None, arm.body, els), + _ => return, + }; + let then_block = match then.kind { + ExprKind::Block(block, _) => block, + _ => return, + }; + let if_chain_span = is_expn_of(expr.span, "if_chain"); + if !els { + check_nested_if_chains(cx, expr, then_block, if_chain_span); + } + let if_chain_span = match if_chain_span { + None => return, + Some(span) => span, + }; + // check for `if a && b;` + if_chain! { + if let Some(cond) = cond; + if let ExprKind::Binary(op, _, _) = cond.kind; + if op.node == BinOpKind::And; + if cx.sess().source_map().is_multiline(cond.span); + then { + span_lint(cx, IF_CHAIN_STYLE, cond.span, "`if a && b;` should be `if a; if b;`"); + } + } + if is_first_if_chain_expr(cx, expr.hir_id, if_chain_span) + && is_if_chain_then(then_block.stmts, then_block.expr, if_chain_span) + { + span_lint(cx, IF_CHAIN_STYLE, expr.span, "`if_chain!` only has one `if`") + } + } +} + +fn check_nested_if_chains( + cx: &LateContext<'_>, + if_expr: &Expr<'_>, + then_block: &Block<'_>, + if_chain_span: Option, +) { + #[rustfmt::skip] + let (head, tail) = match *then_block { + Block { stmts, expr: Some(tail), .. } => (stmts, tail), + Block { + stmts: &[ + ref head @ .., + Stmt { kind: StmtKind::Expr(tail) | StmtKind::Semi(tail), .. } + ], + .. + } => (head, tail), + _ => return, + }; + if_chain! { + if matches!(tail.kind, + ExprKind::If(_, _, None) + | ExprKind::Match(.., MatchSource::IfLetDesugar { contains_else_clause: false })); + let sm = cx.sess().source_map(); + if head + .iter() + .all(|stmt| matches!(stmt.kind, StmtKind::Local(..)) && !sm.is_multiline(stmt.span)); + if if_chain_span.is_some() || !is_else_clause(cx.tcx, if_expr); + then {} else { return } + } + let (span, msg) = match (if_chain_span, is_expn_of(tail.span, "if_chain")) { + (None, Some(_)) => (if_expr.span, "this `if` can be part of the inner `if_chain!`"), + (Some(_), None) => (tail.span, "this `if` can be part of the outer `if_chain!`"), + (Some(a), Some(b)) if a != b => (b, "this `if_chain!` can be merged with the outer `if_chain!`"), + _ => return, + }; + span_lint_and_then(cx, IF_CHAIN_STYLE, span, msg, |diag| { + let (span, msg) = match head { + [] => return, + [stmt] => (stmt.span, "this `let` statement can also be in the `if_chain!`"), + [a, .., b] => ( + a.span.to(b.span), + "these `let` statements can also be in the `if_chain!`", + ), + }; + diag.span_help(span, msg); + }); +} + +fn is_first_if_chain_expr(cx: &LateContext<'_>, hir_id: HirId, if_chain_span: Span) -> bool { + cx.tcx + .hir() + .parent_iter(hir_id) + .find(|(_, node)| { + #[rustfmt::skip] + !matches!(node, Node::Expr(Expr { kind: ExprKind::Block(..), .. }) | Node::Stmt(_)) + }) + .map_or(false, |(id, _)| { + is_expn_of(cx.tcx.hir().span(id), "if_chain") != Some(if_chain_span) + }) +} + +/// Checks a trailing slice of statements and expression of a `Block` to see if they are part +/// of the `then {..}` portion of an `if_chain!` +fn is_if_chain_then(stmts: &[Stmt<'_>], expr: Option<&Expr<'_>>, if_chain_span: Span) -> bool { + let span = if let [stmt, ..] = stmts { + stmt.span + } else if let Some(expr) = expr { + expr.span + } else { + // empty `then {}` + return true; + }; + is_expn_of(span, "if_chain").map_or(true, |span| span != if_chain_span) +} + +/// Creates a `Span` for `let x = ..;` in an `if_chain!` call. +fn if_chain_local_span(cx: &LateContext<'_>, local: &Local<'_>, if_chain_span: Span) -> Span { + let mut span = local.pat.span; + if let Some(init) = local.init { + span = span.to(init.span); + } + span.adjust(if_chain_span.ctxt().outer_expn()); + let sm = cx.sess().source_map(); + let span = sm.span_extend_to_prev_str(span, "let", false); + let span = sm.span_extend_to_next_char(span, ';', false); + Span::new(span.lo() - BytePos(3), span.hi() + BytePos(1), span.ctxt()) +} diff --git a/tests/ui-internal/if_chain_style.rs b/tests/ui-internal/if_chain_style.rs new file mode 100644 index 00000000000..8e871707aa8 --- /dev/null +++ b/tests/ui-internal/if_chain_style.rs @@ -0,0 +1,92 @@ +#![warn(clippy::if_chain_style)] +#![allow(clippy::no_effect)] + +extern crate if_chain; + +use if_chain::if_chain; + +fn main() { + if true { + let x = ""; + // `if_chain!` inside `if` + if_chain! { + if true; + if true; + then {} + } + } + if_chain! { + if true + // multi-line AND'ed conditions + && false; + if let Some(1) = Some(1); + // `let` before `then` + let x = ""; + then { + (); + } + } + if_chain! { + // single `if` condition + if true; + then { + let x = ""; + // nested if + if true {} + } + } + if_chain! { + // starts with `let ..` + let x = ""; + if let Some(1) = Some(1); + then { + let x = ""; + let x = ""; + // nested if_chain! + if_chain! { + if true; + if true; + then {} + } + } + } +} + +fn negative() { + if true { + (); + if_chain! { + if true; + if true; + then { (); } + } + } + if_chain! { + if true; + let x = ""; + if true; + then { (); } + } + if_chain! { + if true; + if true; + then { + if true { 1 } else { 2 } + } else { + 3 + } + }; + if true { + if_chain! { + if true; + if true; + then {} + } + } else if false { + if_chain! { + if true; + if false; + then {} + } + } +} diff --git a/tests/ui-internal/if_chain_style.stderr b/tests/ui-internal/if_chain_style.stderr new file mode 100644 index 00000000000..b53c3ea05da --- /dev/null +++ b/tests/ui-internal/if_chain_style.stderr @@ -0,0 +1,85 @@ +error: this `if` can be part of the inner `if_chain!` + --> $DIR/if_chain_style.rs:9:5 + | +LL | / if true { +LL | | let x = ""; +LL | | // `if_chain!` inside `if` +LL | | if_chain! { +... | +LL | | } +LL | | } + | |_____^ + | + = note: `-D clippy::if-chain-style` implied by `-D warnings` +help: this `let` statement can also be in the `if_chain!` + --> $DIR/if_chain_style.rs:10:9 + | +LL | let x = ""; + | ^^^^^^^^^^^ + +error: `if a && b;` should be `if a; if b;` + --> $DIR/if_chain_style.rs:19:12 + | +LL | if true + | ____________^ +LL | | // multi-line AND'ed conditions +LL | | && false; + | |____________________^ + +error: `let` expression should be inside `then { .. }` + --> $DIR/if_chain_style.rs:24:9 + | +LL | let x = ""; + | ^^^^^^^^^^^ + +error: this `if` can be part of the outer `if_chain!` + --> $DIR/if_chain_style.rs:35:13 + | +LL | if true {} + | ^^^^^^^^^^ + | +help: this `let` statement can also be in the `if_chain!` + --> $DIR/if_chain_style.rs:33:13 + | +LL | let x = ""; + | ^^^^^^^^^^^ + +error: `if_chain!` only has one `if` + --> $DIR/if_chain_style.rs:29:5 + | +LL | / if_chain! { +LL | | // single `if` condition +LL | | if true; +LL | | then { +... | +LL | | } +LL | | } + | |_____^ + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `let` expression should be above the `if_chain!` + --> $DIR/if_chain_style.rs:40:9 + | +LL | let x = ""; + | ^^^^^^^^^^^ + +error: this `if_chain!` can be merged with the outer `if_chain!` + --> $DIR/if_chain_style.rs:46:13 + | +LL | / if_chain! { +LL | | if true; +LL | | if true; +LL | | then {} +LL | | } + | |_____________^ + | +help: these `let` statements can also be in the `if_chain!` + --> $DIR/if_chain_style.rs:43:13 + | +LL | / let x = ""; +LL | | let x = ""; + | |_______________________^ + +error: aborting due to 7 previous errors + -- cgit 1.4.1-3-g733a5 From aaba9b78a210bc20dfdb1aff24d93acc22677a47 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Wed, 31 Mar 2021 14:58:17 -0400 Subject: Fix `redundant_clone` fp where the cloned value is modified while the clone is in use. --- clippy_lints/src/redundant_clone.rs | 252 +++++++++++++++++++++--------------- tests/ui/redundant_clone.fixed | 24 ++++ tests/ui/redundant_clone.rs | 24 ++++ tests/ui/redundant_clone.stderr | 20 +-- 4 files changed, 207 insertions(+), 113 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 37678fac1d2..9656ee64c81 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -199,79 +199,72 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { (local, deref_clone_ret) }; - let is_temp = mir.local_kind(ret_local) == mir::LocalKind::Temp; - - // 1. `local` can be moved out if it is not used later. - // 2. If `ret_local` is a temporary and is neither consumed nor mutated, we can remove this `clone` - // call anyway. - let (used, consumed_or_mutated) = traversal::ReversePostorder::new(&mir, bb).skip(1).fold( - (false, !is_temp), - |(used, consumed), (tbb, tdata)| { - // Short-circuit - if (used && consumed) || - // Give up on loops - tdata.terminator().successors().any(|s| *s == bb) - { - return (true, true); + let clone_usage = if local == ret_local { + CloneUsage { + cloned_used: false, + cloned_consume_or_mutate_loc: None, + clone_consumed_or_mutated: true, + } + } else { + let clone_usage = visit_clone_usage(local, ret_local, &mir, bb); + if clone_usage.cloned_used && clone_usage.clone_consumed_or_mutated { + // cloned value is used, and the clone is modified or moved + continue; + } else if let Some(loc) = clone_usage.cloned_consume_or_mutate_loc { + // cloned value is mutated, and the clone is alive. + if possible_borrower.is_alive_at(ret_local, loc) { + continue; } + } + clone_usage + }; - let mut vis = LocalUseVisitor { - used: (local, false), - consumed_or_mutated: (ret_local, false), - }; - vis.visit_basic_block_data(tbb, tdata); - (used || vis.used.1, consumed || vis.consumed_or_mutated.1) - }, - ); - - if !used || !consumed_or_mutated { - let span = terminator.source_info.span; - let scope = terminator.source_info.scope; - let node = mir.source_scopes[scope] - .local_data - .as_ref() - .assert_crate_local() - .lint_root; - - if_chain! { - if let Some(snip) = snippet_opt(cx, span); - if let Some(dot) = snip.rfind('.'); - then { - let sugg_span = span.with_lo( - span.lo() + BytePos(u32::try_from(dot).unwrap()) - ); - let mut app = Applicability::MaybeIncorrect; - - let call_snip = &snip[dot + 1..]; - // Machine applicable when `call_snip` looks like `foobar()` - if let Some(call_snip) = call_snip.strip_suffix("()").map(str::trim) { - if call_snip.as_bytes().iter().all(|b| b.is_ascii_alphabetic() || *b == b'_') { - app = Applicability::MachineApplicable; - } + let span = terminator.source_info.span; + let scope = terminator.source_info.scope; + let node = mir.source_scopes[scope] + .local_data + .as_ref() + .assert_crate_local() + .lint_root; + + if_chain! { + if let Some(snip) = snippet_opt(cx, span); + if let Some(dot) = snip.rfind('.'); + then { + let sugg_span = span.with_lo( + span.lo() + BytePos(u32::try_from(dot).unwrap()) + ); + let mut app = Applicability::MaybeIncorrect; + + let call_snip = &snip[dot + 1..]; + // Machine applicable when `call_snip` looks like `foobar()` + if let Some(call_snip) = call_snip.strip_suffix("()").map(str::trim) { + if call_snip.as_bytes().iter().all(|b| b.is_ascii_alphabetic() || *b == b'_') { + app = Applicability::MachineApplicable; } + } - span_lint_hir_and_then(cx, REDUNDANT_CLONE, node, sugg_span, "redundant clone", |diag| { - diag.span_suggestion( - sugg_span, - "remove this", - String::new(), - app, + span_lint_hir_and_then(cx, REDUNDANT_CLONE, node, sugg_span, "redundant clone", |diag| { + diag.span_suggestion( + sugg_span, + "remove this", + String::new(), + app, + ); + if clone_usage.cloned_used { + diag.span_note( + span, + "cloned value is neither consumed nor mutated", ); - if used { - diag.span_note( - span, - "cloned value is neither consumed nor mutated", - ); - } else { - diag.span_note( - span.with_hi(span.lo() + BytePos(u32::try_from(dot).unwrap())), - "this value is dropped without further use", - ); - } - }); - } else { - span_lint_hir(cx, REDUNDANT_CLONE, node, span, "redundant clone"); - } + } else { + diag.span_note( + span.with_hi(span.lo() + BytePos(u32::try_from(dot).unwrap())), + "this value is dropped without further use", + ); + } + }); + } else { + span_lint_hir(cx, REDUNDANT_CLONE, node, span, "redundant clone"); } } } @@ -365,49 +358,97 @@ fn base_local_and_movability<'tcx>( (local, deref || field || slice) } -struct LocalUseVisitor { - used: (mir::Local, bool), - consumed_or_mutated: (mir::Local, bool), +#[derive(Default)] +struct CloneUsage { + /// Whether the cloned value is used after the clone. + cloned_used: bool, + /// The first location where the cloned value is consumed or mutated, if any. + cloned_consume_or_mutate_loc: Option, + /// Whether the clone value is mutated. + clone_consumed_or_mutated: bool, } - -impl<'tcx> mir::visit::Visitor<'tcx> for LocalUseVisitor { - fn visit_basic_block_data(&mut self, block: mir::BasicBlock, data: &mir::BasicBlockData<'tcx>) { - let statements = &data.statements; - for (statement_index, statement) in statements.iter().enumerate() { - self.visit_statement(statement, mir::Location { block, statement_index }); - } - - self.visit_terminator( - data.terminator(), - mir::Location { - block, - statement_index: statements.len(), - }, - ); +fn visit_clone_usage(cloned: mir::Local, clone: mir::Local, mir: &mir::Body<'_>, bb: mir::BasicBlock) -> CloneUsage { + struct V { + cloned: mir::Local, + clone: mir::Local, + result: CloneUsage, } + impl<'tcx> mir::visit::Visitor<'tcx> for V { + fn visit_basic_block_data(&mut self, block: mir::BasicBlock, data: &mir::BasicBlockData<'tcx>) { + let statements = &data.statements; + for (statement_index, statement) in statements.iter().enumerate() { + self.visit_statement(statement, mir::Location { block, statement_index }); + } - fn visit_place(&mut self, place: &mir::Place<'tcx>, ctx: PlaceContext, _: mir::Location) { - let local = place.local; - - if local == self.used.0 - && !matches!( - ctx, - PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_) - ) - { - self.used.1 = true; + self.visit_terminator( + data.terminator(), + mir::Location { + block, + statement_index: statements.len(), + }, + ); } - if local == self.consumed_or_mutated.0 { - match ctx { - PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) - | PlaceContext::MutatingUse(MutatingUseContext::Borrow) => { - self.consumed_or_mutated.1 = true; - }, - _ => {}, + fn visit_place(&mut self, place: &mir::Place<'tcx>, ctx: PlaceContext, loc: mir::Location) { + let local = place.local; + + if local == self.cloned + && !matches!( + ctx, + PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_) + ) + { + self.result.cloned_used = true; + self.result.cloned_consume_or_mutate_loc = self.result.cloned_consume_or_mutate_loc.or_else(|| { + matches!( + ctx, + PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) + | PlaceContext::MutatingUse(MutatingUseContext::Borrow) + ) + .then(|| loc) + }); + } else if local == self.clone { + match ctx { + PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) + | PlaceContext::MutatingUse(MutatingUseContext::Borrow) => { + self.result.clone_consumed_or_mutated = true; + }, + _ => {}, + } } } } + + let init = CloneUsage { + cloned_used: false, + cloned_consume_or_mutate_loc: None, + // Consider non-temporary clones consumed. + // TODO: Actually check for mutation of non-temporaries. + clone_consumed_or_mutated: mir.local_kind(clone) != mir::LocalKind::Temp, + }; + traversal::ReversePostorder::new(&mir, bb) + .skip(1) + .fold(init, |usage, (tbb, tdata)| { + // Short-circuit + if (usage.cloned_used && usage.clone_consumed_or_mutated) || + // Give up on loops + tdata.terminator().successors().any(|s| *s == bb) + { + return CloneUsage { + cloned_used: true, + clone_consumed_or_mutated: true, + ..usage + }; + } + + let mut v = V { + cloned, + clone, + result: usage, + }; + v.visit_basic_block_data(tbb, tdata); + v.result + }) } /// Determines liveness of each local purely based on `StorageLive`/`Dead`. @@ -623,4 +664,9 @@ impl PossibleBorrowerMap<'_, '_> { self.bitset.0 == self.bitset.1 } + + fn is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool { + self.maybe_live.seek_after_primary_effect(at); + self.maybe_live.contains(local) + } } diff --git a/tests/ui/redundant_clone.fixed b/tests/ui/redundant_clone.fixed index ec309109ed5..f5da703cd1d 100644 --- a/tests/ui/redundant_clone.fixed +++ b/tests/ui/redundant_clone.fixed @@ -54,6 +54,7 @@ fn main() { not_consumed(); issue_5405(); manually_drop(); + clone_then_move_cloned(); } #[derive(Clone)] @@ -182,3 +183,26 @@ fn manually_drop() { Arc::from_raw(p); } } + +fn clone_then_move_cloned() { + // issue #5973 + let x = Some(String::new()); + // ok, x is moved while the clone is in use. + assert_eq!(x.clone(), None, "not equal {}", x.unwrap()); + + // issue #5595 + fn foo(_: &Alpha, _: F) {} + let x = Alpha; + // ok, data is moved while the clone is in use. + foo(&x.clone(), move || { + let _ = x; + }); + + // issue #6998 + struct S(String); + impl S { + fn m(&mut self) {} + } + let mut x = S(String::new()); + x.0.clone().chars().for_each(|_| x.m()); +} diff --git a/tests/ui/redundant_clone.rs b/tests/ui/redundant_clone.rs index b57027456e0..fd7f31a1cc5 100644 --- a/tests/ui/redundant_clone.rs +++ b/tests/ui/redundant_clone.rs @@ -54,6 +54,7 @@ fn main() { not_consumed(); issue_5405(); manually_drop(); + clone_then_move_cloned(); } #[derive(Clone)] @@ -182,3 +183,26 @@ fn manually_drop() { Arc::from_raw(p); } } + +fn clone_then_move_cloned() { + // issue #5973 + let x = Some(String::new()); + // ok, x is moved while the clone is in use. + assert_eq!(x.clone(), None, "not equal {}", x.unwrap()); + + // issue #5595 + fn foo(_: &Alpha, _: F) {} + let x = Alpha; + // ok, data is moved while the clone is in use. + foo(&x.clone(), move || { + let _ = x; + }); + + // issue #6998 + struct S(String); + impl S { + fn m(&mut self) {} + } + let mut x = S(String::new()); + x.0.clone().chars().for_each(|_| x.m()); +} diff --git a/tests/ui/redundant_clone.stderr b/tests/ui/redundant_clone.stderr index 821e7934be8..529a6de91e2 100644 --- a/tests/ui/redundant_clone.stderr +++ b/tests/ui/redundant_clone.stderr @@ -108,61 +108,61 @@ LL | let _t = tup.0.clone(); | ^^^^^ error: redundant clone - --> $DIR/redundant_clone.rs:62:25 + --> $DIR/redundant_clone.rs:63:25 | LL | if b { (a.clone(), a.clone()) } else { (Alpha, a) } | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:62:24 + --> $DIR/redundant_clone.rs:63:24 | LL | if b { (a.clone(), a.clone()) } else { (Alpha, a) } | ^ error: redundant clone - --> $DIR/redundant_clone.rs:119:15 + --> $DIR/redundant_clone.rs:120:15 | LL | let _s = s.clone(); | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:119:14 + --> $DIR/redundant_clone.rs:120:14 | LL | let _s = s.clone(); | ^ error: redundant clone - --> $DIR/redundant_clone.rs:120:15 + --> $DIR/redundant_clone.rs:121:15 | LL | let _t = t.clone(); | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:120:14 + --> $DIR/redundant_clone.rs:121:14 | LL | let _t = t.clone(); | ^ error: redundant clone - --> $DIR/redundant_clone.rs:130:19 + --> $DIR/redundant_clone.rs:131:19 | LL | let _f = f.clone(); | ^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/redundant_clone.rs:130:18 + --> $DIR/redundant_clone.rs:131:18 | LL | let _f = f.clone(); | ^ error: redundant clone - --> $DIR/redundant_clone.rs:142:14 + --> $DIR/redundant_clone.rs:143:14 | LL | let y = x.clone().join("matthias"); | ^^^^^^^^ help: remove this | note: cloned value is neither consumed nor mutated - --> $DIR/redundant_clone.rs:142:13 + --> $DIR/redundant_clone.rs:143:13 | LL | let y = x.clone().join("matthias"); | ^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From 6325fe1f5481f6dde2cabda173575a334a3cb8a2 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Thu, 1 Apr 2021 10:39:44 +0900 Subject: clippy_utils: fix needless parenthesis output from sugg::Sugg::maybe_par --- clippy_utils/src/sugg.rs | 42 ++++++++++++++++++++++-- tests/ui/floating_point_log.fixed | 2 +- tests/ui/floating_point_log.stderr | 2 +- tests/ui/from_str_radix_10.stderr | 2 +- tests/ui/manual_memcpy/with_loop_counters.stderr | 2 +- 5 files changed, 44 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index b2fe4317154..0633a19391f 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -267,17 +267,44 @@ impl<'a> Sugg<'a> { Sugg::NonParen(..) => self, // `(x)` and `(x).y()` both don't need additional parens. Sugg::MaybeParen(sugg) => { - if sugg.starts_with('(') && sugg.ends_with(')') { + if has_enclosing_paren(&sugg) { Sugg::MaybeParen(sugg) } else { Sugg::NonParen(format!("({})", sugg).into()) } }, - Sugg::BinOp(_, sugg) => Sugg::NonParen(format!("({})", sugg).into()), + Sugg::BinOp(_, sugg) => { + if has_enclosing_paren(&sugg) { + Sugg::NonParen(sugg) + } else { + Sugg::NonParen(format!("({})", sugg).into()) + } + }, } } } +/// 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() { + let mut depth = 1; + while let Some(c) = chars.next() { + if c == '(' { + depth += 1; + } else if c == ')' { + depth -= 1; + } + if depth == 0 { + break; + } + } + chars.next().is_none() + } else { + false + } +} + // Copied from the rust standart library, and then edited macro_rules! forward_binop_impls_to_ref { (impl $imp:ident, $method:ident for $t:ty, type Output = $o:ty) => { @@ -668,6 +695,8 @@ impl DiagnosticBuilderExt for rustc_errors::DiagnosticBuilder #[cfg(test)] mod test { use super::Sugg; + + use rustc_ast::util::parser::AssocOp; use std::borrow::Cow; const SUGGESTION: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("function_call()")); @@ -681,4 +710,13 @@ mod test { fn blockify_transforms_sugg_into_a_block() { assert_eq!("{ function_call() }", SUGGESTION.blockify().to_string()); } + + #[test] + fn binop_maybe_par() { + let sugg = Sugg::BinOp(AssocOp::Add, "(1 + 1)".into()); + assert_eq!("(1 + 1)", sugg.maybe_par().to_string()); + + let sugg = Sugg::BinOp(AssocOp::Add, "(1 + 1) + (1 + 1)".into()); + assert_eq!("((1 + 1) + (1 + 1))", sugg.maybe_par().to_string()); + } } diff --git a/tests/ui/floating_point_log.fixed b/tests/ui/floating_point_log.fixed index 7dc7ee94aff..5b487bb8fcf 100644 --- a/tests/ui/floating_point_log.fixed +++ b/tests/ui/floating_point_log.fixed @@ -27,7 +27,7 @@ fn check_ln1p() { let _ = (x / 2.0).ln_1p(); let _ = x.powi(3).ln_1p(); let _ = (x.powi(3) / 2.0).ln_1p(); - let _ = ((std::f32::consts::E - 1.0)).ln_1p(); + let _ = (std::f32::consts::E - 1.0).ln_1p(); let _ = x.ln_1p(); let _ = x.powi(3).ln_1p(); let _ = (x + 2.0).ln_1p(); diff --git a/tests/ui/floating_point_log.stderr b/tests/ui/floating_point_log.stderr index 900dc2b7933..96e5a154441 100644 --- a/tests/ui/floating_point_log.stderr +++ b/tests/ui/floating_point_log.stderr @@ -90,7 +90,7 @@ error: ln(1 + x) can be computed more accurately --> $DIR/floating_point_log.rs:30:13 | LL | let _ = (1.0 + (std::f32::consts::E - 1.0)).ln(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `((std::f32::consts::E - 1.0)).ln_1p()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(std::f32::consts::E - 1.0).ln_1p()` error: ln(1 + x) can be computed more accurately --> $DIR/floating_point_log.rs:31:13 diff --git a/tests/ui/from_str_radix_10.stderr b/tests/ui/from_str_radix_10.stderr index 471bf52a9a7..da5c16f8d01 100644 --- a/tests/ui/from_str_radix_10.stderr +++ b/tests/ui/from_str_radix_10.stderr @@ -28,7 +28,7 @@ error: this call to `from_str_radix` can be replaced with a call to `str::parse` --> $DIR/from_str_radix_10.rs:32:5 | LL | u16::from_str_radix(&("10".to_owned() + "5"), 10)?; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(("10".to_owned() + "5")).parse::()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `("10".to_owned() + "5").parse::()` error: this call to `from_str_radix` can be replaced with a call to `str::parse` --> $DIR/from_str_radix_10.rs:33:5 diff --git a/tests/ui/manual_memcpy/with_loop_counters.stderr b/tests/ui/manual_memcpy/with_loop_counters.stderr index 2547b19f5d1..a2f2dfce168 100644 --- a/tests/ui/manual_memcpy/with_loop_counters.stderr +++ b/tests/ui/manual_memcpy/with_loop_counters.stderr @@ -43,7 +43,7 @@ LL | / for i in 3..(3 + src.len()) { LL | | dst[i] = src[count]; LL | | count += 1; LL | | } - | |_____^ help: try replacing the loop by: `dst[3..((3 + src.len()))].clone_from_slice(&src[..((3 + src.len()) - 3)]);` + | |_____^ help: try replacing the loop by: `dst[3..(3 + src.len())].clone_from_slice(&src[..((3 + src.len()) - 3)]);` error: it looks like you're manually copying between slices --> $DIR/with_loop_counters.rs:35:5 -- cgit 1.4.1-3-g733a5 From 9f6f001988ae32e06b3abee5d869ad921fd614ae Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Fri, 2 Apr 2021 13:45:38 +0900 Subject: same_item_push: Don't trigger same_item_push if the vec is used in the loop body --- clippy_lints/src/loops/same_item_push.rs | 137 +++++++++++++++++++------------ tests/ui/same_item_push.rs | 7 ++ 2 files changed, 91 insertions(+), 53 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/loops/same_item_push.rs b/clippy_lints/src/loops/same_item_push.rs index 62c131968e7..b3d784474c8 100644 --- a/clippy_lints/src/loops/same_item_push.rs +++ b/clippy_lints/src/loops/same_item_push.rs @@ -1,11 +1,13 @@ use super::SAME_ITEM_PUSH; use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::path_to_local; use clippy_utils::source::snippet_with_macro_callsite; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use if_chain::if_chain; +use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; -use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, Node, Pat, PatKind, Stmt, StmtKind}; +use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, Node, Pat, PatKind, Stmt, StmtKind}; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; use rustc_span::symbol::sym; @@ -41,59 +43,55 @@ pub(super) fn check<'tcx>( } // Determine whether it is safe to lint the body - let mut same_item_push_visitor = SameItemPushVisitor { - should_lint: true, - vec_push: None, - cx, - }; + let mut same_item_push_visitor = SameItemPushVisitor::new(cx); walk_expr(&mut same_item_push_visitor, body); - if same_item_push_visitor.should_lint { - if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push { - let vec_ty = cx.typeck_results().expr_ty(vec); - let ty = vec_ty.walk().nth(1).unwrap().expect_ty(); - if cx - .tcx - .lang_items() - .clone_trait() - .map_or(false, |id| implements_trait(cx, ty, id, &[])) - { - // Make sure that the push does not involve possibly mutating values - match pushed_item.kind { - ExprKind::Path(ref qpath) => { - match cx.qpath_res(qpath, pushed_item.hir_id) { - // immutable bindings that are initialized with literal or constant - Res::Local(hir_id) => { - let node = cx.tcx.hir().get(hir_id); - if_chain! { - if let Node::Binding(pat) = node; - if let PatKind::Binding(bind_ann, ..) = pat.kind; - if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable); - let parent_node = cx.tcx.hir().get_parent_node(hir_id); - if let Some(Node::Local(parent_let_expr)) = cx.tcx.hir().find(parent_node); - if let Some(init) = parent_let_expr.init; - then { - match init.kind { - // immutable bindings that are initialized with literal - ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item), - // immutable bindings that are initialized with constant - ExprKind::Path(ref path) => { - if let Res::Def(DefKind::Const, ..) = cx.qpath_res(path, init.hir_id) { - emit_lint(cx, vec, pushed_item); - } + if_chain! { + if same_item_push_visitor.should_lint(); + if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push; + let vec_ty = cx.typeck_results().expr_ty(vec); + let ty = vec_ty.walk().nth(1).unwrap().expect_ty(); + if cx + .tcx + .lang_items() + .clone_trait() + .map_or(false, |id| implements_trait(cx, ty, id, &[])); + then { + // Make sure that the push does not involve possibly mutating values + match pushed_item.kind { + ExprKind::Path(ref qpath) => { + match cx.qpath_res(qpath, pushed_item.hir_id) { + // immutable bindings that are initialized with literal or constant + Res::Local(hir_id) => { + let node = cx.tcx.hir().get(hir_id); + if_chain! { + if let Node::Binding(pat) = node; + if let PatKind::Binding(bind_ann, ..) = pat.kind; + if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable); + let parent_node = cx.tcx.hir().get_parent_node(hir_id); + if let Some(Node::Local(parent_let_expr)) = cx.tcx.hir().find(parent_node); + if let Some(init) = parent_let_expr.init; + then { + match init.kind { + // immutable bindings that are initialized with literal + ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item), + // immutable bindings that are initialized with constant + ExprKind::Path(ref path) => { + if let Res::Def(DefKind::Const, ..) = cx.qpath_res(path, init.hir_id) { + emit_lint(cx, vec, pushed_item); } - _ => {}, } + _ => {}, } } - }, - // constant - Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item), - _ => {}, - } - }, - ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item), - _ => {}, - } + } + }, + // constant + Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item), + _ => {}, + } + }, + ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item), + _ => {}, } } } @@ -101,10 +99,38 @@ pub(super) fn check<'tcx>( // Scans the body of the for loop and determines whether lint should be given struct SameItemPushVisitor<'a, 'tcx> { - should_lint: bool, + non_deterministic_expr: bool, + multiple_pushes: bool, // this field holds the last vec push operation visited, which should be the only push seen vec_push: Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)>, cx: &'a LateContext<'tcx>, + used_locals: FxHashSet, +} + +impl<'a, 'tcx> SameItemPushVisitor<'a, 'tcx> { + fn new(cx: &'a LateContext<'tcx>) -> Self { + Self { + non_deterministic_expr: false, + multiple_pushes: false, + vec_push: None, + cx, + used_locals: FxHashSet::default(), + } + } + + fn should_lint(&self) -> bool { + if_chain! { + if !self.non_deterministic_expr; + if !self.multiple_pushes; + if let Some((vec, _)) = self.vec_push; + if let Some(hir_id) = path_to_local(vec); + then { + !self.used_locals.contains(&hir_id) + } else { + false + } + } + } } impl<'a, 'tcx> Visitor<'tcx> for SameItemPushVisitor<'a, 'tcx> { @@ -113,9 +139,14 @@ impl<'a, 'tcx> Visitor<'tcx> for SameItemPushVisitor<'a, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { match &expr.kind { // Non-determinism may occur ... don't give a lint - ExprKind::Loop(..) | ExprKind::Match(..) => self.should_lint = false, + ExprKind::Loop(..) | ExprKind::Match(..) | ExprKind::If(..) => self.non_deterministic_expr = true, ExprKind::Block(block, _) => self.visit_block(block), - _ => {}, + _ => { + if let Some(hir_id) = path_to_local(expr) { + self.used_locals.insert(hir_id); + } + walk_expr(self, expr); + }, } } @@ -140,7 +171,7 @@ impl<'a, 'tcx> Visitor<'tcx> for SameItemPushVisitor<'a, 'tcx> { self.vec_push = vec_push_option; } else { // There are multiple pushes ... don't lint - self.should_lint = false; + self.multiple_pushes = true; } } } diff --git a/tests/ui/same_item_push.rs b/tests/ui/same_item_push.rs index a37c8782ec3..9d420ec672a 100644 --- a/tests/ui/same_item_push.rs +++ b/tests/ui/same_item_push.rs @@ -148,4 +148,11 @@ fn main() { }; vec.push(item); } + + // Fix #6987 + let mut vec = Vec::new(); + for _ in 0..10 { + vec.push(1); + vec.extend(&[2]); + } } -- cgit 1.4.1-3-g733a5 From 33798bb0646b32680a204ef39aa1aae07c9ac0f5 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 2 Apr 2021 10:03:35 -0500 Subject: Improve needless_collect output --- clippy_lints/src/loops/needless_collect.rs | 11 ++++---- clippy_utils/src/diagnostics.rs | 6 ++-- tests/ui/needless_collect_indirect.stderr | 45 +++++++++++++++++------------- 3 files changed, 35 insertions(+), 27 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/loops/needless_collect.rs index 96b0f2e6cd7..e0c5caf5136 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/loops/needless_collect.rs @@ -10,8 +10,8 @@ use rustc_hir::intravisit::{walk_block, walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::{Block, Expr, ExprKind, GenericArg, HirId, Local, Pat, PatKind, QPath, StmtKind}; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; -use rustc_span::source_map::Span; use rustc_span::symbol::{sym, Ident}; +use rustc_span::{MultiSpan, Span}; const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed"; @@ -65,7 +65,7 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo Local { pat: Pat { hir_id: pat_id, kind: PatKind::Binding(_, _, ident, .. ), .. }, init: Some(ref init_expr), .. } ) = stmt.kind; - if let ExprKind::MethodCall(ref method_name, _, &[ref iter_source], ..) = init_expr.kind; + if let ExprKind::MethodCall(ref method_name, collect_span, &[ref iter_source], ..) = init_expr.kind; if method_name.ident.name == sym!(collect) && is_trait_method(cx, &init_expr, sym::Iterator); if let Some(ref generic_args) = method_name.args; if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0); @@ -74,7 +74,7 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo is_type_diagnostic_item(cx, ty, sym::vecdeque_type) || match_type(cx, ty, &paths::LINKED_LIST); if let Some(iter_calls) = detect_iter_and_into_iters(block, *ident); - if iter_calls.len() == 1; + if let [iter_call] = &*iter_calls; then { let mut used_count_visitor = UsedCountVisitor { cx, @@ -87,11 +87,12 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo } // Suggest replacing iter_call with iter_replacement, and removing stmt - let iter_call = &iter_calls[0]; + let mut span = MultiSpan::from_span(collect_span); + span.push_span_label(iter_call.span, "the iterator could be used here instead".into()); span_lint_and_then( cx, super::NEEDLESS_COLLECT, - stmt.span.until(iter_call.span), + span, NEEDLESS_COLLECT_MSG, |diag| { let iter_replacement = format!("{}{}", Sugg::hir(cx, iter_source, ".."), iter_call.get_iter_method(cx)); diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs index e9c9cb12b9d..73a2fa992b3 100644 --- a/clippy_utils/src/diagnostics.rs +++ b/clippy_utils/src/diagnostics.rs @@ -133,9 +133,11 @@ pub fn span_lint_and_note<'a, T: LintContext>( /// /// If you need to customize your lint output a lot, use this function. /// If you change the signature, remember to update the internal lint `CollapsibleCalls` -pub fn span_lint_and_then<'a, T: LintContext, F>(cx: &'a T, lint: &'static Lint, sp: Span, msg: &str, f: F) +pub fn span_lint_and_then(cx: &C, lint: &'static Lint, sp: S, msg: &str, f: F) where - F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>), + C: LintContext, + S: Into, + F: FnOnce(&mut DiagnosticBuilder<'_>), { cx.struct_span_lint(lint, sp, |diag| { let mut diag = diag.build(msg); diff --git a/tests/ui/needless_collect_indirect.stderr b/tests/ui/needless_collect_indirect.stderr index 76e789d9052..c773b841f3b 100644 --- a/tests/ui/needless_collect_indirect.stderr +++ b/tests/ui/needless_collect_indirect.stderr @@ -1,9 +1,10 @@ error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:5:5 + --> $DIR/needless_collect_indirect.rs:5:39 | -LL | / let indirect_iter = sample.iter().collect::>(); -LL | | indirect_iter.into_iter().map(|x| (x, x + 1)).collect::>(); - | |____^ +LL | let indirect_iter = sample.iter().collect::>(); + | ^^^^^^^ +LL | indirect_iter.into_iter().map(|x| (x, x + 1)).collect::>(); + | ------------------------- the iterator could be used here instead | = note: `-D clippy::needless-collect` implied by `-D warnings` help: use the original Iterator instead of collecting it and then producing a new one @@ -13,11 +14,12 @@ LL | sample.iter().map(|x| (x, x + 1)).collect::>(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:7:5 + --> $DIR/needless_collect_indirect.rs:7:38 | -LL | / let indirect_len = sample.iter().collect::>(); -LL | | indirect_len.len(); - | |____^ +LL | let indirect_len = sample.iter().collect::>(); + | ^^^^^^^ +LL | indirect_len.len(); + | ------------------ the iterator could be used here instead | help: take the original Iterator's count instead of collecting it and finding the length | @@ -26,11 +28,12 @@ LL | sample.iter().count(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:9:5 + --> $DIR/needless_collect_indirect.rs:9:40 | -LL | / let indirect_empty = sample.iter().collect::>(); -LL | | indirect_empty.is_empty(); - | |____^ +LL | let indirect_empty = sample.iter().collect::>(); + | ^^^^^^^ +LL | indirect_empty.is_empty(); + | ------------------------- the iterator could be used here instead | help: check if the original Iterator has anything instead of collecting it and seeing if it's empty | @@ -39,11 +42,12 @@ LL | sample.iter().next().is_none(); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:11:5 + --> $DIR/needless_collect_indirect.rs:11:43 | -LL | / let indirect_contains = sample.iter().collect::>(); -LL | | indirect_contains.contains(&&5); - | |____^ +LL | let indirect_contains = sample.iter().collect::>(); + | ^^^^^^^ +LL | indirect_contains.contains(&&5); + | ------------------------------- the iterator could be used here instead | help: check if the original Iterator contains an element instead of collecting then checking | @@ -52,11 +56,12 @@ LL | sample.iter().any(|x| x == &5); | error: avoid using `collect()` when not needed - --> $DIR/needless_collect_indirect.rs:23:5 + --> $DIR/needless_collect_indirect.rs:23:48 | -LL | / let non_copy_contains = sample.into_iter().collect::>(); -LL | | non_copy_contains.contains(&a); - | |____^ +LL | let non_copy_contains = sample.into_iter().collect::>(); + | ^^^^^^^ +LL | non_copy_contains.contains(&a); + | ------------------------------ the iterator could be used here instead | help: check if the original Iterator contains an element instead of collecting then checking | -- cgit 1.4.1-3-g733a5 From c05760ff90fbf4ac677db592b2efe25fab5fac6c Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 2 Apr 2021 22:24:52 -0400 Subject: Fix `macro_use_import` ICE --- clippy_lints/src/macro_use.rs | 1 + tests/ui/macro_use_imports.fixed | 6 +++++- tests/ui/macro_use_imports.rs | 6 +++++- 3 files changed, 11 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index d573c297838..68cba192c4e 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -114,6 +114,7 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports { .iter() .find(|attr| attr.ident().map(|s| s.to_string()) == Some("macro_use".to_string())); if let Res::Def(DefKind::Mod, id) = path.res; + if !id.is_local(); then { for kid in cx.tcx.item_children(id).iter() { if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res { diff --git a/tests/ui/macro_use_imports.fixed b/tests/ui/macro_use_imports.fixed index 91e34c62160..51c66a46368 100644 --- a/tests/ui/macro_use_imports.fixed +++ b/tests/ui/macro_use_imports.fixed @@ -4,7 +4,7 @@ // run-rustfix // ignore-32bit -#![allow(unused_imports, unreachable_code, unused_variables, dead_code)] +#![allow(unused_imports, unreachable_code, unused_variables, dead_code, unused_attributes)] #![allow(clippy::single_component_path_imports)] #![warn(clippy::macro_use_imports)] @@ -40,4 +40,8 @@ mod a { } } +// issue #7015, ICE due to calling `item_children` with local `DefId` +#[macro_use] +use a as b; + fn main() {} diff --git a/tests/ui/macro_use_imports.rs b/tests/ui/macro_use_imports.rs index 9c3c50c5d49..2011129bc94 100644 --- a/tests/ui/macro_use_imports.rs +++ b/tests/ui/macro_use_imports.rs @@ -4,7 +4,7 @@ // run-rustfix // ignore-32bit -#![allow(unused_imports, unreachable_code, unused_variables, dead_code)] +#![allow(unused_imports, unreachable_code, unused_variables, dead_code, unused_attributes)] #![allow(clippy::single_component_path_imports)] #![warn(clippy::macro_use_imports)] @@ -40,4 +40,8 @@ mod a { } } +// issue #7015, ICE due to calling `item_children` with local `DefId` +#[macro_use] +use a as b; + fn main() {} -- cgit 1.4.1-3-g733a5 From 7014340d5713b9401d95a5ca3287163fd407da46 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 2 Apr 2021 11:56:32 -0500 Subject: Fix ICE --- clippy_lints/src/matches.rs | 14 ++++++++------ tests/ui/crashes/ice-7012.rs | 17 +++++++++++++++++ 2 files changed, 25 insertions(+), 6 deletions(-) create mode 100644 tests/ui/crashes/ice-7012.rs (limited to 'tests') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index b13db4cd45c..ba49097fd3b 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -1046,16 +1046,18 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) path }, PatKind::TupleStruct(path, patterns, ..) => { - if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p)) { - let id = cx.qpath_res(path, pat.hir_id).def_id(); - missing_variants.retain(|e| e.ctor_def_id != Some(id)); + if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() { + if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p)) { + missing_variants.retain(|e| e.ctor_def_id != Some(id)); + } } path }, PatKind::Struct(path, patterns, ..) => { - if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p.pat)) { - let id = cx.qpath_res(path, pat.hir_id).def_id(); - missing_variants.retain(|e| e.def_id != id); + if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() { + if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p.pat)) { + missing_variants.retain(|e| e.def_id != id); + } } path }, diff --git a/tests/ui/crashes/ice-7012.rs b/tests/ui/crashes/ice-7012.rs new file mode 100644 index 00000000000..60bdbc4f124 --- /dev/null +++ b/tests/ui/crashes/ice-7012.rs @@ -0,0 +1,17 @@ +#![allow(clippy::all)] + +enum _MyOption { + None, + Some(()), +} + +impl _MyOption { + fn _foo(&self) { + match self { + &Self::Some(_) => {}, + _ => {}, + } + } +} + +fn main() {} -- cgit 1.4.1-3-g733a5 From 5102c9cc694fd6374370c00f7c9518da14c0156e Mon Sep 17 00:00:00 2001 From: daxpedda Date: Sat, 3 Apr 2021 22:52:48 +0200 Subject: Remove author requirement for `cargo_common_metadata` --- clippy_lints/src/cargo_common_metadata.rs | 10 ++-------- tests/ui-cargo/cargo_common_metadata/fail/src/main.stderr | 6 ++---- .../cargo_common_metadata/fail_publish/src/main.stderr | 6 ++---- .../cargo_common_metadata/fail_publish_true/src/main.stderr | 6 ++---- tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml | 1 - 5 files changed, 8 insertions(+), 21 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/cargo_common_metadata.rs b/clippy_lints/src/cargo_common_metadata.rs index fce5c559672..8097a1c8326 100644 --- a/clippy_lints/src/cargo_common_metadata.rs +++ b/clippy_lints/src/cargo_common_metadata.rs @@ -20,11 +20,10 @@ declare_clippy_lint! { /// /// **Example:** /// ```toml - /// # This `Cargo.toml` is missing an authors field: + /// # This `Cargo.toml` is missing a description field: /// [package] /// name = "clippy" /// version = "0.0.212" - /// description = "A bunch of helpful lints to avoid common pitfalls in Rust" /// repository = "https://github.com/rust-lang/rust-clippy" /// readme = "README.md" /// license = "MIT OR Apache-2.0" @@ -32,14 +31,13 @@ declare_clippy_lint! { /// categories = ["development-tools", "development-tools::cargo-plugins"] /// ``` /// - /// Should include an authors field like: + /// Should include a description field like: /// /// ```toml /// # This `Cargo.toml` includes all common metadata /// [package] /// name = "clippy" /// version = "0.0.212" - /// authors = ["Someone "] /// description = "A bunch of helpful lints to avoid common pitfalls in Rust" /// repository = "https://github.com/rust-lang/rust-clippy" /// readme = "README.md" @@ -97,10 +95,6 @@ impl LateLintPass<'_> for CargoCommonMetadata { // only run the lint if publish is `None` (`publish = true` or skipped entirely) // or if the vector isn't empty (`publish = ["something"]`) if package.publish.as_ref().filter(|publish| publish.is_empty()).is_none() || self.ignore_publish { - if is_empty_vec(&package.authors) { - missing_warning(cx, &package, "package.authors"); - } - if is_empty_str(&package.description) { missing_warning(cx, &package, "package.description"); } diff --git a/tests/ui-cargo/cargo_common_metadata/fail/src/main.stderr b/tests/ui-cargo/cargo_common_metadata/fail/src/main.stderr index c8ae6c820df..5e9aa8dc36a 100644 --- a/tests/ui-cargo/cargo_common_metadata/fail/src/main.stderr +++ b/tests/ui-cargo/cargo_common_metadata/fail/src/main.stderr @@ -1,9 +1,7 @@ -error: package `cargo_common_metadata` is missing `package.authors` metadata +error: package `cargo_common_metadata` is missing `package.description` metadata | = note: `-D clippy::cargo-common-metadata` implied by `-D warnings` -error: package `cargo_common_metadata` is missing `package.description` metadata - error: package `cargo_common_metadata` is missing `either package.license or package.license_file` metadata error: package `cargo_common_metadata` is missing `package.repository` metadata @@ -14,5 +12,5 @@ error: package `cargo_common_metadata` is missing `package.keywords` metadata error: package `cargo_common_metadata` is missing `package.categories` metadata -error: aborting due to 7 previous errors +error: aborting due to 6 previous errors diff --git a/tests/ui-cargo/cargo_common_metadata/fail_publish/src/main.stderr b/tests/ui-cargo/cargo_common_metadata/fail_publish/src/main.stderr index c8ae6c820df..5e9aa8dc36a 100644 --- a/tests/ui-cargo/cargo_common_metadata/fail_publish/src/main.stderr +++ b/tests/ui-cargo/cargo_common_metadata/fail_publish/src/main.stderr @@ -1,9 +1,7 @@ -error: package `cargo_common_metadata` is missing `package.authors` metadata +error: package `cargo_common_metadata` is missing `package.description` metadata | = note: `-D clippy::cargo-common-metadata` implied by `-D warnings` -error: package `cargo_common_metadata` is missing `package.description` metadata - error: package `cargo_common_metadata` is missing `either package.license or package.license_file` metadata error: package `cargo_common_metadata` is missing `package.repository` metadata @@ -14,5 +12,5 @@ error: package `cargo_common_metadata` is missing `package.keywords` metadata error: package `cargo_common_metadata` is missing `package.categories` metadata -error: aborting due to 7 previous errors +error: aborting due to 6 previous errors diff --git a/tests/ui-cargo/cargo_common_metadata/fail_publish_true/src/main.stderr b/tests/ui-cargo/cargo_common_metadata/fail_publish_true/src/main.stderr index c8ae6c820df..5e9aa8dc36a 100644 --- a/tests/ui-cargo/cargo_common_metadata/fail_publish_true/src/main.stderr +++ b/tests/ui-cargo/cargo_common_metadata/fail_publish_true/src/main.stderr @@ -1,9 +1,7 @@ -error: package `cargo_common_metadata` is missing `package.authors` metadata +error: package `cargo_common_metadata` is missing `package.description` metadata | = note: `-D clippy::cargo-common-metadata` implied by `-D warnings` -error: package `cargo_common_metadata` is missing `package.description` metadata - error: package `cargo_common_metadata` is missing `either package.license or package.license_file` metadata error: package `cargo_common_metadata` is missing `package.repository` metadata @@ -14,5 +12,5 @@ error: package `cargo_common_metadata` is missing `package.keywords` metadata error: package `cargo_common_metadata` is missing `package.categories` metadata -error: aborting due to 7 previous errors +error: aborting due to 6 previous errors diff --git a/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml b/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml index 737e84e963c..cb4774d43a2 100644 --- a/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml +++ b/tests/ui-cargo/cargo_common_metadata/pass/Cargo.toml @@ -2,7 +2,6 @@ name = "cargo_common_metadata" version = "0.1.0" publish = false -authors = ["Random person from the Internet "] description = "A test package for the cargo_common_metadata lint" repository = "https://github.com/someone/cargo_common_metadata" readme = "README.md" -- cgit 1.4.1-3-g733a5 From 81dfb9ecfb13f67a56176bd82c846530858b8ef5 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 4 Apr 2021 14:21:02 +0200 Subject: Check path imports per module --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/single_component_path_imports.rs | 150 +++++++++++----------- tests/ui/single_component_path_imports.fixed | 7 + tests/ui/single_component_path_imports.rs | 7 + tests/ui/single_component_path_imports.stderr | 12 +- 5 files changed, 96 insertions(+), 82 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 97557fefe3e..f013613119c 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1232,7 +1232,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box as_conversions::AsConversions); store.register_late_pass(|| box let_underscore::LetUnderscore); store.register_late_pass(|| box atomic_ordering::AtomicOrdering); - store.register_early_pass(|| box single_component_path_imports::SingleComponentPathImports::default()); + store.register_early_pass(|| box single_component_path_imports::SingleComponentPathImports); let max_fn_params_bools = conf.max_fn_params_bools; let max_struct_bools = conf.max_struct_bools; store.register_early_pass(move || box excessive_bools::ExcessiveBools::new(max_struct_bools, max_fn_params_bools)); diff --git a/clippy_lints/src/single_component_path_imports.rs b/clippy_lints/src/single_component_path_imports.rs index 901f7642a12..adf0d7998f8 100644 --- a/clippy_lints/src/single_component_path_imports.rs +++ b/clippy_lints/src/single_component_path_imports.rs @@ -1,12 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::in_macro; -use rustc_ast::{Crate, Item, ItemKind, ModKind, UseTreeKind}; +use rustc_ast::{ptr::P, Crate, Item, ItemKind, ModKind, UseTreeKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::edition::Edition; -use rustc_span::symbol::kw; -use rustc_span::{Span, Symbol}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{edition::Edition, symbol::kw, Span, Symbol}; declare_clippy_lint! { /// **What it does:** Checking for imports with single component use path. @@ -36,94 +34,96 @@ declare_clippy_lint! { "imports with single component path are redundant" } -#[derive(Default)] -pub struct SingleComponentPathImports { - /// keep track of imports reused with `self` keyword, - /// such as `self::crypto_hash` in the example below - /// - /// ```rust,ignore - /// use self::crypto_hash::{Algorithm, Hasher}; - /// ``` - imports_reused_with_self: Vec, - /// keep track of single use statements - /// such as `crypto_hash` in the example below - /// - /// ```rust,ignore - /// use crypto_hash; - /// ``` - single_use_usages: Vec<(Symbol, Span)>, -} - -impl_lint_pass!(SingleComponentPathImports => [SINGLE_COMPONENT_PATH_IMPORTS]); +declare_lint_pass!(SingleComponentPathImports => [SINGLE_COMPONENT_PATH_IMPORTS]); impl EarlyLintPass for SingleComponentPathImports { fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) { if cx.sess.opts.edition < Edition::Edition2018 { return; } - for item in &krate.items { - self.track_uses(&item); - } - for single_use in &self.single_use_usages { - if !self.imports_reused_with_self.contains(&single_use.0) { - span_lint_and_sugg( - cx, - SINGLE_COMPONENT_PATH_IMPORTS, - single_use.1, - "this import is redundant", - "remove it entirely", - String::new(), - Applicability::MachineApplicable, - ); - } - } + check_mod(cx, &krate.items); } } -impl SingleComponentPathImports { - fn track_uses(&mut self, item: &Item) { - if in_macro(item.span) || item.vis.kind.is_pub() { - return; +fn check_mod(cx: &EarlyContext<'_>, items: &[P]) { + // keep track of imports reused with `self` keyword, + // such as `self::crypto_hash` in the example below + // ```rust,ignore + // use self::crypto_hash::{Algorithm, Hasher}; + // ``` + let mut imports_reused_with_self = Vec::new(); + + // keep track of single use statements + // such as `crypto_hash` in the example below + // ```rust,ignore + // use crypto_hash; + // ``` + let mut single_use_usages = Vec::new(); + + for item in items { + track_uses(cx, &item, &mut imports_reused_with_self, &mut single_use_usages); + } + + for single_use in &single_use_usages { + if !imports_reused_with_self.contains(&single_use.0) { + span_lint_and_sugg( + cx, + SINGLE_COMPONENT_PATH_IMPORTS, + single_use.1, + "this import is redundant", + "remove it entirely", + String::new(), + Applicability::MachineApplicable, + ); } + } +} + +fn track_uses( + cx: &EarlyContext<'_>, + item: &Item, + imports_reused_with_self: &mut Vec, + single_use_usages: &mut Vec<(Symbol, Span)>, +) { + if in_macro(item.span) || item.vis.kind.is_pub() { + return; + } + + match &item.kind { + ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) => { + check_mod(cx, &items); + }, + ItemKind::Use(use_tree) => { + let segments = &use_tree.prefix.segments; - match &item.kind { - ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) => { - for item in items.iter() { - self.track_uses(&item); + // keep track of `use some_module;` usages + if segments.len() == 1 { + if let UseTreeKind::Simple(None, _, _) = use_tree.kind { + let ident = &segments[0].ident; + single_use_usages.push((ident.name, item.span)); } - }, - ItemKind::Use(use_tree) => { - let segments = &use_tree.prefix.segments; + return; + } - // keep track of `use some_module;` usages - if segments.len() == 1 { - if let UseTreeKind::Simple(None, _, _) = use_tree.kind { - let ident = &segments[0].ident; - self.single_use_usages.push((ident.name, item.span)); - } + // keep track of `use self::some_module` usages + if segments[0].ident.name == kw::SelfLower { + // simple case such as `use self::module::SomeStruct` + if segments.len() > 1 { + imports_reused_with_self.push(segments[1].ident.name); return; } - // keep track of `use self::some_module` usages - if segments[0].ident.name == kw::SelfLower { - // simple case such as `use self::module::SomeStruct` - if segments.len() > 1 { - self.imports_reused_with_self.push(segments[1].ident.name); - return; - } - - // nested case such as `use self::{module1::Struct1, module2::Struct2}` - if let UseTreeKind::Nested(trees) = &use_tree.kind { - for tree in trees { - let segments = &tree.0.prefix.segments; - if !segments.is_empty() { - self.imports_reused_with_self.push(segments[0].ident.name); - } + // nested case such as `use self::{module1::Struct1, module2::Struct2}` + if let UseTreeKind::Nested(trees) = &use_tree.kind { + for tree in trees { + let segments = &tree.0.prefix.segments; + if !segments.is_empty() { + imports_reused_with_self.push(segments[0].ident.name); } } } - }, - _ => {}, - } + } + }, + _ => {}, } } diff --git a/tests/ui/single_component_path_imports.fixed b/tests/ui/single_component_path_imports.fixed index 226d2b315d7..f66b445b7b6 100644 --- a/tests/ui/single_component_path_imports.fixed +++ b/tests/ui/single_component_path_imports.fixed @@ -25,3 +25,10 @@ mod hello_mod { #[allow(dead_code)] fn hello_mod() {} } + +mod hi_mod { + use self::regex::{Regex, RegexSet}; + use regex; + #[allow(dead_code)] + fn hi_mod() {} +} diff --git a/tests/ui/single_component_path_imports.rs b/tests/ui/single_component_path_imports.rs index 88bf7f1fc5a..09d48658595 100644 --- a/tests/ui/single_component_path_imports.rs +++ b/tests/ui/single_component_path_imports.rs @@ -25,3 +25,10 @@ mod hello_mod { #[allow(dead_code)] fn hello_mod() {} } + +mod hi_mod { + use self::regex::{Regex, RegexSet}; + use regex; + #[allow(dead_code)] + fn hi_mod() {} +} diff --git a/tests/ui/single_component_path_imports.stderr b/tests/ui/single_component_path_imports.stderr index 646e6d3647a..7005fa8f125 100644 --- a/tests/ui/single_component_path_imports.stderr +++ b/tests/ui/single_component_path_imports.stderr @@ -1,16 +1,16 @@ error: this import is redundant - --> $DIR/single_component_path_imports.rs:6:1 + --> $DIR/single_component_path_imports.rs:24:5 | -LL | use regex; - | ^^^^^^^^^^ help: remove it entirely +LL | use regex; + | ^^^^^^^^^^ help: remove it entirely | = note: `-D clippy::single-component-path-imports` implied by `-D warnings` error: this import is redundant - --> $DIR/single_component_path_imports.rs:24:5 + --> $DIR/single_component_path_imports.rs:6:1 | -LL | use regex; - | ^^^^^^^^^^ help: remove it entirely +LL | use regex; + | ^^^^^^^^^^ help: remove it entirely error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From a00de90cebedbf24e0cb75ca347f1752335aedca Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 5 Apr 2021 00:09:13 -0400 Subject: Fix ICE in `missing_panics_doc` --- clippy_lints/src/doc.rs | 5 ++++- tests/ui/doc.rs | 21 +++++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 69800f9d331..69d47850091 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -11,7 +11,7 @@ use rustc_errors::emitter::EmitterWriter; use rustc_errors::Handler; use rustc_hir as hir; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; -use rustc_hir::{Expr, ExprKind, QPath}; +use rustc_hir::{AnonConst, Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; @@ -735,6 +735,9 @@ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> { intravisit::walk_expr(self, expr); } + // Panics in const blocks will cause compilation to fail. + fn visit_anon_const(&mut self, _: &'tcx AnonConst) {} + fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) } diff --git a/tests/ui/doc.rs b/tests/ui/doc.rs index d2c666bd290..c946a047f1b 100644 --- a/tests/ui/doc.rs +++ b/tests/ui/doc.rs @@ -1,8 +1,8 @@ //! This file tests for the `DOC_MARKDOWN` lint. -#![allow(dead_code)] +#![allow(dead_code, incomplete_features)] #![warn(clippy::doc_markdown)] -#![feature(custom_inner_attributes)] +#![feature(custom_inner_attributes, const_generics, const_evaluatable_checked, const_option)] #![rustfmt::skip] /// The foo_bar function does _nothing_. See also foo::bar. (note the dot there) @@ -202,3 +202,20 @@ fn issue_2343() {} /// This should not cause an ICE: /// __|_ _|__||_| fn pulldown_cmark_crash() {} + +// issue #7033 - const_evaluatable_checked ICE +struct S +where [(); N.checked_next_power_of_two().unwrap()]: { + arr: [T; N.checked_next_power_of_two().unwrap()], + n: usize, +} + +impl S +where [(); N.checked_next_power_of_two().unwrap()]: { + fn new() -> Self { + Self { + arr: [T::default(); N.checked_next_power_of_two().unwrap()], + n: 0, + } + } +} -- cgit 1.4.1-3-g733a5 From 8e5dd4b2e9baa107dc3cf8abf44a4208fdf0f489 Mon Sep 17 00:00:00 2001 From: Aliénore Bouttefeux Date: Mon, 5 Apr 2021 09:16:48 +0200 Subject: add test for missing_panic_doc on assert_eq/assert_ne --- tests/ui/missing_panics_doc.rs | 32 ++++++++++++++++++++++++++++++++ tests/ui/missing_panics_doc.stderr | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 65 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/ui/missing_panics_doc.rs b/tests/ui/missing_panics_doc.rs index 3fe35c75799..2e1379a58a6 100644 --- a/tests/ui/missing_panics_doc.rs +++ b/tests/ui/missing_panics_doc.rs @@ -33,6 +33,18 @@ pub fn unreachable_and_panic() { if true { unreachable!() } else { panic!() } } +/// This needs to be documented +pub fn assert_eq() { + let x = 0; + assert_eq!(x, 0); +} + +/// This needs to be documented +pub fn assert_ne() { + let x = 0; + assert_ne!(x, 0); +} + /// This is documented /// /// # Panics @@ -83,6 +95,26 @@ pub fn unreachable_amd_panic_documented() { if true { unreachable!() } else { panic!() } } +/// This is documented +/// +/// # Panics +/// +/// Panics if `x` is not 0. +pub fn assert_eq_documented() { + let x = 0; + assert_eq!(x, 0); +} + +/// This is documented +/// +/// # Panics +/// +/// Panics if `x` is 0. +pub fn assert_ne_documented() { + let x = 0; + assert_ne!(x, 0); +} + /// This is okay because it is private fn unwrap_private() { let result = Err("Hi"); diff --git a/tests/ui/missing_panics_doc.stderr b/tests/ui/missing_panics_doc.stderr index 37da6bfd92d..ba96a6a12b5 100644 --- a/tests/ui/missing_panics_doc.stderr +++ b/tests/ui/missing_panics_doc.stderr @@ -78,5 +78,37 @@ LL | if true { unreachable!() } else { panic!() } | ^^^^^^^^ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 5 previous errors +error: docs for function which may panic missing `# Panics` section + --> $DIR/missing_panics_doc.rs:37:1 + | +LL | / pub fn assert_eq() { +LL | | let x = 0; +LL | | assert_eq!(x, 0); +LL | | } + | |_^ + | +note: first possible panic found here + --> $DIR/missing_panics_doc.rs:39:5 + | +LL | assert_eq!(x, 0); + | ^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: docs for function which may panic missing `# Panics` section + --> $DIR/missing_panics_doc.rs:43:1 + | +LL | / pub fn assert_ne() { +LL | | let x = 0; +LL | | assert_ne!(x, 0); +LL | | } + | |_^ + | +note: first possible panic found here + --> $DIR/missing_panics_doc.rs:45:5 + | +LL | assert_ne!(x, 0); + | ^^^^^^^^^^^^^^^^^ + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 7 previous errors -- cgit 1.4.1-3-g733a5 From d1df73228a7cdadb4d635c6ed77daeeaebbe10ff Mon Sep 17 00:00:00 2001 From: xFrednet Date: Fri, 11 Dec 2020 22:29:53 +0000 Subject: A new lint for shared code in if blocks * Added expression check for shared_code_in_if_blocks * Finishing touches for the shared_code_in_if_blocks lint * Applying PR suggestions * Update lints yay * Moved test into subfolder --- CHANGELOG.md | 1 + clippy_lints/src/copies.rs | 305 +++++++++++++++++++-- clippy_lints/src/lib.rs | 2 + clippy_lints/src/literal_representation.rs | 40 ++- .../src/methods/manual_saturating_arithmetic.rs | 48 ++-- clippy_utils/src/hir_utils.rs | 9 + tests/ui/checked_unwrap/complex_conditionals.rs | 2 +- tests/ui/if_same_then_else.rs | 3 +- tests/ui/if_same_then_else.stderr | 88 +++--- tests/ui/if_same_then_else2.rs | 3 +- tests/ui/if_same_then_else2.stderr | 80 +++--- tests/ui/let_if_seq.rs | 3 +- tests/ui/let_if_seq.stderr | 8 +- tests/ui/shared_code_in_if_blocks/shared_at_bot.rs | 47 ++++ tests/ui/shared_code_in_if_blocks/shared_at_top.rs | 83 ++++++ .../shared_at_top_and_bot.rs | 22 ++ .../ui/shared_code_in_if_blocks/valid_if_blocks.rs | 157 +++++++++++ 17 files changed, 737 insertions(+), 164 deletions(-) create mode 100644 tests/ui/shared_code_in_if_blocks/shared_at_bot.rs create mode 100644 tests/ui/shared_code_in_if_blocks/shared_at_top.rs create mode 100644 tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.rs create mode 100644 tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index fb4a0b500f2..790bca4ac18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2455,6 +2455,7 @@ Released 2018-09-13 [`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse [`shadow_same`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_same [`shadow_unrelated`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_unrelated +[`shared_code_in_if_blocks`]: https://rust-lang.github.io/rust-clippy/master/index.html#shared_code_in_if_blocks [`short_circuit_statement`]: https://rust-lang.github.io/rust-clippy/master/index.html#short_circuit_statement [`should_assert_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_assert_eq [`should_implement_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 46093a16571..29ff1970705 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -3,7 +3,10 @@ use clippy_utils::{eq_expr_value, in_macro, search_same, SpanlessEq, SpanlessHas use clippy_utils::{get_parent_expr, if_sequence}; use rustc_hir::{Block, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; +use std::borrow::Cow; declare_clippy_lint! { /// **What it does:** Checks for consecutive `if`s with the same condition. @@ -104,7 +107,45 @@ declare_clippy_lint! { "`if` with the same `then` and `else` blocks" } -declare_lint_pass!(CopyAndPaste => [IFS_SAME_COND, SAME_FUNCTIONS_IN_IF_CONDITION, IF_SAME_THEN_ELSE]); +declare_clippy_lint! { + /// **What it does:** Checks if the `if` and `else` block contain shared code that can be + /// moved out of the blocks. + /// + /// **Why is this bad?** Duplicate code is less maintainable. + /// + /// **Known problems:** Hopefully none. + /// + /// **Example:** + /// ```ignore + /// let foo = if … { + /// println!("Hello World"); + /// 13 + /// } else { + /// println!("Hello World"); + /// 42 + /// }; + /// ``` + /// + /// Could be written as: + /// ```ignore + /// println!("Hello World"); + /// let foo = if … { + /// 13 + /// } else { + /// 42 + /// }; + /// ``` + pub SHARED_CODE_IN_IF_BLOCKS, + pedantic, + "`if` statement with shared code in all blocks" +} + +declare_lint_pass!(CopyAndPaste => [ + IFS_SAME_COND, + SAME_FUNCTIONS_IN_IF_CONDITION, + IF_SAME_THEN_ELSE, + SHARED_CODE_IN_IF_BLOCKS +]); impl<'tcx> LateLintPass<'tcx> for CopyAndPaste { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { @@ -121,30 +162,256 @@ impl<'tcx> LateLintPass<'tcx> for CopyAndPaste { } let (conds, blocks) = if_sequence(expr); - lint_same_then_else(cx, &blocks); + // Conditions lint_same_cond(cx, &conds); lint_same_fns_in_if_cond(cx, &conds); + // Block duplication + lint_same_then_else(cx, &blocks, conds.len() != blocks.len(), expr); } } } -/// Implementation of `IF_SAME_THEN_ELSE`. -fn lint_same_then_else(cx: &LateContext<'_>, blocks: &[&Block<'_>]) { - let eq: &dyn Fn(&&Block<'_>, &&Block<'_>) -> bool = - &|&lhs, &rhs| -> bool { SpanlessEq::new(cx).eq_block(lhs, rhs) }; +/// Implementation of `SHARED_CODE_IN_IF_BLOCKS` and `IF_SAME_THEN_ELSE` if the blocks are equal. +fn lint_same_then_else<'tcx>( + cx: &LateContext<'tcx>, + blocks: &[&Block<'tcx>], + has_unconditional_else: bool, + expr: &'tcx Expr<'_>, +) { + // We only lint ifs with multiple blocks + // TODO xFrednet 2021-01-01: Check if it's an else if block + if blocks.len() < 2 { + return; + } - if let Some((i, j)) = search_same_sequenced(blocks, eq) { - span_lint_and_note( + let has_expr = blocks[0].expr.is_some(); + + // Check if each block has shared code + let mut start_eq = usize::MAX; + let mut end_eq = usize::MAX; + let mut expr_eq = true; + for (index, win) in blocks.windows(2).enumerate() { + let l_stmts = win[0].stmts; + let r_stmts = win[1].stmts; + + let mut evaluator = SpanlessEq::new(cx); + let current_start_eq = count_eq(&mut l_stmts.iter(), &mut r_stmts.iter(), |l, r| evaluator.eq_stmt(l, r)); + let current_end_eq = count_eq(&mut l_stmts.iter().rev(), &mut r_stmts.iter().rev(), |l, r| { + evaluator.eq_stmt(l, r) + }); + let block_expr_eq = both(&win[0].expr, &win[1].expr, |l, r| evaluator.eq_expr(l, r)); + + // IF_SAME_THEN_ELSE + // We only lint the first two blocks (index == 0). Further blocks will be linted when that if + // statement is checked + if index == 0 && block_expr_eq && l_stmts.len() == r_stmts.len() && l_stmts.len() == current_start_eq { + span_lint_and_note( + cx, + IF_SAME_THEN_ELSE, + win[0].span, + "this `if` has identical blocks", + Some(win[1].span), + "same as this", + ); + + return; + } + + start_eq = start_eq.min(current_start_eq); + end_eq = end_eq.min(current_end_eq); + expr_eq &= block_expr_eq; + + // We can return if the eq count is 0 from both sides or if it has no unconditional else case + if !has_unconditional_else || (start_eq == 0 && end_eq == 0 && (has_expr && !expr_eq)) { + return; + } + } + + if has_expr && !expr_eq { + end_eq = 0; + } + + // Check if the regions are overlapping. Set `end_eq` to prevent the overlap + let min_block_size = blocks.iter().map(|x| x.stmts.len()).min().unwrap(); + if (start_eq + end_eq) > min_block_size { + end_eq = min_block_size - start_eq; + } + + // Only the start is the same + if start_eq != 0 && end_eq == 0 && (!has_expr || !expr_eq) { + emit_shared_code_in_if_blocks_lint(cx, start_eq, 0, false, blocks, expr); + } else if end_eq != 0 && (!has_expr || !expr_eq) { + let block = blocks[blocks.len() - 1]; + let stmts = block.stmts.split_at(start_eq).1; + let (block_stmts, moved_stmts) = stmts.split_at(stmts.len() - end_eq); + + // Scan block + let mut walker = SymbolFinderVisitor::new(cx); + for stmt in block_stmts { + intravisit::walk_stmt(&mut walker, stmt); + } + let mut block_defs = walker.defs; + + // Scan moved stmts + let mut moved_start: Option = None; + let mut walker = SymbolFinderVisitor::new(cx); + for (index, stmt) in moved_stmts.iter().enumerate() { + intravisit::walk_stmt(&mut walker, stmt); + + for value in &walker.uses { + // Well we can't move this and all prev statements. So reset + if block_defs.contains(&value) { + moved_start = Some(index + 1); + walker.defs.drain().for_each(|x| { + block_defs.insert(x); + }); + } + } + + walker.uses.clear(); + } + + if let Some(moved_start) = moved_start { + end_eq -= moved_start; + } + + let mut end_linable = true; + if let Some(expr) = block.expr { + intravisit::walk_expr(&mut walker, expr); + end_linable = walker.uses.iter().any(|x| !block_defs.contains(x)); + } + + emit_shared_code_in_if_blocks_lint(cx, start_eq, end_eq, end_linable, blocks, expr); + } +} + +fn emit_shared_code_in_if_blocks_lint( + cx: &LateContext<'tcx>, + start_stmts: usize, + end_stmts: usize, + lint_end: bool, + blocks: &[&Block<'tcx>], + if_expr: &'tcx Expr<'_>, +) { + if start_stmts == 0 && !lint_end { + return; + } + + // (help, span, suggestion) + let mut suggestions: Vec<(&str, Span, String)> = vec![]; + + 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 cond_span = first_line_of_span(cx, if_expr.span).until(block.span); + let cond_snippet = reindent_multiline(snippet(cx, cond_span, "_"), false, None); + let cond_indent = indent_of(cx, cond_span); + let moved_span = block.stmts[0].span.source_callsite().to(span_end); + let moved_snippet = reindent_multiline(snippet(cx, moved_span, "_"), true, None); + let suggestion = moved_snippet.to_string() + "\n" + &cond_snippet + "{"; + let suggestion = reindent_multiline(Cow::Borrowed(&suggestion), true, cond_indent); + + let span = span_start.to(span_end); + suggestions.push(("START HELP", span, suggestion.to_string())); + } + + if lint_end { + let block = blocks[blocks.len() - 1]; + let span_end = block.span.shrink_to_hi(); + + let moved_start = if end_stmts == 0 && block.expr.is_some() { + block.expr.unwrap().span + } else { + block.stmts[block.stmts.len() - end_stmts].span + } + .source_callsite(); + let moved_end = if let Some(expr) = block.expr { + expr.span + } else { + block.stmts[block.stmts.len() - 1].span + } + .source_callsite(); + + let moved_span = moved_start.to(moved_end); + let moved_snipped = reindent_multiline(snippet(cx, moved_span, "_"), true, None); + let indent = indent_of(cx, if_expr.span.shrink_to_hi()); + let suggestion = "}\n".to_string() + &moved_snipped; + let suggestion = reindent_multiline(Cow::Borrowed(&suggestion), true, indent); + + let span = moved_start.to(span_end); + suggestions.push(("END_RANGE", span, suggestion.to_string())); + } + + if suggestions.len() == 1 { + let (_, span, sugg) = &suggestions[0]; + span_lint_and_sugg( cx, - IF_SAME_THEN_ELSE, - j.span, - "this `if` has identical blocks", - Some(i.span), - "same as this", + SHARED_CODE_IN_IF_BLOCKS, + *span, + "All code blocks contain the same code", + "Consider moving the code out like this", + sugg.clone(), + Applicability::Unspecified, + ); + } else { + span_lint_and_then( + cx, + SHARED_CODE_IN_IF_BLOCKS, + if_expr.span, + "All if blocks contain the same code", + move |diag| { + for (help, span, sugg) in suggestions { + diag.span_suggestion(span, help, sugg, Applicability::Unspecified); + } + }, ); } } +pub struct SymbolFinderVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + defs: FxHashSet, + uses: FxHashSet, +} + +impl<'a, 'tcx> SymbolFinderVisitor<'a, 'tcx> { + fn new(cx: &'a LateContext<'tcx>) -> Self { + SymbolFinderVisitor { + cx, + defs: FxHashSet::default(), + uses: FxHashSet::default(), + } + } +} + +impl<'a, 'tcx> Visitor<'tcx> for SymbolFinderVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::All(self.cx.tcx.hir()) + } + + fn visit_local(&mut self, l: &'tcx rustc_hir::Local<'tcx>) { + let local_id = l.pat.hir_id; + self.defs.insert(local_id); + if let Some(expr) = l.init { + intravisit::walk_expr(self, expr); + } + } + + fn visit_qpath(&mut self, qpath: &'tcx rustc_hir::QPath<'tcx>, id: HirId, _span: rustc_span::Span) { + if let rustc_hir::QPath::Resolved(_, ref path) = *qpath { + if path.segments.len() == 1 { + if let rustc_hir::def::Res::Local(var) = self.cx.qpath_res(qpath, id) { + self.uses.insert(var); + } + } + } + } +} + /// Implementation of `IFS_SAME_COND`. fn lint_same_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { let hash: &dyn Fn(&&Expr<'_>) -> u64 = &|expr| -> u64 { @@ -198,15 +465,3 @@ fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { ); } } - -fn search_same_sequenced(exprs: &[T], eq: Eq) -> Option<(&T, &T)> -where - Eq: Fn(&T, &T) -> bool, -{ - for win in exprs.windows(2) { - if eq(&win[0], &win[1]) { - return Some((&win[0], &win[1])); - } - } - None -} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index b93e6b5b2e8..ba98a2a2607 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -616,6 +616,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &copies::IFS_SAME_COND, &copies::IF_SAME_THEN_ELSE, &copies::SAME_FUNCTIONS_IN_IF_CONDITION, + &copies::SHARED_CODE_IN_IF_BLOCKS, ©_iterator::COPY_ITERATOR, &create_dir::CREATE_DIR, &dbg_macro::DBG_MACRO, @@ -1365,6 +1366,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&casts::PTR_AS_PTR), LintId::of(&checked_conversions::CHECKED_CONVERSIONS), LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION), + LintId::of(&copies::SHARED_CODE_IN_IF_BLOCKS), LintId::of(©_iterator::COPY_ITERATOR), LintId::of(&default::DEFAULT_TRAIT_ACCESS), LintId::of(&dereference::EXPLICIT_DEREF_METHODS), diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index 7fd55151226..b736eeff6bf 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -464,32 +464,28 @@ impl DecimalLiteralRepresentation { { return Err(WarningType::DecimalRepresentation); } - } else if digits.len() < 4 { - // Lint for Literals with a hex-representation of 2 or 3 digits - let f = &digits[0..1]; // first digit - let s = &digits[1..]; // suffix - - // Powers of 2 - if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && s.chars().all(|c| c == '0')) - // Powers of 2 minus 1 - || ((f.eq("1") || f.eq("3") || f.eq("7") || f.eq("F")) && s.chars().all(|c| c == 'F')) - { - return Err(WarningType::DecimalRepresentation); - } } else { - // Lint for Literals with a hex-representation of 4 digits or more let f = &digits[0..1]; // first digit let m = &digits[1..digits.len() - 1]; // middle digits, except last let s = &digits[1..]; // suffix - - // Powers of 2 with a margin of +15/-16 - if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && m.chars().all(|c| c == '0')) - || ((f.eq("1") || f.eq("3") || f.eq("7") || f.eq("F")) && m.chars().all(|c| c == 'F')) - // Lint for representations with only 0s and Fs, while allowing 7 as the first - // digit - || ((f.eq("7") || f.eq("F")) && s.chars().all(|c| c == '0' || c == 'F')) - { - return Err(WarningType::DecimalRepresentation); + if digits.len() < 4 { + // Powers of 2 + if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && s.chars().all(|c| c == '0')) + // Powers of 2 minus 1 + || ((f.eq("1") || f.eq("3") || f.eq("7") || f.eq("F")) && s.chars().all(|c| c == 'F')) + { + return Err(WarningType::DecimalRepresentation); + } + } else { + // Powers of 2 with a margin of +15/-16 + if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && m.chars().all(|c| c == '0')) + || ((f.eq("1") || f.eq("3") || f.eq("7") || f.eq("F")) && m.chars().all(|c| c == 'F')) + // Lint for representations with only 0s and Fs, while allowing 7 as the first + // digit + || ((f.eq("7") || f.eq("F")) && s.chars().all(|c| c == '0' || c == 'F')) + { + return Err(WarningType::DecimalRepresentation); + } } } diff --git a/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/clippy_lints/src/methods/manual_saturating_arithmetic.rs index 6c5a842a912..ecb8b72ef46 100644 --- a/clippy_lints/src/methods/manual_saturating_arithmetic.rs +++ b/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -44,44 +44,28 @@ pub fn check( // "mul" is omitted because lhs can be negative. _ => return, } - - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - super::MANUAL_SATURATING_ARITHMETIC, - expr.span, - "manual saturating arithmetic", - &format!("try using `saturating_{}`", arith), - format!( - "{}.saturating_{}({})", - snippet_with_applicability(cx, arith_lhs.span, "..", &mut applicability), - arith, - snippet_with_applicability(cx, arith_rhs.span, "..", &mut applicability), - ), - applicability, - ); } else { match (mm, arith) { (MinMax::Max, "add" | "mul") | (MinMax::Min, "sub") => (), _ => return, } - - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - super::MANUAL_SATURATING_ARITHMETIC, - expr.span, - "manual saturating arithmetic", - &format!("try using `saturating_{}`", arith), - format!( - "{}.saturating_{}({})", - snippet_with_applicability(cx, arith_lhs.span, "..", &mut applicability), - arith, - snippet_with_applicability(cx, arith_rhs.span, "..", &mut applicability), - ), - applicability, - ); } + + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + super::MANUAL_SATURATING_ARITHMETIC, + expr.span, + "manual saturating arithmetic", + &format!("try using `saturating_{}`", arith), + format!( + "{}.saturating_{}({})", + snippet_with_applicability(cx, arith_lhs.span, "..", &mut applicability), + arith, + snippet_with_applicability(cx, arith_rhs.span, "..", &mut applicability), + ), + applicability, + ); } #[derive(PartialEq, Eq)] diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 0c3b8b89171..8b90fedbafe 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -483,6 +483,15 @@ pub fn over(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) - left.len() == right.len() && left.iter().zip(right).all(|(x, y)| eq_fn(x, y)) } +/// Counts how many elements of the slices are equal as per `eq_fn`. +pub fn count_eq( + left: &mut dyn Iterator, + right: &mut dyn Iterator, + mut eq_fn: impl FnMut(&X, &X) -> bool, +) -> usize { + left.zip(right).take_while(|(l, r)| eq_fn(l, r)).count() +} + /// Checks if two expressions evaluate to the same value, and don't contain any side effects. pub fn eq_expr_value(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>) -> bool { SpanlessEq::new(cx).deny_side_effects().eq_expr(left, right) diff --git a/tests/ui/checked_unwrap/complex_conditionals.rs b/tests/ui/checked_unwrap/complex_conditionals.rs index c986c992a07..bb5b6f5ec04 100644 --- a/tests/ui/checked_unwrap/complex_conditionals.rs +++ b/tests/ui/checked_unwrap/complex_conditionals.rs @@ -1,5 +1,5 @@ #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] -#![allow(clippy::if_same_then_else)] +#![allow(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] fn test_complex_conditions() { let x: Result<(), ()> = Ok(()); diff --git a/tests/ui/if_same_then_else.rs b/tests/ui/if_same_then_else.rs index 9c5fe02f751..35a2e139c11 100644 --- a/tests/ui/if_same_then_else.rs +++ b/tests/ui/if_same_then_else.rs @@ -5,7 +5,8 @@ clippy::never_loop, clippy::no_effect, clippy::unused_unit, - clippy::zero_divided_by_zero + clippy::zero_divided_by_zero, + clippy::shared_code_in_if_blocks )] struct Foo { diff --git a/tests/ui/if_same_then_else.stderr b/tests/ui/if_same_then_else.stderr index d9fdf06fa8b..2f38052fc20 100644 --- a/tests/ui/if_same_then_else.stderr +++ b/tests/ui/if_same_then_else.stderr @@ -1,111 +1,111 @@ error: this `if` has identical blocks - --> $DIR/if_same_then_else.rs:28:12 + --> $DIR/if_same_then_else.rs:21:13 | -LL | } else { - | ____________^ -LL | | //~ ERROR same body as `if` block +LL | if true { + | _____________^ LL | | Foo { bar: 42 }; LL | | 0..10; +LL | | ..; ... | LL | | foo(); -LL | | } +LL | | } else { | |_____^ | = note: `-D clippy::if-same-then-else` implied by `-D warnings` note: same as this - --> $DIR/if_same_then_else.rs:20:13 + --> $DIR/if_same_then_else.rs:29:12 | -LL | if true { - | _____________^ +LL | } else { + | ____________^ +LL | | //~ ERROR same body as `if` block LL | | Foo { bar: 42 }; LL | | 0..10; -LL | | ..; ... | LL | | foo(); -LL | | } else { +LL | | } | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else.rs:66:12 - | -LL | } else { - | ____________^ -LL | | //~ ERROR same body as `if` block -LL | | 0.0 -LL | | }; - | |_____^ - | -note: same as this - --> $DIR/if_same_then_else.rs:64:21 + --> $DIR/if_same_then_else.rs:65:21 | LL | let _ = if true { | _____________________^ LL | | 0.0 LL | | } else { | |_____^ - -error: this `if` has identical blocks - --> $DIR/if_same_then_else.rs:73:12 + | +note: same as this + --> $DIR/if_same_then_else.rs:67:12 | LL | } else { | ____________^ LL | | //~ ERROR same body as `if` block -LL | | -0.0 +LL | | 0.0 LL | | }; | |_____^ - | -note: same as this - --> $DIR/if_same_then_else.rs:71:21 + +error: this `if` has identical blocks + --> $DIR/if_same_then_else.rs:72:21 | LL | let _ = if true { | _____________________^ LL | | -0.0 LL | | } else { | |_____^ - -error: this `if` has identical blocks - --> $DIR/if_same_then_else.rs:89:12 + | +note: same as this + --> $DIR/if_same_then_else.rs:74:12 | LL | } else { | ____________^ LL | | //~ ERROR same body as `if` block -LL | | 42 +LL | | -0.0 LL | | }; | |_____^ - | -note: same as this - --> $DIR/if_same_then_else.rs:87:21 + +error: this `if` has identical blocks + --> $DIR/if_same_then_else.rs:88:21 | LL | let _ = if true { | _____________________^ LL | | 42 LL | | } else { | |_____^ - -error: this `if` has identical blocks - --> $DIR/if_same_then_else.rs:101:12 + | +note: same as this + --> $DIR/if_same_then_else.rs:90:12 | LL | } else { | ____________^ LL | | //~ ERROR same body as `if` block +LL | | 42 +LL | | }; + | |_____^ + +error: this `if` has identical blocks + --> $DIR/if_same_then_else.rs:95:13 + | +LL | if true { + | _____________^ LL | | let bar = if true { 42 } else { 43 }; LL | | +LL | | while foo() { ... | LL | | bar + 1; -LL | | } +LL | | } else { | |_____^ | note: same as this - --> $DIR/if_same_then_else.rs:94:13 + --> $DIR/if_same_then_else.rs:102:12 | -LL | if true { - | _____________^ +LL | } else { + | ____________^ +LL | | //~ ERROR same body as `if` block LL | | let bar = if true { 42 } else { 43 }; LL | | -LL | | while foo() { ... | LL | | bar + 1; -LL | | } else { +LL | | } | |_____^ error: aborting due to 5 previous errors diff --git a/tests/ui/if_same_then_else2.rs b/tests/ui/if_same_then_else2.rs index a2ff1b741ca..8164b125570 100644 --- a/tests/ui/if_same_then_else2.rs +++ b/tests/ui/if_same_then_else2.rs @@ -5,7 +5,8 @@ clippy::collapsible_if, clippy::ifs_same_cond, clippy::needless_return, - clippy::single_element_loop + clippy::single_element_loop, + clippy::shared_code_in_if_blocks )] fn if_same_then_else2() -> Result<&'static str, ()> { diff --git a/tests/ui/if_same_then_else2.stderr b/tests/ui/if_same_then_else2.stderr index 454322d8aac..941b92f23f3 100644 --- a/tests/ui/if_same_then_else2.stderr +++ b/tests/ui/if_same_then_else2.stderr @@ -24,18 +24,22 @@ LL | | if foo.is_some() { LL | | } LL | | } else { | |_____^ - -error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:35:12 + | + = note: `-D clippy::if-same-then-else` implied by `-D warnings` +note: same as this + --> $DIR/if_same_then_else2.rs:21:12 | LL | } else { | ____________^ LL | | //~ ERROR same body as `if` block -LL | | if let Some(a) = Some(42) {} +LL | | for _ in &[42] { +LL | | let foo: &Option<_> = &Some::(42); +... | +LL | | } LL | | } | |_____^ - | -note: same as this + +error: this `if` has identical blocks --> $DIR/if_same_then_else2.rs:33:13 | LL | if true { @@ -43,18 +47,18 @@ LL | if true { LL | | if let Some(a) = Some(42) {} LL | | } else { | |_____^ - -error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:42:12 + | +note: same as this + --> $DIR/if_same_then_else2.rs:35:12 | LL | } else { | ____________^ LL | | //~ ERROR same body as `if` block -LL | | if let (1, .., 3) = (1, 2, 3) {} +LL | | if let Some(a) = Some(42) {} LL | | } | |_____^ - | -note: same as this + +error: this `if` has identical blocks --> $DIR/if_same_then_else2.rs:40:13 | LL | if true { @@ -62,18 +66,18 @@ LL | if true { LL | | if let (1, .., 3) = (1, 2, 3) {} LL | | } else { | |_____^ - -error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:92:12 + | +note: same as this + --> $DIR/if_same_then_else2.rs:42:12 | LL | } else { | ____________^ LL | | //~ ERROR same body as `if` block -LL | | f32::NAN -LL | | }; +LL | | if let (1, .., 3) = (1, 2, 3) {} +LL | | } | |_____^ - | -note: same as this + +error: this `if` has identical blocks --> $DIR/if_same_then_else2.rs:90:21 | LL | let _ = if true { @@ -81,18 +85,18 @@ LL | let _ = if true { LL | | f32::NAN LL | | } else { | |_____^ - -error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:99:12 + | +note: same as this + --> $DIR/if_same_then_else2.rs:92:12 | LL | } else { | ____________^ LL | | //~ ERROR same body as `if` block -LL | | Ok("foo")?; -LL | | } +LL | | f32::NAN +LL | | }; | |_____^ - | -note: same as this + +error: this `if` has identical blocks --> $DIR/if_same_then_else2.rs:97:13 | LL | if true { @@ -100,18 +104,18 @@ LL | if true { LL | | Ok("foo")?; LL | | } else { | |_____^ - -error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:124:12 + | +note: same as this + --> $DIR/if_same_then_else2.rs:99:12 | LL | } else { | ____________^ -LL | | let foo = ""; -LL | | return Ok(&foo[0..]); +LL | | //~ ERROR same body as `if` block +LL | | Ok("foo")?; LL | | } | |_____^ - | -note: same as this + +error: this `if` has identical blocks --> $DIR/if_same_then_else2.rs:121:20 | LL | } else if true { @@ -120,6 +124,16 @@ LL | | let foo = ""; LL | | return Ok(&foo[0..]); LL | | } else { | |_____^ + | +note: same as this + --> $DIR/if_same_then_else2.rs:124:12 + | +LL | } else { + | ____________^ +LL | | let foo = ""; +LL | | return Ok(&foo[0..]); +LL | | } + | |_____^ error: aborting due to 6 previous errors diff --git a/tests/ui/let_if_seq.rs b/tests/ui/let_if_seq.rs index 32a67f181df..9fd3f875a5f 100644 --- a/tests/ui/let_if_seq.rs +++ b/tests/ui/let_if_seq.rs @@ -2,7 +2,8 @@ unused_variables, unused_assignments, clippy::similar_names, - clippy::blacklisted_name + clippy::blacklisted_name, + clippy::shared_code_in_if_blocks )] #![warn(clippy::useless_let_if_seq)] diff --git a/tests/ui/let_if_seq.stderr b/tests/ui/let_if_seq.stderr index 7de560c7348..9cf2e10a5ee 100644 --- a/tests/ui/let_if_seq.stderr +++ b/tests/ui/let_if_seq.stderr @@ -1,5 +1,5 @@ error: `if _ { .. } else { .. }` is an expression - --> $DIR/let_if_seq.rs:64:5 + --> $DIR/let_if_seq.rs:65:5 | LL | / let mut foo = 0; LL | | if f() { @@ -11,7 +11,7 @@ LL | | } = note: you might not need `mut` at all error: `if _ { .. } else { .. }` is an expression - --> $DIR/let_if_seq.rs:69:5 + --> $DIR/let_if_seq.rs:70:5 | LL | / let mut bar = 0; LL | | if f() { @@ -25,7 +25,7 @@ LL | | } = note: you might not need `mut` at all error: `if _ { .. } else { .. }` is an expression - --> $DIR/let_if_seq.rs:77:5 + --> $DIR/let_if_seq.rs:78:5 | LL | / let quz; LL | | if f() { @@ -36,7 +36,7 @@ LL | | } | |_____^ help: it is more idiomatic to write: `let quz = if f() { 42 } else { 0 };` error: `if _ { .. } else { .. }` is an expression - --> $DIR/let_if_seq.rs:106:5 + --> $DIR/let_if_seq.rs:107:5 | LL | / let mut baz = 0; LL | | if f() { diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs b/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs new file mode 100644 index 00000000000..22f1fe95f79 --- /dev/null +++ b/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs @@ -0,0 +1,47 @@ +#![allow(dead_code)] +#![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] + +// This tests the shared_code_in_if_blocks lint at the end of blocks + +fn simple_examples() { + // TODO xFrednet 2021-01-06: Test with const literals at the end + let x = 1; + + let _ = if x == 7 { + println!("Branch I"); + let start_value = 0; + println!("=^.^="); + + // Same but not moveable due to `start_value` + let _ = start_value; + + // The rest is self contained and moveable => Only lint the rest + let result = false; + println!("Block end!"); + result + } else { + println!("Branch II"); + let start_value = 8; + println!("xD"); + + // Same but not moveable due to `start_value` + let _ = start_value; + + // The rest is self contained and moveable => Only lint the rest + let result = false; + println!("Block end!"); + result + }; +} + +/// Simple examples where the move can cause some problems due to moved values +fn simple_but_suggestion_is_invalid() { + // TODO xFrednet 2021-01-12: This +} + +/// Tests where the blocks are not linted due to the used value scope +fn not_moveable_due_to_value_scope() { + // TODO xFrednet 2021-01-12: This +} + +fn main() {} diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_top.rs b/tests/ui/shared_code_in_if_blocks/shared_at_top.rs new file mode 100644 index 00000000000..0a6828127e9 --- /dev/null +++ b/tests/ui/shared_code_in_if_blocks/shared_at_top.rs @@ -0,0 +1,83 @@ +#![allow(dead_code, clippy::eval_order_dependence)] +#![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] + +// This tests the shared_code_in_if_blocks lint at the start of blocks + +fn simple_examples() { + let x = 0; + + // Simple + if true { + println!("Hello World!"); + println!("I'm branch nr: 1"); + } else { + println!("Hello World!"); + println!("I'm branch nr: 2"); + } + + // Else if + if x == 0 { + let y = 9; + println!("The value y was set to: `{}`", y); + let _z = y; + + println!("I'm the true start index of arrays"); + } else if x == 1 { + let y = 9; + println!("The value y was set to: `{}`", y); + let _z = y; + + println!("I start counting from 1 so my array starts from `1`"); + } else { + let y = 9; + println!("The value y was set to: `{}`", y); + let _z = y; + + println!("Ha, Pascal allows you to start the array where you want") + } + + // Return a value + let _ = if x == 7 { + let y = 16; + println!("What can I say except: \"you're welcome?\""); + let _ = y; + x + } else { + let y = 16; + println!("Thank you"); + y + }; +} + +/// Simple examples where the move can cause some problems due to moved values +fn simple_but_suggestion_is_invalid() { + let x = 10; + + // Can't be automatically moved because used_value_name is getting used again + let used_value_name = 19; + if x == 10 { + let used_value_name = "Different type"; + println!("Str: {}", used_value_name); + let _ = 1; + } else { + let used_value_name = "Different type"; + println!("Str: {}", used_value_name); + let _ = 2; + } + let _ = used_value_name; + + // This can be automatically moved as `can_be_overridden` is not used again + let can_be_overridden = 8; + let _ = can_be_overridden; + if x == 11 { + let can_be_overridden = "Move me"; + println!("I'm also moveable"); + let _ = 111; + } else { + let can_be_overridden = "Move me"; + println!("I'm also moveable"); + let _ = 222; + } +} + +fn main() {} diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.rs b/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.rs new file mode 100644 index 00000000000..8de69623e5d --- /dev/null +++ b/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.rs @@ -0,0 +1,22 @@ +#![allow(dead_code)] +#![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] + +// shared_code_in_if_blocks at the top and bottom of the if blocks + +fn main() { + // TODO xFrednet 2021-01-12: This +} + +// General TODOs By xFrednet: + +// +// * Make a test with overlapping eq regions (else ifs) +// * Test if as function parameter, tuple constructor, index, while loop condition +// * Test where only the expression is the same +// * Test where the block only has an expression +// * Test with let on a previous line let _ = \n if... +// * Tests with unreadable formatting (Inline if, Not indented) +// * Test multiline condition if x == 9 \n x == 8 {} +// * Test if for return/break (Only move to front) +// * Test in inline closures +// * Test with structs and tuples diff --git a/tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs b/tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs new file mode 100644 index 00000000000..1f12e9348d4 --- /dev/null +++ b/tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs @@ -0,0 +1,157 @@ +#![allow(dead_code, clippy::eval_order_dependence)] +#![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] + +// This tests the shared_code_in_if_blocks lint at the start of blocks + +// Tests with value references are includes in "shared_code_at_bot.rs" + +fn valid_examples() { + let x = 2; + + // The edge statements are different + if x == 9 { + let y = 1 << 5; + + println!("This is the same: vvv"); + let _z = y; + println!("The block expression is different"); + + println!("Different end 1"); + } else { + let y = 1 << 7; + + println!("This is the same: vvv"); + let _z = y; + println!("The block expression is different"); + + println!("Different end 2"); + } + + // No else + if x == 2 { + println!("Hello world!"); + println!("Hello back, how are you?"); + + // This is different vvvv + println!("Howdy stranger =^.^="); + + println!("Bye Bye World"); + } else if x == 9 { + println!("Hello world!"); + println!("Hello back, how are you?"); + + // This is different vvvv + println!("Hello reviewer :D"); + + println!("Bye Bye World"); + } + + // Overlapping statements only in else if blocks -> Don't lint + if x == 0 { + println!("I'm important!") + } else if x == 17 { + println!("I share code in else if"); + + println!("x is 17"); + } else { + println!("I share code in else if"); + + println!("x is nether x nor 17"); + } + + // Mutability is different + if x == 13 { + let mut y = 9; + println!("Value y is: {}", y); + y += 16; + let _z1 = y; + } else { + let y = 9; + println!("Value y is: {}", y); + let _z2 = y; + } + + // Same blocks but at start and bottom so no `if_same_then_else` lint + if x == 418 { + let y = 9; + let z = 8; + let _ = (x, y, z); + // Don't tell the programmer, my code is also in the else block + } else if x == 419 { + println!("+-----------+"); + println!("| |"); + println!("| O O |"); + println!("| ° |"); + println!("| \\_____/ |"); + println!("| |"); + println!("+-----------+"); + } else { + let y = 9; + let z = 8; + let _ = (x, y, z); + // I'm so much better than the x == 418 block. Trust me + } +} + +/// This makes sure that the `if_same_then_else` masks the `shared_code_in_if_blocks` lint +fn trigger_other_lint() { + let x = 0; + let y = 1; + + // Same block + if x == 0 { + let u = 19; + println!("How are u today?"); + let _ = "This is a string"; + } else { + let u = 19; + println!("How are u today?"); + let _ = "This is a string"; + } + + // More complex same blocks + if x == 17 { + #[derive(Debug)] + struct Duck { + num: u64, + }; + let pet = Duck { num: 18 }; + println!("{:?}", pet); + } else { + #[derive(Debug)] + struct Duck { + num: u64, + }; + let pet = Duck { num: 18 }; + println!("{:?}", pet); + } + + // Only same expression + let _ = if x == 6 { 7 } else { 7 }; + + // Same in else if block + let _ = if x == 67 { + println!("Well I'm the most important block"); + "I'm a pretty string" + } else if x == 68 { + println!("I'm a doppelgänger"); + // Don't listen to my clone below + + if y == 90 { + "=^.^=" + } else { + ":D" + } + } else { + // Don't listen to my clone above + println!("I'm a doppelgänger"); + + if y == 90 { + "=^.^=" + } else { + ":D" + } + }; +} + +fn main() {} -- cgit 1.4.1-3-g733a5 From 469ff96db3cc397be46ea9d19c72148c6a95eb6a Mon Sep 17 00:00:00 2001 From: xFrednet Date: Wed, 13 Jan 2021 20:01:15 +0100 Subject: The shared_code_in_if_blocks lint only operats on entire if expr not else ifs --- clippy_lints/src/copies.rs | 52 ++++++---- tests/ui/shared_code_in_if_blocks/shared_at_bot.rs | 107 ++++++++++++++++++++- .../ui/shared_code_in_if_blocks/valid_if_blocks.rs | 10 ++ 3 files changed, 148 insertions(+), 21 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 29ff1970705..7162d809ec6 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -1,7 +1,12 @@ -use clippy_utils::diagnostics::span_lint_and_note; -use clippy_utils::{eq_expr_value, in_macro, search_same, SpanlessEq, SpanlessHash}; -use clippy_utils::{get_parent_expr, if_sequence}; -use rustc_hir::{Block, Expr, ExprKind}; +use crate::utils::{both, count_eq, eq_expr_value, in_macro, search_same, SpanlessEq, SpanlessHash}; +use crate::utils::{ + first_line_of_span, get_parent_expr, higher, if_sequence, indent_of, parent_node_is_if_expr, reindent_multiline, + snippet, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, +}; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Applicability; +use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::{Block, Expr, HirId}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -136,7 +141,7 @@ declare_clippy_lint! { /// }; /// ``` pub SHARED_CODE_IN_IF_BLOCKS, - pedantic, + nursery, "`if` statement with shared code in all blocks" } @@ -180,7 +185,7 @@ fn lint_same_then_else<'tcx>( ) { // We only lint ifs with multiple blocks // TODO xFrednet 2021-01-01: Check if it's an else if block - if blocks.len() < 2 { + if blocks.len() < 2 || parent_node_is_if_expr(expr, cx) { return; } @@ -190,7 +195,7 @@ fn lint_same_then_else<'tcx>( let mut start_eq = usize::MAX; let mut end_eq = usize::MAX; let mut expr_eq = true; - for (index, win) in blocks.windows(2).enumerate() { + for win in blocks.windows(2) { let l_stmts = win[0].stmts; let r_stmts = win[1].stmts; @@ -202,9 +207,7 @@ fn lint_same_then_else<'tcx>( let block_expr_eq = both(&win[0].expr, &win[1].expr, |l, r| evaluator.eq_expr(l, r)); // IF_SAME_THEN_ELSE - // We only lint the first two blocks (index == 0). Further blocks will be linted when that if - // statement is checked - if index == 0 && block_expr_eq && l_stmts.len() == r_stmts.len() && l_stmts.len() == current_start_eq { + if block_expr_eq && l_stmts.len() == r_stmts.len() && l_stmts.len() == current_start_eq { span_lint_and_note( cx, IF_SAME_THEN_ELSE, @@ -215,16 +218,24 @@ fn lint_same_then_else<'tcx>( ); return; + } else { + println!( + "{:?}\n - expr_eq: {:10}, l_stmts.len(): {:10}, r_stmts.len(): {:10}", + win[0].span, + block_expr_eq, + l_stmts.len(), + r_stmts.len() + ) } start_eq = start_eq.min(current_start_eq); end_eq = end_eq.min(current_end_eq); expr_eq &= block_expr_eq; + } - // We can return if the eq count is 0 from both sides or if it has no unconditional else case - if !has_unconditional_else || (start_eq == 0 && end_eq == 0 && (has_expr && !expr_eq)) { - return; - } + // SHARED_CODE_IN_IF_BLOCKS prerequisites + if !has_unconditional_else || (start_eq == 0 && end_eq == 0 && (has_expr && !expr_eq)) { + return; } if has_expr && !expr_eq { @@ -275,11 +286,14 @@ fn lint_same_then_else<'tcx>( end_eq -= moved_start; } - let mut end_linable = true; - if let Some(expr) = block.expr { + let end_linable = if let Some(expr) = block.expr { intravisit::walk_expr(&mut walker, expr); - end_linable = walker.uses.iter().any(|x| !block_defs.contains(x)); - } + walker.uses.iter().any(|x| !block_defs.contains(x)) + } else if end_eq == 0 { + false + } else { + true + }; emit_shared_code_in_if_blocks_lint(cx, start_eq, end_eq, end_linable, blocks, expr); } @@ -351,7 +365,7 @@ fn emit_shared_code_in_if_blocks_lint( SHARED_CODE_IN_IF_BLOCKS, *span, "All code blocks contain the same code", - "Consider moving the code out like this", + "Consider moving the statements out like this", sugg.clone(), Applicability::Unspecified, ); diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs b/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs index 22f1fe95f79..b85bb2e1f96 100644 --- a/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs +++ b/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs @@ -32,16 +32,119 @@ fn simple_examples() { println!("Block end!"); result }; + + if x == 9 { + println!("The index is: 6"); + + println!("Same end of block"); + } else if x == 8 { + println!("The index is: 4"); + + println!("Same end of block"); + } else { + println!("Same end of block"); + } + + // TODO xFrednet 2021-01.13: Fix lint for `if let` + let index = Some(8); + if let Some(index) = index { + println!("The index is: {}", index); + + println!("Same end of block"); + } else { + println!("Same end of block"); + } + + if x == 9 { + if x == 8 { + // No parent!! + println!("Hello World"); + println!("---"); + } else { + println!("Hello World"); + } + } } /// Simple examples where the move can cause some problems due to moved values fn simple_but_suggestion_is_invalid() { - // TODO xFrednet 2021-01-12: This + let x = 16; + + // Local value + let later_used_value = 17; + if x == 9 { + let _ = 9; + let later_used_value = "A string value"; + println!("{}", later_used_value); + } else { + let later_used_value = "A string value"; + println!("{}", later_used_value); + // I'm expecting a note about this + } + println!("{}", later_used_value); + + // outer function + if x == 78 { + let simple_examples = "I now identify as a &str :)"; + println!("This is the new simple_example: {}", simple_examples); + } else { + println!("Separator print statement"); + + let simple_examples = "I now identify as a &str :)"; + println!("This is the new simple_example: {}", simple_examples); + } + simple_examples(); } /// Tests where the blocks are not linted due to the used value scope fn not_moveable_due_to_value_scope() { - // TODO xFrednet 2021-01-12: This + let x = 18; + + // Using a local value in the moved code + if x == 9 { + let y = 18; + println!("y is: `{}`", y); + } else { + let y = "A string"; + println!("y is: `{}`", y); + } + + // Using a local value in the expression + let _ = if x == 0 { + let mut result = x + 1; + + println!("1. Doing some calculations"); + println!("2. Some more calculations"); + println!("3. Setting result"); + + result + } else { + let mut result = x - 1; + + println!("1. Doing some calculations"); + println!("2. Some more calculations"); + println!("3. Setting result"); + + result + }; + + let _ = if x == 7 { + let z1 = 100; + println!("z1: {}", z1); + + let z2 = z1; + println!("z2: {}", z2); + + z2 + } else { + let z1 = 300; + println!("z1: {}", z1); + + let z2 = z1; + println!("z2: {}", z2); + + z2 + }; } fn main() {} diff --git a/tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs b/tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs index 1f12e9348d4..480758777f1 100644 --- a/tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs +++ b/tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs @@ -152,6 +152,16 @@ fn trigger_other_lint() { ":D" } }; + + if x == 0 { + println!("I'm single"); + } else if x == 68 { + println!("I'm a doppelgänger"); + // Don't listen to my clone below + } else { + // Don't listen to my clone above + println!("I'm a doppelgänger"); + } } fn main() {} -- cgit 1.4.1-3-g733a5 From 8efc6acc05387738313798069b8553d0ab9c92e5 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sat, 16 Jan 2021 14:04:14 +0100 Subject: Improved shared_code_in_if_blocks output readability and added tests --- clippy_lints/src/copies.rs | 69 +++++++++------ tests/ui/shared_code_in_if_blocks/shared_at_bot.rs | 35 ++++++++ .../shared_at_top_and_bot.rs | 98 ++++++++++++++++++---- 3 files changed, 161 insertions(+), 41 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 7162d809ec6..089f8c3ab0d 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -1,16 +1,16 @@ use crate::utils::{both, count_eq, eq_expr_value, in_macro, search_same, SpanlessEq, SpanlessHash}; use crate::utils::{ - first_line_of_span, get_parent_expr, higher, if_sequence, indent_of, parent_node_is_if_expr, reindent_multiline, - snippet, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, + first_line_of_span, get_parent_expr, if_sequence, indent_of, parent_node_is_if_expr, reindent_multiline, snippet, + snippet_opt, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, }; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; -use rustc_hir::{Block, Expr, HirId}; +use rustc_hir::{Block, Expr, ExprKind, HirId}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Span; +use rustc_span::{source_map::Span, BytePos}; use std::borrow::Cow; declare_clippy_lint! { @@ -218,14 +218,6 @@ fn lint_same_then_else<'tcx>( ); return; - } else { - println!( - "{:?}\n - expr_eq: {:10}, l_stmts.len(): {:10}, r_stmts.len(): {:10}", - win[0].span, - block_expr_eq, - l_stmts.len(), - r_stmts.len() - ) } start_eq = start_eq.min(current_start_eq); @@ -328,7 +320,7 @@ fn emit_shared_code_in_if_blocks_lint( let suggestion = reindent_multiline(Cow::Borrowed(&suggestion), true, cond_indent); let span = span_start.to(span_end); - suggestions.push(("START HELP", span, suggestion.to_string())); + suggestions.push(("start", span, suggestion.to_string())); } if lint_end { @@ -354,31 +346,56 @@ fn emit_shared_code_in_if_blocks_lint( let suggestion = "}\n".to_string() + &moved_snipped; let suggestion = reindent_multiline(Cow::Borrowed(&suggestion), true, indent); - let span = moved_start.to(span_end); - suggestions.push(("END_RANGE", span, suggestion.to_string())); + let mut span = moved_start.to(span_end); + // Improve formatting if the inner block has indention (i.e. normal Rust formatting) + let test_span = Span::new(span.lo() - BytePos(4), span.lo(), span.ctxt()); + if snippet_opt(cx, test_span) + .map(|snip| snip == " ") + .unwrap_or_default() + { + span = span.with_lo(test_span.lo()); + } + + suggestions.push(("end", span, suggestion.to_string())); } if suggestions.len() == 1 { - let (_, span, sugg) = &suggestions[0]; + let (place_str, span, sugg) = suggestions.pop().unwrap(); + let msg = format!("All if blocks contain the same code at the {}", place_str); + let help = format!("Consider moving the {} statements out like this", place_str); span_lint_and_sugg( cx, SHARED_CODE_IN_IF_BLOCKS, - *span, - "All code blocks contain the same code", - "Consider moving the statements out like this", - sugg.clone(), + span, + msg.as_str(), + help.as_str(), + sugg, Applicability::Unspecified, ); - } else { + } else if suggestions.len() == 2 { + let (_, end_span, end_sugg) = suggestions.pop().unwrap(); + let (_, start_span, start_sugg) = suggestions.pop().unwrap(); span_lint_and_then( cx, SHARED_CODE_IN_IF_BLOCKS, - if_expr.span, - "All if blocks contain the same code", + start_span, + "All if blocks contain the same code at the start and the end. Here at the start:", move |diag| { - for (help, span, sugg) in suggestions { - diag.span_suggestion(span, help, sugg, Applicability::Unspecified); - } + diag.span_note(end_span, "And here at the end:"); + + diag.span_suggestion( + start_span, + "Consider moving the start statements out like this:", + start_sugg, + Applicability::Unspecified, + ); + + diag.span_suggestion( + end_span, + "And consider moving the end statements out like this:", + end_sugg, + Applicability::Unspecified, + ); }, ); } diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs b/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs index b85bb2e1f96..a586a1c9d45 100644 --- a/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs +++ b/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs @@ -33,6 +33,7 @@ fn simple_examples() { result }; + // Else if block if x == 9 { println!("The index is: 6"); @@ -40,9 +41,32 @@ fn simple_examples() { } else if x == 8 { println!("The index is: 4"); + // We should only get a lint trigger for the last statement + println!("This is also eq with the else block"); println!("Same end of block"); } else { println!("Same end of block"); + println!("This is also eq with the else block"); + } + + // Use of outer scope value + let outer_scope_value = "I'm outside the if block"; + if x < 99 { + let z = "How are you"; + println!("I'm a local because I use the value `z`: `{}`", z); + + println!( + "I'm moveable because I know: `outer_scope_value`: '{}'", + outer_scope_value + ); + } else { + let z = 45678000; + println!("I'm a local because I use the value `z`: `{}`", z); + + println!( + "I'm moveable because I know: `outer_scope_value`: '{}'", + outer_scope_value + ); } // TODO xFrednet 2021-01.13: Fix lint for `if let` @@ -147,4 +171,15 @@ fn not_moveable_due_to_value_scope() { }; } +#[rustfmt::skip] +fn test_suggestion_with_weird_formatting() { + let x = 9; + let mut a = 0; + let mut b = 0; + + // The error message still looks weird tbh but this is the best I can do + // for weird formatting + if x == 17 { b = 1; a = 0x99; } else { a = 0x99; } +} + fn main() {} diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.rs b/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.rs index 8de69623e5d..70f969aaf2e 100644 --- a/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.rs +++ b/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.rs @@ -3,20 +3,88 @@ // shared_code_in_if_blocks at the top and bottom of the if blocks -fn main() { - // TODO xFrednet 2021-01-12: This +struct DataPack { + id: u32, + name: String, + some_data: Vec, } -// General TODOs By xFrednet: - -// -// * Make a test with overlapping eq regions (else ifs) -// * Test if as function parameter, tuple constructor, index, while loop condition -// * Test where only the expression is the same -// * Test where the block only has an expression -// * Test with let on a previous line let _ = \n if... -// * Tests with unreadable formatting (Inline if, Not indented) -// * Test multiline condition if x == 9 \n x == 8 {} -// * Test if for return/break (Only move to front) -// * Test in inline closures -// * Test with structs and tuples +fn overlapping_eq_regions() { + let x = 9; + + // Overlap with separator + if x == 7 { + let t = 7; + let _overlap_start = t * 2; + let _overlap_end = 2 * t; + let _u = 9; + } else { + let t = 7; + let _overlap_start = t * 2; + let _overlap_end = 2 * t; + println!("Overlap separator"); + let _overlap_start = t * 2; + let _overlap_end = 2 * t; + let _u = 9; + } + + // Overlap with separator + if x == 99 { + let r = 7; + let _overlap_start = r; + let _overlap_middle = r * r; + let _overlap_end = r * r * r; + let z = "end"; + } else { + let r = 7; + let _overlap_start = r; + let _overlap_middle = r * r; + let _overlap_middle = r * r; + let _overlap_end = r * r * r; + let z = "end"; + } +} + +fn complexer_example() { + fn gen_id(x: u32, y: u32) -> u32 { + let x = x & 0x0000_ffff; + let y = (y & 0xffff_0000) << 16; + x | y + } + + fn process_data(data: DataPack) { + let _ = data; + } + + let x = 8; + let y = 9; + if (x > 7 && y < 13) || (x + y) % 2 == 1 { + let a = 0xcafe; + let b = 0xffff00ff; + let e_id = gen_id(a, b); + + println!("From the a `{}` to the b `{}`", a, b); + + let pack = DataPack { + id: e_id, + name: "Player 1".to_string(), + some_data: vec![0x12, 0x34, 0x56, 0x78, 0x90], + }; + process_data(pack); + } else { + let a = 0xcafe; + let b = 0xffff00ff; + let e_id = gen_id(a, b); + + println!("The new ID is '{}'", e_id); + + let pack = DataPack { + id: e_id, + name: "Player 1".to_string(), + some_data: vec![0x12, 0x34, 0x56, 0x78, 0x90], + }; + process_data(pack); + } +} + +fn main() {} -- cgit 1.4.1-3-g733a5 From b1d26e544f10b814d9793294d0c05dd2ace5d0dc Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sat, 16 Jan 2021 20:37:50 +0100 Subject: Improved shared_code_in_if_blocks message and added test stderrs --- clippy_lints/src/copies.rs | 169 ++++++++++++++++----- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/literal_representation.rs | 40 ++--- tests/ui/if_same_then_else2.stderr | 22 +-- tests/ui/shared_code_in_if_blocks/shared_at_bot.rs | 19 +++ .../shared_code_in_if_blocks/shared_at_bot.stderr | 131 ++++++++++++++++ .../shared_code_in_if_blocks/shared_at_top.stderr | 83 ++++++++++ .../shared_at_top_and_bot.rs | 29 ++++ .../shared_at_top_and_bot.stderr | 154 +++++++++++++++++++ .../valid_if_blocks.stderr | 107 +++++++++++++ 10 files changed, 692 insertions(+), 64 deletions(-) create mode 100644 tests/ui/shared_code_in_if_blocks/shared_at_bot.stderr create mode 100644 tests/ui/shared_code_in_if_blocks/shared_at_top.stderr create mode 100644 tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.stderr create mode 100644 tests/ui/shared_code_in_if_blocks/valid_if_blocks.stderr (limited to 'tests') diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 089f8c3ab0d..47732f4766d 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -1,16 +1,16 @@ use crate::utils::{both, count_eq, eq_expr_value, in_macro, search_same, SpanlessEq, SpanlessHash}; use crate::utils::{ - first_line_of_span, get_parent_expr, if_sequence, indent_of, parent_node_is_if_expr, reindent_multiline, snippet, - snippet_opt, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, + first_line_of_span, get_enclosing_block, get_parent_expr, if_sequence, indent_of, parent_node_is_if_expr, + reindent_multiline, snippet, snippet_opt, span_lint_and_note, span_lint_and_then, ContainsName, }; use rustc_data_structures::fx::FxHashSet; -use rustc_errors::Applicability; +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_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{source_map::Span, BytePos}; +use rustc_span::{source_map::Span, symbol::Symbol, BytePos}; use std::borrow::Cow; declare_clippy_lint! { @@ -184,7 +184,6 @@ fn lint_same_then_else<'tcx>( expr: &'tcx Expr<'_>, ) { // We only lint ifs with multiple blocks - // TODO xFrednet 2021-01-01: Check if it's an else if block if blocks.len() < 2 || parent_node_is_if_expr(expr, cx) { return; } @@ -242,36 +241,61 @@ fn lint_same_then_else<'tcx>( // Only the start is the same if start_eq != 0 && end_eq == 0 && (!has_expr || !expr_eq) { - emit_shared_code_in_if_blocks_lint(cx, start_eq, 0, false, blocks, expr); - } else if end_eq != 0 && (!has_expr || !expr_eq) { + let block = blocks[0]; + let start_stmts = block.stmts.split_at(start_eq).0; + + let mut start_walker = UsedValueFinderVisitor::new(cx); + for stmt in start_stmts { + intravisit::walk_stmt(&mut start_walker, stmt); + } + + emit_shared_code_in_if_blocks_lint( + cx, + start_eq, + 0, + false, + check_for_warn_of_moved_symbol(cx, &start_walker.def_symbols, expr), + blocks, + expr, + ); + } else if end_eq != 0 || (has_expr && expr_eq) { let block = blocks[blocks.len() - 1]; - let stmts = block.stmts.split_at(start_eq).1; - let (block_stmts, moved_stmts) = stmts.split_at(stmts.len() - end_eq); + let (start_stmts, block_stmts) = block.stmts.split_at(start_eq); + let (block_stmts, end_stmts) = block_stmts.split_at(block_stmts.len() - end_eq); + + // Scan start + let mut start_walker = UsedValueFinderVisitor::new(cx); + for stmt in start_stmts { + intravisit::walk_stmt(&mut start_walker, stmt); + } + let mut moved_syms = start_walker.def_symbols; // Scan block - let mut walker = SymbolFinderVisitor::new(cx); + let mut block_walker = UsedValueFinderVisitor::new(cx); for stmt in block_stmts { - intravisit::walk_stmt(&mut walker, stmt); + intravisit::walk_stmt(&mut block_walker, stmt); } - let mut block_defs = walker.defs; + let mut block_defs = block_walker.defs; // Scan moved stmts let mut moved_start: Option = None; - let mut walker = SymbolFinderVisitor::new(cx); - for (index, stmt) in moved_stmts.iter().enumerate() { - intravisit::walk_stmt(&mut walker, stmt); + let mut end_walker = UsedValueFinderVisitor::new(cx); + for (index, stmt) in end_stmts.iter().enumerate() { + intravisit::walk_stmt(&mut end_walker, stmt); - for value in &walker.uses { + for value in &end_walker.uses { // Well we can't move this and all prev statements. So reset if block_defs.contains(&value) { moved_start = Some(index + 1); - walker.defs.drain().for_each(|x| { + end_walker.defs.drain().for_each(|x| { block_defs.insert(x); }); + + end_walker.def_symbols.clear(); } } - walker.uses.clear(); + end_walker.uses.clear(); } if let Some(moved_start) = moved_start { @@ -279,15 +303,65 @@ fn lint_same_then_else<'tcx>( } let end_linable = if let Some(expr) = block.expr { - intravisit::walk_expr(&mut walker, expr); - walker.uses.iter().any(|x| !block_defs.contains(x)) + intravisit::walk_expr(&mut end_walker, expr); + end_walker.uses.iter().any(|x| !block_defs.contains(x)) } else if end_eq == 0 { false } else { true }; - emit_shared_code_in_if_blocks_lint(cx, start_eq, end_eq, end_linable, blocks, expr); + if end_linable { + end_walker.def_symbols.drain().for_each(|x| { + moved_syms.insert(x); + }); + } + + emit_shared_code_in_if_blocks_lint( + cx, + start_eq, + end_eq, + end_linable, + check_for_warn_of_moved_symbol(cx, &moved_syms, expr), + blocks, + expr, + ); + } +} + +fn check_for_warn_of_moved_symbol( + cx: &LateContext<'tcx>, + symbols: &FxHashSet, + if_expr: &'tcx Expr<'_>, +) -> bool { + // Obs true as we include the current if block + if let Some(block) = get_enclosing_block(cx, if_expr.hir_id) { + let ignore_span = block.span.shrink_to_lo().to(if_expr.span); + + symbols + .iter() + .filter(|sym| !sym.as_str().starts_with('_')) + .any(move |sym| { + let mut walker = ContainsName { + name: *sym, + result: false, + }; + + // Scan block + block + .stmts + .iter() + .filter(|stmt| !ignore_span.overlaps(stmt.span)) + .for_each(|stmt| intravisit::walk_stmt(&mut walker, stmt)); + + if let Some(expr) = block.expr { + intravisit::walk_expr(&mut walker, expr); + } + + walker.result + }) + } else { + false } } @@ -296,6 +370,7 @@ fn emit_shared_code_in_if_blocks_lint( start_stmts: usize, end_stmts: usize, lint_end: bool, + warn_about_moved_symbol: bool, blocks: &[&Block<'tcx>], if_expr: &'tcx Expr<'_>, ) { @@ -305,7 +380,9 @@ fn emit_shared_code_in_if_blocks_lint( // (help, span, suggestion) let mut suggestions: Vec<(&str, Span, String)> = vec![]; + let mut add_expr_note = false; + // Construct suggestions if start_stmts > 0 { let block = blocks[0]; let span_start = first_line_of_span(cx, if_expr.span).shrink_to_lo(); @@ -357,21 +434,29 @@ fn emit_shared_code_in_if_blocks_lint( } suggestions.push(("end", span, suggestion.to_string())); + add_expr_note = !cx.typeck_results().expr_ty(if_expr).is_unit() } + let add_optional_msgs = |diag: &mut DiagnosticBuilder<'_>| { + if add_expr_note { + diag.note("The end suggestion probably needs some adjustments to use the expression result correctly."); + } + + if warn_about_moved_symbol { + diag.warn("Some moved values might need to be renamed to avoid wrong references."); + } + }; + + // Emit lint if suggestions.len() == 1 { let (place_str, span, sugg) = suggestions.pop().unwrap(); let msg = format!("All if blocks contain the same code at the {}", place_str); let help = format!("Consider moving the {} statements out like this", place_str); - span_lint_and_sugg( - cx, - SHARED_CODE_IN_IF_BLOCKS, - span, - msg.as_str(), - help.as_str(), - sugg, - Applicability::Unspecified, - ); + span_lint_and_then(cx, SHARED_CODE_IN_IF_BLOCKS, span, msg.as_str(), |diag| { + diag.span_suggestion(span, help.as_str(), sugg, Applicability::Unspecified); + + add_optional_msgs(diag); + }); } else if suggestions.len() == 2 { let (_, end_span, end_sugg) = suggestions.pop().unwrap(); let (_, start_span, start_sugg) = suggestions.pop().unwrap(); @@ -396,28 +481,39 @@ fn emit_shared_code_in_if_blocks_lint( end_sugg, Applicability::Unspecified, ); + + add_optional_msgs(diag); }, ); } } -pub struct SymbolFinderVisitor<'a, 'tcx> { +/// This visitor collects HirIds and Symbols of defined symbols and HirIds of used values. +struct UsedValueFinderVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, + + /// The `HirId`s of defined values in the scanned statements defs: FxHashSet, + + /// The Symbols of the defined symbols in the scanned statements + def_symbols: FxHashSet, + + /// The `HirId`s of the used values uses: FxHashSet, } -impl<'a, 'tcx> SymbolFinderVisitor<'a, 'tcx> { +impl<'a, 'tcx> UsedValueFinderVisitor<'a, 'tcx> { fn new(cx: &'a LateContext<'tcx>) -> Self { - SymbolFinderVisitor { + UsedValueFinderVisitor { cx, defs: FxHashSet::default(), + def_symbols: FxHashSet::default(), uses: FxHashSet::default(), } } } -impl<'a, 'tcx> Visitor<'tcx> for SymbolFinderVisitor<'a, 'tcx> { +impl<'a, 'tcx> Visitor<'tcx> for UsedValueFinderVisitor<'a, 'tcx> { type Map = Map<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap { @@ -427,6 +523,11 @@ impl<'a, 'tcx> Visitor<'tcx> for SymbolFinderVisitor<'a, 'tcx> { fn visit_local(&mut self, l: &'tcx rustc_hir::Local<'tcx>) { let local_id = l.pat.hir_id; self.defs.insert(local_id); + + if let Some(sym) = l.pat.simple_ident() { + self.def_symbols.insert(sym.name); + } + if let Some(expr) = l.init { intravisit::walk_expr(self, expr); } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index ba98a2a2607..9afbf95a342 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1366,7 +1366,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&casts::PTR_AS_PTR), LintId::of(&checked_conversions::CHECKED_CONVERSIONS), LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION), - LintId::of(&copies::SHARED_CODE_IN_IF_BLOCKS), LintId::of(©_iterator::COPY_ITERATOR), LintId::of(&default::DEFAULT_TRAIT_ACCESS), LintId::of(&dereference::EXPLICIT_DEREF_METHODS), @@ -2064,6 +2063,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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::SHARED_CODE_IN_IF_BLOCKS), LintId::of(&disallowed_method::DISALLOWED_METHOD), LintId::of(&fallible_impl_from::FALLIBLE_IMPL_FROM), LintId::of(&floating_point_arithmetic::IMPRECISE_FLOPS), diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index b736eeff6bf..7fd55151226 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -464,28 +464,32 @@ impl DecimalLiteralRepresentation { { return Err(WarningType::DecimalRepresentation); } + } else if digits.len() < 4 { + // Lint for Literals with a hex-representation of 2 or 3 digits + let f = &digits[0..1]; // first digit + let s = &digits[1..]; // suffix + + // Powers of 2 + if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && s.chars().all(|c| c == '0')) + // Powers of 2 minus 1 + || ((f.eq("1") || f.eq("3") || f.eq("7") || f.eq("F")) && s.chars().all(|c| c == 'F')) + { + return Err(WarningType::DecimalRepresentation); + } } else { + // Lint for Literals with a hex-representation of 4 digits or more let f = &digits[0..1]; // first digit let m = &digits[1..digits.len() - 1]; // middle digits, except last let s = &digits[1..]; // suffix - if digits.len() < 4 { - // Powers of 2 - if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && s.chars().all(|c| c == '0')) - // Powers of 2 minus 1 - || ((f.eq("1") || f.eq("3") || f.eq("7") || f.eq("F")) && s.chars().all(|c| c == 'F')) - { - return Err(WarningType::DecimalRepresentation); - } - } else { - // Powers of 2 with a margin of +15/-16 - if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && m.chars().all(|c| c == '0')) - || ((f.eq("1") || f.eq("3") || f.eq("7") || f.eq("F")) && m.chars().all(|c| c == 'F')) - // Lint for representations with only 0s and Fs, while allowing 7 as the first - // digit - || ((f.eq("7") || f.eq("F")) && s.chars().all(|c| c == '0' || c == 'F')) - { - return Err(WarningType::DecimalRepresentation); - } + + // Powers of 2 with a margin of +15/-16 + if ((f.eq("1") || f.eq("2") || f.eq("4") || f.eq("8")) && m.chars().all(|c| c == '0')) + || ((f.eq("1") || f.eq("3") || f.eq("7") || f.eq("F")) && m.chars().all(|c| c == 'F')) + // Lint for representations with only 0s and Fs, while allowing 7 as the first + // digit + || ((f.eq("7") || f.eq("F")) && s.chars().all(|c| c == '0' || c == 'F')) + { + return Err(WarningType::DecimalRepresentation); } } diff --git a/tests/ui/if_same_then_else2.stderr b/tests/ui/if_same_then_else2.stderr index 941b92f23f3..b31eb46a982 100644 --- a/tests/ui/if_same_then_else2.stderr +++ b/tests/ui/if_same_then_else2.stderr @@ -27,7 +27,7 @@ LL | | } else { | = note: `-D clippy::if-same-then-else` implied by `-D warnings` note: same as this - --> $DIR/if_same_then_else2.rs:21:12 + --> $DIR/if_same_then_else2.rs:22:12 | LL | } else { | ____________^ @@ -40,7 +40,7 @@ LL | | } | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:33:13 + --> $DIR/if_same_then_else2.rs:34:13 | LL | if true { | _____________^ @@ -49,7 +49,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:35:12 + --> $DIR/if_same_then_else2.rs:36:12 | LL | } else { | ____________^ @@ -59,7 +59,7 @@ LL | | } | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:40:13 + --> $DIR/if_same_then_else2.rs:41:13 | LL | if true { | _____________^ @@ -68,7 +68,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:42:12 + --> $DIR/if_same_then_else2.rs:43:12 | LL | } else { | ____________^ @@ -78,7 +78,7 @@ LL | | } | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:90:21 + --> $DIR/if_same_then_else2.rs:91:21 | LL | let _ = if true { | _____________________^ @@ -87,7 +87,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:92:12 + --> $DIR/if_same_then_else2.rs:93:12 | LL | } else { | ____________^ @@ -97,7 +97,7 @@ LL | | }; | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:97:13 + --> $DIR/if_same_then_else2.rs:98:13 | LL | if true { | _____________^ @@ -106,7 +106,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:99:12 + --> $DIR/if_same_then_else2.rs:100:12 | LL | } else { | ____________^ @@ -116,7 +116,7 @@ LL | | } | |_____^ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:121:20 + --> $DIR/if_same_then_else2.rs:122:20 | LL | } else if true { | ____________________^ @@ -126,7 +126,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/if_same_then_else2.rs:124:12 + --> $DIR/if_same_then_else2.rs:125:12 | LL | } else { | ____________^ diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs b/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs index a586a1c9d45..5392da7ac7a 100644 --- a/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs +++ b/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs @@ -171,6 +171,25 @@ fn not_moveable_due_to_value_scope() { }; } +/// This should add a note to the lint msg since the moved expression is not `()` +fn added_note_for_expression_use() -> u32 { + let x = 9; + + let _ = if x == 7 { + x << 2 + } else { + let _ = 6; + x << 2 + }; + + if x == 9 { + x * 4 + } else { + let _ = 17; + x * 4 + } +} + #[rustfmt::skip] fn test_suggestion_with_weird_formatting() { let x = 9; diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_bot.stderr b/tests/ui/shared_code_in_if_blocks/shared_at_bot.stderr new file mode 100644 index 00000000000..62a95157d61 --- /dev/null +++ b/tests/ui/shared_code_in_if_blocks/shared_at_bot.stderr @@ -0,0 +1,131 @@ +error: All if blocks contain the same code at the end + --> $DIR/shared_at_bot.rs:31:5 + | +LL | / let result = false; +LL | | println!("Block end!"); +LL | | result +LL | | }; + | |_____^ + | +note: the lint level is defined here + --> $DIR/shared_at_bot.rs:2:36 + | +LL | #![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: The end suggestion probably needs some adjustments to use the expression result correctly. +help: Consider moving the end statements out like this + | +LL | } +LL | let result = false; +LL | println!("Block end!"); +LL | result; + | + +error: All if blocks contain the same code at the end + --> $DIR/shared_at_bot.rs:66:5 + | +LL | / println!( +LL | | "I'm moveable because I know: `outer_scope_value`: '{}'", +LL | | outer_scope_value +LL | | ); +LL | | } + | |_____^ + | +help: Consider moving the end statements out like this + | +LL | } +LL | println!( +LL | "I'm moveable because I know: `outer_scope_value`: '{}'", +LL | outer_scope_value +LL | ); + | + +error: All if blocks contain the same code at the start + --> $DIR/shared_at_bot.rs:83:9 + | +LL | / if x == 8 { +LL | | // No parent!! +LL | | println!("Hello World"); + | |____________________________________^ + | +help: Consider moving the start statements out like this + | +LL | println!("Hello World"); +LL | if x == 8 { + | + +error: All if blocks contain the same code at the end + --> $DIR/shared_at_bot.rs:104:5 + | +LL | / let later_used_value = "A string value"; +LL | | println!("{}", later_used_value); +LL | | // I'm expecting a note about this +LL | | } + | |_____^ + | + = warning: Some moved values might need to be renamed to avoid wrong references. +help: Consider moving the end statements out like this + | +LL | } +LL | let later_used_value = "A string value"; +LL | println!("{}", later_used_value); + | + +error: All if blocks contain the same code at the end + --> $DIR/shared_at_bot.rs:117:5 + | +LL | / let simple_examples = "I now identify as a &str :)"; +LL | | println!("This is the new simple_example: {}", simple_examples); +LL | | } + | |_____^ + | + = warning: Some moved values might need to be renamed to avoid wrong references. +help: Consider moving the end statements out like this + | +LL | } +LL | let simple_examples = "I now identify as a &str :)"; +LL | println!("This is the new simple_example: {}", simple_examples); + | + +error: All if blocks contain the same code at the end + --> $DIR/shared_at_bot.rs:182:5 + | +LL | / x << 2 +LL | | }; + | |_____^ + | + = note: The end suggestion probably needs some adjustments to use the expression result correctly. +help: Consider moving the end statements out like this + | +LL | } +LL | x << 2; + | + +error: All if blocks contain the same code at the end + --> $DIR/shared_at_bot.rs:189:5 + | +LL | / x * 4 +LL | | } + | |_____^ + | + = note: The end suggestion probably needs some adjustments to use the expression result correctly. +help: Consider moving the end statements out like this + | +LL | } +LL | x * 4 + | + +error: All if blocks contain the same code at the end + --> $DIR/shared_at_bot.rs:201:44 + | +LL | if x == 17 { b = 1; a = 0x99; } else { a = 0x99; } + | ^^^^^^^^^^^ + | +help: Consider moving the end statements out like this + | +LL | if x == 17 { b = 1; a = 0x99; } else { } +LL | a = 0x99; + | + +error: aborting due to 8 previous errors + diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_top.stderr b/tests/ui/shared_code_in_if_blocks/shared_at_top.stderr new file mode 100644 index 00000000000..c2bd8a070ed --- /dev/null +++ b/tests/ui/shared_code_in_if_blocks/shared_at_top.stderr @@ -0,0 +1,83 @@ +error: All if blocks contain the same code at the start + --> $DIR/shared_at_top.rs:10:5 + | +LL | / if true { +LL | | println!("Hello World!"); + | |_________________________________^ + | +note: the lint level is defined here + --> $DIR/shared_at_top.rs:2:36 + | +LL | #![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: Consider moving the start statements out like this + | +LL | println!("Hello World!"); +LL | if true { + | + +error: All if blocks contain the same code at the start + --> $DIR/shared_at_top.rs:19:5 + | +LL | / if x == 0 { +LL | | let y = 9; +LL | | println!("The value y was set to: `{}`", y); +LL | | let _z = y; + | |___________________^ + | + = warning: Some moved values might need to be renamed to avoid wrong references. +help: Consider moving the start statements out like this + | +LL | let y = 9; +LL | println!("The value y was set to: `{}`", y); +LL | let _z = y; +LL | if x == 0 { + | + +error: All if blocks contain the same code at the start + --> $DIR/shared_at_top.rs:40:5 + | +LL | / let _ = if x == 7 { +LL | | let y = 16; + | |___________________^ + | +help: Consider moving the start statements out like this + | +LL | let y = 16; +LL | let _ = if x == 7 { + | + +error: All if blocks contain the same code at the start + --> $DIR/shared_at_top.rs:58:5 + | +LL | / if x == 10 { +LL | | let used_value_name = "Different type"; +LL | | println!("Str: {}", used_value_name); + | |_____________________________________________^ + | + = warning: Some moved values might need to be renamed to avoid wrong references. +help: Consider moving the start statements out like this + | +LL | let used_value_name = "Different type"; +LL | println!("Str: {}", used_value_name); +LL | if x == 10 { + | + +error: All if blocks contain the same code at the start + --> $DIR/shared_at_top.rs:72:5 + | +LL | / if x == 11 { +LL | | let can_be_overridden = "Move me"; +LL | | println!("I'm also moveable"); + | |______________________________________^ + | + = warning: Some moved values might need to be renamed to avoid wrong references. +help: Consider moving the start statements out like this + | +LL | let can_be_overridden = "Move me"; +LL | println!("I'm also moveable"); +LL | if x == 11 { + | + +error: aborting due to 5 previous errors + diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.rs b/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.rs index 70f969aaf2e..46a8f931aaf 100644 --- a/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.rs +++ b/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.rs @@ -87,4 +87,33 @@ fn complexer_example() { } } +/// This should add a note to the lint msg since the moved expression is not `()` +fn added_note_for_expression_use() -> u32 { + let x = 9; + + let _ = if x == 7 { + let _ = 19; + + let _splitter = 6; + + x << 2 + } else { + let _ = 19; + + x << 2 + }; + + if x == 9 { + let _ = 17; + + let _splitter = 6; + + x * 4 + } else { + let _ = 17; + + x * 4 + } +} + fn main() {} diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.stderr b/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.stderr new file mode 100644 index 00000000000..1ba7211b469 --- /dev/null +++ b/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.stderr @@ -0,0 +1,154 @@ +error: All if blocks contain the same code at the start and the end. Here at the start: + --> $DIR/shared_at_top_and_bot.rs:16:5 + | +LL | / if x == 7 { +LL | | let t = 7; +LL | | let _overlap_start = t * 2; +LL | | let _overlap_end = 2 * t; + | |_________________________________^ + | +note: the lint level is defined here + --> $DIR/shared_at_top_and_bot.rs:2:36 + | +LL | #![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: And here at the end: + --> $DIR/shared_at_top_and_bot.rs:28:5 + | +LL | / let _u = 9; +LL | | } + | |_____^ +help: Consider moving the start statements out like this: + | +LL | let t = 7; +LL | let _overlap_start = t * 2; +LL | let _overlap_end = 2 * t; +LL | if x == 7 { + | +help: And consider moving the end statements out like this: + | +LL | } +LL | let _u = 9; + | + +error: All if blocks contain the same code at the start and the end. Here at the start: + --> $DIR/shared_at_top_and_bot.rs:32:5 + | +LL | / if x == 99 { +LL | | let r = 7; +LL | | let _overlap_start = r; +LL | | let _overlap_middle = r * r; + | |____________________________________^ + | +note: And here at the end: + --> $DIR/shared_at_top_and_bot.rs:43:5 + | +LL | / let _overlap_end = r * r * r; +LL | | let z = "end"; +LL | | } + | |_____^ + = warning: Some moved values might need to be renamed to avoid wrong references. +help: Consider moving the start statements out like this: + | +LL | let r = 7; +LL | let _overlap_start = r; +LL | let _overlap_middle = r * r; +LL | if x == 99 { + | +help: And consider moving the end statements out like this: + | +LL | } +LL | let _overlap_end = r * r * r; +LL | let z = "end"; + | + +error: All if blocks contain the same code at the start and the end. Here at the start: + --> $DIR/shared_at_top_and_bot.rs:61:5 + | +LL | / if (x > 7 && y < 13) || (x + y) % 2 == 1 { +LL | | let a = 0xcafe; +LL | | let b = 0xffff00ff; +LL | | let e_id = gen_id(a, b); + | |________________________________^ + | +note: And here at the end: + --> $DIR/shared_at_top_and_bot.rs:81:5 + | +LL | / let pack = DataPack { +LL | | id: e_id, +LL | | name: "Player 1".to_string(), +LL | | some_data: vec![0x12, 0x34, 0x56, 0x78, 0x90], +LL | | }; +LL | | process_data(pack); +LL | | } + | |_____^ + = warning: Some moved values might need to be renamed to avoid wrong references. +help: Consider moving the start statements out like this: + | +LL | let a = 0xcafe; +LL | let b = 0xffff00ff; +LL | let e_id = gen_id(a, b); +LL | if (x > 7 && y < 13) || (x + y) % 2 == 1 { + | +help: And consider moving the end statements out like this: + | +LL | } +LL | let pack = DataPack { +LL | id: e_id, +LL | name: "Player 1".to_string(), +LL | some_data: vec![0x12, 0x34, 0x56, 0x78, 0x90], +LL | }; + ... + +error: All if blocks contain the same code at the start and the end. Here at the start: + --> $DIR/shared_at_top_and_bot.rs:94:5 + | +LL | / let _ = if x == 7 { +LL | | let _ = 19; + | |___________________^ + | +note: And here at the end: + --> $DIR/shared_at_top_and_bot.rs:103:5 + | +LL | / x << 2 +LL | | }; + | |_____^ + = note: The end suggestion probably needs some adjustments to use the expression result correctly. +help: Consider moving the start statements out like this: + | +LL | let _ = 19; +LL | let _ = if x == 7 { + | +help: And consider moving the end statements out like this: + | +LL | } +LL | x << 2; + | + +error: All if blocks contain the same code at the start and the end. Here at the start: + --> $DIR/shared_at_top_and_bot.rs:106:5 + | +LL | / if x == 9 { +LL | | let _ = 17; + | |___________________^ + | +note: And here at the end: + --> $DIR/shared_at_top_and_bot.rs:115:5 + | +LL | / x * 4 +LL | | } + | |_____^ + = note: The end suggestion probably needs some adjustments to use the expression result correctly. +help: Consider moving the start statements out like this: + | +LL | let _ = 17; +LL | if x == 9 { + | +help: And consider moving the end statements out like this: + | +LL | } +LL | x * 4 + | + +error: aborting due to 5 previous errors + diff --git a/tests/ui/shared_code_in_if_blocks/valid_if_blocks.stderr b/tests/ui/shared_code_in_if_blocks/valid_if_blocks.stderr new file mode 100644 index 00000000000..003c060f072 --- /dev/null +++ b/tests/ui/shared_code_in_if_blocks/valid_if_blocks.stderr @@ -0,0 +1,107 @@ +error: this `if` has identical blocks + --> $DIR/valid_if_blocks.rs:102:15 + | +LL | if x == 0 { + | _______________^ +LL | | let u = 19; +LL | | println!("How are u today?"); +LL | | let _ = "This is a string"; +LL | | } else { + | |_____^ + | +note: the lint level is defined here + --> $DIR/valid_if_blocks.rs:2:9 + | +LL | #![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +note: same as this + --> $DIR/valid_if_blocks.rs:106:12 + | +LL | } else { + | ____________^ +LL | | let u = 19; +LL | | println!("How are u today?"); +LL | | let _ = "This is a string"; +LL | | } + | |_____^ + +error: All if blocks contain the same code at the end + --> $DIR/valid_if_blocks.rs:125:5 + | +LL | / let pet = Duck { num: 18 }; +LL | | println!("{:?}", pet); +LL | | } + | |_____^ + | +note: the lint level is defined here + --> $DIR/valid_if_blocks.rs:2:36 + | +LL | #![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: Consider moving the end statements out like this + | +LL | } +LL | let pet = Duck { num: 18 }; +LL | println!("{:?}", pet); + | + +error: this `if` has identical blocks + --> $DIR/valid_if_blocks.rs:130:23 + | +LL | let _ = if x == 6 { 7 } else { 7 }; + | ^^^^^ + | +note: same as this + --> $DIR/valid_if_blocks.rs:130:34 + | +LL | let _ = if x == 6 { 7 } else { 7 }; + | ^^^^^ + +error: this `if` has identical blocks + --> $DIR/valid_if_blocks.rs:136:23 + | +LL | } else if x == 68 { + | _______________________^ +LL | | println!("I'm a doppelgänger"); +LL | | // Don't listen to my clone below +LL | | +... | +LL | | } +LL | | } else { + | |_____^ + | +note: same as this + --> $DIR/valid_if_blocks.rs:145:12 + | +LL | } else { + | ____________^ +LL | | // Don't listen to my clone above +LL | | println!("I'm a doppelgänger"); +LL | | +... | +LL | | } +LL | | }; + | |_____^ + +error: this `if` has identical blocks + --> $DIR/valid_if_blocks.rs:158:23 + | +LL | } else if x == 68 { + | _______________________^ +LL | | println!("I'm a doppelgänger"); +LL | | // Don't listen to my clone below +LL | | } else { + | |_____^ + | +note: same as this + --> $DIR/valid_if_blocks.rs:161:12 + | +LL | } else { + | ____________^ +LL | | // Don't listen to my clone above +LL | | println!("I'm a doppelgänger"); +LL | | } + | |_____^ + +error: aborting due to 5 previous errors + -- cgit 1.4.1-3-g733a5 From 65ed5a632f5a5fc99a015e7ad0537c401fb61023 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sat, 16 Jan 2021 21:04:47 +0100 Subject: Updated code for dogfood --- clippy_lints/src/copies.rs | 142 +++++++++++---------- tests/ui/shared_code_in_if_blocks/shared_at_top.rs | 20 +++ .../shared_code_in_if_blocks/shared_at_top.stderr | 40 +++++- 3 files changed, 134 insertions(+), 68 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 47732f4766d..4a701dc8898 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -1,8 +1,9 @@ -use crate::utils::{both, count_eq, eq_expr_value, in_macro, search_same, SpanlessEq, SpanlessHash}; use crate::utils::{ - first_line_of_span, get_enclosing_block, get_parent_expr, if_sequence, indent_of, parent_node_is_if_expr, - reindent_multiline, snippet, snippet_opt, span_lint_and_note, span_lint_and_then, ContainsName, + both, count_eq, eq_expr_value, first_line_of_span, get_enclosing_block, get_parent_expr, if_sequence, in_macro, + indent_of, parent_node_is_if_expr, reindent_multiline, run_lints, search_same, snippet, snippet_opt, + span_lint_and_note, span_lint_and_then, ContainsName, SpanlessEq, SpanlessHash, }; +use if_chain::if_chain; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; @@ -188,57 +189,15 @@ fn lint_same_then_else<'tcx>( return; } - let has_expr = blocks[0].expr.is_some(); - // Check if each block has shared code - let mut start_eq = usize::MAX; - let mut end_eq = usize::MAX; - let mut expr_eq = true; - for win in blocks.windows(2) { - let l_stmts = win[0].stmts; - let r_stmts = win[1].stmts; - - let mut evaluator = SpanlessEq::new(cx); - let current_start_eq = count_eq(&mut l_stmts.iter(), &mut r_stmts.iter(), |l, r| evaluator.eq_stmt(l, r)); - let current_end_eq = count_eq(&mut l_stmts.iter().rev(), &mut r_stmts.iter().rev(), |l, r| { - evaluator.eq_stmt(l, r) - }); - let block_expr_eq = both(&win[0].expr, &win[1].expr, |l, r| evaluator.eq_expr(l, r)); - - // IF_SAME_THEN_ELSE - if block_expr_eq && l_stmts.len() == r_stmts.len() && l_stmts.len() == current_start_eq { - span_lint_and_note( - cx, - IF_SAME_THEN_ELSE, - win[0].span, - "this `if` has identical blocks", - Some(win[1].span), - "same as this", - ); - - return; - } - - start_eq = start_eq.min(current_start_eq); - end_eq = end_eq.min(current_end_eq); - expr_eq &= block_expr_eq; - } + let has_expr = blocks[0].expr.is_some(); + let (start_eq, mut end_eq, expr_eq) = scan_block_for_eq(cx, blocks); // SHARED_CODE_IN_IF_BLOCKS prerequisites if !has_unconditional_else || (start_eq == 0 && end_eq == 0 && (has_expr && !expr_eq)) { return; } - if has_expr && !expr_eq { - end_eq = 0; - } - - // Check if the regions are overlapping. Set `end_eq` to prevent the overlap - let min_block_size = blocks.iter().map(|x| x.stmts.len()).min().unwrap(); - if (start_eq + end_eq) > min_block_size { - end_eq = min_block_size - start_eq; - } - // Only the start is the same if start_eq != 0 && end_eq == 0 && (!has_expr || !expr_eq) { let block = blocks[0]; @@ -302,14 +261,13 @@ fn lint_same_then_else<'tcx>( end_eq -= moved_start; } - let end_linable = if let Some(expr) = block.expr { - intravisit::walk_expr(&mut end_walker, expr); - end_walker.uses.iter().any(|x| !block_defs.contains(x)) - } else if end_eq == 0 { - false - } else { - true - }; + let end_linable = block.expr.map_or_else( + || end_eq != 0, + |expr| { + intravisit::walk_expr(&mut end_walker, expr); + end_walker.uses.iter().any(|x| !block_defs.contains(x)) + }, + ); if end_linable { end_walker.def_symbols.drain().for_each(|x| { @@ -329,13 +287,67 @@ fn lint_same_then_else<'tcx>( } } +fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> (usize, usize, bool) { + let mut start_eq = usize::MAX; + let mut end_eq = usize::MAX; + let mut expr_eq = true; + for win in blocks.windows(2) { + let l_stmts = win[0].stmts; + let r_stmts = win[1].stmts; + + let mut evaluator = SpanlessEq::new(cx); + let current_start_eq = count_eq(&mut l_stmts.iter(), &mut r_stmts.iter(), |l, r| evaluator.eq_stmt(l, r)); + let current_end_eq = count_eq(&mut l_stmts.iter().rev(), &mut r_stmts.iter().rev(), |l, r| { + evaluator.eq_stmt(l, r) + }); + let block_expr_eq = both(&win[0].expr, &win[1].expr, |l, r| evaluator.eq_expr(l, r)); + + // IF_SAME_THEN_ELSE + if_chain! { + if block_expr_eq; + if l_stmts.len() == r_stmts.len(); + if l_stmts.len() == current_start_eq; + if run_lints(cx, &[IF_SAME_THEN_ELSE], win[0].hir_id); + if run_lints(cx, &[IF_SAME_THEN_ELSE], win[1].hir_id); + then { + span_lint_and_note( + cx, + IF_SAME_THEN_ELSE, + win[0].span, + "this `if` has identical blocks", + Some(win[1].span), + "same as this", + ); + + return (0, 0, false); + } + } + + start_eq = start_eq.min(current_start_eq); + end_eq = end_eq.min(current_end_eq); + expr_eq &= block_expr_eq; + } + + let has_expr = blocks[0].expr.is_some(); + if has_expr && !expr_eq { + end_eq = 0; + } + + // Check if the regions are overlapping. Set `end_eq` to prevent the overlap + let min_block_size = blocks.iter().map(|x| x.stmts.len()).min().unwrap(); + if (start_eq + end_eq) > min_block_size { + end_eq = min_block_size - start_eq; + } + + (start_eq, end_eq, expr_eq) +} + fn check_for_warn_of_moved_symbol( cx: &LateContext<'tcx>, symbols: &FxHashSet, if_expr: &'tcx Expr<'_>, ) -> bool { - // Obs true as we include the current if block - if let Some(block) = get_enclosing_block(cx, if_expr.hir_id) { + get_enclosing_block(cx, if_expr.hir_id).map_or(false, |block| { let ignore_span = block.span.shrink_to_lo().to(if_expr.span); symbols @@ -360,9 +372,7 @@ fn check_for_warn_of_moved_symbol( walker.result }) - } else { - false - } + }) } fn emit_shared_code_in_if_blocks_lint( @@ -410,12 +420,10 @@ fn emit_shared_code_in_if_blocks_lint( block.stmts[block.stmts.len() - end_stmts].span } .source_callsite(); - let moved_end = if let Some(expr) = block.expr { - expr.span - } else { - block.stmts[block.stmts.len() - 1].span - } - .source_callsite(); + let moved_end = block + .expr + .map_or_else(|| block.stmts[block.stmts.len() - 1].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); @@ -488,7 +496,7 @@ fn emit_shared_code_in_if_blocks_lint( } } -/// This visitor collects HirIds and Symbols of defined symbols and HirIds of used values. +/// This visitor collects `HirId`s and Symbols of defined symbols and `HirId`s of used values. struct UsedValueFinderVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_top.rs b/tests/ui/shared_code_in_if_blocks/shared_at_top.rs index 0a6828127e9..496939f2a5e 100644 --- a/tests/ui/shared_code_in_if_blocks/shared_at_top.rs +++ b/tests/ui/shared_code_in_if_blocks/shared_at_top.rs @@ -80,4 +80,24 @@ fn simple_but_suggestion_is_invalid() { } } +/// This function tests that the `IS_SAME_THAN_ELSE` only covers the lint if it's enabled. +fn check_if_same_than_else_mask() { + let x = 2021; + + #[allow(clippy::if_same_then_else)] + if x == 2020 { + println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint."); + println!("Because `IF_SAME_THEN_ELSE` is allowed here"); + } else { + println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint."); + println!("Because `IF_SAME_THEN_ELSE` is allowed here"); + } + + if x == 2019 { + println!("This should trigger `IS_SAME_THAN_ELSE` as usual"); + } else { + println!("This should trigger `IS_SAME_THAN_ELSE` as usual"); + } +} + fn main() {} diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_top.stderr b/tests/ui/shared_code_in_if_blocks/shared_at_top.stderr index c2bd8a070ed..d217083c413 100644 --- a/tests/ui/shared_code_in_if_blocks/shared_at_top.stderr +++ b/tests/ui/shared_code_in_if_blocks/shared_at_top.stderr @@ -79,5 +79,43 @@ LL | println!("I'm also moveable"); LL | if x == 11 { | -error: aborting due to 5 previous errors +error: All if blocks contain the same code at the start + --> $DIR/shared_at_top.rs:88:5 + | +LL | / if x == 2020 { +LL | | println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint."); +LL | | println!("Because `IF_SAME_THEN_ELSE` is allowed here"); + | |________________________________________________________________^ + | +help: Consider moving the start statements out like this + | +LL | println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint."); +LL | println!("Because `IF_SAME_THEN_ELSE` is allowed here"); +LL | if x == 2020 { + | + +error: this `if` has identical blocks + --> $DIR/shared_at_top.rs:96:18 + | +LL | if x == 2019 { + | __________________^ +LL | | println!("This should trigger `IS_SAME_THAN_ELSE` as usual"); +LL | | } else { + | |_____^ + | +note: the lint level is defined here + --> $DIR/shared_at_top.rs:2:9 + | +LL | #![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +note: same as this + --> $DIR/shared_at_top.rs:98:12 + | +LL | } else { + | ____________^ +LL | | println!("This should trigger `IS_SAME_THAN_ELSE` as usual"); +LL | | } + | |_____^ + +error: aborting due to 7 previous errors -- cgit 1.4.1-3-g733a5 From c74e49eab911ce5d55b3692324dea64905323a88 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Tue, 23 Feb 2021 21:16:19 +0100 Subject: Adapted the lint to use the new SpanlessEq --- clippy_lints/src/copies.rs | 28 ++++++--- clippy_utils/src/hir_utils.rs | 9 +-- tests/ui/if_same_then_else.stderr | 28 +-------- tests/ui/if_same_then_else2.stderr | 40 +----------- tests/ui/shared_code_in_if_blocks/shared_at_bot.rs | 15 +---- .../shared_code_in_if_blocks/shared_at_bot.stderr | 72 +++++++++++++--------- .../shared_code_in_if_blocks/shared_at_top.stderr | 24 ++++---- .../shared_at_top_and_bot.stderr | 40 ++++++------ .../ui/shared_code_in_if_blocks/valid_if_blocks.rs | 17 ----- .../valid_if_blocks.stderr | 34 +++------- 10 files changed, 111 insertions(+), 196 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 4a701dc8898..7a9f299d8e0 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -295,11 +295,21 @@ fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> (usize, let l_stmts = win[0].stmts; let r_stmts = win[1].stmts; + // `SpanlessEq` now keeps track of the locals and is therefore context sensitive clippy#6752. + // The comparison therefor needs to be done in a way that builds the correct context. let mut evaluator = SpanlessEq::new(cx); + let mut evaluator = evaluator.inter_expr(); + let current_start_eq = count_eq(&mut l_stmts.iter(), &mut r_stmts.iter(), |l, r| evaluator.eq_stmt(l, r)); - let current_end_eq = count_eq(&mut l_stmts.iter().rev(), &mut r_stmts.iter().rev(), |l, r| { - evaluator.eq_stmt(l, r) - }); + + let current_end_eq = { + // We skip the middle statements which can't be equal + let end_comparison_count = l_stmts.len().min(r_stmts.len()) - current_start_eq; + let it1 = l_stmts.iter().skip(l_stmts.len() - end_comparison_count); + let it2 = r_stmts.iter().skip(r_stmts.len() - end_comparison_count); + it1.zip(it2) + .fold(0, |acc, (l, r)| if evaluator.eq_stmt(l, r) { acc + 1 } else { 0 }) + }; let block_expr_eq = both(&win[0].expr, &win[1].expr, |l, r| evaluator.eq_expr(l, r)); // IF_SAME_THEN_ELSE @@ -458,8 +468,8 @@ fn emit_shared_code_in_if_blocks_lint( // Emit lint if suggestions.len() == 1 { let (place_str, span, sugg) = suggestions.pop().unwrap(); - let msg = format!("All if blocks contain the same code at the {}", place_str); - let help = format!("Consider moving the {} statements out like this", place_str); + let msg = format!("all if blocks contain the same code at the {}", place_str); + let help = format!("consider moving the {} statements out like this", place_str); span_lint_and_then(cx, SHARED_CODE_IN_IF_BLOCKS, span, msg.as_str(), |diag| { diag.span_suggestion(span, help.as_str(), sugg, Applicability::Unspecified); @@ -472,20 +482,20 @@ fn emit_shared_code_in_if_blocks_lint( cx, SHARED_CODE_IN_IF_BLOCKS, start_span, - "All if blocks contain the same code at the start and the end. Here at the start:", + "all if blocks contain the same code at the start and the end. Here at the start", move |diag| { - diag.span_note(end_span, "And here at the end:"); + diag.span_note(end_span, "and here at the end"); diag.span_suggestion( start_span, - "Consider moving the start statements out like this:", + "consider moving the start statements out like this", start_sugg, Applicability::Unspecified, ); diag.span_suggestion( end_span, - "And consider moving the end statements out like this:", + "and consider moving the end statements out like this", end_sugg, Applicability::Unspecified, ); diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 8b90fedbafe..000c249bb0f 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -58,13 +58,14 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { /// Use this method to wrap comparisons that may involve inter-expression context. /// See `self.locals`. - fn inter_expr(&mut self) -> HirEqInterExpr<'_, 'a, 'tcx> { + pub fn inter_expr(&mut self) -> HirEqInterExpr<'_, 'a, 'tcx> { HirEqInterExpr { inner: self, locals: HirIdMap::default(), } } + #[allow(dead_code)] pub fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool { self.inter_expr().eq_block(left, right) } @@ -82,7 +83,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { } } -struct HirEqInterExpr<'a, 'b, 'tcx> { +pub struct HirEqInterExpr<'a, 'b, 'tcx> { inner: &'a mut SpanlessEq<'b, 'tcx>, // When binding are declared, the binding ID in the left expression is mapped to the one on the @@ -92,7 +93,7 @@ struct HirEqInterExpr<'a, 'b, 'tcx> { } impl HirEqInterExpr<'_, '_, '_> { - fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool { + pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool { match (&left.kind, &right.kind) { (&StmtKind::Local(ref l), &StmtKind::Local(ref r)) => { self.eq_pat(&l.pat, &r.pat) @@ -159,7 +160,7 @@ impl HirEqInterExpr<'_, '_, '_> { } #[allow(clippy::similar_names)] - fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool { + pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool { if !self.inner.allow_side_effects && differing_macro_contexts(left.span, right.span) { return false; } diff --git a/tests/ui/if_same_then_else.stderr b/tests/ui/if_same_then_else.stderr index 2f38052fc20..74b11bac487 100644 --- a/tests/ui/if_same_then_else.stderr +++ b/tests/ui/if_same_then_else.stderr @@ -82,31 +82,5 @@ LL | | 42 LL | | }; | |_____^ -error: this `if` has identical blocks - --> $DIR/if_same_then_else.rs:95:13 - | -LL | if true { - | _____________^ -LL | | let bar = if true { 42 } else { 43 }; -LL | | -LL | | while foo() { -... | -LL | | bar + 1; -LL | | } else { - | |_____^ - | -note: same as this - --> $DIR/if_same_then_else.rs:102:12 - | -LL | } else { - | ____________^ -LL | | //~ ERROR same body as `if` block -LL | | let bar = if true { 42 } else { 43 }; -LL | | -... | -LL | | bar + 1; -LL | | } - | |_____^ - -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/if_same_then_else2.stderr b/tests/ui/if_same_then_else2.stderr index b31eb46a982..a0e636e3a61 100644 --- a/tests/ui/if_same_then_else2.stderr +++ b/tests/ui/if_same_then_else2.stderr @@ -1,19 +1,5 @@ error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:21:12 - | -LL | } else { - | ____________^ -LL | | //~ ERROR same body as `if` block -LL | | for _ in &[42] { -LL | | let bar: &Option<_> = &Some::(42); -... | -LL | | } -LL | | } - | |_____^ - | - = note: `-D clippy::if-same-then-else` implied by `-D warnings` -note: same as this - --> $DIR/if_same_then_else2.rs:12:13 + --> $DIR/if_same_then_else2.rs:13:13 | LL | if true { | _____________^ @@ -33,7 +19,7 @@ LL | } else { | ____________^ LL | | //~ ERROR same body as `if` block LL | | for _ in &[42] { -LL | | let foo: &Option<_> = &Some::(42); +LL | | let bar: &Option<_> = &Some::(42); ... | LL | | } LL | | } @@ -115,25 +101,5 @@ LL | | Ok("foo")?; LL | | } | |_____^ -error: this `if` has identical blocks - --> $DIR/if_same_then_else2.rs:122:20 - | -LL | } else if true { - | ____________________^ -LL | | let foo = ""; -LL | | return Ok(&foo[0..]); -LL | | } else { - | |_____^ - | -note: same as this - --> $DIR/if_same_then_else2.rs:125:12 - | -LL | } else { - | ____________^ -LL | | let foo = ""; -LL | | return Ok(&foo[0..]); -LL | | } - | |_____^ - -error: aborting due to 6 previous errors +error: aborting due to 5 previous errors diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs b/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs index 5392da7ac7a..7974ea2f59c 100644 --- a/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs +++ b/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs @@ -4,7 +4,6 @@ // This tests the shared_code_in_if_blocks lint at the end of blocks fn simple_examples() { - // TODO xFrednet 2021-01-06: Test with const literals at the end let x = 1; let _ = if x == 7 { @@ -45,8 +44,8 @@ fn simple_examples() { println!("This is also eq with the else block"); println!("Same end of block"); } else { - println!("Same end of block"); println!("This is also eq with the else block"); + println!("Same end of block"); } // Use of outer scope value @@ -69,21 +68,11 @@ fn simple_examples() { ); } - // TODO xFrednet 2021-01.13: Fix lint for `if let` - let index = Some(8); - if let Some(index) = index { - println!("The index is: {}", index); - - println!("Same end of block"); - } else { - println!("Same end of block"); - } - if x == 9 { if x == 8 { // No parent!! - println!("Hello World"); println!("---"); + println!("Hello World"); } else { println!("Hello World"); } diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_bot.stderr b/tests/ui/shared_code_in_if_blocks/shared_at_bot.stderr index 62a95157d61..5ecf47bf920 100644 --- a/tests/ui/shared_code_in_if_blocks/shared_at_bot.stderr +++ b/tests/ui/shared_code_in_if_blocks/shared_at_bot.stderr @@ -1,5 +1,5 @@ -error: All if blocks contain the same code at the end - --> $DIR/shared_at_bot.rs:31:5 +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bot.rs:30:5 | LL | / let result = false; LL | | println!("Block end!"); @@ -13,7 +13,7 @@ note: the lint level is defined here LL | #![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: The end suggestion probably needs some adjustments to use the expression result correctly. -help: Consider moving the end statements out like this +help: consider moving the end statements out like this | LL | } LL | let result = false; @@ -21,8 +21,21 @@ LL | println!("Block end!"); LL | result; | -error: All if blocks contain the same code at the end - --> $DIR/shared_at_bot.rs:66:5 +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bot.rs:48:5 + | +LL | / println!("Same end of block"); +LL | | } + | |_____^ + | +help: consider moving the end statements out like this + | +LL | } +LL | println!("Same end of block"); + | + +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bot.rs:65:5 | LL | / println!( LL | | "I'm moveable because I know: `outer_scope_value`: '{}'", @@ -31,7 +44,7 @@ LL | | ); LL | | } | |_____^ | -help: Consider moving the end statements out like this +help: consider moving the end statements out like this | LL | } LL | println!( @@ -40,22 +53,21 @@ LL | outer_scope_value LL | ); | -error: All if blocks contain the same code at the start - --> $DIR/shared_at_bot.rs:83:9 +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bot.rs:77:9 | -LL | / if x == 8 { -LL | | // No parent!! -LL | | println!("Hello World"); - | |____________________________________^ +LL | / println!("Hello World"); +LL | | } + | |_________^ | -help: Consider moving the start statements out like this +help: consider moving the end statements out like this | +LL | } LL | println!("Hello World"); -LL | if x == 8 { | -error: All if blocks contain the same code at the end - --> $DIR/shared_at_bot.rs:104:5 +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bot.rs:93:5 | LL | / let later_used_value = "A string value"; LL | | println!("{}", later_used_value); @@ -64,15 +76,15 @@ LL | | } | |_____^ | = warning: Some moved values might need to be renamed to avoid wrong references. -help: Consider moving the end statements out like this +help: consider moving the end statements out like this | LL | } LL | let later_used_value = "A string value"; LL | println!("{}", later_used_value); | -error: All if blocks contain the same code at the end - --> $DIR/shared_at_bot.rs:117:5 +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bot.rs:106:5 | LL | / let simple_examples = "I now identify as a &str :)"; LL | | println!("This is the new simple_example: {}", simple_examples); @@ -80,52 +92,52 @@ LL | | } | |_____^ | = warning: Some moved values might need to be renamed to avoid wrong references. -help: Consider moving the end statements out like this +help: consider moving the end statements out like this | LL | } LL | let simple_examples = "I now identify as a &str :)"; LL | println!("This is the new simple_example: {}", simple_examples); | -error: All if blocks contain the same code at the end - --> $DIR/shared_at_bot.rs:182:5 +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bot.rs:171:5 | LL | / x << 2 LL | | }; | |_____^ | = note: The end suggestion probably needs some adjustments to use the expression result correctly. -help: Consider moving the end statements out like this +help: consider moving the end statements out like this | LL | } LL | x << 2; | -error: All if blocks contain the same code at the end - --> $DIR/shared_at_bot.rs:189:5 +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bot.rs:178:5 | LL | / x * 4 LL | | } | |_____^ | = note: The end suggestion probably needs some adjustments to use the expression result correctly. -help: Consider moving the end statements out like this +help: consider moving the end statements out like this | LL | } LL | x * 4 | -error: All if blocks contain the same code at the end - --> $DIR/shared_at_bot.rs:201:44 +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bot.rs:190:44 | LL | if x == 17 { b = 1; a = 0x99; } else { a = 0x99; } | ^^^^^^^^^^^ | -help: Consider moving the end statements out like this +help: consider moving the end statements out like this | LL | if x == 17 { b = 1; a = 0x99; } else { } LL | a = 0x99; | -error: aborting due to 8 previous errors +error: aborting due to 9 previous errors diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_top.stderr b/tests/ui/shared_code_in_if_blocks/shared_at_top.stderr index d217083c413..1ad924aba6a 100644 --- a/tests/ui/shared_code_in_if_blocks/shared_at_top.stderr +++ b/tests/ui/shared_code_in_if_blocks/shared_at_top.stderr @@ -1,4 +1,4 @@ -error: All if blocks contain the same code at the start +error: all if blocks contain the same code at the start --> $DIR/shared_at_top.rs:10:5 | LL | / if true { @@ -10,13 +10,13 @@ note: the lint level is defined here | LL | #![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: Consider moving the start statements out like this +help: consider moving the start statements out like this | LL | println!("Hello World!"); LL | if true { | -error: All if blocks contain the same code at the start +error: all if blocks contain the same code at the start --> $DIR/shared_at_top.rs:19:5 | LL | / if x == 0 { @@ -26,7 +26,7 @@ LL | | let _z = y; | |___________________^ | = warning: Some moved values might need to be renamed to avoid wrong references. -help: Consider moving the start statements out like this +help: consider moving the start statements out like this | LL | let y = 9; LL | println!("The value y was set to: `{}`", y); @@ -34,20 +34,20 @@ LL | let _z = y; LL | if x == 0 { | -error: All if blocks contain the same code at the start +error: all if blocks contain the same code at the start --> $DIR/shared_at_top.rs:40:5 | LL | / let _ = if x == 7 { LL | | let y = 16; | |___________________^ | -help: Consider moving the start statements out like this +help: consider moving the start statements out like this | LL | let y = 16; LL | let _ = if x == 7 { | -error: All if blocks contain the same code at the start +error: all if blocks contain the same code at the start --> $DIR/shared_at_top.rs:58:5 | LL | / if x == 10 { @@ -56,14 +56,14 @@ LL | | println!("Str: {}", used_value_name); | |_____________________________________________^ | = warning: Some moved values might need to be renamed to avoid wrong references. -help: Consider moving the start statements out like this +help: consider moving the start statements out like this | LL | let used_value_name = "Different type"; LL | println!("Str: {}", used_value_name); LL | if x == 10 { | -error: All if blocks contain the same code at the start +error: all if blocks contain the same code at the start --> $DIR/shared_at_top.rs:72:5 | LL | / if x == 11 { @@ -72,14 +72,14 @@ LL | | println!("I'm also moveable"); | |______________________________________^ | = warning: Some moved values might need to be renamed to avoid wrong references. -help: Consider moving the start statements out like this +help: consider moving the start statements out like this | LL | let can_be_overridden = "Move me"; LL | println!("I'm also moveable"); LL | if x == 11 { | -error: All if blocks contain the same code at the start +error: all if blocks contain the same code at the start --> $DIR/shared_at_top.rs:88:5 | LL | / if x == 2020 { @@ -87,7 +87,7 @@ LL | | println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint LL | | println!("Because `IF_SAME_THEN_ELSE` is allowed here"); | |________________________________________________________________^ | -help: Consider moving the start statements out like this +help: consider moving the start statements out like this | LL | println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint."); LL | println!("Because `IF_SAME_THEN_ELSE` is allowed here"); diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.stderr b/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.stderr index 1ba7211b469..9f675a20a6d 100644 --- a/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.stderr +++ b/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.stderr @@ -1,4 +1,4 @@ -error: All if blocks contain the same code at the start and the end. Here at the start: +error: all if blocks contain the same code at the start and the end. Here at the start --> $DIR/shared_at_top_and_bot.rs:16:5 | LL | / if x == 7 { @@ -12,26 +12,26 @@ note: the lint level is defined here | LL | #![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: And here at the end: +note: and here at the end --> $DIR/shared_at_top_and_bot.rs:28:5 | LL | / let _u = 9; LL | | } | |_____^ -help: Consider moving the start statements out like this: +help: consider moving the start statements out like this | LL | let t = 7; LL | let _overlap_start = t * 2; LL | let _overlap_end = 2 * t; LL | if x == 7 { | -help: And consider moving the end statements out like this: +help: and consider moving the end statements out like this | LL | } LL | let _u = 9; | -error: All if blocks contain the same code at the start and the end. Here at the start: +error: all if blocks contain the same code at the start and the end. Here at the start --> $DIR/shared_at_top_and_bot.rs:32:5 | LL | / if x == 99 { @@ -40,7 +40,7 @@ LL | | let _overlap_start = r; LL | | let _overlap_middle = r * r; | |____________________________________^ | -note: And here at the end: +note: and here at the end --> $DIR/shared_at_top_and_bot.rs:43:5 | LL | / let _overlap_end = r * r * r; @@ -48,21 +48,21 @@ LL | | let z = "end"; LL | | } | |_____^ = warning: Some moved values might need to be renamed to avoid wrong references. -help: Consider moving the start statements out like this: +help: consider moving the start statements out like this | LL | let r = 7; LL | let _overlap_start = r; LL | let _overlap_middle = r * r; LL | if x == 99 { | -help: And consider moving the end statements out like this: +help: and consider moving the end statements out like this | LL | } LL | let _overlap_end = r * r * r; LL | let z = "end"; | -error: All if blocks contain the same code at the start and the end. Here at the start: +error: all if blocks contain the same code at the start and the end. Here at the start --> $DIR/shared_at_top_and_bot.rs:61:5 | LL | / if (x > 7 && y < 13) || (x + y) % 2 == 1 { @@ -71,7 +71,7 @@ LL | | let b = 0xffff00ff; LL | | let e_id = gen_id(a, b); | |________________________________^ | -note: And here at the end: +note: and here at the end --> $DIR/shared_at_top_and_bot.rs:81:5 | LL | / let pack = DataPack { @@ -83,14 +83,14 @@ LL | | process_data(pack); LL | | } | |_____^ = warning: Some moved values might need to be renamed to avoid wrong references. -help: Consider moving the start statements out like this: +help: consider moving the start statements out like this | LL | let a = 0xcafe; LL | let b = 0xffff00ff; LL | let e_id = gen_id(a, b); LL | if (x > 7 && y < 13) || (x + y) % 2 == 1 { | -help: And consider moving the end statements out like this: +help: and consider moving the end statements out like this | LL | } LL | let pack = DataPack { @@ -100,51 +100,51 @@ LL | some_data: vec![0x12, 0x34, 0x56, 0x78, 0x90], LL | }; ... -error: All if blocks contain the same code at the start and the end. Here at the start: +error: all if blocks contain the same code at the start and the end. Here at the start --> $DIR/shared_at_top_and_bot.rs:94:5 | LL | / let _ = if x == 7 { LL | | let _ = 19; | |___________________^ | -note: And here at the end: +note: and here at the end --> $DIR/shared_at_top_and_bot.rs:103:5 | LL | / x << 2 LL | | }; | |_____^ = note: The end suggestion probably needs some adjustments to use the expression result correctly. -help: Consider moving the start statements out like this: +help: consider moving the start statements out like this | LL | let _ = 19; LL | let _ = if x == 7 { | -help: And consider moving the end statements out like this: +help: and consider moving the end statements out like this | LL | } LL | x << 2; | -error: All if blocks contain the same code at the start and the end. Here at the start: +error: all if blocks contain the same code at the start and the end. Here at the start --> $DIR/shared_at_top_and_bot.rs:106:5 | LL | / if x == 9 { LL | | let _ = 17; | |___________________^ | -note: And here at the end: +note: and here at the end --> $DIR/shared_at_top_and_bot.rs:115:5 | LL | / x * 4 LL | | } | |_____^ = note: The end suggestion probably needs some adjustments to use the expression result correctly. -help: Consider moving the start statements out like this: +help: consider moving the start statements out like this | LL | let _ = 17; LL | if x == 9 { | -help: And consider moving the end statements out like this: +help: and consider moving the end statements out like this | LL | } LL | x * 4 diff --git a/tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs b/tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs index 480758777f1..a564b30cb1c 100644 --- a/tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs +++ b/tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs @@ -109,23 +109,6 @@ fn trigger_other_lint() { let _ = "This is a string"; } - // More complex same blocks - if x == 17 { - #[derive(Debug)] - struct Duck { - num: u64, - }; - let pet = Duck { num: 18 }; - println!("{:?}", pet); - } else { - #[derive(Debug)] - struct Duck { - num: u64, - }; - let pet = Duck { num: 18 }; - println!("{:?}", pet); - } - // Only same expression let _ = if x == 6 { 7 } else { 7 }; diff --git a/tests/ui/shared_code_in_if_blocks/valid_if_blocks.stderr b/tests/ui/shared_code_in_if_blocks/valid_if_blocks.stderr index 003c060f072..d290c65822b 100644 --- a/tests/ui/shared_code_in_if_blocks/valid_if_blocks.stderr +++ b/tests/ui/shared_code_in_if_blocks/valid_if_blocks.stderr @@ -25,40 +25,20 @@ LL | | let _ = "This is a string"; LL | | } | |_____^ -error: All if blocks contain the same code at the end - --> $DIR/valid_if_blocks.rs:125:5 - | -LL | / let pet = Duck { num: 18 }; -LL | | println!("{:?}", pet); -LL | | } - | |_____^ - | -note: the lint level is defined here - --> $DIR/valid_if_blocks.rs:2:36 - | -LL | #![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: Consider moving the end statements out like this - | -LL | } -LL | let pet = Duck { num: 18 }; -LL | println!("{:?}", pet); - | - error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:130:23 + --> $DIR/valid_if_blocks.rs:113:23 | LL | let _ = if x == 6 { 7 } else { 7 }; | ^^^^^ | note: same as this - --> $DIR/valid_if_blocks.rs:130:34 + --> $DIR/valid_if_blocks.rs:113:34 | LL | let _ = if x == 6 { 7 } else { 7 }; | ^^^^^ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:136:23 + --> $DIR/valid_if_blocks.rs:119:23 | LL | } else if x == 68 { | _______________________^ @@ -71,7 +51,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/valid_if_blocks.rs:145:12 + --> $DIR/valid_if_blocks.rs:128:12 | LL | } else { | ____________^ @@ -84,7 +64,7 @@ LL | | }; | |_____^ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:158:23 + --> $DIR/valid_if_blocks.rs:141:23 | LL | } else if x == 68 { | _______________________^ @@ -94,7 +74,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/valid_if_blocks.rs:161:12 + --> $DIR/valid_if_blocks.rs:144:12 | LL | } else { | ____________^ @@ -103,5 +83,5 @@ LL | | println!("I'm a doppelgänger"); LL | | } | |_____^ -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From 617c65baa90cdbd9228cacad4f2079a9d868d070 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Thu, 25 Feb 2021 23:33:46 +0100 Subject: Moving shared_code_in_if_blocks to clippy::complexity and running lintcheck --- clippy_lints/src/copies.rs | 6 +-- clippy_lints/src/lib.rs | 2 +- clippy_utils/src/hir_utils.rs | 6 ++- clippy_utils/src/lib.rs | 18 ++++++-- .../checked_unwrap/complex_conditionals_nested.rs | 2 +- tests/ui/checked_unwrap/simple_conditionals.rs | 2 +- tests/ui/default_numeric_fallback.rs | 1 + tests/ui/default_numeric_fallback.stderr | 48 +++++++++++----------- tests/ui/if_same_then_else.stderr | 28 ++++++++++++- tests/ui/if_same_then_else2.stderr | 22 +++++++++- tests/ui/needless_bool/simple.rs | 3 +- tests/ui/needless_bool/simple.stderr | 8 ++-- tests/ui/needless_return.fixed | 9 +++- tests/ui/needless_return.rs | 9 +++- tests/ui/needless_return.stderr | 36 ++++++++-------- tests/ui/shared_code_in_if_blocks/shared_at_bot.rs | 16 ++++++++ .../shared_code_in_if_blocks/shared_at_bot.stderr | 10 ++--- .../shared_code_in_if_blocks/shared_at_top.stderr | 6 +-- .../shared_at_top_and_bot.stderr | 8 ++-- .../ui/shared_code_in_if_blocks/valid_if_blocks.rs | 8 ++++ .../valid_if_blocks.stderr | 16 ++++---- 21 files changed, 180 insertions(+), 84 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 7a9f299d8e0..8b94ac96880 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -142,7 +142,7 @@ declare_clippy_lint! { /// }; /// ``` pub SHARED_CODE_IN_IF_BLOCKS, - nursery, + complexity, "`if` statement with shared code in all blocks" } @@ -457,11 +457,11 @@ fn emit_shared_code_in_if_blocks_lint( let add_optional_msgs = |diag: &mut DiagnosticBuilder<'_>| { if add_expr_note { - diag.note("The end suggestion probably needs some adjustments to use the expression result correctly."); + diag.note("The end suggestion probably needs some adjustments to use the expression result correctly"); } if warn_about_moved_symbol { - diag.warn("Some moved values might need to be renamed to avoid wrong references."); + diag.warn("Some moved values might need to be renamed to avoid wrong references"); } }; diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 9afbf95a342..3f5c1c7c526 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1485,6 +1485,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&comparison_chain::COMPARISON_CHAIN), LintId::of(&copies::IFS_SAME_COND), LintId::of(&copies::IF_SAME_THEN_ELSE), + LintId::of(&copies::SHARED_CODE_IN_IF_BLOCKS), LintId::of(&default::FIELD_REASSIGN_WITH_DEFAULT), LintId::of(&derive::DERIVE_HASH_XOR_EQ), LintId::of(&derive::DERIVE_ORD_XOR_PARTIAL_ORD), @@ -2063,7 +2064,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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::SHARED_CODE_IN_IF_BLOCKS), LintId::of(&disallowed_method::DISALLOWED_METHOD), LintId::of(&fallible_impl_from::FALLIBLE_IMPL_FROM), LintId::of(&floating_point_arithmetic::IMPRECISE_FLOPS), diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 000c249bb0f..571eec9e530 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -96,9 +96,11 @@ impl HirEqInterExpr<'_, '_, '_> { pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool { match (&left.kind, &right.kind) { (&StmtKind::Local(ref l), &StmtKind::Local(ref r)) => { - self.eq_pat(&l.pat, &r.pat) + // eq_pat adds the HirIds to the locals map. We therefor call it last to make sure that + // these only get added if the init and type is equal. + both(&l.init, &r.init, |l, r| self.eq_expr(l, r)) && both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r)) - && both(&l.init, &r.init, |l, r| self.eq_expr(l, r)) + && self.eq_pat(&l.pat, &r.pat) }, (&StmtKind::Expr(ref l), &StmtKind::Expr(ref r)) | (&StmtKind::Semi(ref l), &StmtKind::Semi(ref r)) => { self.eq_expr(l, r) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index d6364625e70..f9576068dd6 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -609,9 +609,9 @@ pub fn get_pat_name(pat: &Pat<'_>) -> Option { } } -struct ContainsName { - name: Symbol, - result: bool, +pub struct ContainsName { + pub name: Symbol, + pub result: bool, } impl<'tcx> Visitor<'tcx> for ContainsName { @@ -1216,6 +1216,8 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, (conds, blocks) } +/// This function returns true if the given expression is the `else` or `if else` part of an if +/// statement pub fn parent_node_is_if_expr(expr: &Expr<'_>, cx: &LateContext<'_>) -> bool { let map = cx.tcx.hir(); let parent_id = map.get_parent_node(expr.hir_id); @@ -1326,6 +1328,16 @@ pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { } } +/// This function checks if any of the lints in the slice is enabled for the provided `HirId`. +/// A lint counts as enabled with any of the levels: `Level::Forbid` | `Level::Deny` | `Level::Warn` +/// +/// ```ignore +/// #[deny(clippy::YOUR_AWESOME_LINT)] +/// println!("Hello, World!"); // <- Clippy code: run_lints(cx, &[YOUR_AWESOME_LINT], id) == true +/// +/// #[allow(clippy::YOUR_AWESOME_LINT)] +/// println!("See you soon!"); // <- Clippy code: run_lints(cx, &[YOUR_AWESOME_LINT], id) == false +/// ``` pub fn run_lints(cx: &LateContext<'_>, lints: &[&'static Lint], id: HirId) -> bool { lints.iter().any(|lint| { matches!( diff --git a/tests/ui/checked_unwrap/complex_conditionals_nested.rs b/tests/ui/checked_unwrap/complex_conditionals_nested.rs index 2307996a48f..99e8fb95466 100644 --- a/tests/ui/checked_unwrap/complex_conditionals_nested.rs +++ b/tests/ui/checked_unwrap/complex_conditionals_nested.rs @@ -1,5 +1,5 @@ #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] -#![allow(clippy::if_same_then_else)] +#![allow(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] fn test_nested() { fn nested() { diff --git a/tests/ui/checked_unwrap/simple_conditionals.rs b/tests/ui/checked_unwrap/simple_conditionals.rs index 36967630834..0459100d88a 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.rs +++ b/tests/ui/checked_unwrap/simple_conditionals.rs @@ -1,5 +1,5 @@ #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] -#![allow(clippy::if_same_then_else)] +#![allow(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] macro_rules! m { ($a:expr) => { diff --git a/tests/ui/default_numeric_fallback.rs b/tests/ui/default_numeric_fallback.rs index 0b3758952ac..de89f806c58 100644 --- a/tests/ui/default_numeric_fallback.rs +++ b/tests/ui/default_numeric_fallback.rs @@ -3,6 +3,7 @@ #![allow(clippy::never_loop)] #![allow(clippy::no_effect)] #![allow(clippy::unnecessary_operation)] +#![allow(clippy::shared_code_in_if_blocks)] mod basic_expr { fn test() { diff --git a/tests/ui/default_numeric_fallback.stderr b/tests/ui/default_numeric_fallback.stderr index b31aa4ebcf8..d1c4c8203dd 100644 --- a/tests/ui/default_numeric_fallback.stderr +++ b/tests/ui/default_numeric_fallback.stderr @@ -1,5 +1,5 @@ error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:10:17 + --> $DIR/default_numeric_fallback.rs:11:17 | LL | let x = 22; | ^^ help: consider adding suffix: `22_i32` @@ -7,139 +7,139 @@ LL | let x = 22; = note: `-D clippy::default-numeric-fallback` implied by `-D warnings` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:11:18 + --> $DIR/default_numeric_fallback.rs:12:18 | LL | let x = [1, 2, 3]; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:11:21 + --> $DIR/default_numeric_fallback.rs:12:21 | LL | let x = [1, 2, 3]; | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:11:24 + --> $DIR/default_numeric_fallback.rs:12:24 | LL | let x = [1, 2, 3]; | ^ help: consider adding suffix: `3_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:12:28 + --> $DIR/default_numeric_fallback.rs:13:28 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:12:31 + --> $DIR/default_numeric_fallback.rs:13:31 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:12:44 + --> $DIR/default_numeric_fallback.rs:13:44 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `3_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:12:47 + --> $DIR/default_numeric_fallback.rs:13:47 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `4_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:13:23 + --> $DIR/default_numeric_fallback.rs:14:23 | LL | let x = match 1 { | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:14:13 + --> $DIR/default_numeric_fallback.rs:15:13 | LL | 1 => 1, | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:14:18 + --> $DIR/default_numeric_fallback.rs:15:18 | LL | 1 => 1, | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:15:18 + --> $DIR/default_numeric_fallback.rs:16:18 | LL | _ => 2, | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:19:17 + --> $DIR/default_numeric_fallback.rs:20:17 | LL | let x = 0.12; | ^^^^ help: consider adding suffix: `0.12_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:37:21 + --> $DIR/default_numeric_fallback.rs:38:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:45:21 + --> $DIR/default_numeric_fallback.rs:46:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:51:21 + --> $DIR/default_numeric_fallback.rs:52:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:63:9 + --> $DIR/default_numeric_fallback.rs:64:9 | LL | 1 | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:69:27 + --> $DIR/default_numeric_fallback.rs:70:27 | LL | let f = || -> _ { 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:73:29 + --> $DIR/default_numeric_fallback.rs:74:29 | LL | let f = || -> i32 { 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:87:21 + --> $DIR/default_numeric_fallback.rs:88:21 | LL | generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:90:32 + --> $DIR/default_numeric_fallback.rs:91:32 | LL | let x: _ = generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:108:28 + --> $DIR/default_numeric_fallback.rs:109:28 | LL | GenericStruct { x: 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:111:36 + --> $DIR/default_numeric_fallback.rs:112:36 | LL | let _ = GenericStruct { x: 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:131:23 + --> $DIR/default_numeric_fallback.rs:132:23 | LL | s.generic_arg(1); | ^ help: consider adding suffix: `1_i32` diff --git a/tests/ui/if_same_then_else.stderr b/tests/ui/if_same_then_else.stderr index 74b11bac487..2f38052fc20 100644 --- a/tests/ui/if_same_then_else.stderr +++ b/tests/ui/if_same_then_else.stderr @@ -82,5 +82,31 @@ LL | | 42 LL | | }; | |_____^ -error: aborting due to 4 previous errors +error: this `if` has identical blocks + --> $DIR/if_same_then_else.rs:95:13 + | +LL | if true { + | _____________^ +LL | | let bar = if true { 42 } else { 43 }; +LL | | +LL | | while foo() { +... | +LL | | bar + 1; +LL | | } else { + | |_____^ + | +note: same as this + --> $DIR/if_same_then_else.rs:102:12 + | +LL | } else { + | ____________^ +LL | | //~ ERROR same body as `if` block +LL | | let bar = if true { 42 } else { 43 }; +LL | | +... | +LL | | bar + 1; +LL | | } + | |_____^ + +error: aborting due to 5 previous errors diff --git a/tests/ui/if_same_then_else2.stderr b/tests/ui/if_same_then_else2.stderr index a0e636e3a61..6524be0af85 100644 --- a/tests/ui/if_same_then_else2.stderr +++ b/tests/ui/if_same_then_else2.stderr @@ -101,5 +101,25 @@ LL | | Ok("foo")?; LL | | } | |_____^ -error: aborting due to 5 previous errors +error: this `if` has identical blocks + --> $DIR/if_same_then_else2.rs:122:20 + | +LL | } else if true { + | ____________________^ +LL | | let foo = ""; +LL | | return Ok(&foo[0..]); +LL | | } else { + | |_____^ + | +note: same as this + --> $DIR/if_same_then_else2.rs:125:12 + | +LL | } else { + | ____________^ +LL | | let foo = ""; +LL | | return Ok(&foo[0..]); +LL | | } + | |_____^ + +error: aborting due to 6 previous errors diff --git a/tests/ui/needless_bool/simple.rs b/tests/ui/needless_bool/simple.rs index e9f1428fc3a..678040fa98b 100644 --- a/tests/ui/needless_bool/simple.rs +++ b/tests/ui/needless_bool/simple.rs @@ -4,7 +4,8 @@ dead_code, clippy::no_effect, clippy::if_same_then_else, - clippy::needless_return + clippy::needless_return, + clippy::shared_code_in_if_blocks )] fn main() { diff --git a/tests/ui/needless_bool/simple.stderr b/tests/ui/needless_bool/simple.stderr index c57a8a042fb..0ccc9416bcd 100644 --- a/tests/ui/needless_bool/simple.stderr +++ b/tests/ui/needless_bool/simple.stderr @@ -1,5 +1,5 @@ error: this if-then-else expression will always return true - --> $DIR/simple.rs:13:5 + --> $DIR/simple.rs:14: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 will always return false - --> $DIR/simple.rs:18:5 + --> $DIR/simple.rs:19:5 | LL | / if x { LL | | false @@ -21,7 +21,7 @@ LL | | }; | |_____^ error: this if-then-else expression will always return true - --> $DIR/simple.rs:33:5 + --> $DIR/simple.rs:34:5 | LL | / if x { LL | | return true; @@ -31,7 +31,7 @@ LL | | }; | |_____^ error: this if-then-else expression will always return false - --> $DIR/simple.rs:41:5 + --> $DIR/simple.rs:42:5 | LL | / if x { LL | | return false; diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index 990475fcb58..ebf74cfef12 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -1,7 +1,12 @@ // run-rustfix -#![allow(unused, clippy::needless_bool)] -#![allow(clippy::if_same_then_else, clippy::single_match)] +#![allow(unused)] +#![allow( + clippy::if_same_then_else, + clippy::single_match, + clippy::shared_code_in_if_blocks, + clippy::needless_bool +)] #![warn(clippy::needless_return)] macro_rules! the_answer { diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index dec3d84a020..2bebccdabca 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -1,7 +1,12 @@ // run-rustfix -#![allow(unused, clippy::needless_bool)] -#![allow(clippy::if_same_then_else, clippy::single_match)] +#![allow(unused)] +#![allow( + clippy::if_same_then_else, + clippy::single_match, + clippy::shared_code_in_if_blocks, + clippy::needless_bool +)] #![warn(clippy::needless_return)] macro_rules! the_answer { diff --git a/tests/ui/needless_return.stderr b/tests/ui/needless_return.stderr index ae31d607541..075db22456f 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:18:5 + --> $DIR/needless_return.rs:23:5 | LL | return true; | ^^^^^^^^^^^^ help: remove `return`: `true` @@ -7,103 +7,103 @@ LL | return true; = note: `-D clippy::needless-return` implied by `-D warnings` error: unneeded `return` statement - --> $DIR/needless_return.rs:22:5 + --> $DIR/needless_return.rs:27:5 | LL | return true; | ^^^^^^^^^^^^ help: remove `return`: `true` error: unneeded `return` statement - --> $DIR/needless_return.rs:27:9 + --> $DIR/needless_return.rs:32:9 | 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 false; | ^^^^^^^^^^^^^ help: remove `return`: `false` error: unneeded `return` statement - --> $DIR/needless_return.rs:35:17 + --> $DIR/needless_return.rs:40:17 | LL | true => return false, | ^^^^^^^^^^^^ help: remove `return`: `false` error: unneeded `return` statement - --> $DIR/needless_return.rs:37:13 + --> $DIR/needless_return.rs:42:13 | LL | return true; | ^^^^^^^^^^^^ help: remove `return`: `true` error: unneeded `return` statement - --> $DIR/needless_return.rs:44:9 + --> $DIR/needless_return.rs:49:9 | LL | return true; | ^^^^^^^^^^^^ help: remove `return`: `true` error: unneeded `return` statement - --> $DIR/needless_return.rs:46:16 + --> $DIR/needless_return.rs:51:16 | LL | let _ = || return true; | ^^^^^^^^^^^ help: remove `return`: `true` error: unneeded `return` statement - --> $DIR/needless_return.rs:54:5 + --> $DIR/needless_return.rs:59:5 | LL | return; | ^^^^^^^ help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:59:9 + --> $DIR/needless_return.rs:64:9 | 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:68:14 + --> $DIR/needless_return.rs:73:14 | LL | _ => return, | ^^^^^^ help: replace `return` with an empty block: `{}` error: unneeded `return` statement - --> $DIR/needless_return.rs:83:9 + --> $DIR/needless_return.rs:88:9 | LL | return String::from("test"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")` error: unneeded `return` statement - --> $DIR/needless_return.rs:85:9 + --> $DIR/needless_return.rs:90:9 | LL | return String::new(); | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()` error: unneeded `return` statement - --> $DIR/needless_return.rs:106:32 + --> $DIR/needless_return.rs:111:32 | LL | bar.unwrap_or_else(|_| return) | ^^^^^^ help: replace `return` with an empty block: `{}` error: unneeded `return` statement - --> $DIR/needless_return.rs:111:13 + --> $DIR/needless_return.rs:116:13 | LL | return; | ^^^^^^^ help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:113:20 + --> $DIR/needless_return.rs:118:20 | LL | let _ = || return; | ^^^^^^ help: replace `return` with an empty block: `{}` error: unneeded `return` statement - --> $DIR/needless_return.rs:119:32 + --> $DIR/needless_return.rs:124:32 | LL | res.unwrap_or_else(|_| return Foo) | ^^^^^^^^^^ help: remove `return`: `Foo` diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs b/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs index 7974ea2f59c..6ff362cb752 100644 --- a/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs +++ b/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs @@ -190,4 +190,20 @@ fn test_suggestion_with_weird_formatting() { if x == 17 { b = 1; a = 0x99; } else { a = 0x99; } } +fn fp_test() { + let x = 17; + + if x == 18 { + let y = 19; + if y < x { + println!("Trigger") + } + } else { + let z = 166; + if z < x { + println!("Trigger") + } + } +} + fn main() {} diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_bot.stderr b/tests/ui/shared_code_in_if_blocks/shared_at_bot.stderr index 5ecf47bf920..2268d8f5365 100644 --- a/tests/ui/shared_code_in_if_blocks/shared_at_bot.stderr +++ b/tests/ui/shared_code_in_if_blocks/shared_at_bot.stderr @@ -12,7 +12,7 @@ note: the lint level is defined here | LL | #![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: The end suggestion probably needs some adjustments to use the expression result correctly. + = note: The end suggestion probably needs some adjustments to use the expression result correctly help: consider moving the end statements out like this | LL | } @@ -75,7 +75,7 @@ LL | | // I'm expecting a note about this LL | | } | |_____^ | - = warning: Some moved values might need to be renamed to avoid wrong references. + = warning: Some moved values might need to be renamed to avoid wrong references help: consider moving the end statements out like this | LL | } @@ -91,7 +91,7 @@ LL | | println!("This is the new simple_example: {}", simple_examples); LL | | } | |_____^ | - = warning: Some moved values might need to be renamed to avoid wrong references. + = warning: Some moved values might need to be renamed to avoid wrong references help: consider moving the end statements out like this | LL | } @@ -106,7 +106,7 @@ LL | / x << 2 LL | | }; | |_____^ | - = note: The end suggestion probably needs some adjustments to use the expression result correctly. + = note: The end suggestion probably needs some adjustments to use the expression result correctly help: consider moving the end statements out like this | LL | } @@ -120,7 +120,7 @@ LL | / x * 4 LL | | } | |_____^ | - = note: The end suggestion probably needs some adjustments to use the expression result correctly. + = note: The end suggestion probably needs some adjustments to use the expression result correctly help: consider moving the end statements out like this | LL | } diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_top.stderr b/tests/ui/shared_code_in_if_blocks/shared_at_top.stderr index 1ad924aba6a..76898a6ff02 100644 --- a/tests/ui/shared_code_in_if_blocks/shared_at_top.stderr +++ b/tests/ui/shared_code_in_if_blocks/shared_at_top.stderr @@ -25,7 +25,7 @@ LL | | println!("The value y was set to: `{}`", y); LL | | let _z = y; | |___________________^ | - = warning: Some moved values might need to be renamed to avoid wrong references. + = warning: Some moved values might need to be renamed to avoid wrong references help: consider moving the start statements out like this | LL | let y = 9; @@ -55,7 +55,7 @@ LL | | let used_value_name = "Different type"; LL | | println!("Str: {}", used_value_name); | |_____________________________________________^ | - = warning: Some moved values might need to be renamed to avoid wrong references. + = warning: Some moved values might need to be renamed to avoid wrong references help: consider moving the start statements out like this | LL | let used_value_name = "Different type"; @@ -71,7 +71,7 @@ LL | | let can_be_overridden = "Move me"; LL | | println!("I'm also moveable"); | |______________________________________^ | - = warning: Some moved values might need to be renamed to avoid wrong references. + = warning: Some moved values might need to be renamed to avoid wrong references help: consider moving the start statements out like this | LL | let can_be_overridden = "Move me"; diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.stderr b/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.stderr index 9f675a20a6d..75c3d397f2e 100644 --- a/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.stderr +++ b/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.stderr @@ -47,7 +47,7 @@ LL | / let _overlap_end = r * r * r; LL | | let z = "end"; LL | | } | |_____^ - = warning: Some moved values might need to be renamed to avoid wrong references. + = warning: Some moved values might need to be renamed to avoid wrong references help: consider moving the start statements out like this | LL | let r = 7; @@ -82,7 +82,7 @@ LL | | }; LL | | process_data(pack); LL | | } | |_____^ - = warning: Some moved values might need to be renamed to avoid wrong references. + = warning: Some moved values might need to be renamed to avoid wrong references help: consider moving the start statements out like this | LL | let a = 0xcafe; @@ -113,7 +113,7 @@ note: and here at the end LL | / x << 2 LL | | }; | |_____^ - = note: The end suggestion probably needs some adjustments to use the expression result correctly. + = note: The end suggestion probably needs some adjustments to use the expression result correctly help: consider moving the start statements out like this | LL | let _ = 19; @@ -138,7 +138,7 @@ note: and here at the end LL | / x * 4 LL | | } | |_____^ - = note: The end suggestion probably needs some adjustments to use the expression result correctly. + = note: The end suggestion probably needs some adjustments to use the expression result correctly help: consider moving the start statements out like this | LL | let _ = 17; diff --git a/tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs b/tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs index a564b30cb1c..cd397db47d0 100644 --- a/tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs +++ b/tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs @@ -91,6 +91,14 @@ fn valid_examples() { let _ = (x, y, z); // I'm so much better than the x == 418 block. Trust me } + + let x = 1; + if true { + println!("{}", x); + } else { + let x = 2; + println!("{}", x); + } } /// This makes sure that the `if_same_then_else` masks the `shared_code_in_if_blocks` lint diff --git a/tests/ui/shared_code_in_if_blocks/valid_if_blocks.stderr b/tests/ui/shared_code_in_if_blocks/valid_if_blocks.stderr index d290c65822b..2061cc25d21 100644 --- a/tests/ui/shared_code_in_if_blocks/valid_if_blocks.stderr +++ b/tests/ui/shared_code_in_if_blocks/valid_if_blocks.stderr @@ -1,5 +1,5 @@ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:102:15 + --> $DIR/valid_if_blocks.rs:110:15 | LL | if x == 0 { | _______________^ @@ -15,7 +15,7 @@ note: the lint level is defined here LL | #![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ note: same as this - --> $DIR/valid_if_blocks.rs:106:12 + --> $DIR/valid_if_blocks.rs:114:12 | LL | } else { | ____________^ @@ -26,19 +26,19 @@ LL | | } | |_____^ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:113:23 + --> $DIR/valid_if_blocks.rs:121:23 | LL | let _ = if x == 6 { 7 } else { 7 }; | ^^^^^ | note: same as this - --> $DIR/valid_if_blocks.rs:113:34 + --> $DIR/valid_if_blocks.rs:121:34 | LL | let _ = if x == 6 { 7 } else { 7 }; | ^^^^^ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:119:23 + --> $DIR/valid_if_blocks.rs:127:23 | LL | } else if x == 68 { | _______________________^ @@ -51,7 +51,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/valid_if_blocks.rs:128:12 + --> $DIR/valid_if_blocks.rs:136:12 | LL | } else { | ____________^ @@ -64,7 +64,7 @@ LL | | }; | |_____^ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:141:23 + --> $DIR/valid_if_blocks.rs:149:23 | LL | } else if x == 68 { | _______________________^ @@ -74,7 +74,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/valid_if_blocks.rs:144:12 + --> $DIR/valid_if_blocks.rs:152:12 | LL | } else { | ____________^ -- cgit 1.4.1-3-g733a5 From 7c9e192e055f8b8a8a5f8b177c415440bc2333ce Mon Sep 17 00:00:00 2001 From: xFrednet Date: Fri, 5 Mar 2021 19:23:12 +0100 Subject: Test for empty blocks and update from master --- .../ui/shared_code_in_if_blocks/valid_if_blocks.rs | 17 +++----- .../valid_if_blocks.stderr | 50 ++++++++++++++-------- 2 files changed, 39 insertions(+), 28 deletions(-) (limited to 'tests') diff --git a/tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs b/tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs index cd397db47d0..e63490d5b07 100644 --- a/tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs +++ b/tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs @@ -99,6 +99,11 @@ fn valid_examples() { let x = 2; println!("{}", x); } + + // Let's test empty blocks + if false { + } else { + } } /// This makes sure that the `if_same_then_else` masks the `shared_code_in_if_blocks` lint @@ -128,20 +133,12 @@ fn trigger_other_lint() { println!("I'm a doppelgänger"); // Don't listen to my clone below - if y == 90 { - "=^.^=" - } else { - ":D" - } + if y == 90 { "=^.^=" } else { ":D" } } else { // Don't listen to my clone above println!("I'm a doppelgänger"); - if y == 90 { - "=^.^=" - } else { - ":D" - } + if y == 90 { "=^.^=" } else { ":D" } }; if x == 0 { diff --git a/tests/ui/shared_code_in_if_blocks/valid_if_blocks.stderr b/tests/ui/shared_code_in_if_blocks/valid_if_blocks.stderr index 2061cc25d21..846581456dc 100644 --- a/tests/ui/shared_code_in_if_blocks/valid_if_blocks.stderr +++ b/tests/ui/shared_code_in_if_blocks/valid_if_blocks.stderr @@ -1,11 +1,8 @@ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:110:15 + --> $DIR/valid_if_blocks.rs:104:14 | -LL | if x == 0 { - | _______________^ -LL | | let u = 19; -LL | | println!("How are u today?"); -LL | | let _ = "This is a string"; +LL | if false { + | ______________^ LL | | } else { | |_____^ | @@ -15,7 +12,26 @@ note: the lint level is defined here LL | #![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ note: same as this - --> $DIR/valid_if_blocks.rs:114:12 + --> $DIR/valid_if_blocks.rs:105:12 + | +LL | } else { + | ____________^ +LL | | } + | |_____^ + +error: this `if` has identical blocks + --> $DIR/valid_if_blocks.rs:115:15 + | +LL | if x == 0 { + | _______________^ +LL | | let u = 19; +LL | | println!("How are u today?"); +LL | | let _ = "This is a string"; +LL | | } else { + | |_____^ + | +note: same as this + --> $DIR/valid_if_blocks.rs:119:12 | LL | } else { | ____________^ @@ -26,45 +42,43 @@ LL | | } | |_____^ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:121:23 + --> $DIR/valid_if_blocks.rs:126:23 | LL | let _ = if x == 6 { 7 } else { 7 }; | ^^^^^ | note: same as this - --> $DIR/valid_if_blocks.rs:121:34 + --> $DIR/valid_if_blocks.rs:126:34 | LL | let _ = if x == 6 { 7 } else { 7 }; | ^^^^^ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:127:23 + --> $DIR/valid_if_blocks.rs:132:23 | LL | } else if x == 68 { | _______________________^ LL | | println!("I'm a doppelgänger"); LL | | // Don't listen to my clone below LL | | -... | -LL | | } +LL | | if y == 90 { "=^.^=" } else { ":D" } LL | | } else { | |_____^ | note: same as this - --> $DIR/valid_if_blocks.rs:136:12 + --> $DIR/valid_if_blocks.rs:137:12 | LL | } else { | ____________^ LL | | // Don't listen to my clone above LL | | println!("I'm a doppelgänger"); LL | | -... | -LL | | } +LL | | if y == 90 { "=^.^=" } else { ":D" } LL | | }; | |_____^ error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:149:23 + --> $DIR/valid_if_blocks.rs:146:23 | LL | } else if x == 68 { | _______________________^ @@ -74,7 +88,7 @@ LL | | } else { | |_____^ | note: same as this - --> $DIR/valid_if_blocks.rs:152:12 + --> $DIR/valid_if_blocks.rs:149:12 | LL | } else { | ____________^ @@ -83,5 +97,5 @@ LL | | println!("I'm a doppelgänger"); LL | | } | |_____^ -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors -- cgit 1.4.1-3-g733a5 From a6f54f5dfdfdf0017ffecfbcd6f43352b8b71ca1 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Thu, 1 Apr 2021 18:30:47 +0200 Subject: Renaming the lint to branches_sharing_code and fixing typos --- CHANGELOG.md | 2 +- clippy_lints/src/copies.rs | 35 ++-- clippy_lints/src/lib.rs | 5 +- clippy_utils/src/lib.rs | 2 +- tests/ui/branches_sharing_code/shared_at_bottom.rs | 209 +++++++++++++++++++++ .../branches_sharing_code/shared_at_bottom.stderr | 143 ++++++++++++++ tests/ui/branches_sharing_code/shared_at_top.rs | 103 ++++++++++ .../ui/branches_sharing_code/shared_at_top.stderr | 121 ++++++++++++ .../shared_at_top_and_bottom.rs | 119 ++++++++++++ .../shared_at_top_and_bottom.stderr | 154 +++++++++++++++ tests/ui/branches_sharing_code/valid_if_blocks.rs | 155 +++++++++++++++ .../branches_sharing_code/valid_if_blocks.stderr | 101 ++++++++++ tests/ui/checked_unwrap/complex_conditionals.rs | 2 +- .../checked_unwrap/complex_conditionals_nested.rs | 2 +- tests/ui/checked_unwrap/simple_conditionals.rs | 2 +- tests/ui/default_numeric_fallback.rs | 2 +- tests/ui/if_same_then_else.rs | 2 +- tests/ui/if_same_then_else2.rs | 2 +- tests/ui/let_if_seq.rs | 2 +- tests/ui/needless_bool/simple.rs | 2 +- tests/ui/needless_return.fixed | 2 +- tests/ui/needless_return.rs | 2 +- tests/ui/shared_code_in_if_blocks/shared_at_bot.rs | 209 --------------------- .../shared_code_in_if_blocks/shared_at_bot.stderr | 143 -------------- tests/ui/shared_code_in_if_blocks/shared_at_top.rs | 103 ---------- .../shared_code_in_if_blocks/shared_at_top.stderr | 121 ------------ .../shared_at_top_and_bot.rs | 119 ------------ .../shared_at_top_and_bot.stderr | 154 --------------- .../ui/shared_code_in_if_blocks/valid_if_blocks.rs | 155 --------------- .../valid_if_blocks.stderr | 101 ---------- 30 files changed, 1138 insertions(+), 1136 deletions(-) create mode 100644 tests/ui/branches_sharing_code/shared_at_bottom.rs create mode 100644 tests/ui/branches_sharing_code/shared_at_bottom.stderr create mode 100644 tests/ui/branches_sharing_code/shared_at_top.rs create mode 100644 tests/ui/branches_sharing_code/shared_at_top.stderr create mode 100644 tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs create mode 100644 tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr create mode 100644 tests/ui/branches_sharing_code/valid_if_blocks.rs create mode 100644 tests/ui/branches_sharing_code/valid_if_blocks.stderr delete mode 100644 tests/ui/shared_code_in_if_blocks/shared_at_bot.rs delete mode 100644 tests/ui/shared_code_in_if_blocks/shared_at_bot.stderr delete mode 100644 tests/ui/shared_code_in_if_blocks/shared_at_top.rs delete mode 100644 tests/ui/shared_code_in_if_blocks/shared_at_top.stderr delete mode 100644 tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.rs delete mode 100644 tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.stderr delete mode 100644 tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs delete mode 100644 tests/ui/shared_code_in_if_blocks/valid_if_blocks.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 790bca4ac18..73997192ae0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2129,6 +2129,7 @@ Released 2018-09-13 [`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box [`box_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_vec [`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local +[`branches_sharing_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#branches_sharing_code [`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow [`bytes_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_nth [`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata @@ -2455,7 +2456,6 @@ Released 2018-09-13 [`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse [`shadow_same`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_same [`shadow_unrelated`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_unrelated -[`shared_code_in_if_blocks`]: https://rust-lang.github.io/rust-clippy/master/index.html#shared_code_in_if_blocks [`short_circuit_statement`]: https://rust-lang.github.io/rust-clippy/master/index.html#short_circuit_statement [`should_assert_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_assert_eq [`should_implement_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index ff99a9a09b3..1b982221b06 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -1,7 +1,8 @@ -use crate::utils::{ - both, count_eq, eq_expr_value, first_line_of_span, get_enclosing_block, get_parent_expr, if_sequence, in_macro, - indent_of, parent_node_is_if_expr, reindent_multiline, run_lints, search_same, snippet, snippet_opt, - span_lint_and_note, span_lint_and_then, ContainsName, SpanlessEq, SpanlessHash, +use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then}; +use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, snippet_opt}; +use clippy_utils::{ + both, count_eq, eq_expr_value, get_enclosing_block, get_parent_expr, if_sequence, in_macro, parent_node_is_if_expr, + run_lints, search_same, ContainsName, SpanlessEq, SpanlessHash, }; use if_chain::if_chain; use rustc_data_structures::fx::FxHashSet; @@ -141,7 +142,7 @@ declare_clippy_lint! { /// 42 /// }; /// ``` - pub SHARED_CODE_IN_IF_BLOCKS, + pub BRANCHES_SHARING_CODE, complexity, "`if` statement with shared code in all blocks" } @@ -150,7 +151,7 @@ declare_lint_pass!(CopyAndPaste => [ IFS_SAME_COND, SAME_FUNCTIONS_IN_IF_CONDITION, IF_SAME_THEN_ELSE, - SHARED_CODE_IN_IF_BLOCKS + BRANCHES_SHARING_CODE ]); impl<'tcx> LateLintPass<'tcx> for CopyAndPaste { @@ -173,17 +174,17 @@ impl<'tcx> LateLintPass<'tcx> for CopyAndPaste { lint_same_cond(cx, &conds); lint_same_fns_in_if_cond(cx, &conds); // Block duplication - lint_same_then_else(cx, &blocks, conds.len() != blocks.len(), expr); + lint_same_then_else(cx, &blocks, conds.len() == blocks.len(), expr); } } } } -/// Implementation of `SHARED_CODE_IN_IF_BLOCKS` and `IF_SAME_THEN_ELSE` if the blocks are equal. +/// Implementation of `BRANCHES_SHARING_CODE` and `IF_SAME_THEN_ELSE` if the blocks are equal. fn lint_same_then_else<'tcx>( cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>], - has_unconditional_else: bool, + has_conditional_else: bool, expr: &'tcx Expr<'_>, ) { // We only lint ifs with multiple blocks @@ -195,8 +196,8 @@ fn lint_same_then_else<'tcx>( let has_expr = blocks[0].expr.is_some(); let (start_eq, mut end_eq, expr_eq) = scan_block_for_eq(cx, blocks); - // SHARED_CODE_IN_IF_BLOCKS prerequisites - if !has_unconditional_else || (start_eq == 0 && end_eq == 0 && (has_expr && !expr_eq)) { + // BRANCHES_SHARING_CODE prerequisites + if has_conditional_else || (start_eq == 0 && end_eq == 0 && (has_expr && !expr_eq)) { return; } @@ -210,7 +211,7 @@ fn lint_same_then_else<'tcx>( intravisit::walk_stmt(&mut start_walker, stmt); } - emit_shared_code_in_if_blocks_lint( + emit_branches_sharing_code_lint( cx, start_eq, 0, @@ -277,7 +278,7 @@ fn lint_same_then_else<'tcx>( }); } - emit_shared_code_in_if_blocks_lint( + emit_branches_sharing_code_lint( cx, start_eq, end_eq, @@ -298,7 +299,7 @@ fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> (usize, let r_stmts = win[1].stmts; // `SpanlessEq` now keeps track of the locals and is therefore context sensitive clippy#6752. - // The comparison therefor needs to be done in a way that builds the correct context. + // The comparison therefore needs to be done in a way that builds the correct context. let mut evaluator = SpanlessEq::new(cx); let mut evaluator = evaluator.inter_expr(); @@ -387,7 +388,7 @@ fn check_for_warn_of_moved_symbol( }) } -fn emit_shared_code_in_if_blocks_lint( +fn emit_branches_sharing_code_lint( cx: &LateContext<'tcx>, start_stmts: usize, end_stmts: usize, @@ -472,7 +473,7 @@ fn emit_shared_code_in_if_blocks_lint( let (place_str, span, sugg) = suggestions.pop().unwrap(); let msg = format!("all if blocks contain the same code at the {}", place_str); let help = format!("consider moving the {} statements out like this", place_str); - span_lint_and_then(cx, SHARED_CODE_IN_IF_BLOCKS, span, msg.as_str(), |diag| { + span_lint_and_then(cx, BRANCHES_SHARING_CODE, span, msg.as_str(), |diag| { diag.span_suggestion(span, help.as_str(), sugg, Applicability::Unspecified); add_optional_msgs(diag); @@ -482,7 +483,7 @@ fn emit_shared_code_in_if_blocks_lint( let (_, start_span, start_sugg) = suggestions.pop().unwrap(); span_lint_and_then( cx, - SHARED_CODE_IN_IF_BLOCKS, + BRANCHES_SHARING_CODE, start_span, "all if blocks contain the same code at the start and the end. Here at the start", move |diag| { diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3f5c1c7c526..43e89c2475f 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -613,10 +613,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &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, - &copies::SHARED_CODE_IN_IF_BLOCKS, ©_iterator::COPY_ITERATOR, &create_dir::CREATE_DIR, &dbg_macro::DBG_MACRO, @@ -1483,9 +1483,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&collapsible_if::COLLAPSIBLE_IF), LintId::of(&collapsible_match::COLLAPSIBLE_MATCH), LintId::of(&comparison_chain::COMPARISON_CHAIN), + LintId::of(&copies::BRANCHES_SHARING_CODE), LintId::of(&copies::IFS_SAME_COND), LintId::of(&copies::IF_SAME_THEN_ELSE), - LintId::of(&copies::SHARED_CODE_IN_IF_BLOCKS), LintId::of(&default::FIELD_REASSIGN_WITH_DEFAULT), LintId::of(&derive::DERIVE_HASH_XOR_EQ), LintId::of(&derive::DERIVE_ORD_XOR_PARTIAL_ORD), @@ -1872,6 +1872,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&booleans::NONMINIMAL_BOOL), LintId::of(&casts::CHAR_LIT_AS_U8), LintId::of(&casts::UNNECESSARY_CAST), + LintId::of(&copies::BRANCHES_SHARING_CODE), LintId::of(&double_comparison::DOUBLE_COMPARISONS), LintId::of(&double_parens::DOUBLE_PARENS), LintId::of(&duration_subsec::DURATION_SUBSEC), diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index f9576068dd6..7ce90ffd9f0 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -47,7 +47,7 @@ pub mod usage; pub mod visitors; pub use self::attrs::*; -pub use self::hir_utils::{both, eq_expr_value, over, SpanlessEq, SpanlessHash}; +pub use self::hir_utils::{both, count_eq, eq_expr_value, over, SpanlessEq, SpanlessHash}; use std::collections::hash_map::Entry; use std::hash::BuildHasherDefault; diff --git a/tests/ui/branches_sharing_code/shared_at_bottom.rs b/tests/ui/branches_sharing_code/shared_at_bottom.rs new file mode 100644 index 00000000000..c389c243d44 --- /dev/null +++ b/tests/ui/branches_sharing_code/shared_at_bottom.rs @@ -0,0 +1,209 @@ +#![allow(dead_code)] +#![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] + +// This tests the branches_sharing_code lint at the end of blocks + +fn simple_examples() { + let x = 1; + + let _ = if x == 7 { + println!("Branch I"); + let start_value = 0; + println!("=^.^="); + + // Same but not moveable due to `start_value` + let _ = start_value; + + // The rest is self contained and moveable => Only lint the rest + let result = false; + println!("Block end!"); + result + } else { + println!("Branch II"); + let start_value = 8; + println!("xD"); + + // Same but not moveable due to `start_value` + let _ = start_value; + + // The rest is self contained and moveable => Only lint the rest + let result = false; + println!("Block end!"); + result + }; + + // Else if block + if x == 9 { + println!("The index is: 6"); + + println!("Same end of block"); + } else if x == 8 { + println!("The index is: 4"); + + // We should only get a lint trigger for the last statement + println!("This is also eq with the else block"); + println!("Same end of block"); + } else { + println!("This is also eq with the else block"); + println!("Same end of block"); + } + + // Use of outer scope value + let outer_scope_value = "I'm outside the if block"; + if x < 99 { + let z = "How are you"; + println!("I'm a local because I use the value `z`: `{}`", z); + + println!( + "I'm moveable because I know: `outer_scope_value`: '{}'", + outer_scope_value + ); + } else { + let z = 45678000; + println!("I'm a local because I use the value `z`: `{}`", z); + + println!( + "I'm moveable because I know: `outer_scope_value`: '{}'", + outer_scope_value + ); + } + + if x == 9 { + if x == 8 { + // No parent!! + println!("---"); + println!("Hello World"); + } else { + println!("Hello World"); + } + } +} + +/// Simple examples where the move can cause some problems due to moved values +fn simple_but_suggestion_is_invalid() { + let x = 16; + + // Local value + let later_used_value = 17; + if x == 9 { + let _ = 9; + let later_used_value = "A string value"; + println!("{}", later_used_value); + } else { + let later_used_value = "A string value"; + println!("{}", later_used_value); + // I'm expecting a note about this + } + println!("{}", later_used_value); + + // outer function + if x == 78 { + let simple_examples = "I now identify as a &str :)"; + println!("This is the new simple_example: {}", simple_examples); + } else { + println!("Separator print statement"); + + let simple_examples = "I now identify as a &str :)"; + println!("This is the new simple_example: {}", simple_examples); + } + simple_examples(); +} + +/// Tests where the blocks are not linted due to the used value scope +fn not_moveable_due_to_value_scope() { + let x = 18; + + // Using a local value in the moved code + if x == 9 { + let y = 18; + println!("y is: `{}`", y); + } else { + let y = "A string"; + println!("y is: `{}`", y); + } + + // Using a local value in the expression + let _ = if x == 0 { + let mut result = x + 1; + + println!("1. Doing some calculations"); + println!("2. Some more calculations"); + println!("3. Setting result"); + + result + } else { + let mut result = x - 1; + + println!("1. Doing some calculations"); + println!("2. Some more calculations"); + println!("3. Setting result"); + + result + }; + + let _ = if x == 7 { + let z1 = 100; + println!("z1: {}", z1); + + let z2 = z1; + println!("z2: {}", z2); + + z2 + } else { + let z1 = 300; + println!("z1: {}", z1); + + let z2 = z1; + println!("z2: {}", z2); + + z2 + }; +} + +/// This should add a note to the lint msg since the moved expression is not `()` +fn added_note_for_expression_use() -> u32 { + let x = 9; + + let _ = if x == 7 { + x << 2 + } else { + let _ = 6; + x << 2 + }; + + if x == 9 { + x * 4 + } else { + let _ = 17; + x * 4 + } +} + +#[rustfmt::skip] +fn test_suggestion_with_weird_formatting() { + let x = 9; + let mut a = 0; + let mut b = 0; + + // The error message still looks weird tbh but this is the best I can do + // for weird formatting + if x == 17 { b = 1; a = 0x99; } else { a = 0x99; } +} + +fn fp_test() { + let x = 17; + + if x == 18 { + let y = 19; + if y < x { + println!("Trigger") + } + } else { + let z = 166; + if z < x { + println!("Trigger") + } + } +} + +fn main() {} diff --git a/tests/ui/branches_sharing_code/shared_at_bottom.stderr b/tests/ui/branches_sharing_code/shared_at_bottom.stderr new file mode 100644 index 00000000000..271fcd8b6c1 --- /dev/null +++ b/tests/ui/branches_sharing_code/shared_at_bottom.stderr @@ -0,0 +1,143 @@ +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bottom.rs:30:5 + | +LL | / let result = false; +LL | | println!("Block end!"); +LL | | result +LL | | }; + | |_____^ + | +note: the lint level is defined here + --> $DIR/shared_at_bottom.rs:2:36 + | +LL | #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: The end suggestion probably needs some adjustments to use the expression result correctly +help: consider moving the end statements out like this + | +LL | } +LL | let result = false; +LL | println!("Block end!"); +LL | result; + | + +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bottom.rs:48:5 + | +LL | / println!("Same end of block"); +LL | | } + | |_____^ + | +help: consider moving the end statements out like this + | +LL | } +LL | println!("Same end of block"); + | + +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bottom.rs:65:5 + | +LL | / println!( +LL | | "I'm moveable because I know: `outer_scope_value`: '{}'", +LL | | outer_scope_value +LL | | ); +LL | | } + | |_____^ + | +help: consider moving the end statements out like this + | +LL | } +LL | println!( +LL | "I'm moveable because I know: `outer_scope_value`: '{}'", +LL | outer_scope_value +LL | ); + | + +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bottom.rs:77:9 + | +LL | / println!("Hello World"); +LL | | } + | |_________^ + | +help: consider moving the end statements out like this + | +LL | } +LL | println!("Hello World"); + | + +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bottom.rs:93:5 + | +LL | / let later_used_value = "A string value"; +LL | | println!("{}", later_used_value); +LL | | // I'm expecting a note about this +LL | | } + | |_____^ + | + = warning: Some moved values might need to be renamed to avoid wrong references +help: consider moving the end statements out like this + | +LL | } +LL | let later_used_value = "A string value"; +LL | println!("{}", later_used_value); + | + +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bottom.rs:106:5 + | +LL | / let simple_examples = "I now identify as a &str :)"; +LL | | println!("This is the new simple_example: {}", simple_examples); +LL | | } + | |_____^ + | + = warning: Some moved values might need to be renamed to avoid wrong references +help: consider moving the end statements out like this + | +LL | } +LL | let simple_examples = "I now identify as a &str :)"; +LL | println!("This is the new simple_example: {}", simple_examples); + | + +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bottom.rs:171:5 + | +LL | / x << 2 +LL | | }; + | |_____^ + | + = note: The end suggestion probably needs some adjustments to use the expression result correctly +help: consider moving the end statements out like this + | +LL | } +LL | x << 2; + | + +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bottom.rs:178:5 + | +LL | / x * 4 +LL | | } + | |_____^ + | + = note: The end suggestion probably needs some adjustments to use the expression result correctly +help: consider moving the end statements out like this + | +LL | } +LL | x * 4 + | + +error: all if blocks contain the same code at the end + --> $DIR/shared_at_bottom.rs:190:44 + | +LL | if x == 17 { b = 1; a = 0x99; } else { a = 0x99; } + | ^^^^^^^^^^^ + | +help: consider moving the end statements out like this + | +LL | if x == 17 { b = 1; a = 0x99; } else { } +LL | a = 0x99; + | + +error: aborting due to 9 previous errors + diff --git a/tests/ui/branches_sharing_code/shared_at_top.rs b/tests/ui/branches_sharing_code/shared_at_top.rs new file mode 100644 index 00000000000..e65bcfd7873 --- /dev/null +++ b/tests/ui/branches_sharing_code/shared_at_top.rs @@ -0,0 +1,103 @@ +#![allow(dead_code, clippy::eval_order_dependence)] +#![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] + +// This tests the branches_sharing_code lint at the start of blocks + +fn simple_examples() { + let x = 0; + + // Simple + if true { + println!("Hello World!"); + println!("I'm branch nr: 1"); + } else { + println!("Hello World!"); + println!("I'm branch nr: 2"); + } + + // Else if + if x == 0 { + let y = 9; + println!("The value y was set to: `{}`", y); + let _z = y; + + println!("I'm the true start index of arrays"); + } else if x == 1 { + let y = 9; + println!("The value y was set to: `{}`", y); + let _z = y; + + println!("I start counting from 1 so my array starts from `1`"); + } else { + let y = 9; + println!("The value y was set to: `{}`", y); + let _z = y; + + println!("Ha, Pascal allows you to start the array where you want") + } + + // Return a value + let _ = if x == 7 { + let y = 16; + println!("What can I say except: \"you're welcome?\""); + let _ = y; + x + } else { + let y = 16; + println!("Thank you"); + y + }; +} + +/// Simple examples where the move can cause some problems due to moved values +fn simple_but_suggestion_is_invalid() { + let x = 10; + + // Can't be automatically moved because used_value_name is getting used again + let used_value_name = 19; + if x == 10 { + let used_value_name = "Different type"; + println!("Str: {}", used_value_name); + let _ = 1; + } else { + let used_value_name = "Different type"; + println!("Str: {}", used_value_name); + let _ = 2; + } + let _ = used_value_name; + + // This can be automatically moved as `can_be_overridden` is not used again + let can_be_overridden = 8; + let _ = can_be_overridden; + if x == 11 { + let can_be_overridden = "Move me"; + println!("I'm also moveable"); + let _ = 111; + } else { + let can_be_overridden = "Move me"; + println!("I'm also moveable"); + let _ = 222; + } +} + +/// This function tests that the `IS_SAME_THAN_ELSE` only covers the lint if it's enabled. +fn check_if_same_than_else_mask() { + let x = 2021; + + #[allow(clippy::if_same_then_else)] + if x == 2020 { + println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint."); + println!("Because `IF_SAME_THEN_ELSE` is allowed here"); + } else { + println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint."); + println!("Because `IF_SAME_THEN_ELSE` is allowed here"); + } + + if x == 2019 { + println!("This should trigger `IS_SAME_THAN_ELSE` as usual"); + } else { + println!("This should trigger `IS_SAME_THAN_ELSE` as usual"); + } +} + +fn main() {} diff --git a/tests/ui/branches_sharing_code/shared_at_top.stderr b/tests/ui/branches_sharing_code/shared_at_top.stderr new file mode 100644 index 00000000000..15867e9ea02 --- /dev/null +++ b/tests/ui/branches_sharing_code/shared_at_top.stderr @@ -0,0 +1,121 @@ +error: all if blocks contain the same code at the start + --> $DIR/shared_at_top.rs:10:5 + | +LL | / if true { +LL | | println!("Hello World!"); + | |_________________________________^ + | +note: the lint level is defined here + --> $DIR/shared_at_top.rs:2:36 + | +LL | #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider moving the start statements out like this + | +LL | println!("Hello World!"); +LL | if true { + | + +error: all if blocks contain the same code at the start + --> $DIR/shared_at_top.rs:19:5 + | +LL | / if x == 0 { +LL | | let y = 9; +LL | | println!("The value y was set to: `{}`", y); +LL | | let _z = y; + | |___________________^ + | + = warning: Some moved values might need to be renamed to avoid wrong references +help: consider moving the start statements out like this + | +LL | let y = 9; +LL | println!("The value y was set to: `{}`", y); +LL | let _z = y; +LL | if x == 0 { + | + +error: all if blocks contain the same code at the start + --> $DIR/shared_at_top.rs:40:5 + | +LL | / let _ = if x == 7 { +LL | | let y = 16; + | |___________________^ + | +help: consider moving the start statements out like this + | +LL | let y = 16; +LL | let _ = if x == 7 { + | + +error: all if blocks contain the same code at the start + --> $DIR/shared_at_top.rs:58:5 + | +LL | / if x == 10 { +LL | | let used_value_name = "Different type"; +LL | | println!("Str: {}", used_value_name); + | |_____________________________________________^ + | + = warning: Some moved values might need to be renamed to avoid wrong references +help: consider moving the start statements out like this + | +LL | let used_value_name = "Different type"; +LL | println!("Str: {}", used_value_name); +LL | if x == 10 { + | + +error: all if blocks contain the same code at the start + --> $DIR/shared_at_top.rs:72:5 + | +LL | / if x == 11 { +LL | | let can_be_overridden = "Move me"; +LL | | println!("I'm also moveable"); + | |______________________________________^ + | + = warning: Some moved values might need to be renamed to avoid wrong references +help: consider moving the start statements out like this + | +LL | let can_be_overridden = "Move me"; +LL | println!("I'm also moveable"); +LL | if x == 11 { + | + +error: all if blocks contain the same code at the start + --> $DIR/shared_at_top.rs:88:5 + | +LL | / if x == 2020 { +LL | | println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint."); +LL | | println!("Because `IF_SAME_THEN_ELSE` is allowed here"); + | |________________________________________________________________^ + | +help: consider moving the start statements out like this + | +LL | println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint."); +LL | println!("Because `IF_SAME_THEN_ELSE` is allowed here"); +LL | if x == 2020 { + | + +error: this `if` has identical blocks + --> $DIR/shared_at_top.rs:96:18 + | +LL | if x == 2019 { + | __________________^ +LL | | println!("This should trigger `IS_SAME_THAN_ELSE` as usual"); +LL | | } else { + | |_____^ + | +note: the lint level is defined here + --> $DIR/shared_at_top.rs:2:9 + | +LL | #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +note: same as this + --> $DIR/shared_at_top.rs:98:12 + | +LL | } else { + | ____________^ +LL | | println!("This should trigger `IS_SAME_THAN_ELSE` as usual"); +LL | | } + | |_____^ + +error: aborting due to 7 previous errors + diff --git a/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs new file mode 100644 index 00000000000..deefdad32c9 --- /dev/null +++ b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.rs @@ -0,0 +1,119 @@ +#![allow(dead_code)] +#![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] + +// branches_sharing_code at the top and bottom of the if blocks + +struct DataPack { + id: u32, + name: String, + some_data: Vec, +} + +fn overlapping_eq_regions() { + let x = 9; + + // Overlap with separator + if x == 7 { + let t = 7; + let _overlap_start = t * 2; + let _overlap_end = 2 * t; + let _u = 9; + } else { + let t = 7; + let _overlap_start = t * 2; + let _overlap_end = 2 * t; + println!("Overlap separator"); + let _overlap_start = t * 2; + let _overlap_end = 2 * t; + let _u = 9; + } + + // Overlap with separator + if x == 99 { + let r = 7; + let _overlap_start = r; + let _overlap_middle = r * r; + let _overlap_end = r * r * r; + let z = "end"; + } else { + let r = 7; + let _overlap_start = r; + let _overlap_middle = r * r; + let _overlap_middle = r * r; + let _overlap_end = r * r * r; + let z = "end"; + } +} + +fn complexer_example() { + fn gen_id(x: u32, y: u32) -> u32 { + let x = x & 0x0000_ffff; + let y = (y & 0xffff_0000) << 16; + x | y + } + + fn process_data(data: DataPack) { + let _ = data; + } + + let x = 8; + let y = 9; + if (x > 7 && y < 13) || (x + y) % 2 == 1 { + let a = 0xcafe; + let b = 0xffff00ff; + let e_id = gen_id(a, b); + + println!("From the a `{}` to the b `{}`", a, b); + + let pack = DataPack { + id: e_id, + name: "Player 1".to_string(), + some_data: vec![0x12, 0x34, 0x56, 0x78, 0x90], + }; + process_data(pack); + } else { + let a = 0xcafe; + let b = 0xffff00ff; + let e_id = gen_id(a, b); + + println!("The new ID is '{}'", e_id); + + let pack = DataPack { + id: e_id, + name: "Player 1".to_string(), + some_data: vec![0x12, 0x34, 0x56, 0x78, 0x90], + }; + process_data(pack); + } +} + +/// This should add a note to the lint msg since the moved expression is not `()` +fn added_note_for_expression_use() -> u32 { + let x = 9; + + let _ = if x == 7 { + let _ = 19; + + let _splitter = 6; + + x << 2 + } else { + let _ = 19; + + x << 2 + }; + + if x == 9 { + let _ = 17; + + let _splitter = 6; + + x * 4 + } else { + let _ = 17; + + x * 4 + } +} + +fn main() {} diff --git a/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr new file mode 100644 index 00000000000..212cfb2f1d1 --- /dev/null +++ b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr @@ -0,0 +1,154 @@ +error: all if blocks contain the same code at the start and the end. Here at the start + --> $DIR/shared_at_top_and_bottom.rs:16:5 + | +LL | / if x == 7 { +LL | | let t = 7; +LL | | let _overlap_start = t * 2; +LL | | let _overlap_end = 2 * t; + | |_________________________________^ + | +note: the lint level is defined here + --> $DIR/shared_at_top_and_bottom.rs:2:36 + | +LL | #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: and here at the end + --> $DIR/shared_at_top_and_bottom.rs:28:5 + | +LL | / let _u = 9; +LL | | } + | |_____^ +help: consider moving the start statements out like this + | +LL | let t = 7; +LL | let _overlap_start = t * 2; +LL | let _overlap_end = 2 * t; +LL | if x == 7 { + | +help: and consider moving the end statements out like this + | +LL | } +LL | let _u = 9; + | + +error: all if blocks contain the same code at the start and the end. Here at the start + --> $DIR/shared_at_top_and_bottom.rs:32:5 + | +LL | / if x == 99 { +LL | | let r = 7; +LL | | let _overlap_start = r; +LL | | let _overlap_middle = r * r; + | |____________________________________^ + | +note: and here at the end + --> $DIR/shared_at_top_and_bottom.rs:43:5 + | +LL | / let _overlap_end = r * r * r; +LL | | let z = "end"; +LL | | } + | |_____^ + = warning: Some moved values might need to be renamed to avoid wrong references +help: consider moving the start statements out like this + | +LL | let r = 7; +LL | let _overlap_start = r; +LL | let _overlap_middle = r * r; +LL | if x == 99 { + | +help: and consider moving the end statements out like this + | +LL | } +LL | let _overlap_end = r * r * r; +LL | let z = "end"; + | + +error: all if blocks contain the same code at the start and the end. Here at the start + --> $DIR/shared_at_top_and_bottom.rs:61:5 + | +LL | / if (x > 7 && y < 13) || (x + y) % 2 == 1 { +LL | | let a = 0xcafe; +LL | | let b = 0xffff00ff; +LL | | let e_id = gen_id(a, b); + | |________________________________^ + | +note: and here at the end + --> $DIR/shared_at_top_and_bottom.rs:81:5 + | +LL | / let pack = DataPack { +LL | | id: e_id, +LL | | name: "Player 1".to_string(), +LL | | some_data: vec![0x12, 0x34, 0x56, 0x78, 0x90], +LL | | }; +LL | | process_data(pack); +LL | | } + | |_____^ + = warning: Some moved values might need to be renamed to avoid wrong references +help: consider moving the start statements out like this + | +LL | let a = 0xcafe; +LL | let b = 0xffff00ff; +LL | let e_id = gen_id(a, b); +LL | if (x > 7 && y < 13) || (x + y) % 2 == 1 { + | +help: and consider moving the end statements out like this + | +LL | } +LL | let pack = DataPack { +LL | id: e_id, +LL | name: "Player 1".to_string(), +LL | some_data: vec![0x12, 0x34, 0x56, 0x78, 0x90], +LL | }; + ... + +error: all if blocks contain the same code at the start and the end. Here at the start + --> $DIR/shared_at_top_and_bottom.rs:94:5 + | +LL | / let _ = if x == 7 { +LL | | let _ = 19; + | |___________________^ + | +note: and here at the end + --> $DIR/shared_at_top_and_bottom.rs:103:5 + | +LL | / x << 2 +LL | | }; + | |_____^ + = note: The end suggestion probably needs some adjustments to use the expression result correctly +help: consider moving the start statements out like this + | +LL | let _ = 19; +LL | let _ = if x == 7 { + | +help: and consider moving the end statements out like this + | +LL | } +LL | x << 2; + | + +error: all if blocks contain the same code at the start and the end. Here at the start + --> $DIR/shared_at_top_and_bottom.rs:106:5 + | +LL | / if x == 9 { +LL | | let _ = 17; + | |___________________^ + | +note: and here at the end + --> $DIR/shared_at_top_and_bottom.rs:115:5 + | +LL | / x * 4 +LL | | } + | |_____^ + = note: The end suggestion probably needs some adjustments to use the expression result correctly +help: consider moving the start statements out like this + | +LL | let _ = 17; +LL | if x == 9 { + | +help: and consider moving the end statements out like this + | +LL | } +LL | x * 4 + | + +error: aborting due to 5 previous errors + diff --git a/tests/ui/branches_sharing_code/valid_if_blocks.rs b/tests/ui/branches_sharing_code/valid_if_blocks.rs new file mode 100644 index 00000000000..0c70e3748ec --- /dev/null +++ b/tests/ui/branches_sharing_code/valid_if_blocks.rs @@ -0,0 +1,155 @@ +#![allow(dead_code, clippy::eval_order_dependence)] +#![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] + +// This tests valid if blocks that shouldn't trigger the lint + +// Tests with value references are includes in "shared_code_at_bottom.rs" + +fn valid_examples() { + let x = 2; + + // The edge statements are different + if x == 9 { + let y = 1 << 5; + + println!("This is the same: vvv"); + let _z = y; + println!("The block expression is different"); + + println!("Different end 1"); + } else { + let y = 1 << 7; + + println!("This is the same: vvv"); + let _z = y; + println!("The block expression is different"); + + println!("Different end 2"); + } + + // No else + if x == 2 { + println!("Hello world!"); + println!("Hello back, how are you?"); + + // This is different vvvv + println!("Howdy stranger =^.^="); + + println!("Bye Bye World"); + } else if x == 9 { + println!("Hello world!"); + println!("Hello back, how are you?"); + + // This is different vvvv + println!("Hello reviewer :D"); + + println!("Bye Bye World"); + } + + // Overlapping statements only in else if blocks -> Don't lint + if x == 0 { + println!("I'm important!") + } else if x == 17 { + println!("I share code in else if"); + + println!("x is 17"); + } else { + println!("I share code in else if"); + + println!("x is nether x nor 17"); + } + + // Mutability is different + if x == 13 { + let mut y = 9; + println!("Value y is: {}", y); + y += 16; + let _z1 = y; + } else { + let y = 9; + println!("Value y is: {}", y); + let _z2 = y; + } + + // Same blocks but at start and bottom so no `if_same_then_else` lint + if x == 418 { + let y = 9; + let z = 8; + let _ = (x, y, z); + // Don't tell the programmer, my code is also in the else block + } else if x == 419 { + println!("+-----------+"); + println!("| |"); + println!("| O O |"); + println!("| ° |"); + println!("| \\_____/ |"); + println!("| |"); + println!("+-----------+"); + } else { + let y = 9; + let z = 8; + let _ = (x, y, z); + // I'm so much better than the x == 418 block. Trust me + } + + let x = 1; + if true { + println!("{}", x); + } else { + let x = 2; + println!("{}", x); + } + + // Let's test empty blocks + if false { + } else { + } +} + +/// This makes sure that the `if_same_then_else` masks the `shared_code_in_if_blocks` lint +fn trigger_other_lint() { + let x = 0; + let y = 1; + + // Same block + if x == 0 { + let u = 19; + println!("How are u today?"); + let _ = "This is a string"; + } else { + let u = 19; + println!("How are u today?"); + let _ = "This is a string"; + } + + // Only same expression + let _ = if x == 6 { 7 } else { 7 }; + + // Same in else if block + let _ = if x == 67 { + println!("Well I'm the most important block"); + "I'm a pretty string" + } else if x == 68 { + println!("I'm a doppelgänger"); + // Don't listen to my clone below + + if y == 90 { "=^.^=" } else { ":D" } + } else { + // Don't listen to my clone above + println!("I'm a doppelgänger"); + + if y == 90 { "=^.^=" } else { ":D" } + }; + + if x == 0 { + println!("I'm single"); + } else if x == 68 { + println!("I'm a doppelgänger"); + // Don't listen to my clone below + } else { + // Don't listen to my clone above + println!("I'm a doppelgänger"); + } +} + +fn main() {} diff --git a/tests/ui/branches_sharing_code/valid_if_blocks.stderr b/tests/ui/branches_sharing_code/valid_if_blocks.stderr new file mode 100644 index 00000000000..a815995e717 --- /dev/null +++ b/tests/ui/branches_sharing_code/valid_if_blocks.stderr @@ -0,0 +1,101 @@ +error: this `if` has identical blocks + --> $DIR/valid_if_blocks.rs:104:14 + | +LL | if false { + | ______________^ +LL | | } else { + | |_____^ + | +note: the lint level is defined here + --> $DIR/valid_if_blocks.rs:2:9 + | +LL | #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +note: same as this + --> $DIR/valid_if_blocks.rs:105:12 + | +LL | } else { + | ____________^ +LL | | } + | |_____^ + +error: this `if` has identical blocks + --> $DIR/valid_if_blocks.rs:115:15 + | +LL | if x == 0 { + | _______________^ +LL | | let u = 19; +LL | | println!("How are u today?"); +LL | | let _ = "This is a string"; +LL | | } else { + | |_____^ + | +note: same as this + --> $DIR/valid_if_blocks.rs:119:12 + | +LL | } else { + | ____________^ +LL | | let u = 19; +LL | | println!("How are u today?"); +LL | | let _ = "This is a string"; +LL | | } + | |_____^ + +error: this `if` has identical blocks + --> $DIR/valid_if_blocks.rs:126:23 + | +LL | let _ = if x == 6 { 7 } else { 7 }; + | ^^^^^ + | +note: same as this + --> $DIR/valid_if_blocks.rs:126:34 + | +LL | let _ = if x == 6 { 7 } else { 7 }; + | ^^^^^ + +error: this `if` has identical blocks + --> $DIR/valid_if_blocks.rs:132:23 + | +LL | } else if x == 68 { + | _______________________^ +LL | | println!("I'm a doppelgänger"); +LL | | // Don't listen to my clone below +LL | | +LL | | if y == 90 { "=^.^=" } else { ":D" } +LL | | } else { + | |_____^ + | +note: same as this + --> $DIR/valid_if_blocks.rs:137:12 + | +LL | } else { + | ____________^ +LL | | // Don't listen to my clone above +LL | | println!("I'm a doppelgänger"); +LL | | +LL | | if y == 90 { "=^.^=" } else { ":D" } +LL | | }; + | |_____^ + +error: this `if` has identical blocks + --> $DIR/valid_if_blocks.rs:146:23 + | +LL | } else if x == 68 { + | _______________________^ +LL | | println!("I'm a doppelgänger"); +LL | | // Don't listen to my clone below +LL | | } else { + | |_____^ + | +note: same as this + --> $DIR/valid_if_blocks.rs:149:12 + | +LL | } else { + | ____________^ +LL | | // Don't listen to my clone above +LL | | println!("I'm a doppelgänger"); +LL | | } + | |_____^ + +error: aborting due to 5 previous errors + diff --git a/tests/ui/checked_unwrap/complex_conditionals.rs b/tests/ui/checked_unwrap/complex_conditionals.rs index bb5b6f5ec04..ec082c73b44 100644 --- a/tests/ui/checked_unwrap/complex_conditionals.rs +++ b/tests/ui/checked_unwrap/complex_conditionals.rs @@ -1,5 +1,5 @@ #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] -#![allow(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] +#![allow(clippy::if_same_then_else, clippy::branches_sharing_code)] fn test_complex_conditions() { let x: Result<(), ()> = Ok(()); diff --git a/tests/ui/checked_unwrap/complex_conditionals_nested.rs b/tests/ui/checked_unwrap/complex_conditionals_nested.rs index 99e8fb95466..043ea4148dc 100644 --- a/tests/ui/checked_unwrap/complex_conditionals_nested.rs +++ b/tests/ui/checked_unwrap/complex_conditionals_nested.rs @@ -1,5 +1,5 @@ #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] -#![allow(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] +#![allow(clippy::if_same_then_else, clippy::branches_sharing_code)] fn test_nested() { fn nested() { diff --git a/tests/ui/checked_unwrap/simple_conditionals.rs b/tests/ui/checked_unwrap/simple_conditionals.rs index 0459100d88a..8f23fb28827 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.rs +++ b/tests/ui/checked_unwrap/simple_conditionals.rs @@ -1,5 +1,5 @@ #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] -#![allow(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] +#![allow(clippy::if_same_then_else, clippy::branches_sharing_code)] macro_rules! m { ($a:expr) => { diff --git a/tests/ui/default_numeric_fallback.rs b/tests/ui/default_numeric_fallback.rs index de89f806c58..43468872db0 100644 --- a/tests/ui/default_numeric_fallback.rs +++ b/tests/ui/default_numeric_fallback.rs @@ -3,7 +3,7 @@ #![allow(clippy::never_loop)] #![allow(clippy::no_effect)] #![allow(clippy::unnecessary_operation)] -#![allow(clippy::shared_code_in_if_blocks)] +#![allow(clippy::branches_sharing_code)] mod basic_expr { fn test() { diff --git a/tests/ui/if_same_then_else.rs b/tests/ui/if_same_then_else.rs index 35a2e139c11..ef956745500 100644 --- a/tests/ui/if_same_then_else.rs +++ b/tests/ui/if_same_then_else.rs @@ -6,7 +6,7 @@ clippy::no_effect, clippy::unused_unit, clippy::zero_divided_by_zero, - clippy::shared_code_in_if_blocks + clippy::branches_sharing_code )] struct Foo { diff --git a/tests/ui/if_same_then_else2.rs b/tests/ui/if_same_then_else2.rs index 8164b125570..e4dc5b647df 100644 --- a/tests/ui/if_same_then_else2.rs +++ b/tests/ui/if_same_then_else2.rs @@ -6,7 +6,7 @@ clippy::ifs_same_cond, clippy::needless_return, clippy::single_element_loop, - clippy::shared_code_in_if_blocks + clippy::branches_sharing_code )] fn if_same_then_else2() -> Result<&'static str, ()> { diff --git a/tests/ui/let_if_seq.rs b/tests/ui/let_if_seq.rs index 9fd3f875a5f..2d8f3c2f0e7 100644 --- a/tests/ui/let_if_seq.rs +++ b/tests/ui/let_if_seq.rs @@ -3,7 +3,7 @@ unused_assignments, clippy::similar_names, clippy::blacklisted_name, - clippy::shared_code_in_if_blocks + clippy::branches_sharing_code )] #![warn(clippy::useless_let_if_seq)] diff --git a/tests/ui/needless_bool/simple.rs b/tests/ui/needless_bool/simple.rs index 678040fa98b..588bb88f446 100644 --- a/tests/ui/needless_bool/simple.rs +++ b/tests/ui/needless_bool/simple.rs @@ -5,7 +5,7 @@ clippy::no_effect, clippy::if_same_then_else, clippy::needless_return, - clippy::shared_code_in_if_blocks + clippy::branches_sharing_code )] fn main() { diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index ebf74cfef12..82d95cc041f 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -4,7 +4,7 @@ #![allow( clippy::if_same_then_else, clippy::single_match, - clippy::shared_code_in_if_blocks, + clippy::branches_sharing_code, clippy::needless_bool )] #![warn(clippy::needless_return)] diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index 2bebccdabca..8a471f802e1 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -4,7 +4,7 @@ #![allow( clippy::if_same_then_else, clippy::single_match, - clippy::shared_code_in_if_blocks, + clippy::branches_sharing_code, clippy::needless_bool )] #![warn(clippy::needless_return)] diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs b/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs deleted file mode 100644 index 6ff362cb752..00000000000 --- a/tests/ui/shared_code_in_if_blocks/shared_at_bot.rs +++ /dev/null @@ -1,209 +0,0 @@ -#![allow(dead_code)] -#![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] - -// This tests the shared_code_in_if_blocks lint at the end of blocks - -fn simple_examples() { - let x = 1; - - let _ = if x == 7 { - println!("Branch I"); - let start_value = 0; - println!("=^.^="); - - // Same but not moveable due to `start_value` - let _ = start_value; - - // The rest is self contained and moveable => Only lint the rest - let result = false; - println!("Block end!"); - result - } else { - println!("Branch II"); - let start_value = 8; - println!("xD"); - - // Same but not moveable due to `start_value` - let _ = start_value; - - // The rest is self contained and moveable => Only lint the rest - let result = false; - println!("Block end!"); - result - }; - - // Else if block - if x == 9 { - println!("The index is: 6"); - - println!("Same end of block"); - } else if x == 8 { - println!("The index is: 4"); - - // We should only get a lint trigger for the last statement - println!("This is also eq with the else block"); - println!("Same end of block"); - } else { - println!("This is also eq with the else block"); - println!("Same end of block"); - } - - // Use of outer scope value - let outer_scope_value = "I'm outside the if block"; - if x < 99 { - let z = "How are you"; - println!("I'm a local because I use the value `z`: `{}`", z); - - println!( - "I'm moveable because I know: `outer_scope_value`: '{}'", - outer_scope_value - ); - } else { - let z = 45678000; - println!("I'm a local because I use the value `z`: `{}`", z); - - println!( - "I'm moveable because I know: `outer_scope_value`: '{}'", - outer_scope_value - ); - } - - if x == 9 { - if x == 8 { - // No parent!! - println!("---"); - println!("Hello World"); - } else { - println!("Hello World"); - } - } -} - -/// Simple examples where the move can cause some problems due to moved values -fn simple_but_suggestion_is_invalid() { - let x = 16; - - // Local value - let later_used_value = 17; - if x == 9 { - let _ = 9; - let later_used_value = "A string value"; - println!("{}", later_used_value); - } else { - let later_used_value = "A string value"; - println!("{}", later_used_value); - // I'm expecting a note about this - } - println!("{}", later_used_value); - - // outer function - if x == 78 { - let simple_examples = "I now identify as a &str :)"; - println!("This is the new simple_example: {}", simple_examples); - } else { - println!("Separator print statement"); - - let simple_examples = "I now identify as a &str :)"; - println!("This is the new simple_example: {}", simple_examples); - } - simple_examples(); -} - -/// Tests where the blocks are not linted due to the used value scope -fn not_moveable_due_to_value_scope() { - let x = 18; - - // Using a local value in the moved code - if x == 9 { - let y = 18; - println!("y is: `{}`", y); - } else { - let y = "A string"; - println!("y is: `{}`", y); - } - - // Using a local value in the expression - let _ = if x == 0 { - let mut result = x + 1; - - println!("1. Doing some calculations"); - println!("2. Some more calculations"); - println!("3. Setting result"); - - result - } else { - let mut result = x - 1; - - println!("1. Doing some calculations"); - println!("2. Some more calculations"); - println!("3. Setting result"); - - result - }; - - let _ = if x == 7 { - let z1 = 100; - println!("z1: {}", z1); - - let z2 = z1; - println!("z2: {}", z2); - - z2 - } else { - let z1 = 300; - println!("z1: {}", z1); - - let z2 = z1; - println!("z2: {}", z2); - - z2 - }; -} - -/// This should add a note to the lint msg since the moved expression is not `()` -fn added_note_for_expression_use() -> u32 { - let x = 9; - - let _ = if x == 7 { - x << 2 - } else { - let _ = 6; - x << 2 - }; - - if x == 9 { - x * 4 - } else { - let _ = 17; - x * 4 - } -} - -#[rustfmt::skip] -fn test_suggestion_with_weird_formatting() { - let x = 9; - let mut a = 0; - let mut b = 0; - - // The error message still looks weird tbh but this is the best I can do - // for weird formatting - if x == 17 { b = 1; a = 0x99; } else { a = 0x99; } -} - -fn fp_test() { - let x = 17; - - if x == 18 { - let y = 19; - if y < x { - println!("Trigger") - } - } else { - let z = 166; - if z < x { - println!("Trigger") - } - } -} - -fn main() {} diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_bot.stderr b/tests/ui/shared_code_in_if_blocks/shared_at_bot.stderr deleted file mode 100644 index 2268d8f5365..00000000000 --- a/tests/ui/shared_code_in_if_blocks/shared_at_bot.stderr +++ /dev/null @@ -1,143 +0,0 @@ -error: all if blocks contain the same code at the end - --> $DIR/shared_at_bot.rs:30:5 - | -LL | / let result = false; -LL | | println!("Block end!"); -LL | | result -LL | | }; - | |_____^ - | -note: the lint level is defined here - --> $DIR/shared_at_bot.rs:2:36 - | -LL | #![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: The end suggestion probably needs some adjustments to use the expression result correctly -help: consider moving the end statements out like this - | -LL | } -LL | let result = false; -LL | println!("Block end!"); -LL | result; - | - -error: all if blocks contain the same code at the end - --> $DIR/shared_at_bot.rs:48:5 - | -LL | / println!("Same end of block"); -LL | | } - | |_____^ - | -help: consider moving the end statements out like this - | -LL | } -LL | println!("Same end of block"); - | - -error: all if blocks contain the same code at the end - --> $DIR/shared_at_bot.rs:65:5 - | -LL | / println!( -LL | | "I'm moveable because I know: `outer_scope_value`: '{}'", -LL | | outer_scope_value -LL | | ); -LL | | } - | |_____^ - | -help: consider moving the end statements out like this - | -LL | } -LL | println!( -LL | "I'm moveable because I know: `outer_scope_value`: '{}'", -LL | outer_scope_value -LL | ); - | - -error: all if blocks contain the same code at the end - --> $DIR/shared_at_bot.rs:77:9 - | -LL | / println!("Hello World"); -LL | | } - | |_________^ - | -help: consider moving the end statements out like this - | -LL | } -LL | println!("Hello World"); - | - -error: all if blocks contain the same code at the end - --> $DIR/shared_at_bot.rs:93:5 - | -LL | / let later_used_value = "A string value"; -LL | | println!("{}", later_used_value); -LL | | // I'm expecting a note about this -LL | | } - | |_____^ - | - = warning: Some moved values might need to be renamed to avoid wrong references -help: consider moving the end statements out like this - | -LL | } -LL | let later_used_value = "A string value"; -LL | println!("{}", later_used_value); - | - -error: all if blocks contain the same code at the end - --> $DIR/shared_at_bot.rs:106:5 - | -LL | / let simple_examples = "I now identify as a &str :)"; -LL | | println!("This is the new simple_example: {}", simple_examples); -LL | | } - | |_____^ - | - = warning: Some moved values might need to be renamed to avoid wrong references -help: consider moving the end statements out like this - | -LL | } -LL | let simple_examples = "I now identify as a &str :)"; -LL | println!("This is the new simple_example: {}", simple_examples); - | - -error: all if blocks contain the same code at the end - --> $DIR/shared_at_bot.rs:171:5 - | -LL | / x << 2 -LL | | }; - | |_____^ - | - = note: The end suggestion probably needs some adjustments to use the expression result correctly -help: consider moving the end statements out like this - | -LL | } -LL | x << 2; - | - -error: all if blocks contain the same code at the end - --> $DIR/shared_at_bot.rs:178:5 - | -LL | / x * 4 -LL | | } - | |_____^ - | - = note: The end suggestion probably needs some adjustments to use the expression result correctly -help: consider moving the end statements out like this - | -LL | } -LL | x * 4 - | - -error: all if blocks contain the same code at the end - --> $DIR/shared_at_bot.rs:190:44 - | -LL | if x == 17 { b = 1; a = 0x99; } else { a = 0x99; } - | ^^^^^^^^^^^ - | -help: consider moving the end statements out like this - | -LL | if x == 17 { b = 1; a = 0x99; } else { } -LL | a = 0x99; - | - -error: aborting due to 9 previous errors - diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_top.rs b/tests/ui/shared_code_in_if_blocks/shared_at_top.rs deleted file mode 100644 index 496939f2a5e..00000000000 --- a/tests/ui/shared_code_in_if_blocks/shared_at_top.rs +++ /dev/null @@ -1,103 +0,0 @@ -#![allow(dead_code, clippy::eval_order_dependence)] -#![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] - -// This tests the shared_code_in_if_blocks lint at the start of blocks - -fn simple_examples() { - let x = 0; - - // Simple - if true { - println!("Hello World!"); - println!("I'm branch nr: 1"); - } else { - println!("Hello World!"); - println!("I'm branch nr: 2"); - } - - // Else if - if x == 0 { - let y = 9; - println!("The value y was set to: `{}`", y); - let _z = y; - - println!("I'm the true start index of arrays"); - } else if x == 1 { - let y = 9; - println!("The value y was set to: `{}`", y); - let _z = y; - - println!("I start counting from 1 so my array starts from `1`"); - } else { - let y = 9; - println!("The value y was set to: `{}`", y); - let _z = y; - - println!("Ha, Pascal allows you to start the array where you want") - } - - // Return a value - let _ = if x == 7 { - let y = 16; - println!("What can I say except: \"you're welcome?\""); - let _ = y; - x - } else { - let y = 16; - println!("Thank you"); - y - }; -} - -/// Simple examples where the move can cause some problems due to moved values -fn simple_but_suggestion_is_invalid() { - let x = 10; - - // Can't be automatically moved because used_value_name is getting used again - let used_value_name = 19; - if x == 10 { - let used_value_name = "Different type"; - println!("Str: {}", used_value_name); - let _ = 1; - } else { - let used_value_name = "Different type"; - println!("Str: {}", used_value_name); - let _ = 2; - } - let _ = used_value_name; - - // This can be automatically moved as `can_be_overridden` is not used again - let can_be_overridden = 8; - let _ = can_be_overridden; - if x == 11 { - let can_be_overridden = "Move me"; - println!("I'm also moveable"); - let _ = 111; - } else { - let can_be_overridden = "Move me"; - println!("I'm also moveable"); - let _ = 222; - } -} - -/// This function tests that the `IS_SAME_THAN_ELSE` only covers the lint if it's enabled. -fn check_if_same_than_else_mask() { - let x = 2021; - - #[allow(clippy::if_same_then_else)] - if x == 2020 { - println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint."); - println!("Because `IF_SAME_THEN_ELSE` is allowed here"); - } else { - println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint."); - println!("Because `IF_SAME_THEN_ELSE` is allowed here"); - } - - if x == 2019 { - println!("This should trigger `IS_SAME_THAN_ELSE` as usual"); - } else { - println!("This should trigger `IS_SAME_THAN_ELSE` as usual"); - } -} - -fn main() {} diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_top.stderr b/tests/ui/shared_code_in_if_blocks/shared_at_top.stderr deleted file mode 100644 index 76898a6ff02..00000000000 --- a/tests/ui/shared_code_in_if_blocks/shared_at_top.stderr +++ /dev/null @@ -1,121 +0,0 @@ -error: all if blocks contain the same code at the start - --> $DIR/shared_at_top.rs:10:5 - | -LL | / if true { -LL | | println!("Hello World!"); - | |_________________________________^ - | -note: the lint level is defined here - --> $DIR/shared_at_top.rs:2:36 - | -LL | #![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: consider moving the start statements out like this - | -LL | println!("Hello World!"); -LL | if true { - | - -error: all if blocks contain the same code at the start - --> $DIR/shared_at_top.rs:19:5 - | -LL | / if x == 0 { -LL | | let y = 9; -LL | | println!("The value y was set to: `{}`", y); -LL | | let _z = y; - | |___________________^ - | - = warning: Some moved values might need to be renamed to avoid wrong references -help: consider moving the start statements out like this - | -LL | let y = 9; -LL | println!("The value y was set to: `{}`", y); -LL | let _z = y; -LL | if x == 0 { - | - -error: all if blocks contain the same code at the start - --> $DIR/shared_at_top.rs:40:5 - | -LL | / let _ = if x == 7 { -LL | | let y = 16; - | |___________________^ - | -help: consider moving the start statements out like this - | -LL | let y = 16; -LL | let _ = if x == 7 { - | - -error: all if blocks contain the same code at the start - --> $DIR/shared_at_top.rs:58:5 - | -LL | / if x == 10 { -LL | | let used_value_name = "Different type"; -LL | | println!("Str: {}", used_value_name); - | |_____________________________________________^ - | - = warning: Some moved values might need to be renamed to avoid wrong references -help: consider moving the start statements out like this - | -LL | let used_value_name = "Different type"; -LL | println!("Str: {}", used_value_name); -LL | if x == 10 { - | - -error: all if blocks contain the same code at the start - --> $DIR/shared_at_top.rs:72:5 - | -LL | / if x == 11 { -LL | | let can_be_overridden = "Move me"; -LL | | println!("I'm also moveable"); - | |______________________________________^ - | - = warning: Some moved values might need to be renamed to avoid wrong references -help: consider moving the start statements out like this - | -LL | let can_be_overridden = "Move me"; -LL | println!("I'm also moveable"); -LL | if x == 11 { - | - -error: all if blocks contain the same code at the start - --> $DIR/shared_at_top.rs:88:5 - | -LL | / if x == 2020 { -LL | | println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint."); -LL | | println!("Because `IF_SAME_THEN_ELSE` is allowed here"); - | |________________________________________________________________^ - | -help: consider moving the start statements out like this - | -LL | println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint."); -LL | println!("Because `IF_SAME_THEN_ELSE` is allowed here"); -LL | if x == 2020 { - | - -error: this `if` has identical blocks - --> $DIR/shared_at_top.rs:96:18 - | -LL | if x == 2019 { - | __________________^ -LL | | println!("This should trigger `IS_SAME_THAN_ELSE` as usual"); -LL | | } else { - | |_____^ - | -note: the lint level is defined here - --> $DIR/shared_at_top.rs:2:9 - | -LL | #![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -note: same as this - --> $DIR/shared_at_top.rs:98:12 - | -LL | } else { - | ____________^ -LL | | println!("This should trigger `IS_SAME_THAN_ELSE` as usual"); -LL | | } - | |_____^ - -error: aborting due to 7 previous errors - diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.rs b/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.rs deleted file mode 100644 index 46a8f931aaf..00000000000 --- a/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.rs +++ /dev/null @@ -1,119 +0,0 @@ -#![allow(dead_code)] -#![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] - -// shared_code_in_if_blocks at the top and bottom of the if blocks - -struct DataPack { - id: u32, - name: String, - some_data: Vec, -} - -fn overlapping_eq_regions() { - let x = 9; - - // Overlap with separator - if x == 7 { - let t = 7; - let _overlap_start = t * 2; - let _overlap_end = 2 * t; - let _u = 9; - } else { - let t = 7; - let _overlap_start = t * 2; - let _overlap_end = 2 * t; - println!("Overlap separator"); - let _overlap_start = t * 2; - let _overlap_end = 2 * t; - let _u = 9; - } - - // Overlap with separator - if x == 99 { - let r = 7; - let _overlap_start = r; - let _overlap_middle = r * r; - let _overlap_end = r * r * r; - let z = "end"; - } else { - let r = 7; - let _overlap_start = r; - let _overlap_middle = r * r; - let _overlap_middle = r * r; - let _overlap_end = r * r * r; - let z = "end"; - } -} - -fn complexer_example() { - fn gen_id(x: u32, y: u32) -> u32 { - let x = x & 0x0000_ffff; - let y = (y & 0xffff_0000) << 16; - x | y - } - - fn process_data(data: DataPack) { - let _ = data; - } - - let x = 8; - let y = 9; - if (x > 7 && y < 13) || (x + y) % 2 == 1 { - let a = 0xcafe; - let b = 0xffff00ff; - let e_id = gen_id(a, b); - - println!("From the a `{}` to the b `{}`", a, b); - - let pack = DataPack { - id: e_id, - name: "Player 1".to_string(), - some_data: vec![0x12, 0x34, 0x56, 0x78, 0x90], - }; - process_data(pack); - } else { - let a = 0xcafe; - let b = 0xffff00ff; - let e_id = gen_id(a, b); - - println!("The new ID is '{}'", e_id); - - let pack = DataPack { - id: e_id, - name: "Player 1".to_string(), - some_data: vec![0x12, 0x34, 0x56, 0x78, 0x90], - }; - process_data(pack); - } -} - -/// This should add a note to the lint msg since the moved expression is not `()` -fn added_note_for_expression_use() -> u32 { - let x = 9; - - let _ = if x == 7 { - let _ = 19; - - let _splitter = 6; - - x << 2 - } else { - let _ = 19; - - x << 2 - }; - - if x == 9 { - let _ = 17; - - let _splitter = 6; - - x * 4 - } else { - let _ = 17; - - x * 4 - } -} - -fn main() {} diff --git a/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.stderr b/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.stderr deleted file mode 100644 index 75c3d397f2e..00000000000 --- a/tests/ui/shared_code_in_if_blocks/shared_at_top_and_bot.stderr +++ /dev/null @@ -1,154 +0,0 @@ -error: all if blocks contain the same code at the start and the end. Here at the start - --> $DIR/shared_at_top_and_bot.rs:16:5 - | -LL | / if x == 7 { -LL | | let t = 7; -LL | | let _overlap_start = t * 2; -LL | | let _overlap_end = 2 * t; - | |_________________________________^ - | -note: the lint level is defined here - --> $DIR/shared_at_top_and_bot.rs:2:36 - | -LL | #![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: and here at the end - --> $DIR/shared_at_top_and_bot.rs:28:5 - | -LL | / let _u = 9; -LL | | } - | |_____^ -help: consider moving the start statements out like this - | -LL | let t = 7; -LL | let _overlap_start = t * 2; -LL | let _overlap_end = 2 * t; -LL | if x == 7 { - | -help: and consider moving the end statements out like this - | -LL | } -LL | let _u = 9; - | - -error: all if blocks contain the same code at the start and the end. Here at the start - --> $DIR/shared_at_top_and_bot.rs:32:5 - | -LL | / if x == 99 { -LL | | let r = 7; -LL | | let _overlap_start = r; -LL | | let _overlap_middle = r * r; - | |____________________________________^ - | -note: and here at the end - --> $DIR/shared_at_top_and_bot.rs:43:5 - | -LL | / let _overlap_end = r * r * r; -LL | | let z = "end"; -LL | | } - | |_____^ - = warning: Some moved values might need to be renamed to avoid wrong references -help: consider moving the start statements out like this - | -LL | let r = 7; -LL | let _overlap_start = r; -LL | let _overlap_middle = r * r; -LL | if x == 99 { - | -help: and consider moving the end statements out like this - | -LL | } -LL | let _overlap_end = r * r * r; -LL | let z = "end"; - | - -error: all if blocks contain the same code at the start and the end. Here at the start - --> $DIR/shared_at_top_and_bot.rs:61:5 - | -LL | / if (x > 7 && y < 13) || (x + y) % 2 == 1 { -LL | | let a = 0xcafe; -LL | | let b = 0xffff00ff; -LL | | let e_id = gen_id(a, b); - | |________________________________^ - | -note: and here at the end - --> $DIR/shared_at_top_and_bot.rs:81:5 - | -LL | / let pack = DataPack { -LL | | id: e_id, -LL | | name: "Player 1".to_string(), -LL | | some_data: vec![0x12, 0x34, 0x56, 0x78, 0x90], -LL | | }; -LL | | process_data(pack); -LL | | } - | |_____^ - = warning: Some moved values might need to be renamed to avoid wrong references -help: consider moving the start statements out like this - | -LL | let a = 0xcafe; -LL | let b = 0xffff00ff; -LL | let e_id = gen_id(a, b); -LL | if (x > 7 && y < 13) || (x + y) % 2 == 1 { - | -help: and consider moving the end statements out like this - | -LL | } -LL | let pack = DataPack { -LL | id: e_id, -LL | name: "Player 1".to_string(), -LL | some_data: vec![0x12, 0x34, 0x56, 0x78, 0x90], -LL | }; - ... - -error: all if blocks contain the same code at the start and the end. Here at the start - --> $DIR/shared_at_top_and_bot.rs:94:5 - | -LL | / let _ = if x == 7 { -LL | | let _ = 19; - | |___________________^ - | -note: and here at the end - --> $DIR/shared_at_top_and_bot.rs:103:5 - | -LL | / x << 2 -LL | | }; - | |_____^ - = note: The end suggestion probably needs some adjustments to use the expression result correctly -help: consider moving the start statements out like this - | -LL | let _ = 19; -LL | let _ = if x == 7 { - | -help: and consider moving the end statements out like this - | -LL | } -LL | x << 2; - | - -error: all if blocks contain the same code at the start and the end. Here at the start - --> $DIR/shared_at_top_and_bot.rs:106:5 - | -LL | / if x == 9 { -LL | | let _ = 17; - | |___________________^ - | -note: and here at the end - --> $DIR/shared_at_top_and_bot.rs:115:5 - | -LL | / x * 4 -LL | | } - | |_____^ - = note: The end suggestion probably needs some adjustments to use the expression result correctly -help: consider moving the start statements out like this - | -LL | let _ = 17; -LL | if x == 9 { - | -help: and consider moving the end statements out like this - | -LL | } -LL | x * 4 - | - -error: aborting due to 5 previous errors - diff --git a/tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs b/tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs deleted file mode 100644 index e63490d5b07..00000000000 --- a/tests/ui/shared_code_in_if_blocks/valid_if_blocks.rs +++ /dev/null @@ -1,155 +0,0 @@ -#![allow(dead_code, clippy::eval_order_dependence)] -#![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] - -// This tests the shared_code_in_if_blocks lint at the start of blocks - -// Tests with value references are includes in "shared_code_at_bot.rs" - -fn valid_examples() { - let x = 2; - - // The edge statements are different - if x == 9 { - let y = 1 << 5; - - println!("This is the same: vvv"); - let _z = y; - println!("The block expression is different"); - - println!("Different end 1"); - } else { - let y = 1 << 7; - - println!("This is the same: vvv"); - let _z = y; - println!("The block expression is different"); - - println!("Different end 2"); - } - - // No else - if x == 2 { - println!("Hello world!"); - println!("Hello back, how are you?"); - - // This is different vvvv - println!("Howdy stranger =^.^="); - - println!("Bye Bye World"); - } else if x == 9 { - println!("Hello world!"); - println!("Hello back, how are you?"); - - // This is different vvvv - println!("Hello reviewer :D"); - - println!("Bye Bye World"); - } - - // Overlapping statements only in else if blocks -> Don't lint - if x == 0 { - println!("I'm important!") - } else if x == 17 { - println!("I share code in else if"); - - println!("x is 17"); - } else { - println!("I share code in else if"); - - println!("x is nether x nor 17"); - } - - // Mutability is different - if x == 13 { - let mut y = 9; - println!("Value y is: {}", y); - y += 16; - let _z1 = y; - } else { - let y = 9; - println!("Value y is: {}", y); - let _z2 = y; - } - - // Same blocks but at start and bottom so no `if_same_then_else` lint - if x == 418 { - let y = 9; - let z = 8; - let _ = (x, y, z); - // Don't tell the programmer, my code is also in the else block - } else if x == 419 { - println!("+-----------+"); - println!("| |"); - println!("| O O |"); - println!("| ° |"); - println!("| \\_____/ |"); - println!("| |"); - println!("+-----------+"); - } else { - let y = 9; - let z = 8; - let _ = (x, y, z); - // I'm so much better than the x == 418 block. Trust me - } - - let x = 1; - if true { - println!("{}", x); - } else { - let x = 2; - println!("{}", x); - } - - // Let's test empty blocks - if false { - } else { - } -} - -/// This makes sure that the `if_same_then_else` masks the `shared_code_in_if_blocks` lint -fn trigger_other_lint() { - let x = 0; - let y = 1; - - // Same block - if x == 0 { - let u = 19; - println!("How are u today?"); - let _ = "This is a string"; - } else { - let u = 19; - println!("How are u today?"); - let _ = "This is a string"; - } - - // Only same expression - let _ = if x == 6 { 7 } else { 7 }; - - // Same in else if block - let _ = if x == 67 { - println!("Well I'm the most important block"); - "I'm a pretty string" - } else if x == 68 { - println!("I'm a doppelgänger"); - // Don't listen to my clone below - - if y == 90 { "=^.^=" } else { ":D" } - } else { - // Don't listen to my clone above - println!("I'm a doppelgänger"); - - if y == 90 { "=^.^=" } else { ":D" } - }; - - if x == 0 { - println!("I'm single"); - } else if x == 68 { - println!("I'm a doppelgänger"); - // Don't listen to my clone below - } else { - // Don't listen to my clone above - println!("I'm a doppelgänger"); - } -} - -fn main() {} diff --git a/tests/ui/shared_code_in_if_blocks/valid_if_blocks.stderr b/tests/ui/shared_code_in_if_blocks/valid_if_blocks.stderr deleted file mode 100644 index 846581456dc..00000000000 --- a/tests/ui/shared_code_in_if_blocks/valid_if_blocks.stderr +++ /dev/null @@ -1,101 +0,0 @@ -error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:104:14 - | -LL | if false { - | ______________^ -LL | | } else { - | |_____^ - | -note: the lint level is defined here - --> $DIR/valid_if_blocks.rs:2:9 - | -LL | #![deny(clippy::if_same_then_else, clippy::shared_code_in_if_blocks)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -note: same as this - --> $DIR/valid_if_blocks.rs:105:12 - | -LL | } else { - | ____________^ -LL | | } - | |_____^ - -error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:115:15 - | -LL | if x == 0 { - | _______________^ -LL | | let u = 19; -LL | | println!("How are u today?"); -LL | | let _ = "This is a string"; -LL | | } else { - | |_____^ - | -note: same as this - --> $DIR/valid_if_blocks.rs:119:12 - | -LL | } else { - | ____________^ -LL | | let u = 19; -LL | | println!("How are u today?"); -LL | | let _ = "This is a string"; -LL | | } - | |_____^ - -error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:126:23 - | -LL | let _ = if x == 6 { 7 } else { 7 }; - | ^^^^^ - | -note: same as this - --> $DIR/valid_if_blocks.rs:126:34 - | -LL | let _ = if x == 6 { 7 } else { 7 }; - | ^^^^^ - -error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:132:23 - | -LL | } else if x == 68 { - | _______________________^ -LL | | println!("I'm a doppelgänger"); -LL | | // Don't listen to my clone below -LL | | -LL | | if y == 90 { "=^.^=" } else { ":D" } -LL | | } else { - | |_____^ - | -note: same as this - --> $DIR/valid_if_blocks.rs:137:12 - | -LL | } else { - | ____________^ -LL | | // Don't listen to my clone above -LL | | println!("I'm a doppelgänger"); -LL | | -LL | | if y == 90 { "=^.^=" } else { ":D" } -LL | | }; - | |_____^ - -error: this `if` has identical blocks - --> $DIR/valid_if_blocks.rs:146:23 - | -LL | } else if x == 68 { - | _______________________^ -LL | | println!("I'm a doppelgänger"); -LL | | // Don't listen to my clone below -LL | | } else { - | |_____^ - | -note: same as this - --> $DIR/valid_if_blocks.rs:149:12 - | -LL | } else { - | ____________^ -LL | | // Don't listen to my clone above -LL | | println!("I'm a doppelgänger"); -LL | | } - | |_____^ - -error: aborting due to 5 previous errors - -- cgit 1.4.1-3-g733a5 From 1573d10325f286ffcbba290d27984ded9538a5c3 Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Tue, 6 Apr 2021 06:59:10 +0200 Subject: tabs_in_doc_comments: Fix ICE due to char indexing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a quick-fix for an ICE in `tabs_in_doc_comments`. The problem was that we we're indexing into possibly multi-byte characters, such as '位'. More specifically `get_chunks_of_tabs` was returning indices into multi-byte characters. Those were passed on to a `Span` creation that then caused the ICE. This fix makes sure that we don't return indices that point inside a multi-byte character. *However*, we are still iterating over unicode codepoints, not grapheme clusters. So a seemingly single character like y̆ , which actually consists of two codepoints, will probably still cause incorrect spans in the output. --- clippy_lints/src/tabs_in_doc_comments.rs | 28 +++++++++++++++++----------- tests/ui/crashes/ice-5835.rs | 8 ++++++++ tests/ui/crashes/ice-5835.stderr | 20 ++++++++++++++++++++ 3 files changed, 45 insertions(+), 11 deletions(-) create mode 100644 tests/ui/crashes/ice-5835.rs create mode 100644 tests/ui/crashes/ice-5835.stderr (limited to 'tests') diff --git a/clippy_lints/src/tabs_in_doc_comments.rs b/clippy_lints/src/tabs_in_doc_comments.rs index 88bd2feaadd..3f9692540f7 100644 --- a/clippy_lints/src/tabs_in_doc_comments.rs +++ b/clippy_lints/src/tabs_in_doc_comments.rs @@ -104,30 +104,29 @@ fn get_chunks_of_tabs(the_str: &str) -> Vec<(u32, u32)> { // tracker to decide if the last group of tabs is not closed by a non-tab character let mut is_active = false; - let chars_array: Vec<_> = the_str.chars().collect(); + let char_indices: Vec<_> = the_str.char_indices().collect(); - if chars_array == vec!['\t'] { + if char_indices.len() == 1 && char_indices.first().unwrap().1 == '\t' { return vec![(0, 1)]; } - for (index, arr) in chars_array.windows(2).enumerate() { - let index = u32::try_from(index).expect(line_length_way_to_long); - match arr { - ['\t', '\t'] => { + for entry in char_indices.windows(2) { + match entry { + [(_, '\t'), (_, '\t')] => { // either string starts with double tab, then we have to set it active, // otherwise is_active is true anyway is_active = true; }, - [_, '\t'] => { + [(_, _), (index_b, '\t')] => { // as ['\t', '\t'] is excluded, this has to be a start of a tab group, // set indices accordingly is_active = true; - current_start = index + 1; + current_start = *index_b as u32; }, - ['\t', _] => { + [(_, '\t'), (index_b, _)] => { // this now has to be an end of the group, hence we have to push a new tuple is_active = false; - spans.push((current_start, index + 1)); + spans.push((current_start, *index_b as u32)); }, _ => {}, } @@ -137,7 +136,7 @@ fn get_chunks_of_tabs(the_str: &str) -> Vec<(u32, u32)> { if is_active { spans.push(( current_start, - u32::try_from(the_str.chars().count()).expect(line_length_way_to_long), + u32::try_from(char_indices.last().unwrap().0 + 1).expect(line_length_way_to_long), )); } @@ -148,6 +147,13 @@ fn get_chunks_of_tabs(the_str: &str) -> Vec<(u32, u32)> { mod tests_for_get_chunks_of_tabs { use super::get_chunks_of_tabs; + #[test] + fn test_unicode_han_string() { + let res = get_chunks_of_tabs(" 位\t"); + + assert_eq!(res, vec![(4, 5)]); + } + #[test] fn test_empty_string() { let res = get_chunks_of_tabs(""); diff --git a/tests/ui/crashes/ice-5835.rs b/tests/ui/crashes/ice-5835.rs new file mode 100644 index 00000000000..209a5b1eb09 --- /dev/null +++ b/tests/ui/crashes/ice-5835.rs @@ -0,0 +1,8 @@ +#![rustfmt::skip] + +pub struct Foo { + /// 位 + pub bar: u8, +} + +fn main() {} diff --git a/tests/ui/crashes/ice-5835.stderr b/tests/ui/crashes/ice-5835.stderr new file mode 100644 index 00000000000..e286bc580ad --- /dev/null +++ b/tests/ui/crashes/ice-5835.stderr @@ -0,0 +1,20 @@ +error[E0658]: custom inner attributes are unstable + --> $DIR/ice-5835.rs:1:4 + | +LL | #![rustfmt::skip] + | ^^^^^^^^^^^^^ + | + = note: see issue #54726 for more information + = help: add `#![feature(custom_inner_attributes)]` to the crate attributes to enable + +error: using tabs in doc comments is not recommended + --> $DIR/ice-5835.rs:4:10 + | +LL | /// 位 + | ^^^^ help: consider using four spaces per tab + | + = note: `-D clippy::tabs-in-doc-comments` implied by `-D warnings` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. -- cgit 1.4.1-3-g733a5 From 8a50923da4884c30e96fce2b4408c96f5824db43 Mon Sep 17 00:00:00 2001 From: Horaci Macias Date: Mon, 5 Apr 2021 13:27:39 +0200 Subject: consider mutability on useless_vec suggestions https://github.com/rust-lang/rust-clippy/issues/7035 --- clippy_lints/src/vec.rs | 54 ++++++++++++++++++++++++++++++++++++++----------- tests/ui/vec.fixed | 16 +++++++++++++++ tests/ui/vec.rs | 16 +++++++++++++++ tests/ui/vec.stderr | 44 +++++++++++++++++++++++++++++++++------- 4 files changed, 111 insertions(+), 19 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index 1af9583887f..bc2eb88114e 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -6,7 +6,7 @@ use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_copy; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{BorrowKind, Expr, ExprKind}; +use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -49,10 +49,10 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { if_chain! { if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(expr).kind(); if let ty::Slice(..) = ty.kind(); - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref addressee) = expr.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, mutability, ref addressee) = expr.kind; if let Some(vec_args) = higher::vec_macro(cx, addressee); then { - self.check_vec_macro(cx, &vec_args, expr.span); + self.check_vec_macro(cx, &vec_args, mutability, expr.span); } } @@ -70,14 +70,20 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { .ctxt() .outer_expn_data() .call_site; - self.check_vec_macro(cx, &vec_args, span); + self.check_vec_macro(cx, &vec_args, Mutability::Not, span); } } } } impl UselessVec { - fn check_vec_macro<'tcx>(self, cx: &LateContext<'tcx>, vec_args: &higher::VecArgs<'tcx>, span: Span) { + fn check_vec_macro<'tcx>( + self, + cx: &LateContext<'tcx>, + vec_args: &higher::VecArgs<'tcx>, + mutability: Mutability, + span: Span, + ) { let mut applicability = Applicability::MachineApplicable; let snippet = match *vec_args { higher::VecArgs::Repeat(elem, len) => { @@ -87,11 +93,22 @@ impl UselessVec { return; } - format!( - "&[{}; {}]", - snippet_with_applicability(cx, elem.span, "elem", &mut applicability), - snippet_with_applicability(cx, len.span, "len", &mut applicability) - ) + match mutability { + Mutability::Mut => { + format!( + "&mut [{}; {}]", + snippet_with_applicability(cx, elem.span, "elem", &mut applicability), + snippet_with_applicability(cx, len.span, "len", &mut applicability) + ) + }, + Mutability::Not => { + format!( + "&[{}; {}]", + snippet_with_applicability(cx, elem.span, "elem", &mut applicability), + snippet_with_applicability(cx, len.span, "len", &mut applicability) + ) + }, + } } else { return; } @@ -104,9 +121,22 @@ impl UselessVec { } let span = args[0].span.to(last.span); - format!("&[{}]", snippet_with_applicability(cx, span, "..", &mut applicability)) + match mutability { + Mutability::Mut => { + format!( + "&mut [{}]", + snippet_with_applicability(cx, span, "..", &mut applicability) + ) + }, + Mutability::Not => { + format!("&[{}]", snippet_with_applicability(cx, span, "..", &mut applicability)) + }, + } } else { - "&[]".into() + match mutability { + Mutability::Mut => "&mut []".into(), + Mutability::Not => "&[]".into(), + } } }, }; diff --git a/tests/ui/vec.fixed b/tests/ui/vec.fixed index 85677159620..da35f2e5c1b 100644 --- a/tests/ui/vec.fixed +++ b/tests/ui/vec.fixed @@ -6,9 +6,14 @@ struct NonCopy; fn on_slice(_: &[u8]) {} + +fn on_mut_slice(_: &mut [u8]) {} + #[allow(clippy::ptr_arg)] fn on_vec(_: &Vec) {} +fn on_mut_vec(_: &mut Vec) {} + struct Line { length: usize, } @@ -22,28 +27,38 @@ impl Line { fn main() { on_slice(&[]); on_slice(&[]); + on_mut_slice(&mut []); on_slice(&[1, 2]); on_slice(&[1, 2]); + on_mut_slice(&mut [1, 2]); on_slice(&[1, 2]); on_slice(&[1, 2]); + on_mut_slice(&mut [1, 2]); #[rustfmt::skip] on_slice(&[1, 2]); on_slice(&[1, 2]); + on_mut_slice(&mut [1, 2]); on_slice(&[1; 2]); on_slice(&[1; 2]); + on_mut_slice(&mut [1; 2]); on_vec(&vec![]); on_vec(&vec![1, 2]); on_vec(&vec![1; 2]); + on_mut_vec(&mut vec![]); + on_mut_vec(&mut vec![1, 2]); + on_mut_vec(&mut vec![1; 2]); // Now with non-constant expressions let line = Line { length: 2 }; on_slice(&vec![2; line.length]); on_slice(&vec![2; line.length()]); + on_mut_slice(&mut vec![2; line.length]); + on_mut_slice(&mut vec![2; line.length()]); for a in &[1, 2, 3] { println!("{:?}", a); @@ -54,6 +69,7 @@ fn main() { } on_vec(&vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack` + on_mut_vec(&mut vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack` // Ok for a in vec![1; 201] { diff --git a/tests/ui/vec.rs b/tests/ui/vec.rs index 03b8ee81665..e9ed83e5c5a 100644 --- a/tests/ui/vec.rs +++ b/tests/ui/vec.rs @@ -6,9 +6,14 @@ struct NonCopy; fn on_slice(_: &[u8]) {} + +fn on_mut_slice(_: &mut [u8]) {} + #[allow(clippy::ptr_arg)] fn on_vec(_: &Vec) {} +fn on_mut_vec(_: &mut Vec) {} + struct Line { length: usize, } @@ -22,28 +27,38 @@ impl Line { fn main() { on_slice(&vec![]); on_slice(&[]); + on_mut_slice(&mut vec![]); on_slice(&vec![1, 2]); on_slice(&[1, 2]); + on_mut_slice(&mut vec![1, 2]); on_slice(&vec![1, 2]); on_slice(&[1, 2]); + on_mut_slice(&mut vec![1, 2]); #[rustfmt::skip] on_slice(&vec!(1, 2)); on_slice(&[1, 2]); + on_mut_slice(&mut vec![1, 2]); on_slice(&vec![1; 2]); on_slice(&[1; 2]); + on_mut_slice(&mut vec![1; 2]); on_vec(&vec![]); on_vec(&vec![1, 2]); on_vec(&vec![1; 2]); + on_mut_vec(&mut vec![]); + on_mut_vec(&mut vec![1, 2]); + on_mut_vec(&mut vec![1; 2]); // Now with non-constant expressions let line = Line { length: 2 }; on_slice(&vec![2; line.length]); on_slice(&vec![2; line.length()]); + on_mut_slice(&mut vec![2; line.length]); + on_mut_slice(&mut vec![2; line.length()]); for a in vec![1, 2, 3] { println!("{:?}", a); @@ -54,6 +69,7 @@ fn main() { } on_vec(&vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack` + on_mut_vec(&mut vec![1; 201]); // Ok, size of `vec` higher than `too_large_for_stack` // Ok for a in vec![1; 201] { diff --git a/tests/ui/vec.stderr b/tests/ui/vec.stderr index 37e28ebddb5..7d1de05a5c8 100644 --- a/tests/ui/vec.stderr +++ b/tests/ui/vec.stderr @@ -1,5 +1,5 @@ error: useless use of `vec!` - --> $DIR/vec.rs:23:14 + --> $DIR/vec.rs:28:14 | LL | on_slice(&vec![]); | ^^^^^^^ help: you can use a slice directly: `&[]` @@ -7,34 +7,64 @@ LL | on_slice(&vec![]); = note: `-D clippy::useless-vec` implied by `-D warnings` error: useless use of `vec!` - --> $DIR/vec.rs:26:14 + --> $DIR/vec.rs:30:18 + | +LL | on_mut_slice(&mut vec![]); + | ^^^^^^^^^^^ help: you can use a slice directly: `&mut []` + +error: useless use of `vec!` + --> $DIR/vec.rs:32:14 | LL | on_slice(&vec![1, 2]); | ^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]` error: useless use of `vec!` - --> $DIR/vec.rs:29:14 + --> $DIR/vec.rs:34:18 + | +LL | on_mut_slice(&mut vec![1, 2]); + | ^^^^^^^^^^^^^^^ help: you can use a slice directly: `&mut [1, 2]` + +error: useless use of `vec!` + --> $DIR/vec.rs:36:14 | LL | on_slice(&vec![1, 2]); | ^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]` error: useless use of `vec!` - --> $DIR/vec.rs:32:14 + --> $DIR/vec.rs:38:18 + | +LL | on_mut_slice(&mut vec![1, 2]); + | ^^^^^^^^^^^^^^^ help: you can use a slice directly: `&mut [1, 2]` + +error: useless use of `vec!` + --> $DIR/vec.rs:40:14 | LL | on_slice(&vec!(1, 2)); | ^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]` error: useless use of `vec!` - --> $DIR/vec.rs:35:14 + --> $DIR/vec.rs:42:18 + | +LL | on_mut_slice(&mut vec![1, 2]); + | ^^^^^^^^^^^^^^^ help: you can use a slice directly: `&mut [1, 2]` + +error: useless use of `vec!` + --> $DIR/vec.rs:44:14 | LL | on_slice(&vec![1; 2]); | ^^^^^^^^^^^ help: you can use a slice directly: `&[1; 2]` error: useless use of `vec!` - --> $DIR/vec.rs:48:14 + --> $DIR/vec.rs:46:18 + | +LL | on_mut_slice(&mut vec![1; 2]); + | ^^^^^^^^^^^^^^^ help: you can use a slice directly: `&mut [1; 2]` + +error: useless use of `vec!` + --> $DIR/vec.rs:63:14 | LL | for a in vec![1, 2, 3] { | ^^^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2, 3]` -error: aborting due to 6 previous errors +error: aborting due to 11 previous errors -- cgit 1.4.1-3-g733a5 From 12fce557669a0de230399cf8e6eee4f5307bf87b Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 2 Apr 2021 17:35:32 -0400 Subject: Fix all occurences of `needless_borrow` internally --- clippy_dev/src/fmt.rs | 2 +- clippy_dev/src/lib.rs | 16 +- clippy_lints/src/absurd_extreme_comparisons.rs | 4 +- clippy_lints/src/assertions_on_constants.rs | 12 +- clippy_lints/src/atomic_ordering.rs | 6 +- clippy_lints/src/attrs.rs | 4 +- clippy_lints/src/await_holding_invalid.rs | 2 +- clippy_lints/src/bytecount.rs | 14 +- clippy_lints/src/casts/cast_ptr_alignment.rs | 2 +- clippy_lints/src/casts/cast_sign_loss.rs | 6 +- clippy_lints/src/casts/mod.rs | 2 +- clippy_lints/src/checked_conversions.rs | 16 +- clippy_lints/src/cognitive_complexity.rs | 2 +- clippy_lints/src/comparison_chain.rs | 6 +- clippy_lints/src/copies.rs | 6 +- clippy_lints/src/create_dir.rs | 2 +- clippy_lints/src/default.rs | 10 +- clippy_lints/src/derive.rs | 4 +- clippy_lints/src/doc.rs | 4 +- clippy_lints/src/double_comparison.rs | 4 +- clippy_lints/src/double_parens.rs | 6 +- clippy_lints/src/drop_forget_ref.rs | 2 +- clippy_lints/src/duration_subsec.rs | 4 +- clippy_lints/src/entry.rs | 16 +- clippy_lints/src/eq_op.rs | 14 +- clippy_lints/src/erasing_op.rs | 2 +- clippy_lints/src/eta_reduction.rs | 18 +- clippy_lints/src/eval_order_dependence.rs | 18 +- clippy_lints/src/exit.rs | 2 +- clippy_lints/src/explicit_write.rs | 10 +- clippy_lints/src/fallible_impl_from.rs | 4 +- clippy_lints/src/float_equality_without_abs.rs | 6 +- clippy_lints/src/floating_point_arithmetic.rs | 42 +- clippy_lints/src/format.rs | 38 +- clippy_lints/src/functions/must_use.rs | 28 +- .../src/functions/not_unsafe_ptr_arg_deref.rs | 6 +- clippy_lints/src/functions/result_unit_err.rs | 8 +- clippy_lints/src/functions/too_many_arguments.rs | 2 +- clippy_lints/src/get_last_with_len.rs | 2 +- clippy_lints/src/identity_op.rs | 2 +- clippy_lints/src/if_let_mutex.rs | 6 +- clippy_lints/src/if_let_some_result.rs | 6 +- clippy_lints/src/if_then_some_else_none.rs | 12 +- clippy_lints/src/implicit_hasher.rs | 6 +- clippy_lints/src/implicit_return.rs | 4 +- clippy_lints/src/implicit_saturating_sub.rs | 16 +- clippy_lints/src/indexing_slicing.rs | 2 +- clippy_lints/src/infinite_iter.rs | 12 +- clippy_lints/src/invalid_upcast_comparisons.rs | 6 +- clippy_lints/src/large_enum_variant.rs | 2 +- clippy_lints/src/len_zero.rs | 15 +- clippy_lints/src/let_if_seq.rs | 24 +- clippy_lints/src/let_underscore.rs | 2 +- clippy_lints/src/lib.rs | 2446 ++++++++++---------- clippy_lints/src/lifetimes.rs | 20 +- clippy_lints/src/literal_representation.rs | 4 +- clippy_lints/src/loops/explicit_counter_loop.rs | 2 +- clippy_lints/src/loops/for_kv_map.rs | 4 +- clippy_lints/src/loops/manual_flatten.rs | 4 +- clippy_lints/src/loops/manual_memcpy.rs | 10 +- clippy_lints/src/loops/mod.rs | 6 +- clippy_lints/src/loops/needless_collect.rs | 24 +- clippy_lints/src/loops/needless_range_loop.rs | 18 +- clippy_lints/src/loops/never_loop.rs | 46 +- clippy_lints/src/loops/same_item_push.rs | 2 +- clippy_lints/src/loops/single_element_loop.rs | 4 +- clippy_lints/src/loops/utils.rs | 16 +- clippy_lints/src/loops/while_let_loop.rs | 12 +- clippy_lints/src/loops/while_let_on_iterator.rs | 12 +- clippy_lints/src/manual_strip.rs | 4 +- clippy_lints/src/map_clone.rs | 10 +- clippy_lints/src/map_err_ignore.rs | 2 +- clippy_lints/src/map_identity.rs | 12 +- clippy_lints/src/map_unit_fn.rs | 12 +- clippy_lints/src/match_on_vec_items.rs | 4 +- clippy_lints/src/matches.rs | 81 +- clippy_lints/src/mem_discriminant.rs | 4 +- clippy_lints/src/mem_forget.rs | 2 +- clippy_lints/src/mem_replace.rs | 16 +- clippy_lints/src/methods/bind_instead_of_map.rs | 4 +- clippy_lints/src/methods/chars_cmp.rs | 2 +- clippy_lints/src/methods/expect_fun_call.rs | 8 +- clippy_lints/src/methods/filter_map.rs | 6 +- clippy_lints/src/methods/flat_map_identity.rs | 2 +- clippy_lints/src/methods/implicit_clone.rs | 2 +- clippy_lints/src/methods/iter_next_slice.rs | 2 +- clippy_lints/src/methods/mod.rs | 22 +- clippy_lints/src/methods/option_as_ref_deref.rs | 6 +- clippy_lints/src/methods/or_fun_call.rs | 6 +- clippy_lints/src/methods/search_is_some.rs | 6 +- clippy_lints/src/methods/uninit_assumed_init.rs | 8 +- clippy_lints/src/methods/unnecessary_filter_map.rs | 14 +- clippy_lints/src/methods/unnecessary_fold.rs | 6 +- clippy_lints/src/methods/utils.rs | 2 +- clippy_lints/src/methods/zst_offset.rs | 2 +- clippy_lints/src/minmax.rs | 4 +- clippy_lints/src/misc.rs | 26 +- clippy_lints/src/missing_const_for_fn.rs | 2 +- clippy_lints/src/missing_inline.rs | 2 +- clippy_lints/src/mut_key.rs | 6 +- clippy_lints/src/mut_mut.rs | 4 +- clippy_lints/src/mut_reference.rs | 4 +- clippy_lints/src/needless_bool.rs | 18 +- clippy_lints/src/needless_borrow.rs | 2 +- clippy_lints/src/needless_borrowed_ref.rs | 2 +- clippy_lints/src/needless_for_each.rs | 2 +- clippy_lints/src/needless_pass_by_value.rs | 2 +- clippy_lints/src/needless_update.rs | 2 +- clippy_lints/src/neg_cmp_op_on_partial_ord.rs | 4 +- clippy_lints/src/neg_multiply.rs | 6 +- clippy_lints/src/no_effect.rs | 58 +- clippy_lints/src/non_octal_unix_permissions.rs | 4 +- clippy_lints/src/open_options.rs | 4 +- clippy_lints/src/option_if_let_else.rs | 8 +- clippy_lints/src/overflow_check_conditional.rs | 20 +- clippy_lints/src/pass_by_ref_or_value.rs | 4 +- clippy_lints/src/path_buf_push_overwrite.rs | 2 +- clippy_lints/src/pattern_type_mismatch.rs | 18 +- clippy_lints/src/ptr.rs | 22 +- clippy_lints/src/ptr_eq.rs | 8 +- clippy_lints/src/ptr_offset_with_cast.rs | 6 +- clippy_lints/src/question_mark.rs | 8 +- clippy_lints/src/ranges.rs | 24 +- clippy_lints/src/redundant_clone.rs | 10 +- clippy_lints/src/redundant_closure_call.rs | 16 +- clippy_lints/src/ref_option_ref.rs | 2 +- clippy_lints/src/regex.rs | 4 +- clippy_lints/src/repeat_once.rs | 4 +- clippy_lints/src/returns.rs | 16 +- clippy_lints/src/semicolon_if_nothing_returned.rs | 2 +- clippy_lints/src/shadow.rs | 69 +- clippy_lints/src/slow_vector_initialization.rs | 42 +- clippy_lints/src/strings.rs | 14 +- clippy_lints/src/suspicious_operation_groupings.rs | 16 +- clippy_lints/src/swap.rs | 30 +- clippy_lints/src/tabs_in_doc_comments.rs | 2 +- clippy_lints/src/to_string_in_display.rs | 2 +- clippy_lints/src/trait_bounds.rs | 4 +- clippy_lints/src/transmute/mod.rs | 2 +- .../src/transmute/transmute_float_to_int.rs | 2 +- clippy_lints/src/transmute/utils.rs | 2 +- clippy_lints/src/transmuting_null.rs | 6 +- clippy_lints/src/try_err.rs | 14 +- clippy_lints/src/types/borrowed_box.rs | 8 +- clippy_lints/src/types/mod.rs | 28 +- clippy_lints/src/types/type_complexity.rs | 4 +- clippy_lints/src/types/utils.rs | 2 +- clippy_lints/src/types/vec_box.rs | 4 +- clippy_lints/src/undropped_manually_drops.rs | 2 +- clippy_lints/src/unit_return_expecting_ord.rs | 2 +- clippy_lints/src/unit_types/let_unit_value.rs | 4 +- clippy_lints/src/unit_types/unit_arg.rs | 2 +- clippy_lints/src/unit_types/unit_cmp.rs | 4 +- clippy_lints/src/unit_types/utils.rs | 2 +- clippy_lints/src/unnamed_address.rs | 6 +- clippy_lints/src/unnecessary_sort_by.rs | 16 +- clippy_lints/src/unnecessary_wraps.rs | 2 +- clippy_lints/src/unused_io_amount.rs | 10 +- clippy_lints/src/unused_unit.rs | 2 +- clippy_lints/src/unwrap.rs | 4 +- clippy_lints/src/use_self.rs | 6 +- clippy_lints/src/useless_conversion.rs | 12 +- clippy_lints/src/utils/author.rs | 96 +- clippy_lints/src/utils/inspector.rs | 90 +- clippy_lints/src/utils/internal_lints.rs | 18 +- clippy_lints/src/vec.rs | 2 +- clippy_lints/src/vec_resize_to_zero.rs | 2 +- clippy_lints/src/zero_div_zero.rs | 2 +- clippy_lints/src/zero_sized_map_values.rs | 2 +- src/driver.rs | 12 +- tests/compile-test.rs | 16 +- tests/ui/borrow_interior_mutable_const/others.rs | 6 +- .../ui/borrow_interior_mutable_const/others.stderr | 28 +- tests/ui/collapsible_match2.rs | 7 +- tests/ui/collapsible_match2.stderr | 20 +- tests/ui/escape_analysis.rs | 4 +- tests/ui/implicit_clone.rs | 2 +- tests/ui/into_iter_on_ref.fixed | 2 +- tests/ui/into_iter_on_ref.rs | 2 +- tests/ui/ref_option_ref.rs | 2 +- tests/ui/ref_option_ref.stderr | 2 +- tests/ui/stable_sort_primitive.fixed | 2 +- tests/ui/stable_sort_primitive.rs | 2 +- 183 files changed, 2150 insertions(+), 2162 deletions(-) (limited to 'tests') diff --git a/clippy_dev/src/fmt.rs b/clippy_dev/src/fmt.rs index 111c79c332d..1517cdc9419 100644 --- a/clippy_dev/src/fmt.rs +++ b/clippy_dev/src/fmt.rs @@ -68,7 +68,7 @@ pub fn run(check: bool, verbose: bool) { continue; } - success &= rustfmt(context, &path)?; + success &= rustfmt(context, path)?; } Ok(success) diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index a5e94683878..1e5a140e964 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -101,7 +101,7 @@ impl Lint { #[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())) + .map(|l| format!(" LintId::of({}::{}),", l.module, l.name.to_uppercase())) .sorted() .collect::>() } @@ -154,17 +154,17 @@ pub fn gen_register_lint_list<'a>( 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())) + .sorted_by_key(|l| format!(" {}::{},", l.module, l.name.to_uppercase())) .map(|l| { format!( - " #[cfg(feature = \"internal-lints\")]\n &{}::{},", + " #[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_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); @@ -550,9 +550,9 @@ fn test_gen_lint_group_list() { 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(), + " 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_lints/src/absurd_extreme_comparisons.rs b/clippy_lints/src/absurd_extreme_comparisons.rs index 33c720c666e..5fbf4bdbd18 100644 --- a/clippy_lints/src/absurd_extreme_comparisons.rs +++ b/clippy_lints/src/absurd_extreme_comparisons.rs @@ -44,7 +44,7 @@ declare_lint_pass!(AbsurdExtremeComparisons => [ABSURD_EXTREME_COMPARISONS]); impl<'tcx> LateLintPass<'tcx> for AbsurdExtremeComparisons { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Binary(ref cmp, ref lhs, ref rhs) = expr.kind { + if let ExprKind::Binary(ref cmp, lhs, rhs) = expr.kind { if let Some((culprit, result)) = detect_absurd_comparison(cx, cmp.node, lhs, rhs) { if !expr.span.from_expansion() { let msg = "this comparison involving the minimum or maximum element for this \ @@ -95,7 +95,7 @@ enum AbsurdComparisonResult { } fn is_cast_between_fixed_and_target<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { - if let ExprKind::Cast(ref cast_exp, _) = expr.kind { + if let ExprKind::Cast(cast_exp, _) = expr.kind { let precast_ty = cx.typeck_results().expr_ty(cast_exp); let cast_ty = cx.typeck_results().expr_ty(expr); diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs index 16905781c56..a0993bb6913 100644 --- a/clippy_lints/src/assertions_on_constants.rs +++ b/clippy_lints/src/assertions_on_constants.rs @@ -71,7 +71,7 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants { return; } if_chain! { - if let ExprKind::Unary(_, ref lit) = e.kind; + if let ExprKind::Unary(_, lit) = e.kind; if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.typeck_results(), lit); if is_true; then { @@ -82,7 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants { if assert_span.from_expansion() { return; } - if let Some(assert_match) = match_assert_with_message(&cx, e) { + if let Some(assert_match) = match_assert_with_message(cx, e) { match assert_match { // matched assert but not message AssertKind::WithoutMessage(false) => lint_false_without_message(), @@ -113,17 +113,17 @@ enum AssertKind { /// where `message` is any expression and `c` is a constant bool. fn match_assert_with_message<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option { if_chain! { - if let ExprKind::If(ref cond, ref then, _) = expr.kind; - if let ExprKind::Unary(UnOp::Not, ref expr) = cond.kind; + if let ExprKind::If(cond, then, _) = expr.kind; + if let ExprKind::Unary(UnOp::Not, expr) = cond.kind; // bind the first argument of the `assert!` macro if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.typeck_results(), expr); // block - if let ExprKind::Block(ref block, _) = then.kind; + if let ExprKind::Block(block, _) = then.kind; if block.stmts.is_empty(); if let Some(block_expr) = &block.expr; // inner block is optional. unwrap it if it exists, or use the expression as is otherwise. if let Some(begin_panic_call) = match block_expr.kind { - ExprKind::Block(ref inner_block, _) => &inner_block.expr, + ExprKind::Block(inner_block, _) => &inner_block.expr, _ => &block.expr, }; // function call diff --git a/clippy_lints/src/atomic_ordering.rs b/clippy_lints/src/atomic_ordering.rs index dfb18199325..7ceb01f5590 100644 --- a/clippy_lints/src/atomic_ordering.rs +++ b/clippy_lints/src/atomic_ordering.rs @@ -85,7 +85,7 @@ fn match_ordering_def_path(cx: &LateContext<'_>, did: DefId, orderings: &[&str]) fn check_atomic_load_store(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { - if let ExprKind::MethodCall(ref method_path, _, args, _) = &expr.kind; + if let ExprKind::MethodCall(method_path, _, args, _) = &expr.kind; let method = method_path.ident.name.as_str(); if type_is_atomic(cx, &args[0]); if method == "load" || method == "store"; @@ -120,7 +120,7 @@ fn check_atomic_load_store(cx: &LateContext<'_>, expr: &Expr<'_>) { fn check_memory_fence(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { - if let ExprKind::Call(ref func, ref args) = expr.kind; + if let ExprKind::Call(func, args) = expr.kind; 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 ["fence", "compiler_fence"] @@ -152,7 +152,7 @@ fn opt_ordering_defid(cx: &LateContext<'_>, ord_arg: &Expr<'_>) -> Option fn check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { - if let ExprKind::MethodCall(ref method_path, _, args, _) = &expr.kind; + if let ExprKind::MethodCall(method_path, _, args, _) = &expr.kind; let method = method_path.ident.name.as_str(); if type_is_atomic(cx, &args[0]); if method == "compare_exchange" || method == "compare_exchange_weak" || method == "fetch_update"; diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 0ce46e1c214..382fb03d920 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -602,7 +602,7 @@ fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) { if let NestedMetaItem::MetaItem(meta) = item { match &meta.kind { MetaItemKind::List(list) => { - mismatched.extend(find_mismatched_target_os(&list)); + mismatched.extend(find_mismatched_target_os(list)); }, MetaItemKind::Word => { if_chain! { @@ -629,7 +629,7 @@ fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) { then { let mess = "operating system used in target family position"; - span_lint_and_then(cx, MISMATCHED_TARGET_OS, attr.span, &mess, |diag| { + span_lint_and_then(cx, MISMATCHED_TARGET_OS, attr.span, mess, |diag| { // Avoid showing the unix suggestion multiple times in case // we have more than one mismatch for unix-like systems let mut unix_suggested = false; diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 68eee0520b3..1739a57a240 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -101,7 +101,7 @@ impl LateLintPass<'_> for AwaitHolding { let typeck_results = cx.tcx.typeck_body(body_id); check_interior_types( cx, - &typeck_results.generator_interior_types.as_ref().skip_binder(), + typeck_results.generator_interior_types.as_ref().skip_binder(), body.value.span, ); } diff --git a/clippy_lints/src/bytecount.rs b/clippy_lints/src/bytecount.rs index 1546d823167..877ae002d36 100644 --- a/clippy_lints/src/bytecount.rs +++ b/clippy_lints/src/bytecount.rs @@ -38,17 +38,17 @@ declare_lint_pass!(ByteCount => [NAIVE_BYTECOUNT]); impl<'tcx> LateLintPass<'tcx> for ByteCount { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { - if let ExprKind::MethodCall(ref count, _, ref count_args, _) = expr.kind; + if let ExprKind::MethodCall(count, _, count_args, _) = expr.kind; if count.ident.name == sym!(count); if count_args.len() == 1; - if let ExprKind::MethodCall(ref filter, _, ref filter_args, _) = count_args[0].kind; + if let ExprKind::MethodCall(filter, _, filter_args, _) = count_args[0].kind; if filter.ident.name == sym!(filter); if filter_args.len() == 2; if let ExprKind::Closure(_, _, body_id, _, _) = filter_args[1].kind; let body = cx.tcx.hir().body(body_id); if body.params.len() == 1; - if let Some(argname) = get_pat_name(&body.params[0].pat); - if let ExprKind::Binary(ref op, ref l, ref r) = body.value.kind; + if let Some(argname) = get_pat_name(body.params[0].pat); + if let ExprKind::Binary(ref op, l, r) = body.value.kind; if op.node == BinOpKind::Eq; if match_type(cx, cx.typeck_results().expr_ty(&filter_args[0]).peel_refs(), @@ -64,7 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for ByteCount { if ty::Uint(UintTy::U8) != *cx.typeck_results().expr_ty(needle).peel_refs().kind() { return; } - let haystack = if let ExprKind::MethodCall(ref path, _, ref args, _) = + let haystack = if let ExprKind::MethodCall(path, _, args, _) = filter_args[0].kind { let p = path.ident.name; if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 { @@ -98,10 +98,10 @@ fn check_arg(name: Symbol, arg: Symbol, needle: &Expr<'_>) -> bool { fn get_path_name(expr: &Expr<'_>) -> Option { match expr.kind { - ExprKind::Box(ref e) | ExprKind::AddrOf(BorrowKind::Ref, _, ref e) | ExprKind::Unary(UnOp::Deref, ref e) => { + ExprKind::Box(e) | ExprKind::AddrOf(BorrowKind::Ref, _, e) | ExprKind::Unary(UnOp::Deref, e) => { get_path_name(e) }, - ExprKind::Block(ref b, _) => { + ExprKind::Block(b, _) => { if b.stmts.is_empty() { b.expr.as_ref().and_then(|p| get_path_name(p)) } else { diff --git a/clippy_lints/src/casts/cast_ptr_alignment.rs b/clippy_lints/src/casts/cast_ptr_alignment.rs index 5208156ffd2..62a119d662b 100644 --- a/clippy_lints/src/casts/cast_ptr_alignment.rs +++ b/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -10,7 +10,7 @@ use rustc_target::abi::LayoutOf; use super::CAST_PTR_ALIGNMENT; pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Cast(ref cast_expr, cast_to) = expr.kind { + if let ExprKind::Cast(cast_expr, cast_to) = expr.kind { if is_hir_ty_cfg_dependant(cx, cast_to) { return; } diff --git a/clippy_lints/src/casts/cast_sign_loss.rs b/clippy_lints/src/casts/cast_sign_loss.rs index bf722d0a3f4..040e0ca8864 100644 --- a/clippy_lints/src/casts/cast_sign_loss.rs +++ b/clippy_lints/src/casts/cast_sign_loss.rs @@ -30,7 +30,7 @@ fn should_lint(cx: &LateContext<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast } // Don't lint for positive constants. - let const_val = constant(cx, &cx.typeck_results(), cast_op); + let const_val = constant(cx, cx.typeck_results(), cast_op); if_chain! { if let Some((Constant::Int(n), _)) = const_val; if let ty::Int(ity) = *cast_from.kind(); @@ -41,14 +41,14 @@ fn should_lint(cx: &LateContext<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast } // Don't lint for the result of methods that always return non-negative values. - if let ExprKind::MethodCall(ref path, _, _, _) = cast_op.kind { + if let ExprKind::MethodCall(path, _, _, _) = cast_op.kind { let mut method_name = path.ident.name.as_str(); let allowed_methods = ["abs", "checked_abs", "rem_euclid", "checked_rem_euclid"]; if_chain! { if method_name == "unwrap"; if let Some(arglist) = method_chain_args(cast_op, &["unwrap"]); - if let ExprKind::MethodCall(ref inner_path, _, _, _) = &arglist[0][0].kind; + if let ExprKind::MethodCall(inner_path, _, _, _) = &arglist[0][0].kind; then { method_name = inner_path.ident.name.as_str(); } diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index d9e172c01a7..ae4fdd12c41 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -372,7 +372,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { return; } - if let ExprKind::Cast(ref cast_expr, cast_to) = expr.kind { + if let ExprKind::Cast(cast_expr, cast_to) = expr.kind { if is_hir_ty_cfg_dependant(cx, cast_to) { return; } diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index b38c70bcc41..d7136f84cc3 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -64,7 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for CheckedConversions { let result = if_chain! { if !in_external_macro(cx.sess(), item.span); - if let ExprKind::Binary(op, ref left, ref right) = &item.kind; + if let ExprKind::Binary(op, left, right) = &item.kind; then { match op.node { @@ -200,7 +200,7 @@ impl ConversionType { /// Check for `expr <= (to_type::MAX as from_type)` fn check_upper_bound<'tcx>(expr: &'tcx Expr<'tcx>) -> Option> { if_chain! { - if let ExprKind::Binary(ref op, ref left, ref right) = &expr.kind; + if let ExprKind::Binary(ref op, left, right) = &expr.kind; if let Some((candidate, check)) = normalize_le_ge(op, left, right); if let Some((from, to)) = get_types_from_cast(check, INTS, "max_value", "MAX"); @@ -219,7 +219,7 @@ fn check_lower_bound<'tcx>(expr: &'tcx Expr<'tcx>) -> Option> { } // First of we need a binary containing the expression & the cast - if let ExprKind::Binary(ref op, ref left, ref right) = &expr.kind { + if let ExprKind::Binary(ref op, left, right) = &expr.kind { normalize_le_ge(op, right, left).and_then(|(l, r)| check_function(l, r)) } else { None @@ -260,7 +260,7 @@ fn get_types_from_cast<'a>( // or `to_type::MAX as from_type` let call_from_cast: Option<(&Expr<'_>, &str)> = if_chain! { // to_type::max_value(), from_type - if let ExprKind::Cast(ref limit, ref from_type) = &expr.kind; + if let ExprKind::Cast(limit, from_type) = &expr.kind; if let TyKind::Path(ref from_type_path) = &from_type.kind; if let Some(from_sym) = int_ty_to_sym(from_type_path); @@ -275,7 +275,7 @@ fn get_types_from_cast<'a>( let limit_from: Option<(&Expr<'_>, &str)> = call_from_cast.or_else(|| { if_chain! { // `from_type::from, to_type::max_value()` - if let ExprKind::Call(ref from_func, ref args) = &expr.kind; + if let ExprKind::Call(from_func, args) = &expr.kind; // `to_type::max_value()` if args.len() == 1; if let limit = &args[0]; @@ -317,9 +317,9 @@ fn get_types_from_cast<'a>( /// Gets the type which implements the called function fn get_implementing_type<'a>(path: &QPath<'_>, candidates: &'a [&str], function: &str) -> Option<&'a str> { if_chain! { - if let QPath::TypeRelative(ref ty, ref path) = &path; + if let QPath::TypeRelative(ty, path) = &path; if path.ident.name.as_str() == function; - if let TyKind::Path(QPath::Resolved(None, ref tp)) = &ty.kind; + if let TyKind::Path(QPath::Resolved(None, tp)) = &ty.kind; if let [int] = &*tp.segments; then { let name = &int.ident.name.as_str(); @@ -333,7 +333,7 @@ fn get_implementing_type<'a>(path: &QPath<'_>, candidates: &'a [&str], function: /// Gets the type as a string, if it is a supported integer fn int_ty_to_sym<'tcx>(path: &QPath<'_>) -> Option<&'tcx str> { if_chain! { - if let QPath::Resolved(_, ref path) = *path; + if let QPath::Resolved(_, path) = *path; if let [ty] = &*path.segments; then { let name = &ty.ident.name.as_str(); diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 4cc542f723c..f62c6a9c325 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -152,7 +152,7 @@ impl<'tcx> Visitor<'tcx> for CcHelper { ExprKind::If(_, _, _) => { self.cc += 1; }, - ExprKind::Match(_, ref arms, _) => { + ExprKind::Match(_, arms, _) => { if arms.len() > 1 { self.cc += 1; } diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs index d601cb7c224..31ae63b5184 100644 --- a/clippy_lints/src/comparison_chain.rs +++ b/clippy_lints/src/comparison_chain.rs @@ -71,10 +71,8 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain { } for cond in conds.windows(2) { - if let ( - &ExprKind::Binary(ref kind1, ref lhs1, ref rhs1), - &ExprKind::Binary(ref kind2, ref lhs2, ref rhs2), - ) = (&cond[0].kind, &cond[1].kind) + if let (&ExprKind::Binary(ref kind1, lhs1, rhs1), &ExprKind::Binary(ref kind2, lhs2, rhs2)) = + (&cond[0].kind, &cond[1].kind) { if !kind_is_cmp(kind1.node) || !kind_is_cmp(kind2.node) { return; diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 1b982221b06..8b503c9a030 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -160,7 +160,7 @@ impl<'tcx> LateLintPass<'tcx> for CopyAndPaste { if let ExprKind::If(_, _, _) = expr.kind { // skip ifs directly in else, it will be checked in the parent if if let Some(&Expr { - kind: ExprKind::If(_, _, Some(ref else_expr)), + kind: ExprKind::If(_, _, Some(else_expr)), .. }) = get_parent_expr(cx, expr) { @@ -247,7 +247,7 @@ fn lint_same_then_else<'tcx>( for value in &end_walker.uses { // Well we can't move this and all prev statements. So reset - if block_defs.contains(&value) { + if block_defs.contains(value) { moved_start = Some(index + 1); end_walker.defs.drain().for_each(|x| { block_defs.insert(x); @@ -555,7 +555,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UsedValueFinderVisitor<'a, 'tcx> { } fn visit_qpath(&mut self, qpath: &'tcx rustc_hir::QPath<'tcx>, id: HirId, _span: rustc_span::Span) { - if let rustc_hir::QPath::Resolved(_, ref path) = *qpath { + if let rustc_hir::QPath::Resolved(_, path) = *qpath { if path.segments.len() == 1 { if let rustc_hir::def::Res::Local(var) = self.cx.qpath_res(qpath, id) { self.uses.insert(var); diff --git a/clippy_lints/src/create_dir.rs b/clippy_lints/src/create_dir.rs index ac890c90c97..7b5cce6462a 100644 --- a/clippy_lints/src/create_dir.rs +++ b/clippy_lints/src/create_dir.rs @@ -33,7 +33,7 @@ declare_lint_pass!(CreateDir => [CREATE_DIR]); impl LateLintPass<'_> for CreateDir { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { - if let ExprKind::Call(ref func, ref args) = expr.kind; + if let ExprKind::Call(func, args) = expr.kind; if let ExprKind::Path(ref path) = func.kind; if let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id(); if match_def_path(cx, def_id, &paths::STD_FS_CREATE_DIR); diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index 8a2146d93e8..710da8fe9e0 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -77,7 +77,7 @@ impl LateLintPass<'_> for Default { if_chain! { // Avoid cases already linted by `field_reassign_with_default` if !self.reassigned_linted.contains(&expr.span); - if let ExprKind::Call(ref path, ..) = expr.kind; + if let ExprKind::Call(path, ..) = expr.kind; if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id); if let ExprKind::Path(ref qpath) = path.kind; if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id(); @@ -246,7 +246,7 @@ impl LateLintPass<'_> for Default { /// Checks if the given expression is the `default` method belonging to the `Default` trait. fn is_expr_default<'tcx>(expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> bool { if_chain! { - if let ExprKind::Call(ref fn_expr, _) = &expr.kind; + if let ExprKind::Call(fn_expr, _) = &expr.kind; if let ExprKind::Path(qpath) = &fn_expr.kind; if let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id); then { @@ -262,11 +262,11 @@ fn is_expr_default<'tcx>(expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> bool fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Option<(Ident, &'tcx Expr<'tcx>)> { if_chain! { // only take assignments - if let StmtKind::Semi(ref later_expr) = this.kind; - if let ExprKind::Assign(ref assign_lhs, ref assign_rhs, _) = later_expr.kind; + if let StmtKind::Semi(later_expr) = this.kind; + if let ExprKind::Assign(assign_lhs, assign_rhs, _) = later_expr.kind; // only take assignments to fields where the left-hand side field is a field of // the same binding as the previous statement - if let ExprKind::Field(ref binding, field_ident) = assign_lhs.kind; + if let ExprKind::Field(binding, field_ident) = assign_lhs.kind; if let ExprKind::Path(QPath::Resolved(_, path)) = binding.kind; if let Some(second_binding_name) = path.segments.last(); if second_binding_name.ident.name == binding_name; diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 9fad85f8ef4..647af3bdc04 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -199,7 +199,7 @@ fn check_hash_peq<'tcx>( then { // Look for the PartialEq implementations for `ty` cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| { - let peq_is_automatically_derived = is_automatically_derived(&cx.tcx.get_attrs(impl_id)); + let peq_is_automatically_derived = is_automatically_derived(cx.tcx.get_attrs(impl_id)); if peq_is_automatically_derived == hash_is_automatically_derived { return; @@ -253,7 +253,7 @@ fn check_ord_partial_ord<'tcx>( then { // Look for the PartialOrd implementations for `ty` cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| { - let partial_ord_is_automatically_derived = is_automatically_derived(&cx.tcx.get_attrs(impl_id)); + let partial_ord_is_automatically_derived = is_automatically_derived(cx.tcx.get_attrs(impl_id)); if partial_ord_is_automatically_derived == ord_is_automatically_derived { return; diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index b5796ccf43f..fb53b55ebd6 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -710,8 +710,8 @@ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> { // check for `begin_panic` if_chain! { - if let ExprKind::Call(ref func_expr, _) = expr.kind; - if let ExprKind::Path(QPath::Resolved(_, ref path)) = func_expr.kind; + if let ExprKind::Call(func_expr, _) = expr.kind; + if let ExprKind::Path(QPath::Resolved(_, path)) = func_expr.kind; if let Some(path_def_id) = path.res.opt_def_id(); if match_panic_def_id(self.cx, path_def_id); if is_expn_of(expr.span, "unreachable").is_none(); diff --git a/clippy_lints/src/double_comparison.rs b/clippy_lints/src/double_comparison.rs index 1d291565ec1..58543ae6e4e 100644 --- a/clippy_lints/src/double_comparison.rs +++ b/clippy_lints/src/double_comparison.rs @@ -47,7 +47,7 @@ impl<'tcx> DoubleComparisons { }, _ => return, }; - if !(eq_expr_value(cx, &llhs, &rlhs) && eq_expr_value(cx, &lrhs, &rrhs)) { + if !(eq_expr_value(cx, llhs, rlhs) && eq_expr_value(cx, lrhs, rrhs)) { return; } macro_rules! lint_double_comparison { @@ -88,7 +88,7 @@ impl<'tcx> DoubleComparisons { impl<'tcx> LateLintPass<'tcx> for DoubleComparisons { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Binary(ref kind, ref lhs, ref rhs) = expr.kind { + if let ExprKind::Binary(ref kind, lhs, rhs) = expr.kind { Self::check_binop(cx, kind.node, lhs, rhs, expr.span); } } diff --git a/clippy_lints/src/double_parens.rs b/clippy_lints/src/double_parens.rs index 5afdcb3c09f..e4e4a93b011 100644 --- a/clippy_lints/src/double_parens.rs +++ b/clippy_lints/src/double_parens.rs @@ -50,7 +50,7 @@ impl EarlyLintPass for DoubleParens { match expr.kind { ExprKind::Paren(ref in_paren) => match in_paren.kind { ExprKind::Paren(_) | ExprKind::Tup(_) => { - span_lint(cx, DOUBLE_PARENS, expr.span, &msg); + span_lint(cx, DOUBLE_PARENS, expr.span, msg); }, _ => {}, }, @@ -58,7 +58,7 @@ impl EarlyLintPass for DoubleParens { if params.len() == 1 { let param = ¶ms[0]; if let ExprKind::Paren(_) = param.kind { - span_lint(cx, DOUBLE_PARENS, param.span, &msg); + span_lint(cx, DOUBLE_PARENS, param.span, msg); } } }, @@ -66,7 +66,7 @@ impl EarlyLintPass for DoubleParens { if params.len() == 2 { let param = ¶ms[1]; if let ExprKind::Paren(_) = param.kind { - span_lint(cx, DOUBLE_PARENS, param.span, &msg); + span_lint(cx, DOUBLE_PARENS, param.span, msg); } } }, diff --git a/clippy_lints/src/drop_forget_ref.rs b/clippy_lints/src/drop_forget_ref.rs index 7e7ec017bbb..b5b29760636 100644 --- a/clippy_lints/src/drop_forget_ref.rs +++ b/clippy_lints/src/drop_forget_ref.rs @@ -113,7 +113,7 @@ declare_lint_pass!(DropForgetRef => [DROP_REF, FORGET_REF, DROP_COPY, FORGET_COP impl<'tcx> LateLintPass<'tcx> for DropForgetRef { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { - if let ExprKind::Call(ref path, ref args) = expr.kind; + if let ExprKind::Call(path, args) = expr.kind; if let ExprKind::Path(ref qpath) = path.kind; if args.len() == 1; if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id(); diff --git a/clippy_lints/src/duration_subsec.rs b/clippy_lints/src/duration_subsec.rs index 746c1f6df91..529807770f3 100644 --- a/clippy_lints/src/duration_subsec.rs +++ b/clippy_lints/src/duration_subsec.rs @@ -43,8 +43,8 @@ declare_lint_pass!(DurationSubsec => [DURATION_SUBSEC]); impl<'tcx> LateLintPass<'tcx> for DurationSubsec { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { - if let ExprKind::Binary(Spanned { node: BinOpKind::Div, .. }, ref left, ref right) = expr.kind; - if let ExprKind::MethodCall(ref method_path, _ , ref args, _) = left.kind; + if let ExprKind::Binary(Spanned { node: BinOpKind::Div, .. }, left, right) = expr.kind; + if let ExprKind::MethodCall(method_path, _ , args, _) = left.kind; if match_type(cx, cx.typeck_results().expr_ty(&args[0]).peel_refs(), &paths::DURATION); if let Some((Constant::Int(divisor), _)) = constant(cx, cx.typeck_results(), right); then { diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index 25eb048448c..a815df1691a 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -57,14 +57,14 @@ declare_lint_pass!(HashMapPass => [MAP_ENTRY]); impl<'tcx> LateLintPass<'tcx> for HashMapPass { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::If(ref check, ref then_block, ref else_block) = expr.kind { - if let ExprKind::Unary(UnOp::Not, ref check) = check.kind { + if let ExprKind::If(check, then_block, ref else_block) = expr.kind { + if let ExprKind::Unary(UnOp::Not, check) = check.kind { if let Some((ty, map, key)) = check_cond(cx, check) { // in case of `if !m.contains_key(&k) { m.insert(k, v); }` // we can give a better error message let sole_expr = { else_block.is_none() - && if let ExprKind::Block(ref then_block, _) = then_block.kind { + && if let ExprKind::Block(then_block, _) = then_block.kind { (then_block.expr.is_some() as usize) + then_block.stmts.len() == 1 } else { true @@ -81,9 +81,9 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { sole_expr, }; - walk_expr(&mut visitor, &**then_block); + walk_expr(&mut visitor, then_block); } - } else if let Some(ref else_block) = *else_block { + } else if let Some(else_block) = *else_block { if let Some((ty, map, key)) = check_cond(cx, check) { let mut visitor = InsertVisitor { cx, @@ -103,10 +103,10 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { fn check_cond<'a>(cx: &LateContext<'_>, check: &'a Expr<'a>) -> Option<(&'static str, &'a Expr<'a>, &'a Expr<'a>)> { if_chain! { - if let ExprKind::MethodCall(ref path, _, ref params, _) = check.kind; + if let ExprKind::MethodCall(path, _, params, _) = check.kind; if params.len() >= 2; if path.ident.name == sym!(contains_key); - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref key) = params[1].kind; + if let ExprKind::AddrOf(BorrowKind::Ref, _, key) = params[1].kind; then { let map = ¶ms[0]; let obj_ty = cx.typeck_results().expr_ty(map).peel_refs(); @@ -140,7 +140,7 @@ impl<'a, 'tcx, 'b> Visitor<'tcx> for InsertVisitor<'a, 'tcx, 'b> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { if_chain! { - if let ExprKind::MethodCall(ref path, _, ref params, _) = expr.kind; + if let ExprKind::MethodCall(path, _, params, _) = expr.kind; if params.len() == 3; if path.ident.name == sym!(insert); if get_item_name(self.cx, self.map) == get_item_name(self.cx, ¶ms[0]); diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index 6d7046ac8b7..90f391b5f5c 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -65,12 +65,12 @@ const ASSERT_MACRO_NAMES: [&str; 4] = ["assert_eq", "assert_ne", "debug_assert_e impl<'tcx> LateLintPass<'tcx> for EqOp { #[allow(clippy::similar_names, clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if let ExprKind::Block(ref block, _) = e.kind { + if let ExprKind::Block(block, _) = e.kind { for stmt in block.stmts { for amn in &ASSERT_MACRO_NAMES { if_chain! { if is_expn_of(stmt.span, amn).is_some(); - if let StmtKind::Semi(ref matchexpr) = stmt.kind; + if let StmtKind::Semi(matchexpr) = stmt.kind; if let Some(macro_args) = higher::extract_assert_macro_args(matchexpr); if macro_args.len() == 2; let (lhs, rhs) = (macro_args[0], macro_args[1]); @@ -88,12 +88,12 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { } } } - if let ExprKind::Binary(op, ref left, ref right) = e.kind { + if let ExprKind::Binary(op, left, right) = e.kind { if e.span.from_expansion() { return; } let macro_with_not_op = |expr_kind: &ExprKind<'_>| { - if let ExprKind::Unary(_, ref expr) = *expr_kind { + if let ExprKind::Unary(_, expr) = *expr_kind { in_macro(expr.span) } else { false @@ -135,7 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { // do not suggest to dereference literals (&ExprKind::Lit(..), _) | (_, &ExprKind::Lit(..)) => {}, // &foo == &bar - (&ExprKind::AddrOf(BorrowKind::Ref, _, ref l), &ExprKind::AddrOf(BorrowKind::Ref, _, ref r)) => { + (&ExprKind::AddrOf(BorrowKind::Ref, _, l), &ExprKind::AddrOf(BorrowKind::Ref, _, r)) => { let lty = cx.typeck_results().expr_ty(l); let rty = cx.typeck_results().expr_ty(r); let lcpy = is_copy(cx, lty); @@ -198,7 +198,7 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { } }, // &foo == bar - (&ExprKind::AddrOf(BorrowKind::Ref, _, ref l), _) => { + (&ExprKind::AddrOf(BorrowKind::Ref, _, l), _) => { let lty = cx.typeck_results().expr_ty(l); let lcpy = is_copy(cx, lty); if (requires_ref || lcpy) @@ -222,7 +222,7 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { } }, // foo == &bar - (_, &ExprKind::AddrOf(BorrowKind::Ref, _, ref r)) => { + (_, &ExprKind::AddrOf(BorrowKind::Ref, _, r)) => { let rty = cx.typeck_results().expr_ty(r); let rcpy = is_copy(cx, rty); if (requires_ref || rcpy) diff --git a/clippy_lints/src/erasing_op.rs b/clippy_lints/src/erasing_op.rs index 59602616781..f95ca86a2d0 100644 --- a/clippy_lints/src/erasing_op.rs +++ b/clippy_lints/src/erasing_op.rs @@ -34,7 +34,7 @@ impl<'tcx> LateLintPass<'tcx> for ErasingOp { if e.span.from_expansion() { return; } - if let ExprKind::Binary(ref cmp, ref left, ref right) = e.kind { + if let ExprKind::Binary(ref cmp, left, right) = e.kind { match cmp.node { BinOpKind::Mul | BinOpKind::BitAnd => { check(cx, left, e.span); diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 99253555a95..2f1aa53236d 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -87,7 +87,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { } fn check_closure(cx: &LateContext<'_>, expr: &Expr<'_>) { - if let ExprKind::Closure(_, ref decl, eid, _, _) = expr.kind { + if let ExprKind::Closure(_, decl, eid, _, _) = expr.kind { let body = cx.tcx.hir().body(eid); let ex = &body.value; @@ -109,7 +109,7 @@ fn check_closure(cx: &LateContext<'_>, expr: &Expr<'_>) { } if_chain!( - if let ExprKind::Call(ref caller, ref args) = ex.kind; + if let ExprKind::Call(caller, args) = ex.kind; if let ExprKind::Path(_) = caller.kind; @@ -142,7 +142,7 @@ fn check_closure(cx: &LateContext<'_>, expr: &Expr<'_>) { ); if_chain!( - if let ExprKind::MethodCall(ref path, _, ref args, _) = ex.kind; + if let ExprKind::MethodCall(path, _, args, _) = ex.kind; // Not the same number of arguments, there is no way the closure is the same as the function return; if args.len() == decl.inputs.len(); @@ -178,7 +178,7 @@ fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: def_id::DefId, self_a let actual_type_of_self = &cx.typeck_results().node_type(self_arg.hir_id); if let Some(trait_id) = cx.tcx.trait_of_item(method_def_id) { - if match_borrow_depth(expected_type_of_self, &actual_type_of_self) + if match_borrow_depth(expected_type_of_self, actual_type_of_self) && implements_trait(cx, actual_type_of_self, trait_id, &[]) { return Some(cx.tcx.def_path_str(trait_id)); @@ -187,8 +187,8 @@ fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: def_id::DefId, self_a cx.tcx.impl_of_method(method_def_id).and_then(|_| { //a type may implicitly implement other type's methods (e.g. Deref) - if match_types(expected_type_of_self, &actual_type_of_self) { - return Some(get_type_name(cx, &actual_type_of_self)); + if match_types(expected_type_of_self, actual_type_of_self) { + return Some(get_type_name(cx, actual_type_of_self)); } None }) @@ -196,7 +196,7 @@ fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: def_id::DefId, self_a fn match_borrow_depth(lhs: Ty<'_>, rhs: Ty<'_>) -> bool { match (&lhs.kind(), &rhs.kind()) { - (ty::Ref(_, t1, mut1), ty::Ref(_, t2, mut2)) => mut1 == mut2 && match_borrow_depth(&t1, &t2), + (ty::Ref(_, t1, mut1), ty::Ref(_, t2, mut2)) => mut1 == mut2 && match_borrow_depth(t1, t2), (l, r) => !matches!((l, r), (ty::Ref(_, _, _), _) | (_, ty::Ref(_, _, _))), } } @@ -218,7 +218,7 @@ fn match_types(lhs: Ty<'_>, rhs: Ty<'_>) -> bool { fn get_type_name(cx: &LateContext<'_>, ty: Ty<'_>) -> String { match ty.kind() { ty::Adt(t, _) => cx.tcx.def_path_str(t.did), - ty::Ref(_, r, _) => get_type_name(cx, &r), + ty::Ref(_, r, _) => get_type_name(cx, r), _ => ty.to_string(), } } @@ -230,7 +230,7 @@ fn compare_inputs( for (closure_input, function_arg) in closure_inputs.zip(call_args) { if let PatKind::Binding(_, _, ident, _) = closure_input.pat.kind { // XXXManishearth Should I be checking the binding mode here? - if let ExprKind::Path(QPath::Resolved(None, ref p)) = function_arg.kind { + if let ExprKind::Path(QPath::Resolved(None, p)) = function_arg.kind { if p.segments.len() != 1 { // If it's a proper path, it can't be a local variable return false; diff --git a/clippy_lints/src/eval_order_dependence.rs b/clippy_lints/src/eval_order_dependence.rs index ea33a4d98fd..762f64fe37a 100644 --- a/clippy_lints/src/eval_order_dependence.rs +++ b/clippy_lints/src/eval_order_dependence.rs @@ -71,7 +71,7 @@ impl<'tcx> LateLintPass<'tcx> for EvalOrderDependence { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // Find a write to a local variable. match expr.kind { - ExprKind::Assign(ref lhs, ..) | ExprKind::AssignOp(_, ref lhs, _) => { + ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) => { if let Some(var) = path_to_local(lhs) { let mut visitor = ReadVisitor { cx, @@ -87,12 +87,12 @@ impl<'tcx> LateLintPass<'tcx> for EvalOrderDependence { } fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { match stmt.kind { - StmtKind::Local(ref local) => { - if let Local { init: Some(ref e), .. } = **local { + StmtKind::Local(local) => { + if let Local { init: Some(e), .. } = local { DivergenceVisitor { cx }.visit_expr(e); } }, - StmtKind::Expr(ref e) | StmtKind::Semi(ref e) => DivergenceVisitor { cx }.maybe_walk_expr(e), + StmtKind::Expr(e) | StmtKind::Semi(e) => DivergenceVisitor { cx }.maybe_walk_expr(e), StmtKind::Item(..) => {}, } } @@ -106,7 +106,7 @@ impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> { fn maybe_walk_expr(&mut self, e: &'tcx Expr<'_>) { match e.kind { ExprKind::Closure(..) => {}, - ExprKind::Match(ref e, arms, _) => { + ExprKind::Match(e, arms, _) => { self.visit_expr(e); for arm in arms { if let Some(Guard::If(if_expr)) = arm.guard { @@ -130,7 +130,7 @@ impl<'a, 'tcx> Visitor<'tcx> for DivergenceVisitor<'a, 'tcx> { fn visit_expr(&mut self, e: &'tcx Expr<'_>) { match e.kind { ExprKind::Continue(_) | ExprKind::Break(_, _) | ExprKind::Ret(_) => self.report_diverging_sub_expr(e), - ExprKind::Call(ref func, _) => { + ExprKind::Call(func, _) => { let typ = self.cx.typeck_results().expr_ty(func); match typ.kind() { ty::FnDef(..) | ty::FnPtr(_) => { @@ -266,10 +266,10 @@ fn check_expr<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, expr: &'tcx Expr<'_>) - fn check_stmt<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, stmt: &'tcx Stmt<'_>) -> StopEarly { match stmt.kind { - StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => check_expr(vis, expr), + StmtKind::Expr(expr) | StmtKind::Semi(expr) => check_expr(vis, expr), // If the declaration is of a local variable, check its initializer // expression if it has one. Otherwise, keep going. - StmtKind::Local(ref local) => local + StmtKind::Local(local) => local .init .as_ref() .map_or(StopEarly::KeepGoing, |expr| check_expr(vis, expr)), @@ -343,7 +343,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ReadVisitor<'a, 'tcx> { /// Returns `true` if `expr` is the LHS of an assignment, like `expr = ...`. fn is_in_assignment_position(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { if let Some(parent) = get_parent_expr(cx, expr) { - if let ExprKind::Assign(ref lhs, ..) = parent.kind { + if let ExprKind::Assign(lhs, ..) = parent.kind { return lhs.hir_id == expr.hir_id; } } diff --git a/clippy_lints/src/exit.rs b/clippy_lints/src/exit.rs index 84fac998662..16246e548b6 100644 --- a/clippy_lints/src/exit.rs +++ b/clippy_lints/src/exit.rs @@ -28,7 +28,7 @@ declare_lint_pass!(Exit => [EXIT]); impl<'tcx> LateLintPass<'tcx> for Exit { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { if_chain! { - if let ExprKind::Call(ref path_expr, ref _args) = e.kind; + if let ExprKind::Call(path_expr, _args) = e.kind; if let ExprKind::Path(ref path) = path_expr.kind; if let Some(def_id) = cx.qpath_res(path, path_expr.hir_id).opt_def_id(); if match_def_path(cx, def_id, &paths::EXIT); diff --git a/clippy_lints/src/explicit_write.rs b/clippy_lints/src/explicit_write.rs index 4146b7baa10..da4936ff25b 100644 --- a/clippy_lints/src/explicit_write.rs +++ b/clippy_lints/src/explicit_write.rs @@ -34,11 +34,11 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { // match call to unwrap - if let ExprKind::MethodCall(ref unwrap_fun, _, ref unwrap_args, _) = expr.kind; + if let ExprKind::MethodCall(unwrap_fun, _, unwrap_args, _) = expr.kind; if unwrap_fun.ident.name == sym::unwrap; // match call to write_fmt if !unwrap_args.is_empty(); - if let ExprKind::MethodCall(ref write_fun, _, write_args, _) = + if let ExprKind::MethodCall(write_fun, _, write_args, _) = unwrap_args[0].kind; if write_fun.ident.name == sym!(write_fmt); // match calls to std::io::stdout() / std::io::stderr () @@ -135,10 +135,10 @@ fn write_output_string(write_args: &[Expr<'_>]) -> Option { if_chain! { // Obtain the string that should be printed if write_args.len() > 1; - if let ExprKind::Call(_, ref output_args) = write_args[1].kind; + if let ExprKind::Call(_, output_args) = write_args[1].kind; if !output_args.is_empty(); - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref output_string_expr) = output_args[0].kind; - if let ExprKind::Array(ref string_exprs) = output_string_expr.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, _, output_string_expr) = output_args[0].kind; + if let ExprKind::Array(string_exprs) = output_string_expr.kind; // we only want to provide an automatic suggestion for simple (non-format) strings if string_exprs.len() == 1; if let ExprKind::Lit(ref lit) = string_exprs[0].kind; diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs index 52a5a7acf0d..2937fcb9ca0 100644 --- a/clippy_lints/src/fallible_impl_from.rs +++ b/clippy_lints/src/fallible_impl_from.rs @@ -81,8 +81,8 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[h fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { // check for `begin_panic` if_chain! { - if let ExprKind::Call(ref func_expr, _) = expr.kind; - if let ExprKind::Path(QPath::Resolved(_, ref path)) = func_expr.kind; + if let ExprKind::Call(func_expr, _) = expr.kind; + if let ExprKind::Path(QPath::Resolved(_, path)) = func_expr.kind; if let Some(path_def_id) = path.res.opt_def_id(); if match_panic_def_id(self.lcx, path_def_id); if is_expn_of(expr.span, "unreachable").is_none(); diff --git a/clippy_lints/src/float_equality_without_abs.rs b/clippy_lints/src/float_equality_without_abs.rs index 0c59db360f9..b5ebe5f90ba 100644 --- a/clippy_lints/src/float_equality_without_abs.rs +++ b/clippy_lints/src/float_equality_without_abs.rs @@ -48,7 +48,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatEqualityWithoutAbs { let rhs; // check if expr is a binary expression with a lt or gt operator - if let ExprKind::Binary(op, ref left, ref right) = expr.kind { + if let ExprKind::Binary(op, left, right) = expr.kind { match op.node { BinOpKind::Lt => { lhs = left; @@ -88,8 +88,8 @@ impl<'tcx> LateLintPass<'tcx> for FloatEqualityWithoutAbs { if let ty::Float(_) = t_val_r.kind(); then { - let sug_l = sugg::Sugg::hir(cx, &val_l, ".."); - let sug_r = sugg::Sugg::hir(cx, &val_r, ".."); + let sug_l = sugg::Sugg::hir(cx, val_l, ".."); + let sug_r = sugg::Sugg::hir(cx, val_r, ".."); // format the suggestion let suggestion = format!("{}.abs()", sugg::make_assoc(AssocOp::Subtract, &sug_l, &sug_r).maybe_par()); // spans the lint diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 0b5f0379ce6..e0b687b0205 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -131,7 +131,7 @@ fn prepare_receiver_sugg<'a>(cx: &LateContext<'_>, mut expr: &'a Expr<'a>) -> Su let mut suggestion = Sugg::hir(cx, expr, ".."); if let ExprKind::Unary(UnOp::Neg, inner_expr) = &expr.kind { - expr = &inner_expr; + expr = inner_expr; } if_chain! { @@ -313,8 +313,8 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { Spanned { node: BinOpKind::Add, .. }, - ref lhs, - ref rhs, + lhs, + rhs, ) = parent.kind { let other_addend = if lhs.hir_id == expr.hir_id { rhs } else { lhs }; @@ -329,7 +329,7 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { "{}.mul_add({}, {})", Sugg::hir(cx, &args[0], ".."), Sugg::hir(cx, &args[0], ".."), - Sugg::hir(cx, &other_addend, ".."), + Sugg::hir(cx, other_addend, ".."), ), Applicability::MachineApplicable, ); @@ -356,18 +356,18 @@ fn detect_hypot(cx: &LateContext<'_>, args: &[Expr<'_>]) -> Option { Spanned { node: BinOpKind::Add, .. }, - ref add_lhs, - ref add_rhs, + add_lhs, + add_rhs, ) = args[0].kind { // check if expression of the form x * x + y * y if_chain! { - if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, ref lmul_lhs, ref lmul_rhs) = add_lhs.kind; - if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, ref rmul_lhs, ref rmul_rhs) = add_rhs.kind; + if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, lmul_lhs, lmul_rhs) = add_lhs.kind; + if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, rmul_lhs, rmul_rhs) = add_rhs.kind; if eq_expr_value(cx, lmul_lhs, lmul_rhs); if eq_expr_value(cx, rmul_lhs, rmul_rhs); then { - return Some(format!("{}.hypot({})", Sugg::hir(cx, &lmul_lhs, ".."), Sugg::hir(cx, &rmul_lhs, ".."))); + return Some(format!("{}.hypot({})", Sugg::hir(cx, lmul_lhs, ".."), Sugg::hir(cx, rmul_lhs, ".."))); } } @@ -376,13 +376,13 @@ fn detect_hypot(cx: &LateContext<'_>, args: &[Expr<'_>]) -> Option { if let ExprKind::MethodCall( PathSegment { ident: lmethod_name, .. }, ref _lspan, - ref largs, + largs, _ ) = add_lhs.kind; if let ExprKind::MethodCall( PathSegment { ident: rmethod_name, .. }, ref _rspan, - ref rargs, + rargs, _ ) = add_rhs.kind; if lmethod_name.as_str() == "powi" && rmethod_name.as_str() == "powi"; @@ -416,11 +416,11 @@ fn check_hypot(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { // and suggest usage of `x.exp_m1() - (y - 1)` instead fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { - if let ExprKind::Binary(Spanned { node: BinOpKind::Sub, .. }, ref lhs, ref rhs) = expr.kind; + if let ExprKind::Binary(Spanned { node: BinOpKind::Sub, .. }, lhs, rhs) = expr.kind; if cx.typeck_results().expr_ty(lhs).is_floating_point(); if let Some((value, _)) = constant(cx, cx.typeck_results(), rhs); if F32(1.0) == value || F64(1.0) == value; - if let ExprKind::MethodCall(ref path, _, ref method_args, _) = lhs.kind; + if let ExprKind::MethodCall(path, _, method_args, _) = lhs.kind; if cx.typeck_results().expr_ty(&method_args[0]).is_floating_point(); if path.ident.name.as_str() == "exp"; then { @@ -442,7 +442,7 @@ fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) { fn is_float_mul_expr<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(&'a Expr<'a>, &'a Expr<'a>)> { if_chain! { - if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, ref lhs, ref rhs) = &expr.kind; + if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, lhs, rhs) = &expr.kind; if cx.typeck_results().expr_ty(lhs).is_floating_point(); if cx.typeck_results().expr_ty(rhs).is_floating_point(); then { @@ -604,8 +604,8 @@ fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) { fn are_same_base_logs(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>) -> bool { if_chain! { - if let ExprKind::MethodCall(PathSegment { ident: method_name_a, .. }, _, ref args_a, _) = expr_a.kind; - if let ExprKind::MethodCall(PathSegment { ident: method_name_b, .. }, _, ref args_b, _) = expr_b.kind; + if let ExprKind::MethodCall(PathSegment { ident: method_name_a, .. }, _, args_a, _) = expr_a.kind; + if let ExprKind::MethodCall(PathSegment { ident: method_name_b, .. }, _, args_b, _) = expr_b.kind; then { return method_name_a.as_str() == method_name_b.as_str() && args_a.len() == args_b.len() && @@ -630,8 +630,8 @@ fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) { rhs, ) = &expr.kind; if are_same_base_logs(cx, lhs, rhs); - if let ExprKind::MethodCall(_, _, ref largs, _) = lhs.kind; - if let ExprKind::MethodCall(_, _, ref rargs, _) = rhs.kind; + if let ExprKind::MethodCall(_, _, largs, _) = lhs.kind; + if let ExprKind::MethodCall(_, _, rargs, _) = rhs.kind; then { span_lint_and_sugg( cx, @@ -675,7 +675,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) { expr.span, "conversion to degrees can be done more accurately", "consider using", - format!("{}.to_degrees()", Sugg::hir(cx, &mul_lhs, "..")), + format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, "..")), Applicability::MachineApplicable, ); } else if @@ -688,7 +688,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) { expr.span, "conversion to radians can be done more accurately", "consider using", - format!("{}.to_radians()", Sugg::hir(cx, &mul_lhs, "..")), + format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, "..")), Applicability::MachineApplicable, ); } @@ -698,7 +698,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) { impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::MethodCall(ref path, _, args, _) = &expr.kind { + if let ExprKind::MethodCall(path, _, args, _) = &expr.kind { let recv_ty = cx.typeck_results().expr_ty(&args[0]); if recv_ty.is_floating_point() { diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index a33f987b423..4729abbd8e3 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -78,8 +78,8 @@ fn span_useless_format(cx: &T, span: Span, help: &str, mut sugg: fn on_argumentv1_new<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>]) -> Option { if_chain! { - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref format_args) = expr.kind; - if let ExprKind::Array(ref elems) = arms[0].body.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, _, format_args) = expr.kind; + if let ExprKind::Array(elems) = arms[0].body.kind; if elems.len() == 1; if let Some(args) = match_function_call(cx, &elems[0], &paths::FMT_ARGUMENTV1_NEW); // matches `core::fmt::Display::fmt` @@ -88,10 +88,10 @@ fn on_argumentv1_new<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arms: & if let Some(did) = cx.qpath_res(qpath, args[1].hir_id).opt_def_id(); if match_def_path(cx, did, &paths::DISPLAY_FMT_METHOD); // check `(arg0,)` in match block - if let PatKind::Tuple(ref pats, None) = arms[0].pat.kind; + if let PatKind::Tuple(pats, None) = arms[0].pat.kind; if pats.len() == 1; then { - let ty = cx.typeck_results().pat_ty(&pats[0]).peel_refs(); + let ty = cx.typeck_results().pat_ty(pats[0]).peel_refs(); if *ty.kind() != rustc_middle::ty::Str && !is_type_diagnostic_item(cx, ty, sym::string_type) { return None; } @@ -101,7 +101,7 @@ fn on_argumentv1_new<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arms: & } } else { let snip = snippet(cx, format_args.span, ""); - if let ExprKind::MethodCall(ref path, _, _, _) = format_args.kind { + if let ExprKind::MethodCall(path, _, _, _) = format_args.kind { if path.ident.name == sym!(to_string) { return Some(format!("{}", snip)); } @@ -120,16 +120,16 @@ fn on_new_v1<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option [], }` if tup.is_empty() { @@ -152,16 +152,16 @@ fn on_new_v1_fmt<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option) -> bool { if_chain! { - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref expr) = expr.kind; - if let ExprKind::Array(ref exprs) = expr.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, _, expr) = expr.kind; + if let ExprKind::Array(exprs) = expr.kind; if exprs.len() == 1; // struct `core::fmt::rt::v1::Argument` - if let ExprKind::Struct(_, ref fields, _) = exprs[0].kind; + if let ExprKind::Struct(_, fields, _) = exprs[0].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(_, ref fields, _) = format_field.expr.kind; + 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; diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index 9e943c647fe..db5fe90b663 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -25,12 +25,12 @@ pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { let is_public = cx.access_levels.is_exported(item.hir_id()); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); if let Some(attr) = attr { - check_needless_must_use(cx, &sig.decl, item.hir_id(), item.span, fn_header_span, attr); + check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr); return; } else if is_public && !is_proc_macro(cx.sess(), attrs) && attr_by_name(attrs, "no_mangle").is_none() { check_must_use_candidate( cx, - &sig.decl, + sig.decl, cx.tcx.hir().body(*body_id), item.span, item.hir_id(), @@ -48,11 +48,11 @@ pub(super) fn check_impl_item(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem< let attrs = cx.tcx.hir().attrs(item.hir_id()); let attr = must_use_attr(attrs); if let Some(attr) = attr { - check_needless_must_use(cx, &sig.decl, item.hir_id(), item.span, fn_header_span, attr); + check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr); } else if is_public && !is_proc_macro(cx.sess(), attrs) && trait_ref_of_method(cx, item.hir_id()).is_none() { check_must_use_candidate( cx, - &sig.decl, + sig.decl, cx.tcx.hir().body(*body_id), item.span, item.hir_id(), @@ -71,13 +71,13 @@ pub(super) fn check_trait_item(cx: &LateContext<'tcx>, item: &'tcx hir::TraitIte let attrs = cx.tcx.hir().attrs(item.hir_id()); let attr = must_use_attr(attrs); if let Some(attr) = attr { - check_needless_must_use(cx, &sig.decl, item.hir_id(), item.span, fn_header_span, attr); + check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr); } else if let hir::TraitFn::Provided(eid) = *eid { let body = cx.tcx.hir().body(eid); if attr.is_none() && is_public && !is_proc_macro(cx.sess(), attrs) { check_must_use_candidate( cx, - &sig.decl, + sig.decl, body, item.span, item.hir_id(), @@ -160,8 +160,8 @@ fn check_must_use_candidate<'tcx>( fn returns_unit(decl: &hir::FnDecl<'_>) -> bool { match decl.output { hir::FnRetTy::DefaultReturn(_) => true, - hir::FnRetTy::Return(ref ty) => match ty.kind { - hir::TyKind::Tup(ref tys) => tys.is_empty(), + hir::FnRetTy::Return(ty) => match ty.kind { + hir::TyKind::Tup(tys) => tys.is_empty(), hir::TyKind::Never => true, _ => false, }, @@ -170,7 +170,7 @@ fn returns_unit(decl: &hir::FnDecl<'_>) -> bool { fn has_mutable_arg(cx: &LateContext<'_>, body: &hir::Body<'_>) -> bool { let mut tys = DefIdSet::default(); - body.params.iter().any(|param| is_mutable_pat(cx, ¶m.pat, &mut tys)) + body.params.iter().any(|param| is_mutable_pat(cx, param.pat, &mut tys)) } fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut DefIdSet) -> bool { @@ -178,7 +178,7 @@ fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut DefIdSet) return false; // ignore `_` patterns } if cx.tcx.has_typeck_results(pat.hir_id.owner.to_def_id()) { - is_mutable_ty(cx, &cx.tcx.typeck(pat.hir_id.owner).pat_ty(pat), pat.span, tys) + is_mutable_ty(cx, cx.tcx.typeck(pat.hir_id.owner).pat_ty(pat), pat.span, tys) } else { false } @@ -190,12 +190,12 @@ fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &m match *ty.kind() { // primitive types are never mutable ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false, - ty::Adt(ref adt, ref substs) => { + ty::Adt(adt, substs) => { tys.insert(adt.did) && !ty.is_freeze(cx.tcx.at(span), cx.param_env) || KNOWN_WRAPPER_TYS.iter().any(|path| match_def_path(cx, adt.did, path)) && substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys)) }, - ty::Tuple(ref substs) => substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys)), + ty::Tuple(substs) => substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys)), ty::Array(ty, _) | ty::Slice(ty) => is_mutable_ty(cx, ty, span, tys), ty::RawPtr(ty::TypeAndMut { ty, mutbl }) | ty::Ref(_, ty, mutbl) => { mutbl == hir::Mutability::Mut || is_mutable_ty(cx, ty, span, tys) @@ -239,7 +239,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for StaticMutVisitor<'a, 'tcx> { tys.clear(); } }, - Assign(ref target, ..) | AssignOp(_, ref target, _) | AddrOf(_, hir::Mutability::Mut, ref target) => { + Assign(target, ..) | AssignOp(_, target, _) | AddrOf(_, hir::Mutability::Mut, target) => { self.mutates_static |= is_mutated_static(target) }, _ => {}, @@ -257,7 +257,7 @@ fn is_mutated_static(e: &hir::Expr<'_>) -> bool { match e.kind { Path(QPath::Resolved(_, path)) => !matches!(path.res, Res::Local(_)), Path(_) => true, - Field(ref inner, _) | Index(ref inner, _) => is_mutated_static(inner), + Field(inner, _) | Index(inner, _) => is_mutated_static(inner), _ => false, } } diff --git a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs index cc69f4aed0c..b8ea6990866 100644 --- a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs +++ b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs @@ -27,7 +27,7 @@ pub(super) fn check_fn( pub(super) fn check_trait_item(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { if let hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(eid)) = item.kind { let body = cx.tcx.hir().body(eid); - check_raw_ptr(cx, sig.header.unsafety, &sig.decl, body, item.hir_id()); + check_raw_ptr(cx, sig.header.unsafety, sig.decl, body, item.hir_id()); } } @@ -77,7 +77,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for DerefVisitor<'a, 'tcx> { fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { match expr.kind { - hir::ExprKind::Call(ref f, args) => { + hir::ExprKind::Call(f, args) => { let ty = self.typeck_results.expr_ty(f); if type_is_unsafe_function(self.cx, ty) { @@ -96,7 +96,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for DerefVisitor<'a, 'tcx> { } } }, - hir::ExprKind::Unary(hir::UnOp::Deref, ref ptr) => self.check_arg(ptr), + hir::ExprKind::Unary(hir::UnOp::Deref, ptr) => self.check_arg(ptr), _ => (), } diff --git a/clippy_lints/src/functions/result_unit_err.rs b/clippy_lints/src/functions/result_unit_err.rs index 3d1092ce21f..c073f312d38 100644 --- a/clippy_lints/src/functions/result_unit_err.rs +++ b/clippy_lints/src/functions/result_unit_err.rs @@ -18,7 +18,7 @@ pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { let is_public = cx.access_levels.is_exported(item.hir_id()); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); if is_public { - check_result_unit_err(cx, &sig.decl, item.span, fn_header_span); + check_result_unit_err(cx, sig.decl, item.span, fn_header_span); } } } @@ -28,7 +28,7 @@ pub(super) fn check_impl_item(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem< let is_public = cx.access_levels.is_exported(item.hir_id()); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); if is_public && trait_ref_of_method(cx, item.hir_id()).is_none() { - check_result_unit_err(cx, &sig.decl, item.span, fn_header_span); + check_result_unit_err(cx, sig.decl, item.span, fn_header_span); } } } @@ -38,7 +38,7 @@ pub(super) fn check_trait_item(cx: &LateContext<'tcx>, item: &'tcx hir::TraitIte let is_public = cx.access_levels.is_exported(item.hir_id()); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); if is_public { - check_result_unit_err(cx, &sig.decl, item.span, fn_header_span); + check_result_unit_err(cx, sig.decl, item.span, fn_header_span); } } } @@ -46,7 +46,7 @@ pub(super) fn check_trait_item(cx: &LateContext<'tcx>, item: &'tcx hir::TraitIte fn check_result_unit_err(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, item_span: Span, fn_header_span: Span) { if_chain! { if !in_external_macro(cx.sess(), item_span); - if let hir::FnRetTy::Return(ref ty) = decl.output; + if let hir::FnRetTy::Return(ty) = decl.output; let ty = hir_ty_to_ty(cx.tcx, ty); if is_type_diagnostic_item(cx, ty, sym::result_type); if let ty::Adt(_, substs) = ty.kind(); diff --git a/clippy_lints/src/functions/too_many_arguments.rs b/clippy_lints/src/functions/too_many_arguments.rs index 62b1e6bd7ca..63a14d8d4cd 100644 --- a/clippy_lints/src/functions/too_many_arguments.rs +++ b/clippy_lints/src/functions/too_many_arguments.rs @@ -49,7 +49,7 @@ pub(super) fn check_trait_item( if sig.header.abi == Abi::Rust { check_arg_number( cx, - &sig.decl, + sig.decl, item.span.with_hi(sig.decl.output.span().hi()), too_many_arguments_threshold, ); diff --git a/clippy_lints/src/get_last_with_len.rs b/clippy_lints/src/get_last_with_len.rs index cbcef567c53..3707e792177 100644 --- a/clippy_lints/src/get_last_with_len.rs +++ b/clippy_lints/src/get_last_with_len.rs @@ -51,7 +51,7 @@ impl<'tcx> LateLintPass<'tcx> for GetLastWithLen { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { // Is a method call - if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind; + if let ExprKind::MethodCall(path, _, args, _) = expr.kind; // Method name is "get" if path.ident.name == sym!(get); diff --git a/clippy_lints/src/identity_op.rs b/clippy_lints/src/identity_op.rs index 8bed5e1bf64..366b3b46a8a 100644 --- a/clippy_lints/src/identity_op.rs +++ b/clippy_lints/src/identity_op.rs @@ -35,7 +35,7 @@ impl<'tcx> LateLintPass<'tcx> for IdentityOp { if e.span.from_expansion() { return; } - if let ExprKind::Binary(cmp, ref left, ref right) = e.kind { + if let ExprKind::Binary(cmp, left, right) = e.kind { if is_allowed(cx, cmp, left, right) { return; } diff --git a/clippy_lints/src/if_let_mutex.rs b/clippy_lints/src/if_let_mutex.rs index 56dfcc6a506..f661f7ede82 100644 --- a/clippy_lints/src/if_let_mutex.rs +++ b/clippy_lints/src/if_let_mutex.rs @@ -55,8 +55,8 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex { cx, }; if let ExprKind::Match( - ref op, - ref arms, + op, + arms, MatchSource::IfLetDesugar { contains_else_clause: true, }, @@ -64,7 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex { { op_visit.visit_expr(op); if op_visit.mutex_lock_called { - for arm in *arms { + for arm in arms { arm_visit.visit_arm(arm); } diff --git a/clippy_lints/src/if_let_some_result.rs b/clippy_lints/src/if_let_some_result.rs index 6e9280c8c7e..611da3744ee 100644 --- a/clippy_lints/src/if_let_some_result.rs +++ b/clippy_lints/src/if_let_some_result.rs @@ -44,9 +44,9 @@ declare_lint_pass!(OkIfLet => [IF_LET_SOME_RESULT]); impl<'tcx> LateLintPass<'tcx> for OkIfLet { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { //begin checking variables - if let ExprKind::Match(ref op, ref body, MatchSource::IfLetDesugar { .. }) = expr.kind; //test if expr is if let - if let ExprKind::MethodCall(_, ok_span, ref result_types, _) = op.kind; //check is expr.ok() has type Result.ok(, _) - if let PatKind::TupleStruct(QPath::Resolved(_, ref x), ref y, _) = body[0].pat.kind; //get operation + if let ExprKind::Match(op, body, MatchSource::IfLetDesugar { .. }) = expr.kind; //test if expr is if let + if let ExprKind::MethodCall(_, ok_span, result_types, _) = op.kind; //check is expr.ok() has type Result.ok(, _) + if let PatKind::TupleStruct(QPath::Resolved(_, x), y, _) = body[0].pat.kind; //get operation if method_chain_args(op, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized; if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&result_types[0]), sym::result_type); if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some"; diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index 0b5bf060d4c..ee16519692f 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -72,15 +72,15 @@ impl LateLintPass<'_> for IfThenSomeElseNone { } if_chain! { - if let ExprKind::If(ref cond, ref then, Some(ref els)) = expr.kind; - if let ExprKind::Block(ref then_block, _) = then.kind; - if let Some(ref then_expr) = then_block.expr; - if let ExprKind::Call(ref then_call, [then_arg]) = then_expr.kind; + if let ExprKind::If(cond, then, Some(els)) = expr.kind; + if let ExprKind::Block(then_block, _) = then.kind; + if let Some(then_expr) = then_block.expr; + if let ExprKind::Call(then_call, [then_arg]) = then_expr.kind; if let ExprKind::Path(ref then_call_qpath) = then_call.kind; if match_qpath(then_call_qpath, &clippy_utils::paths::OPTION_SOME); - if let ExprKind::Block(ref els_block, _) = els.kind; + if let ExprKind::Block(els_block, _) = els.kind; if els_block.stmts.is_empty(); - if let Some(ref els_expr) = els_block.expr; + if let Some(els_expr) = els_block.expr; if let ExprKind::Path(ref els_call_qpath) = els_expr.kind; if match_qpath(els_call_qpath, &clippy_utils::paths::OPTION_NONE); then { diff --git a/clippy_lints/src/implicit_hasher.rs b/clippy_lints/src/implicit_hasher.rs index 0b748b4d72d..77a38544edc 100644 --- a/clippy_lints/src/implicit_hasher.rs +++ b/clippy_lints/src/implicit_hasher.rs @@ -207,7 +207,7 @@ enum ImplicitHasherType<'tcx> { impl<'tcx> ImplicitHasherType<'tcx> { /// Checks that `ty` is a target type without a `BuildHasher`. fn new(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'_>) -> Option { - if let TyKind::Path(QPath::Resolved(None, ref path)) = hir_ty.kind { + if let TyKind::Path(QPath::Resolved(None, path)) = hir_ty.kind { let params: Vec<_> = path .segments .last() @@ -330,8 +330,8 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 't fn visit_expr(&mut self, e: &'tcx Expr<'_>) { if_chain! { - if let ExprKind::Call(ref fun, ref args) = e.kind; - if let ExprKind::Path(QPath::TypeRelative(ref ty, ref method)) = fun.kind; + if let ExprKind::Call(fun, args) = e.kind; + if let ExprKind::Path(QPath::TypeRelative(ty, method)) = fun.kind; if let TyKind::Path(QPath::Resolved(None, ty_path)) = ty.kind; then { if !TyS::same_type(self.target.ty(), self.maybe_typeck_results.unwrap().expr_ty(e)) { diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs index 6863645a92d..6b379b0d59b 100644 --- a/clippy_lints/src/implicit_return.rs +++ b/clippy_lints/src/implicit_return.rs @@ -100,10 +100,10 @@ fn expr_match(cx: &LateContext<'_>, expr: &Expr<'_>) { if check_all_arms { for arm in arms { - expr_match(cx, &arm.body); + expr_match(cx, arm.body); } } else { - expr_match(cx, &arms.first().expect("`if let` doesn't have a single arm").body); + expr_match(cx, arms.first().expect("`if let` doesn't have a single arm").body); } }, // skip if it already has a return statement diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index 5207c628987..cba3183e869 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -46,21 +46,21 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { if let ExprKind::If(cond, then, None) = &expr.kind; // Check if the conditional expression is a binary operation - if let ExprKind::Binary(ref cond_op, ref cond_left, ref cond_right) = cond.kind; + if let ExprKind::Binary(ref cond_op, cond_left, cond_right) = cond.kind; // Ensure that the binary operator is >, != and < if BinOpKind::Ne == cond_op.node || BinOpKind::Gt == cond_op.node || BinOpKind::Lt == cond_op.node; // Check if the true condition block has only one statement - if let ExprKind::Block(ref block, _) = then.kind; + if let ExprKind::Block(block, _) = then.kind; if block.stmts.len() == 1 && block.expr.is_none(); // Check if assign operation is done - if let StmtKind::Semi(ref e) = block.stmts[0].kind; + if let StmtKind::Semi(e) = block.stmts[0].kind; if let Some(target) = subtracts_one(cx, e); // Extracting out the variable name - if let ExprKind::Path(QPath::Resolved(_, ref ares_path)) = target.kind; + if let ExprKind::Path(QPath::Resolved(_, ares_path)) = target.kind; then { // Handle symmetric conditions in the if statement @@ -104,7 +104,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { print_lint_and_sugg(cx, &var_name, expr); }; }, - ExprKind::Call(ref func, _) => { + ExprKind::Call(func, _) => { if let ExprKind::Path(ref cond_num_path) = func.kind { if INT_TYPES.iter().any(|int_type| match_qpath(cond_num_path, &[int_type, "min_value"])) { print_lint_and_sugg(cx, &var_name, expr); @@ -120,7 +120,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &Expr<'a>) -> Option<&'a Expr<'a>> { match expr.kind { - ExprKind::AssignOp(ref op1, ref target, ref value) => { + ExprKind::AssignOp(ref op1, target, value) => { if_chain! { if BinOpKind::Sub == op1.node; // Check if literal being subtracted is one @@ -133,9 +133,9 @@ fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &Expr<'a>) -> Option<&'a Expr<' } } }, - ExprKind::Assign(ref target, ref value, _) => { + ExprKind::Assign(target, value, _) => { if_chain! { - if let ExprKind::Binary(ref op1, ref left1, ref right1) = value.kind; + if let ExprKind::Binary(ref op1, left1, right1) = value.kind; if BinOpKind::Sub == op1.node; if SpanlessEq::new(cx).eq_expr(left1, target); diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs index 94d39019608..1c54599abc4 100644 --- a/clippy_lints/src/indexing_slicing.rs +++ b/clippy_lints/src/indexing_slicing.rs @@ -88,7 +88,7 @@ declare_lint_pass!(IndexingSlicing => [INDEXING_SLICING, OUT_OF_BOUNDS_INDEXING] impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Index(ref array, ref index) = &expr.kind { + if let ExprKind::Index(array, index) = &expr.kind { let ty = cx.typeck_results().expr_ty(array).peel_refs(); if let Some(range) = higher::range(index) { // Ranged indexes, i.e., &x[n..m], &x[n..], &x[..n] and &x[..] diff --git a/clippy_lints/src/infinite_iter.rs b/clippy_lints/src/infinite_iter.rs index fb35bc1e780..bbb4ddc613a 100644 --- a/clippy_lints/src/infinite_iter.rs +++ b/clippy_lints/src/infinite_iter.rs @@ -139,7 +139,7 @@ const HEURISTICS: [(&str, usize, Heuristic, Finiteness); 19] = [ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { match expr.kind { - ExprKind::MethodCall(ref method, _, ref args, _) => { + ExprKind::MethodCall(method, _, args, _) => { for &(name, len, heuristic, cap) in &HEURISTICS { if method.ident.name.as_str() == name && args.len() == len { return (match heuristic { @@ -159,9 +159,9 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { } Finite }, - ExprKind::Block(ref block, _) => block.expr.as_ref().map_or(Finite, |e| is_infinite(cx, e)), - ExprKind::Box(ref e) | ExprKind::AddrOf(BorrowKind::Ref, _, ref e) => is_infinite(cx, e), - ExprKind::Call(ref path, _) => { + ExprKind::Block(block, _) => block.expr.as_ref().map_or(Finite, |e| is_infinite(cx, e)), + ExprKind::Box(e) | ExprKind::AddrOf(BorrowKind::Ref, _, e) => is_infinite(cx, e), + ExprKind::Call(path, _) => { if let ExprKind::Path(ref qpath) = path.kind { match_qpath(qpath, &paths::REPEAT).into() } else { @@ -215,7 +215,7 @@ const INFINITE_COLLECTORS: [&[&str]; 8] = [ fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { match expr.kind { - ExprKind::MethodCall(ref method, _, ref args, _) => { + ExprKind::MethodCall(method, _, args, _) => { for &(name, len) in &COMPLETING_METHODS { if method.ident.name.as_str() == name && args.len() == len { return is_infinite(cx, &args[0]); @@ -240,7 +240,7 @@ fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { } } }, - ExprKind::Binary(op, ref l, ref r) => { + ExprKind::Binary(op, l, r) => { if op.node.is_comparison() { return is_infinite(cx, l).and(is_infinite(cx, r)).and(MaybeInfinite); } diff --git a/clippy_lints/src/invalid_upcast_comparisons.rs b/clippy_lints/src/invalid_upcast_comparisons.rs index d183fc41f31..c67c02eefa5 100644 --- a/clippy_lints/src/invalid_upcast_comparisons.rs +++ b/clippy_lints/src/invalid_upcast_comparisons.rs @@ -86,7 +86,7 @@ impl Ord for FullInt { } fn numeric_cast_precast_bounds<'a>(cx: &LateContext<'_>, expr: &'a Expr<'_>) -> Option<(FullInt, FullInt)> { - if let ExprKind::Cast(ref cast_exp, _) = expr.kind { + if let ExprKind::Cast(cast_exp, _) = expr.kind { let pre_cast_ty = cx.typeck_results().expr_ty(cast_exp); let cast_ty = cx.typeck_results().expr_ty(expr); // if it's a cast from i32 to u32 wrapping will invalidate all these checks @@ -131,7 +131,7 @@ fn node_as_const_fullint<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> } fn err_upcast_comparison(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, always: bool) { - if let ExprKind::Cast(ref cast_val, _) = expr.kind { + if let ExprKind::Cast(cast_val, _) = expr.kind { span_lint( cx, INVALID_UPCAST_COMPARISONS, @@ -203,7 +203,7 @@ fn upcast_comparison_bounds_err<'tcx>( impl<'tcx> LateLintPass<'tcx> for InvalidUpcastComparisons { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Binary(ref cmp, ref lhs, ref rhs) = expr.kind { + if let ExprKind::Binary(ref cmp, lhs, rhs) = expr.kind { let normalized = comparisons::normalize_comparison(cmp.node, lhs, rhs); let (rel, normalized_lhs, normalized_rhs) = if let Some(val) = normalized { val diff --git a/clippy_lints/src/large_enum_variant.rs b/clippy_lints/src/large_enum_variant.rs index 76584dc1822..f166748d86b 100644 --- a/clippy_lints/src/large_enum_variant.rs +++ b/clippy_lints/src/large_enum_variant.rs @@ -113,7 +113,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { ); if variant.fields.len() == 1 { let span = match def.variants[i].data { - VariantData::Struct(ref fields, ..) | VariantData::Tuple(ref fields, ..) => { + VariantData::Struct(fields, ..) | VariantData::Tuple(fields, ..) => { fields[0].ty.span }, VariantData::Unit(..) => unreachable!(), diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 78152ad9019..bb57adff7be 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -121,7 +121,7 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { return; } - if let ItemKind::Trait(_, _, _, _, ref trait_items) = item.kind { + if let ItemKind::Trait(_, _, _, _, trait_items) = item.kind { check_trait_items(cx, item, trait_items); } } @@ -162,7 +162,7 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { return; } - if let ExprKind::Binary(Spanned { node: cmp, .. }, ref left, ref right) = expr.kind { + if let ExprKind::Binary(Spanned { node: cmp, .. }, left, right) = expr.kind { match cmp { BinOpKind::Eq => { check_cmp(cx, expr.span, left, right, "", 0); // len == 0 @@ -372,8 +372,7 @@ fn check_for_is_empty( } fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_>, op: &str, compare_to: u32) { - if let (&ExprKind::MethodCall(ref method_path, _, ref args, _), &ExprKind::Lit(ref lit)) = (&method.kind, &lit.kind) - { + if let (&ExprKind::MethodCall(method_path, _, args, _), &ExprKind::Lit(ref lit)) = (&method.kind, &lit.kind) { // check if we are in an is_empty() method if let Some(name) = get_item_name(cx, method) { if name.as_str() == "is_empty" { @@ -451,7 +450,7 @@ fn is_empty_string(expr: &Expr<'_>) -> bool { } fn is_empty_array(expr: &Expr<'_>) -> bool { - if let ExprKind::Array(ref arr) = expr.kind { + if let ExprKind::Array(arr) = expr.kind { return arr.is_empty(); } false @@ -480,17 +479,17 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { cx.tcx .associated_items(*imp) .in_definition_order() - .any(|item| is_is_empty(cx, &item)) + .any(|item| is_is_empty(cx, item)) }) } let ty = &cx.typeck_results().expr_ty(expr).peel_refs(); match ty.kind() { - ty::Dynamic(ref tt, ..) => tt.principal().map_or(false, |principal| { + ty::Dynamic(tt, ..) => tt.principal().map_or(false, |principal| { cx.tcx .associated_items(principal.def_id()) .in_definition_order() - .any(|item| is_is_empty(cx, &item)) + .any(|item| is_is_empty(cx, item)) }), ty::Projection(ref proj) => has_is_empty_impl(cx, proj.item_def_id), ty::Adt(id, _) => has_is_empty_impl(cx, id.did), diff --git a/clippy_lints/src/let_if_seq.rs b/clippy_lints/src/let_if_seq.rs index 2d7d9c9befb..67eae4d87bb 100644 --- a/clippy_lints/src/let_if_seq.rs +++ b/clippy_lints/src/let_if_seq.rs @@ -61,13 +61,13 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq { while let Some(stmt) = it.next() { if_chain! { if let Some(expr) = it.peek(); - if let hir::StmtKind::Local(ref local) = stmt.kind; + if let hir::StmtKind::Local(local) = stmt.kind; if let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind; - if let hir::StmtKind::Expr(ref if_) = expr.kind; - if let hir::ExprKind::If(ref cond, ref then, ref else_) = if_.kind; + if let hir::StmtKind::Expr(if_) = expr.kind; + if let hir::ExprKind::If(cond, then, ref else_) = if_.kind; let mut used_visitor = LocalUsedVisitor::new(cx, canonical_id); if !used_visitor.check_expr(cond); - if let hir::ExprKind::Block(ref then, _) = then.kind; + if let hir::ExprKind::Block(then, _) = then.kind; if let Some(value) = check_assign(cx, canonical_id, &*then); if !used_visitor.check_expr(value); then { @@ -79,20 +79,20 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq { ); if has_interior_mutability { return; } - let (default_multi_stmts, default) = if let Some(ref else_) = *else_ { - if let hir::ExprKind::Block(ref else_, _) = else_.kind { + let (default_multi_stmts, default) = if let Some(else_) = *else_ { + if let hir::ExprKind::Block(else_, _) = else_.kind { if let Some(default) = check_assign(cx, canonical_id, else_) { (else_.stmts.len() > 1, default) - } else if let Some(ref default) = local.init { - (true, &**default) + } else if let Some(default) = local.init { + (true, default) } else { continue; } } else { continue; } - } else if let Some(ref default) = local.init { - (false, &**default) + } else if let Some(default) = local.init { + (false, default) } else { continue; }; @@ -144,8 +144,8 @@ fn check_assign<'tcx>( if_chain! { if block.expr.is_none(); if let Some(expr) = block.stmts.iter().last(); - if let hir::StmtKind::Semi(ref expr) = expr.kind; - if let hir::ExprKind::Assign(ref var, ref value, _) = expr.kind; + if let hir::StmtKind::Semi(expr) = expr.kind; + if let hir::ExprKind::Assign(var, value, _) = expr.kind; if path_to_local_id(var, decl); then { let mut v = LocalUsedVisitor::new(cx, decl); diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs index 7e3a76ded2c..17e23781db7 100644 --- a/clippy_lints/src/let_underscore.rs +++ b/clippy_lints/src/let_underscore.rs @@ -116,7 +116,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { if_chain! { if let PatKind::Wild = local.pat.kind; - if let Some(ref init) = local.init; + if let Some(init) = local.init; then { let init_ty = cx.typeck_results().expr_ty(init); let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() { diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 43e89c2475f..cd92b551abd 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -541,482 +541,482 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: // 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, + utils::internal_lints::CLIPPY_LINTS_INTERNAL, #[cfg(feature = "internal-lints")] - &utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS, + utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS, #[cfg(feature = "internal-lints")] - &utils::internal_lints::COMPILER_LINT_FUNCTIONS, + utils::internal_lints::COMPILER_LINT_FUNCTIONS, #[cfg(feature = "internal-lints")] - &utils::internal_lints::DEFAULT_LINT, + utils::internal_lints::DEFAULT_LINT, #[cfg(feature = "internal-lints")] - &utils::internal_lints::IF_CHAIN_STYLE, + utils::internal_lints::IF_CHAIN_STYLE, #[cfg(feature = "internal-lints")] - &utils::internal_lints::INTERNING_DEFINED_SYMBOL, + utils::internal_lints::INTERNING_DEFINED_SYMBOL, #[cfg(feature = "internal-lints")] - &utils::internal_lints::INVALID_PATHS, + utils::internal_lints::INVALID_PATHS, #[cfg(feature = "internal-lints")] - &utils::internal_lints::LINT_WITHOUT_LINT_PASS, + utils::internal_lints::LINT_WITHOUT_LINT_PASS, #[cfg(feature = "internal-lints")] - &utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM, + utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM, #[cfg(feature = "internal-lints")] - &utils::internal_lints::OUTER_EXPN_EXPN_DATA, + utils::internal_lints::OUTER_EXPN_EXPN_DATA, #[cfg(feature = "internal-lints")] - &utils::internal_lints::PRODUCE_ICE, + 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, - &atomic_ordering::INVALID_ATOMIC_ORDERING, - &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, - &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, - ©_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, - &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, - &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, - &enum_variants::PUB_ENUM_VARIANT_NAMES, - &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, - &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_let_some_result::IF_LET_SOME_RESULT, - &if_not_else::IF_NOT_ELSE, - &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, - &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, - ¯o_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_identity::MAP_IDENTITY, - &map_unit_fn::OPTION_MAP_UNIT_FN, - &map_unit_fn::RESULT_MAP_UNIT_FN, - &match_on_vec_items::MATCH_ON_VEC_ITEMS, - &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::CLONE_DOUBLE_REF, - &methods::CLONE_ON_COPY, - &methods::CLONE_ON_REF_PTR, - &methods::EXPECT_FUN_CALL, - &methods::EXPECT_USED, - &methods::FILETYPE_IS_FILE, - &methods::FILTER_MAP, - &methods::FILTER_MAP_IDENTITY, - &methods::FILTER_MAP_NEXT, - &methods::FILTER_NEXT, - &methods::FLAT_MAP_IDENTITY, - &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::MAP_COLLECT_RESULT_UNIT, - &methods::MAP_FLATTEN, - &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::UNINIT_ASSUMED_INIT, - &methods::UNNECESSARY_FILTER_MAP, - &methods::UNNECESSARY_FOLD, - &methods::UNNECESSARY_LAZY_EVALUATIONS, - &methods::UNWRAP_USED, - &methods::USELESS_ASREF, - &methods::WRONG_PUB_SELF_CONVENTION, - &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_inline::MISSING_INLINE_IN_PUBLIC_ITEMS, - &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_bool::BOOL_COMPARISON, - &needless_bool::NEEDLESS_BOOL, - &needless_borrow::NEEDLESS_BORROW, - &needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE, - &needless_continue::NEEDLESS_CONTINUE, - &needless_for_each::NEEDLESS_FOR_EACH, - &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, - &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::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, - ®ex::INVALID_REGEX, - ®ex::TRIVIAL_REGEX, - &repeat_once::REPEAT_ONCE, - &returns::LET_AND_RETURN, - &returns::NEEDLESS_RETURN, - &self_assignment::SELF_ASSIGNMENT, - &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, - &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_VEC, - &types::LINKEDLIST, - &types::OPTION_OPTION, - &types::RC_BUFFER, - &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_sort_by::UNNECESSARY_SORT_BY, - &unnecessary_wraps::UNNECESSARY_WRAPS, - &unnested_or_patterns::UNNESTED_OR_PATTERNS, - &unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, - &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, + 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, + atomic_ordering::INVALID_ATOMIC_ORDERING, + 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, + 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, + 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, + 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, + enum_variants::PUB_ENUM_VARIANT_NAMES, + 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, + 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_let_some_result::IF_LET_SOME_RESULT, + if_not_else::IF_NOT_ELSE, + 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, + 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_identity::MAP_IDENTITY, + map_unit_fn::OPTION_MAP_UNIT_FN, + map_unit_fn::RESULT_MAP_UNIT_FN, + match_on_vec_items::MATCH_ON_VEC_ITEMS, + 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::CLONE_DOUBLE_REF, + methods::CLONE_ON_COPY, + methods::CLONE_ON_REF_PTR, + methods::EXPECT_FUN_CALL, + methods::EXPECT_USED, + methods::FILETYPE_IS_FILE, + methods::FILTER_MAP, + methods::FILTER_MAP_IDENTITY, + methods::FILTER_MAP_NEXT, + methods::FILTER_NEXT, + methods::FLAT_MAP_IDENTITY, + 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::MAP_COLLECT_RESULT_UNIT, + methods::MAP_FLATTEN, + 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::UNINIT_ASSUMED_INIT, + methods::UNNECESSARY_FILTER_MAP, + methods::UNNECESSARY_FOLD, + methods::UNNECESSARY_LAZY_EVALUATIONS, + methods::UNWRAP_USED, + methods::USELESS_ASREF, + methods::WRONG_PUB_SELF_CONVENTION, + 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_inline::MISSING_INLINE_IN_PUBLIC_ITEMS, + 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_bool::BOOL_COMPARISON, + needless_bool::NEEDLESS_BOOL, + needless_borrow::NEEDLESS_BORROW, + needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE, + needless_continue::NEEDLESS_CONTINUE, + needless_for_each::NEEDLESS_FOR_EACH, + 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, + 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::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, + self_assignment::SELF_ASSIGNMENT, + 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, + 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_VEC, + types::LINKEDLIST, + types::OPTION_OPTION, + types::RC_BUFFER, + 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_sort_by::UNNECESSARY_SORT_BY, + unnecessary_wraps::UNNECESSARY_WRAPS, + unnested_or_patterns::UNNESTED_OR_PATTERNS, + unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, + 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` @@ -1295,792 +1295,792 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box if_then_some_else_none::IfThenSomeElseNone::new(msrv)); 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(&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(&methods::WRONG_PUB_SELF_CONVENTION), - 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_inline::MISSING_INLINE_IN_PUBLIC_ITEMS), - 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(&semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED), - 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(&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), + 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(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(methods::WRONG_PUB_SELF_CONVENTION), + 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_inline::MISSING_INLINE_IN_PUBLIC_ITEMS), + 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(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED), + 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(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(©_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(&enum_variants::PUB_ENUM_VARIANT_NAMES), - 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(&infinite_iter::MAYBE_INFINITE_ITER), - LintId::of(&invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS), - LintId::of(&items_after_statements::ITEMS_AFTER_STATEMENTS), - 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(¯o_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::FILTER_MAP), - LintId::of(&methods::FILTER_MAP_NEXT), - 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::USED_UNDERSCORE_BINDING), - LintId::of(&misc_early::UNSEPARATED_LITERAL_SUFFIX), - LintId::of(&mut_mut::MUT_MUT), - 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::SIMILAR_NAMES), - LintId::of(&option_if_let_else::OPTION_IF_LET_ELSE), - 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(&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(&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_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), + 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(enum_variants::PUB_ENUM_VARIANT_NAMES), + 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(infinite_iter::MAYBE_INFINITE_ITER), + LintId::of(invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS), + LintId::of(items_after_statements::ITEMS_AFTER_STATEMENTS), + 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::FILTER_MAP), + LintId::of(methods::FILTER_MAP_NEXT), + 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::USED_UNDERSCORE_BINDING), + LintId::of(misc_early::UNSEPARATED_LITERAL_SUFFIX), + LintId::of(mut_mut::MUT_MUT), + 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::SIMILAR_NAMES), + LintId::of(option_if_let_else::OPTION_IF_LET_ELSE), + 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(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(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_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), ]); #[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), + 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(&atomic_ordering::INVALID_ATOMIC_ORDERING), - 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(&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::BRANCHES_SHARING_CODE), - LintId::of(&copies::IFS_SAME_COND), - LintId::of(&copies::IF_SAME_THEN_ELSE), - LintId::of(&default::FIELD_REASSIGN_WITH_DEFAULT), - 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_let_some_result::IF_LET_SOME_RESULT), - LintId::of(&inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR), - 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_identity::MAP_IDENTITY), - LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN), - LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN), - 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::FILTER_MAP_IDENTITY), - LintId::of(&methods::FILTER_NEXT), - LintId::of(&methods::FLAT_MAP_IDENTITY), - LintId::of(&methods::FROM_ITER_INSTEAD_OF_COLLECT), - 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::MAP_COLLECT_RESULT_UNIT), - 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::UNINIT_ASSUMED_INIT), - LintId::of(&methods::UNNECESSARY_FILTER_MAP), - LintId::of(&methods::UNNECESSARY_FOLD), - LintId::of(&methods::UNNECESSARY_LAZY_EVALUATIONS), - 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::FLOAT_CMP), - 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_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), - 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_expressive_names::MANY_SINGLE_CHAR_NAMES), - 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::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(®ex::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(&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(&suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), - 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_PTR), - 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_VEC), - LintId::of(&types::REDUNDANT_ALLOCATION), - LintId::of(&types::TYPE_COMPLEXITY), - LintId::of(&types::VEC_BOX), - LintId::of(&undropped_manually_drops::UNDROPPED_MANUALLY_DROPS), - LintId::of(&unicode::INVISIBLE_CHARACTERS), - LintId::of(&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), + 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(atomic_ordering::INVALID_ATOMIC_ORDERING), + 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(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::BRANCHES_SHARING_CODE), + LintId::of(copies::IFS_SAME_COND), + LintId::of(copies::IF_SAME_THEN_ELSE), + LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT), + 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_let_some_result::IF_LET_SOME_RESULT), + LintId::of(inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR), + 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_identity::MAP_IDENTITY), + LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN), + LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN), + 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::FILTER_MAP_IDENTITY), + LintId::of(methods::FILTER_NEXT), + LintId::of(methods::FLAT_MAP_IDENTITY), + LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT), + 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::MAP_COLLECT_RESULT_UNIT), + 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::UNINIT_ASSUMED_INIT), + LintId::of(methods::UNNECESSARY_FILTER_MAP), + LintId::of(methods::UNNECESSARY_FOLD), + LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS), + 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::FLOAT_CMP), + 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_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), + 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_expressive_names::MANY_SINGLE_CHAR_NAMES), + 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::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(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(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), + 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_PTR), + 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_VEC), + LintId::of(types::REDUNDANT_ALLOCATION), + LintId::of(types::TYPE_COMPLEXITY), + LintId::of(types::VEC_BOX), + LintId::of(undropped_manually_drops::UNDROPPED_MANUALLY_DROPS), + LintId::of(unicode::INVISIBLE_CHARACTERS), + LintId::of(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(&attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), - LintId::of(&blacklisted_name::BLACKLISTED_NAME), - LintId::of(&blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), - 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(&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::RESULT_UNIT_ERR), - LintId::of(&if_let_some_result::IF_LET_SOME_RESULT), - LintId::of(&inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR), - 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::EMPTY_LOOP), - 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(&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::FROM_ITER_INSTEAD_OF_COLLECT), - 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::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(&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(&non_expressive_names::MANY_SINGLE_CHAR_NAMES), - 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(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), - LintId::of(&suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), - 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), + LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS), + LintId::of(assign_ops::ASSIGN_OP_PATTERN), + LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), + LintId::of(blacklisted_name::BLACKLISTED_NAME), + LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), + 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(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::RESULT_UNIT_ERR), + LintId::of(if_let_some_result::IF_LET_SOME_RESULT), + LintId::of(inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR), + 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::EMPTY_LOOP), + 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(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::FROM_ITER_INSTEAD_OF_COLLECT), + 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::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(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(non_expressive_names::MANY_SINGLE_CHAR_NAMES), + 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(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), + LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), + 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(&assign_ops::MISREFACTORED_ASSIGN_OP), - 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(&copies::BRANCHES_SHARING_CODE), - 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(&eval_order_dependence::EVAL_ORDER_DEPENDENCE), - 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::MUT_RANGE_BOUND), - 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_identity::MAP_IDENTITY), - 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::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::SUSPICIOUS_MAP), - 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_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(&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_PTR), - 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), + LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP), + 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(copies::BRANCHES_SHARING_CODE), + 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(eval_order_dependence::EVAL_ORDER_DEPENDENCE), + 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::MUT_RANGE_BOUND), + 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_identity::MAP_IDENTITY), + 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::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::SUSPICIOUS_MAP), + 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_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(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_PTR), + 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(&atomic_ordering::INVALID_ATOMIC_ORDERING), - 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(&float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS), - 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::FOR_LOOPS_OVER_FALLIBLES), - 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::UNINIT_ASSUMED_INIT), - LintId::of(&methods::ZST_OFFSET), - LintId::of(&minmax::MIN_MAX), - LintId::of(&misc::CMP_NAN), - LintId::of(&misc::FLOAT_CMP), - LintId::of(&misc::MODULO_ONE), - LintId::of(&mut_key::MUTABLE_KEY_TYPE), - 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::MUT_FROM_REF), - LintId::of(&ranges::REVERSED_EMPTY_RANGES), - LintId::of(®ex::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(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), - LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), - 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), + LintId::of(absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS), + LintId::of(approx_const::APPROX_CONSTANT), + LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC), + LintId::of(atomic_ordering::INVALID_ATOMIC_ORDERING), + 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(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS), + 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::FOR_LOOPS_OVER_FALLIBLES), + 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::UNINIT_ASSUMED_INIT), + LintId::of(methods::ZST_OFFSET), + LintId::of(minmax::MIN_MAX), + LintId::of(misc::CMP_NAN), + LintId::of(misc::FLOAT_CMP), + LintId::of(misc::MODULO_ONE), + LintId::of(mut_key::MUTABLE_KEY_TYPE), + 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::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(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), + LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), + 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::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::ITER_NTH), - 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_VEC), - LintId::of(&types::REDUNDANT_ALLOCATION), - LintId::of(&vec::USELESS_VEC), - LintId::of(&vec_init_then_push::VEC_INIT_THEN_PUSH), + 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::ITER_NTH), + 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_VEC), + 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(&multiple_crate_versions::MULTIPLE_CRATE_VERSIONS), - LintId::of(&wildcard_dependencies::WILDCARD_DEPENDENCIES), + LintId::of(cargo_common_metadata::CARGO_COMMON_METADATA), + LintId::of(multiple_crate_versions::MULTIPLE_CRATE_VERSIONS), + LintId::of(wildcard_dependencies::WILDCARD_DEPENDENCIES), ]); 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(&disallowed_method::DISALLOWED_METHOD), - 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(&needless_borrow::NEEDLESS_BORROW), - LintId::of(&path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE), - LintId::of(&redundant_pub_crate::REDUNDANT_PUB_CRATE), - LintId::of(®ex::TRIVIAL_REGEX), - LintId::of(&strings::STRING_LIT_AS_BYTES), - LintId::of(&transmute::USELESS_TRANSMUTE), - LintId::of(&use_self::USE_SELF), + LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR), + LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY), + LintId::of(disallowed_method::DISALLOWED_METHOD), + 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(needless_borrow::NEEDLESS_BORROW), + 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(transmute::USELESS_TRANSMUTE), + LintId::of(use_self::USE_SELF), ]); } diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index e3b3fa21cab..116ad072837 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -81,7 +81,7 @@ declare_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]); impl<'tcx> LateLintPass<'tcx> for Lifetimes { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if let ItemKind::Fn(ref sig, ref generics, id) = item.kind { - check_fn_inner(cx, &sig.decl, Some(id), generics, item.span, true); + check_fn_inner(cx, sig.decl, Some(id), generics, item.span, true); } } @@ -90,7 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { let report_extra_lifetimes = trait_ref_of_method(cx, item.hir_id()).is_none(); check_fn_inner( cx, - &sig.decl, + sig.decl, Some(id), &item.generics, item.span, @@ -105,7 +105,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { TraitFn::Required(_) => None, TraitFn::Provided(id) => Some(id), }; - check_fn_inner(cx, &sig.decl, body, &item.generics, item.span, true); + check_fn_inner(cx, sig.decl, body, &item.generics, item.span, true); } } } @@ -149,7 +149,7 @@ fn check_fn_inner<'tcx>( .last() .expect("a path must have at least one segment") .args; - if let Some(ref params) = *params { + if let Some(params) = *params { let lifetimes = params.args.iter().filter_map(|arg| match arg { GenericArg::Lifetime(lt) => Some(lt), _ => None, @@ -163,7 +163,7 @@ fn check_fn_inner<'tcx>( } } } - if could_use_elision(cx, decl, body, &generics.params) { + if could_use_elision(cx, decl, body, generics.params) { span_lint( cx, NEEDLESS_LIFETIMES, @@ -201,7 +201,7 @@ fn could_use_elision<'tcx>( input_visitor.visit_ty(arg); } // extract lifetimes in output type - if let Return(ref ty) = func.output { + if let Return(ty) = func.output { output_visitor.visit_ty(ty); } for lt in named_generics { @@ -416,12 +416,12 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, where_clause: &'tcx WhereCl // a predicate like F: Trait or F: for<'a> Trait<'a> let mut visitor = RefVisitor::new(cx); // walk the type F, it may not contain LT refs - walk_ty(&mut visitor, &pred.bounded_ty); + walk_ty(&mut visitor, pred.bounded_ty); if !visitor.all_lts().is_empty() { return true; } // if the bounds define new lifetimes, they are fine to occur - let allowed_lts = allowed_lts_from(&pred.bound_generic_params); + let allowed_lts = allowed_lts_from(pred.bound_generic_params); // now walk the bounds for bound in pred.bounds.iter() { walk_param_bound(&mut visitor, bound); @@ -433,8 +433,8 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, where_clause: &'tcx WhereCl }, WherePredicate::EqPredicate(ref pred) => { let mut visitor = RefVisitor::new(cx); - walk_ty(&mut visitor, &pred.lhs_ty); - walk_ty(&mut visitor, &pred.rhs_ty); + walk_ty(&mut visitor, pred.lhs_ty); + walk_ty(&mut visitor, pred.rhs_ty); if !visitor.lts.is_empty() { return true; } diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index 7fd55151226..1d63af25803 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -248,7 +248,7 @@ impl LiteralDigitGrouping { fn check_lit(self, cx: &EarlyContext<'_>, lit: &Lit) { if_chain! { if let Some(src) = snippet_opt(cx, lit.span); - if let Some(mut num_lit) = NumericLiteral::from_lit(&src, &lit); + if let Some(mut num_lit) = NumericLiteral::from_lit(&src, lit); then { if !Self::check_for_mistyped_suffix(cx, lit.span, &mut num_lit) { return; @@ -438,7 +438,7 @@ impl DecimalLiteralRepresentation { if_chain! { if let LitKind::Int(val, _) = lit.kind; if let Some(src) = snippet_opt(cx, lit.span); - if let Some(num_lit) = NumericLiteral::from_lit(&src, &lit); + if let Some(num_lit) = NumericLiteral::from_lit(&src, lit); if num_lit.radix == Radix::Decimal; if val >= u128::from(self.threshold); then { diff --git a/clippy_lints/src/loops/explicit_counter_loop.rs b/clippy_lints/src/loops/explicit_counter_loop.rs index f14dbb1d642..98e60f7ed85 100644 --- a/clippy_lints/src/loops/explicit_counter_loop.rs +++ b/clippy_lints/src/loops/explicit_counter_loop.rs @@ -26,7 +26,7 @@ pub(super) fn check<'tcx>( // For each candidate, check the parent block to see if // it's initialized to zero at the start of the loop. - if let Some(block) = get_enclosing_block(&cx, expr.hir_id) { + if let Some(block) = get_enclosing_block(cx, expr.hir_id) { for id in increment_visitor.into_results() { let mut initialize_visitor = InitializeVisitor::new(cx, expr, id); walk_block(&mut initialize_visitor, block); diff --git a/clippy_lints/src/loops/for_kv_map.rs b/clippy_lints/src/loops/for_kv_map.rs index 8f18f54119b..666b8c58728 100644 --- a/clippy_lints/src/loops/for_kv_map.rs +++ b/clippy_lints/src/loops/for_kv_map.rs @@ -19,7 +19,7 @@ pub(super) fn check<'tcx>( ) { let pat_span = pat.span; - if let PatKind::Tuple(ref pat, _) = pat.kind { + if let PatKind::Tuple(pat, _) = pat.kind { if pat.len() == 2 { let arg_span = arg.span; let (new_pat_span, kind, ty, mutbl) = match *cx.typeck_results().expr_ty(arg).kind() { @@ -35,7 +35,7 @@ pub(super) fn check<'tcx>( Mutability::Mut => "_mut", }; let arg = match arg.kind { - ExprKind::AddrOf(BorrowKind::Ref, _, ref expr) => &**expr, + ExprKind::AddrOf(BorrowKind::Ref, _, expr) => expr, _ => arg, }; diff --git a/clippy_lints/src/loops/manual_flatten.rs b/clippy_lints/src/loops/manual_flatten.rs index 574ad8c0f29..94743cfcf46 100644 --- a/clippy_lints/src/loops/manual_flatten.rs +++ b/clippy_lints/src/loops/manual_flatten.rs @@ -18,7 +18,7 @@ pub(super) fn check<'tcx>( body: &'tcx Expr<'_>, span: Span, ) { - if let ExprKind::Block(ref block, _) = body.kind { + if let ExprKind::Block(block, _) = body.kind { // Ensure the `if let` statement is the only expression or statement in the for-loop let inner_expr = if block.stmts.len() == 1 && block.expr.is_none() { let match_stmt = &block.stmts[0]; @@ -36,7 +36,7 @@ pub(super) fn check<'tcx>( if_chain! { if let Some(inner_expr) = inner_expr; if let ExprKind::Match( - ref match_expr, ref match_arms, MatchSource::IfLetDesugar{ contains_else_clause: false } + match_expr, match_arms, MatchSource::IfLetDesugar{ contains_else_clause: false } ) = inner_expr.kind; // Ensure match_expr in `if let` statement is the same as the pat from the for-loop if let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind; diff --git a/clippy_lints/src/loops/manual_memcpy.rs b/clippy_lints/src/loops/manual_memcpy.rs index 3c5abb7a3c4..47005aba388 100644 --- a/clippy_lints/src/loops/manual_memcpy.rs +++ b/clippy_lints/src/loops/manual_memcpy.rs @@ -63,8 +63,8 @@ pub(super) fn check<'tcx>( if let ExprKind::Index(base_right, idx_right) = rhs.kind; if is_slice_like(cx, cx.typeck_results().expr_ty(base_left)); if is_slice_like(cx, cx.typeck_results().expr_ty(base_right)); - if let Some((start_left, offset_left)) = get_details_from_idx(cx, &idx_left, &starts); - if let Some((start_right, offset_right)) = get_details_from_idx(cx, &idx_right, &starts); + if let Some((start_left, offset_left)) = get_details_from_idx(cx, idx_left, &starts); + if let Some((start_right, offset_right)) = get_details_from_idx(cx, idx_right, &starts); // Source and destination must be different if path_to_local(base_left) != path_to_local(base_right); @@ -168,8 +168,8 @@ fn build_manual_memcpy_suggestion<'tcx>( }, }; - let (dst_offset, dst_limit) = print_offset_and_limit(&dst); - let (src_offset, src_limit) = print_offset_and_limit(&src); + let (dst_offset, dst_limit) = print_offset_and_limit(dst); + let (src_offset, src_limit) = print_offset_and_limit(src); let dst_base_str = snippet(cx, dst.base.span, "???"); let src_base_str = snippet(cx, src.base.span, "???"); @@ -435,7 +435,7 @@ fn get_loop_counters<'a, 'tcx>( // For each candidate, check the parent block to see if // it's initialized to zero at the start of the loop. - get_enclosing_block(&cx, expr.hir_id).and_then(|block| { + get_enclosing_block(cx, expr.hir_id).and_then(|block| { increment_visitor .into_results() .filter_map(move |var_id| { diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index 20291491998..28acefd51fe 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -562,7 +562,7 @@ impl<'tcx> LateLintPass<'tcx> for Loops { // check for `loop { if let {} else break }` that could be `while let` // (also matches an explicit "match" instead of "if let") // (even if the "match" or "if let" is used for declaration) - if let ExprKind::Loop(ref block, _, LoopSource::Loop, _) = expr.kind { + if let ExprKind::Loop(block, _, LoopSource::Loop, _) = expr.kind { // also check for empty `loop {}` statements, skipping those in #[panic_handler] empty_loop::check(cx, expr, block); while_let_loop::check(cx, expr, block); @@ -570,7 +570,7 @@ impl<'tcx> LateLintPass<'tcx> for Loops { while_let_on_iterator::check(cx, expr); - if let Some((cond, body)) = higher::while_loop(&expr) { + if let Some((cond, body)) = higher::while_loop(expr) { while_immutable_condition::check(cx, cond, body); } @@ -602,7 +602,7 @@ fn check_for_loop<'tcx>( fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>, expr: &Expr<'_>) { let mut next_loop_linted = false; // whether or not ITER_NEXT_LOOP lint was used - if let ExprKind::MethodCall(ref method, _, ref args, _) = arg.kind { + if let ExprKind::MethodCall(method, _, args, _) = arg.kind { // just the receiver, no arguments if args.len() == 1 { let method_name = &*method.ident.as_str(); diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/loops/needless_collect.rs index e0c5caf5136..4d73aef76e8 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/loops/needless_collect.rs @@ -21,10 +21,10 @@ pub(super) fn check<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { } fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { if_chain! { - if let ExprKind::MethodCall(ref method, _, ref args, _) = expr.kind; - if let ExprKind::MethodCall(ref chain_method, method0_span, _, _) = args[0].kind; + if let ExprKind::MethodCall(method, _, args, _) = expr.kind; + if let ExprKind::MethodCall(chain_method, method0_span, _, _) = args[0].kind; if chain_method.ident.name == sym!(collect) && is_trait_method(cx, &args[0], sym::Iterator); - if let Some(ref generic_args) = chain_method.args; + if let Some(generic_args) = chain_method.args; if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0); let ty = cx.typeck_results().node_type(ty.hir_id); if is_type_diagnostic_item(cx, ty, sym::vec_type) @@ -58,16 +58,16 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont } fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { - if let ExprKind::Block(ref block, _) = expr.kind { - for ref stmt in block.stmts { + if let ExprKind::Block(block, _) = expr.kind { + for stmt in block.stmts { if_chain! { if let StmtKind::Local( Local { pat: Pat { hir_id: pat_id, kind: PatKind::Binding(_, _, ident, .. ), .. }, - init: Some(ref init_expr), .. } + init: Some(init_expr), .. } ) = stmt.kind; - if let ExprKind::MethodCall(ref method_name, collect_span, &[ref iter_source], ..) = init_expr.kind; - if method_name.ident.name == sym!(collect) && is_trait_method(cx, &init_expr, sym::Iterator); - if let Some(ref generic_args) = method_name.args; + if let ExprKind::MethodCall(method_name, collect_span, &[ref iter_source], ..) = init_expr.kind; + if method_name.ident.name == sym!(collect) && is_trait_method(cx, init_expr, sym::Iterator); + if let Some(generic_args) = method_name.args; if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0); if let ty = cx.typeck_results().node_type(ty.hir_id); if is_type_diagnostic_item(cx, ty, sym::vec_type) || @@ -165,8 +165,8 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor { fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { // Check function calls on our collection if_chain! { - if let ExprKind::MethodCall(method_name, _, ref args, _) = &expr.kind; - if let Some(Expr { kind: ExprKind::Path(QPath::Resolved(_, ref path)), .. }) = args.get(0); + if let ExprKind::MethodCall(method_name, _, args, _) = &expr.kind; + if let Some(Expr { kind: ExprKind::Path(QPath::Resolved(_, path)), .. }) = args.get(0); if let &[name] = &path.segments; if name.ident == self.target; then { @@ -193,7 +193,7 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor { } // Check if the collection is used for anything else if_chain! { - if let Expr { kind: ExprKind::Path(QPath::Resolved(_, ref path)), .. } = expr; + if let Expr { kind: ExprKind::Path(QPath::Resolved(_, path)), .. } = expr; if let &[name] = &path.segments; if name.ident == self.target; then { diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 60afa449a45..56141fb3837 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -96,7 +96,7 @@ pub(super) fn check<'tcx>( let take = if let Some(end) = *end { let mut take_expr = end; - if let ExprKind::Binary(ref op, ref left, ref right) = end.kind { + if let ExprKind::Binary(ref op, left, right) = end.kind { if let BinOpKind::Add = op.node { let start_equal_left = SpanlessEq::new(cx).eq_expr(start, left); let start_equal_right = SpanlessEq::new(cx).eq_expr(start, right); @@ -190,10 +190,10 @@ pub(super) fn check<'tcx>( fn is_len_call(expr: &Expr<'_>, var: Symbol) -> bool { if_chain! { - if let ExprKind::MethodCall(ref method, _, ref len_args, _) = expr.kind; + if let ExprKind::MethodCall(method, _, len_args, _) = expr.kind; if len_args.len() == 1; if method.ident.name == sym!(len); - if let ExprKind::Path(QPath::Resolved(_, ref path)) = len_args[0].kind; + if let ExprKind::Path(QPath::Resolved(_, path)) = len_args[0].kind; if path.segments.len() == 1; if path.segments[0].ident.name == var; then { @@ -254,7 +254,7 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> { if_chain! { // the indexed container is referenced by a name if let ExprKind::Path(ref seqpath) = seqexpr.kind; - if let QPath::Resolved(None, ref seqvar) = *seqpath; + if let QPath::Resolved(None, seqvar) = *seqpath; if seqvar.segments.len() == 1; let index_used_directly = path_to_local_id(idx, self.var); let indexed_indirectly = { @@ -310,7 +310,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { if_chain! { // a range index op - if let ExprKind::MethodCall(ref meth, _, ref args, _) = expr.kind; + if let ExprKind::MethodCall(meth, _, args, _) = expr.kind; if (meth.ident.name == sym::index && match_trait_method(self.cx, expr, &paths::INDEX)) || (meth.ident.name == sym::index_mut && match_trait_method(self.cx, expr, &paths::INDEX_MUT)); if !self.check(&args[1], &args[0], expr); @@ -319,7 +319,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { if_chain! { // an index op - if let ExprKind::Index(ref seqexpr, ref idx) = expr.kind; + if let ExprKind::Index(seqexpr, idx) = expr.kind; if !self.check(idx, seqexpr, expr); then { return } } @@ -340,19 +340,19 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { let old = self.prefer_mutable; match expr.kind { - ExprKind::AssignOp(_, ref lhs, ref rhs) | ExprKind::Assign(ref lhs, ref rhs, _) => { + ExprKind::AssignOp(_, lhs, rhs) | ExprKind::Assign(lhs, rhs, _) => { self.prefer_mutable = true; self.visit_expr(lhs); self.prefer_mutable = false; self.visit_expr(rhs); }, - ExprKind::AddrOf(BorrowKind::Ref, mutbl, ref expr) => { + ExprKind::AddrOf(BorrowKind::Ref, mutbl, expr) => { if mutbl == Mutability::Mut { self.prefer_mutable = true; } self.visit_expr(expr); }, - ExprKind::Call(ref f, args) => { + ExprKind::Call(f, args) => { self.visit_expr(f); for expr in args { let ty = self.cx.typeck_results().expr_ty_adjusted(expr); diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index f63a3761a0d..e2cb4638018 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -5,7 +5,7 @@ use rustc_lint::LateContext; use std::iter::{once, Iterator}; pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Loop(ref block, _, _, _) = expr.kind { + if let ExprKind::Loop(block, _, _, _) = expr.kind { match never_loop_block(block, expr.hir_id) { NeverLoopResult::AlwaysBreak => span_lint(cx, NEVER_LOOP, expr.span, "this loop never actually loops"), NeverLoopResult::MayContinueMainLoop | NeverLoopResult::Otherwise => (), @@ -76,36 +76,36 @@ fn never_loop_expr_seq<'a, T: Iterator>>(es: &mut T, main_lo fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<&'tcx Expr<'tcx>> { match stmt.kind { - StmtKind::Semi(ref e, ..) | StmtKind::Expr(ref e, ..) => Some(e), - StmtKind::Local(ref local) => local.init.as_deref(), + StmtKind::Semi(e, ..) | StmtKind::Expr(e, ..) => Some(e), + StmtKind::Local(local) => local.init.as_deref(), StmtKind::Item(..) => None, } } fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { match expr.kind { - ExprKind::Box(ref e) - | ExprKind::Unary(_, ref e) - | ExprKind::Cast(ref e, _) - | ExprKind::Type(ref e, _) - | ExprKind::Field(ref e, _) - | ExprKind::AddrOf(_, _, ref e) - | ExprKind::Struct(_, _, Some(ref e)) - | ExprKind::Repeat(ref e, _) - | ExprKind::DropTemps(ref e) => never_loop_expr(e, main_loop_id), - ExprKind::Array(ref es) | ExprKind::MethodCall(_, _, ref es, _) | ExprKind::Tup(ref es) => { + ExprKind::Box(e) + | ExprKind::Unary(_, e) + | ExprKind::Cast(e, _) + | ExprKind::Type(e, _) + | ExprKind::Field(e, _) + | ExprKind::AddrOf(_, _, e) + | ExprKind::Struct(_, _, Some(e)) + | ExprKind::Repeat(e, _) + | ExprKind::DropTemps(e) => never_loop_expr(e, main_loop_id), + ExprKind::Array(es) | ExprKind::MethodCall(_, _, es, _) | ExprKind::Tup(es) => { never_loop_expr_all(&mut es.iter(), main_loop_id) }, - ExprKind::Call(ref e, ref es) => never_loop_expr_all(&mut once(&**e).chain(es.iter()), main_loop_id), - ExprKind::Binary(_, ref e1, ref e2) - | ExprKind::Assign(ref e1, ref e2, _) - | ExprKind::AssignOp(_, ref e1, ref e2) - | ExprKind::Index(ref e1, ref e2) => never_loop_expr_all(&mut [&**e1, &**e2].iter().cloned(), main_loop_id), - ExprKind::Loop(ref b, _, _, _) => { + ExprKind::Call(e, es) => never_loop_expr_all(&mut once(e).chain(es.iter()), main_loop_id), + ExprKind::Binary(_, e1, e2) + | ExprKind::Assign(e1, e2, _) + | ExprKind::AssignOp(_, e1, e2) + | ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().cloned(), main_loop_id), + ExprKind::Loop(b, _, _, _) => { // Break can come from the inner loop so remove them. absorb_break(&never_loop_block(b, main_loop_id)) }, - ExprKind::If(ref e, ref e2, ref e3) => { + ExprKind::If(e, e2, ref e3) => { let e1 = never_loop_expr(e, main_loop_id); let e2 = never_loop_expr(e2, main_loop_id); let e3 = e3 @@ -113,7 +113,7 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { .map_or(NeverLoopResult::Otherwise, |e| never_loop_expr(e, main_loop_id)); combine_seq(e1, combine_branches(e2, e3)) }, - ExprKind::Match(ref e, ref arms, _) => { + ExprKind::Match(e, arms, _) => { let e = never_loop_expr(e, main_loop_id); if arms.is_empty() { e @@ -122,7 +122,7 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { combine_seq(e, arms) } }, - ExprKind::Block(ref b, _) => never_loop_block(b, main_loop_id), + ExprKind::Block(b, _) => never_loop_block(b, main_loop_id), ExprKind::Continue(d) => { let id = d .target_id @@ -136,7 +136,7 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { ExprKind::Break(_, ref e) | ExprKind::Ret(ref e) => e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| { combine_seq(never_loop_expr(e, main_loop_id), NeverLoopResult::AlwaysBreak) }), - ExprKind::InlineAsm(ref asm) => asm + ExprKind::InlineAsm(asm) => asm .operands .iter() .map(|(o, _)| match o { diff --git a/clippy_lints/src/loops/same_item_push.rs b/clippy_lints/src/loops/same_item_push.rs index b3d784474c8..cb2c83e9029 100644 --- a/clippy_lints/src/loops/same_item_push.rs +++ b/clippy_lints/src/loops/same_item_push.rs @@ -161,7 +161,7 @@ impl<'a, 'tcx> Visitor<'tcx> for SameItemPushVisitor<'a, 'tcx> { if vec_push_option.is_none() { // Current statement is not a push so visit inside match &s.kind { - StmtKind::Expr(expr) | StmtKind::Semi(expr) => self.visit_expr(&expr), + StmtKind::Expr(expr) | StmtKind::Semi(expr) => self.visit_expr(expr), _ => {}, } } else { diff --git a/clippy_lints/src/loops/single_element_loop.rs b/clippy_lints/src/loops/single_element_loop.rs index 8451c1c6130..fc067e81bca 100644 --- a/clippy_lints/src/loops/single_element_loop.rs +++ b/clippy_lints/src/loops/single_element_loop.rs @@ -15,12 +15,12 @@ pub(super) fn check<'tcx>( expr: &'tcx Expr<'_>, ) { if_chain! { - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref arg_expr) = arg.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, _, arg_expr) = arg.kind; if let PatKind::Binding(.., target, _) = pat.kind; if let ExprKind::Array([arg_expression]) = arg_expr.kind; if let ExprKind::Path(ref list_item) = arg_expression.kind; if let Some(list_item_name) = single_segment_path(list_item).map(|ps| ps.ident.name); - if let ExprKind::Block(ref block, _) = body.kind; + if let ExprKind::Block(block, _) = body.kind; if !block.stmts.is_empty(); then { diff --git a/clippy_lints/src/loops/utils.rs b/clippy_lints/src/loops/utils.rs index fc287d51249..4db6644b9d7 100644 --- a/clippy_lints/src/loops/utils.rs +++ b/clippy_lints/src/loops/utils.rs @@ -65,7 +65,7 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { } match parent.kind { - ExprKind::AssignOp(op, ref lhs, ref rhs) => { + ExprKind::AssignOp(op, lhs, rhs) => { if lhs.hir_id == expr.hir_id { *state = if op.node == BinOpKind::Add && is_integer_const(self.cx, rhs, 1) @@ -79,7 +79,7 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { }; } }, - ExprKind::Assign(ref lhs, _, _) if lhs.hir_id == expr.hir_id => { + ExprKind::Assign(lhs, _, _) if lhs.hir_id == expr.hir_id => { *state = IncrementVisitorVarState::DontWarn }, ExprKind::AddrOf(BorrowKind::Ref, mutability, _) if mutability == Mutability::Mut => { @@ -153,7 +153,7 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) { // Look for declarations of the variable if_chain! { - if let StmtKind::Local(ref local) = stmt.kind; + if let StmtKind::Local(local) = stmt.kind; if local.pat.hir_id == self.var_id; if let PatKind::Binding(.., ident, _) = local.pat.kind; then { @@ -191,10 +191,10 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { if let Some(parent) = get_parent_expr(self.cx, expr) { match parent.kind { - ExprKind::AssignOp(_, ref lhs, _) if lhs.hir_id == expr.hir_id => { + ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == expr.hir_id => { self.state = InitializeVisitorState::DontWarn; }, - ExprKind::Assign(ref lhs, ref rhs, _) if lhs.hir_id == expr.hir_id => { + ExprKind::Assign(lhs, rhs, _) if lhs.hir_id == expr.hir_id => { self.state = if_chain! { if self.depth == 0; if let InitializeVisitorState::Declared(name) @@ -273,7 +273,7 @@ impl<'tcx> Visitor<'tcx> for LoopNestVisitor { return; } match expr.kind { - ExprKind::Assign(ref path, _, _) | ExprKind::AssignOp(_, ref path, _) => { + ExprKind::Assign(path, _, _) | ExprKind::AssignOp(_, path, _) => { if path_to_local_id(path, self.iterator) { self.nesting = RuledOut; } @@ -327,7 +327,7 @@ pub(super) fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applic // (&mut x).into_iter() ==> x.iter_mut() match &arg.kind { ExprKind::AddrOf(BorrowKind::Ref, mutability, arg_inner) - if has_iter_method(cx, cx.typeck_results().expr_ty(&arg_inner)).is_some() => + if has_iter_method(cx, cx.typeck_results().expr_ty(arg_inner)).is_some() => { let meth_name = match mutability { Mutability::Mut => "iter_mut", @@ -335,7 +335,7 @@ pub(super) fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applic }; format!( "{}.{}()", - sugg::Sugg::hir_with_applicability(cx, &arg_inner, "_", applic_ref).maybe_par(), + sugg::Sugg::hir_with_applicability(cx, arg_inner, "_", applic_ref).maybe_par(), meth_name, ) } diff --git a/clippy_lints/src/loops/while_let_loop.rs b/clippy_lints/src/loops/while_let_loop.rs index ffe8c0c5494..9c172079852 100644 --- a/clippy_lints/src/loops/while_let_loop.rs +++ b/clippy_lints/src/loops/while_let_loop.rs @@ -11,14 +11,14 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &' let inner_stmt_expr = extract_expr_from_first_stmt(loop_block); // or extract the first expression (if any) from the block if let Some(inner) = inner_stmt_expr.or_else(|| extract_first_expr(loop_block)) { - if let ExprKind::Match(ref matchexpr, ref arms, ref source) = inner.kind { + if let ExprKind::Match(matchexpr, arms, ref source) = inner.kind { // ensure "if let" compatible match structure match *source { MatchSource::Normal | MatchSource::IfLetDesugar { .. } => { if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() - && is_simple_break_expr(&arms[1].body) + && is_simple_break_expr(arms[1].body) { if in_external_macro(cx.sess(), expr.span) { return; @@ -57,7 +57,7 @@ fn extract_expr_from_first_stmt<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr< if block.stmts.is_empty() { return None; } - if let StmtKind::Local(ref local) = block.stmts[0].kind { + if let StmtKind::Local(local) = block.stmts[0].kind { local.init //.map(|expr| expr) } else { None @@ -67,9 +67,9 @@ fn extract_expr_from_first_stmt<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr< /// If a block begins with an expression (with or without semicolon), return it. fn extract_first_expr<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> { match block.expr { - Some(ref expr) if block.stmts.is_empty() => Some(expr), + Some(expr) if block.stmts.is_empty() => Some(expr), None if !block.stmts.is_empty() => match block.stmts[0].kind { - StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => Some(expr), + StmtKind::Expr(expr) | StmtKind::Semi(expr) => Some(expr), StmtKind::Local(..) | StmtKind::Item(..) => None, }, _ => None, @@ -82,7 +82,7 @@ fn extract_first_expr<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> { fn is_simple_break_expr(expr: &Expr<'_>) -> bool { match expr.kind { ExprKind::Break(dest, ref passed_expr) if dest.label.is_none() && passed_expr.is_none() => true, - ExprKind::Block(ref b, _) => extract_first_expr(b).map_or(false, |subexpr| is_simple_break_expr(subexpr)), + ExprKind::Block(b, _) => extract_first_expr(b).map_or(false, |subexpr| is_simple_break_expr(subexpr)), _ => false, } } diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index 57fc6250a9a..82715d9bafa 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -16,12 +16,10 @@ use rustc_middle::hir::map::Map; use rustc_span::symbol::sym; pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Match(ref match_expr, ref arms, MatchSource::WhileLetDesugar) = expr.kind { + if let ExprKind::Match(match_expr, arms, MatchSource::WhileLetDesugar) = expr.kind { let pat = &arms[0].pat.kind; - if let ( - &PatKind::TupleStruct(ref qpath, ref pat_args, _), - &ExprKind::MethodCall(ref method_path, _, ref method_args, _), - ) = (pat, &match_expr.kind) + if let (&PatKind::TupleStruct(ref qpath, pat_args, _), &ExprKind::MethodCall(method_path, _, method_args, _)) = + (pat, &match_expr.kind) { let iter_expr = &method_args[0]; @@ -40,8 +38,8 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { && is_trait_method(cx, match_expr, sym::Iterator) && lhs_constructor.ident.name == sym::Some && (pat_args.is_empty() - || !is_refutable(cx, &pat_args[0]) - && !is_used_inside(cx, iter_expr, &arms[0].body) + || !is_refutable(cx, pat_args[0]) + && !is_used_inside(cx, iter_expr, arms[0].body) && !is_iterator_used_after_while_let(cx, iter_expr) && !is_nested(cx, expr, &method_args[0])) { diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 9da37efddac..dfa464ddb81 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -91,7 +91,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { } else { return; }; - let target_res = cx.qpath_res(&target_path, target_arg.hir_id); + let target_res = cx.qpath_res(target_path, target_arg.hir_id); if target_res == Res::Err { return; }; @@ -174,7 +174,7 @@ fn eq_pattern_length<'tcx>(cx: &LateContext<'tcx>, pattern: &Expr<'_>, expr: &'t // Tests if `expr` is a `&str`. fn is_ref_str(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - match cx.typeck_results().expr_ty_adjusted(&expr).kind() { + match cx.typeck_results().expr_ty_adjusted(expr).kind() { ty::Ref(_, ty, _) => ty.is_str(), _ => false, } diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs index 9f1ab1f695d..99c35ae3bbf 100644 --- a/clippy_lints/src/map_clone.rs +++ b/clippy_lints/src/map_clone.rs @@ -52,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for MapClone { } if_chain! { - if let hir::ExprKind::MethodCall(ref method, _, ref args, _) = e.kind; + if let hir::ExprKind::MethodCall(method, _, args, _) = e.kind; if args.len() == 2; if method.ident.name == sym::map; let ty = cx.typeck_results().expr_ty(&args[0]); @@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for MapClone { let closure_body = cx.tcx.hir().body(body_id); let closure_expr = remove_blocks(&closure_body.value); match closure_body.params[0].pat.kind { - hir::PatKind::Ref(ref inner, hir::Mutability::Not) => if let hir::PatKind::Binding( + hir::PatKind::Ref(inner, hir::Mutability::Not) => if let hir::PatKind::Binding( hir::BindingAnnotation::Unannotated, .., name, None ) = inner.kind { if ident_eq(name, closure_expr) { @@ -71,14 +71,14 @@ impl<'tcx> LateLintPass<'tcx> for MapClone { }, hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, .., name, None) => { match closure_expr.kind { - hir::ExprKind::Unary(hir::UnOp::Deref, ref inner) => { + hir::ExprKind::Unary(hir::UnOp::Deref, inner) => { if ident_eq(name, inner) { if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() { lint(cx, e.span, args[0].span, true); } } }, - hir::ExprKind::MethodCall(ref method, _, [obj], _) => if_chain! { + hir::ExprKind::MethodCall(method, _, [obj], _) => if_chain! { if ident_eq(name, obj) && method.ident.name == sym::clone; if let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id); if let Some(trait_id) = cx.tcx.trait_of_item(fn_id); @@ -109,7 +109,7 @@ impl<'tcx> LateLintPass<'tcx> for MapClone { } fn ident_eq(name: Ident, path: &hir::Expr<'_>) -> bool { - if let hir::ExprKind::Path(hir::QPath::Resolved(None, ref path)) = path.kind { + if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = path.kind { path.segments.len() == 1 && path.segments[0].ident == name } else { false diff --git a/clippy_lints/src/map_err_ignore.rs b/clippy_lints/src/map_err_ignore.rs index a6a63961be5..425a9734e5f 100644 --- a/clippy_lints/src/map_err_ignore.rs +++ b/clippy_lints/src/map_err_ignore.rs @@ -112,7 +112,7 @@ impl<'tcx> LateLintPass<'tcx> for MapErrIgnore { } // check if this is a method call (e.g. x.foo()) - if let ExprKind::MethodCall(ref method, _t_span, ref args, _) = e.kind { + if let ExprKind::MethodCall(method, _t_span, args, _) = e.kind { // only work if the method name is `map_err` and there are only 2 arguments (e.g. x.map_err(|_|[1] // Enum::Variant[2])) if method.ident.as_str() == "map_err" && args.len() == 2 { diff --git a/clippy_lints/src/map_identity.rs b/clippy_lints/src/map_identity.rs index 75191e1f9ee..e7719e7663d 100644 --- a/clippy_lints/src/map_identity.rs +++ b/clippy_lints/src/map_identity.rs @@ -61,7 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for MapIdentity { /// map(). Otherwise, returns None. fn get_map_argument<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a [Expr<'a>]> { if_chain! { - if let ExprKind::MethodCall(ref method, _, ref args, _) = expr.kind; + if let ExprKind::MethodCall(method, _, args, _) = expr.kind; if args.len() == 2 && method.ident.name == sym::map; let caller_ty = cx.typeck_results().expr_ty(&args[0]); if is_trait_method(cx, expr, sym::Iterator) @@ -80,7 +80,7 @@ fn get_map_argument<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { match expr.kind { ExprKind::Closure(_, _, body_id, _, _) => is_body_identity_function(cx, cx.tcx.hir().body(body_id)), - ExprKind::Path(QPath::Resolved(_, ref path)) => match_path(path, &paths::STD_CONVERT_IDENTITY), + ExprKind::Path(QPath::Resolved(_, path)) => match_path(path, &paths::STD_CONVERT_IDENTITY), _ => false, } } @@ -99,12 +99,12 @@ fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool { match body.kind { ExprKind::Path(QPath::Resolved(None, _)) => match_expr_param(cx, body, params[0].pat), - ExprKind::Ret(Some(ref ret_val)) => match_expr_param(cx, ret_val, params[0].pat), - ExprKind::Block(ref block, _) => { + ExprKind::Ret(Some(ret_val)) => match_expr_param(cx, ret_val, params[0].pat), + ExprKind::Block(block, _) => { if_chain! { if block.stmts.len() == 1; - if let StmtKind::Semi(ref expr) | StmtKind::Expr(ref expr) = block.stmts[0].kind; - if let ExprKind::Ret(Some(ref ret_val)) = expr.kind; + if let StmtKind::Semi(expr) | StmtKind::Expr(expr) = block.stmts[0].kind; + if let ExprKind::Ret(Some(ret_val)) = expr.kind; then { match_expr_param(cx, ret_val, params[0].pat) } else { diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index 5cc16244a0d..57cd907e77e 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -133,7 +133,7 @@ fn reduce_unit_expression<'a>(cx: &LateContext<'_>, expr: &'a hir::Expr<'_>) -> // Calls can't be reduced any more Some(expr.span) }, - hir::ExprKind::Block(ref block, _) => { + hir::ExprKind::Block(block, _) => { match (block.stmts, block.expr.as_ref()) { (&[], Some(inner_expr)) => { // If block only contains an expression, @@ -144,8 +144,8 @@ fn reduce_unit_expression<'a>(cx: &LateContext<'_>, expr: &'a hir::Expr<'_>) -> // If block only contains statements, // reduce `{ X; }` to `X` or `X;` match inner_stmt.kind { - hir::StmtKind::Local(ref local) => Some(local.span), - hir::StmtKind::Expr(ref e) => Some(e.span), + hir::StmtKind::Local(local) => Some(local.span), + hir::StmtKind::Expr(e) => Some(e.span), hir::StmtKind::Semi(..) => Some(inner_stmt.span), hir::StmtKind::Item(..) => None, } @@ -169,12 +169,12 @@ fn unit_closure<'tcx>( expr: &hir::Expr<'_>, ) -> Option<(&'tcx hir::Param<'tcx>, &'tcx hir::Expr<'tcx>)> { if_chain! { - if let hir::ExprKind::Closure(_, ref decl, inner_expr_id, _, _) = expr.kind; + if let hir::ExprKind::Closure(_, decl, inner_expr_id, _, _) = expr.kind; let body = cx.tcx.hir().body(inner_expr_id); let body_expr = &body.value; if decl.inputs.len() == 1; if is_unit_expression(cx, body_expr); - if let Some(binding) = iter_input_pats(&decl, body).next(); + if let Some(binding) = iter_input_pats(decl, body).next(); then { return Some((binding, body_expr)); } @@ -267,7 +267,7 @@ impl<'tcx> LateLintPass<'tcx> for MapUnit { return; } - if let hir::StmtKind::Semi(ref expr) = stmt.kind { + if let hir::StmtKind::Semi(expr) = stmt.kind { if let Some(arglists) = method_chain_args(expr, &["map"]) { lint_map_unit_fn(cx, stmt, expr, arglists[0]); } diff --git a/clippy_lints/src/match_on_vec_items.rs b/clippy_lints/src/match_on_vec_items.rs index ccaa5e98c83..ca6fb0831fe 100644 --- a/clippy_lints/src/match_on_vec_items.rs +++ b/clippy_lints/src/match_on_vec_items.rs @@ -51,7 +51,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchOnVecItems { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if_chain! { if !in_external_macro(cx.sess(), expr.span); - if let ExprKind::Match(ref match_expr, _, MatchSource::Normal) = expr.kind; + if let ExprKind::Match(match_expr, _, MatchSource::Normal) = expr.kind; if let Some(idx_expr) = is_vec_indexing(cx, match_expr); if let ExprKind::Index(vec, idx) = idx_expr.kind; @@ -78,7 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchOnVecItems { fn is_vec_indexing<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { if_chain! { - if let ExprKind::Index(ref array, ref index) = expr.kind; + if let ExprKind::Index(array, index) = expr.kind; if is_vector(cx, array); if !is_full_range(cx, index); diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index c68ae00be01..a892e24482b 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -589,7 +589,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { lint_match_arms(cx, expr); } - if let ExprKind::Match(ref ex, ref arms, MatchSource::Normal) = expr.kind { + if let ExprKind::Match(ex, arms, MatchSource::Normal) = expr.kind { check_single_match(cx, ex, arms, expr); check_match_bool(cx, ex, arms, expr); check_overlapping_arms(cx, ex, arms); @@ -604,7 +604,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { check_match_single_binding(cx, ex, arms, expr); } } - if let ExprKind::Match(ref ex, ref arms, _) = expr.kind { + if let ExprKind::Match(ex, arms, _) = expr.kind { check_match_ref_pats(cx, ex, arms, expr); } } @@ -613,14 +613,14 @@ impl<'tcx> LateLintPass<'tcx> for Matches { if_chain! { if !in_external_macro(cx.sess(), local.span); if !in_macro(local.span); - if let Some(ref expr) = local.init; - if let ExprKind::Match(ref target, ref arms, MatchSource::Normal) = expr.kind; + if let Some(expr) = local.init; + if let ExprKind::Match(target, arms, MatchSource::Normal) = expr.kind; if arms.len() == 1 && arms[0].guard.is_none(); if let PatKind::TupleStruct( - QPath::Resolved(None, ref variant_name), ref args, _) = arms[0].pat.kind; + QPath::Resolved(None, variant_name), args, _) = arms[0].pat.kind; if args.len() == 1; - if let PatKind::Binding(_, arg, ..) = strip_pat_refs(&args[0]).kind; - let body = remove_blocks(&arms[0].body); + if let PatKind::Binding(_, arg, ..) = strip_pat_refs(args[0]).kind; + let body = remove_blocks(arms[0].body); if path_to_local_id(body, arg); then { @@ -649,7 +649,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { if_chain! { if !in_external_macro(cx.sess(), pat.span); if !in_macro(pat.span); - if let PatKind::Struct(QPath::Resolved(_, ref path), fields, true) = pat.kind; + if let PatKind::Struct(QPath::Resolved(_, path), fields, true) = pat.kind; if let Some(def_id) = path.res.opt_def_id(); let ty = cx.tcx.type_of(def_id); if let ty::Adt(def, _) = ty.kind(); @@ -761,7 +761,7 @@ fn report_single_match_single_pattern( // PartialEq for different reference counts may not exist. "&".repeat(ref_count_diff), snippet(cx, arms[0].pat.span, ".."), - expr_block(cx, &arms[0].body, None, "..", Some(expr.span)), + expr_block(cx, arms[0].body, None, "..", Some(expr.span)), els_str, ); (msg, sugg) @@ -771,7 +771,7 @@ fn report_single_match_single_pattern( "if let {} = {} {}{}", snippet(cx, arms[0].pat.span, ".."), snippet(cx, ex.span, ".."), - expr_block(cx, &arms[0].body, None, "..", Some(expr.span)), + expr_block(cx, arms[0].body, None, "..", Some(expr.span)), els_str, ); (msg, sugg) @@ -809,7 +809,7 @@ fn check_single_match_opt_like( ]; let path = match arms[1].pat.kind { - PatKind::TupleStruct(ref path, ref inner, _) => { + PatKind::TupleStruct(ref path, inner, _) => { // Contains any non wildcard patterns (e.g., `Err(err)`)? if !inner.iter().all(is_wild) { return; @@ -841,7 +841,7 @@ fn check_match_bool(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: move |diag| { if arms.len() == 2 { // no guards - let exprs = if let PatKind::Lit(ref arm_bool) = arms[0].pat.kind { + let exprs = if let PatKind::Lit(arm_bool) = arms[0].pat.kind { if let ExprKind::Lit(ref lit) = arm_bool.kind { match lit.node { LitKind::Bool(true) => Some((&*arms[0].body, &*arms[1].body)), @@ -917,7 +917,7 @@ fn check_wild_err_arm<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm let ex_ty = cx.typeck_results().expr_ty(ex).peel_refs(); if is_type_diagnostic_item(cx, ex_ty, sym::result_type) { for arm in arms { - if let PatKind::TupleStruct(ref path, ref inner, _) = arm.pat.kind { + if let PatKind::TupleStruct(ref path, inner, _) = arm.pat.kind { let path_str = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false)); if path_str == "Err" { let mut matching_wild = inner.iter().any(is_wild); @@ -937,7 +937,7 @@ fn check_wild_err_arm<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm } if_chain! { if matching_wild; - if let ExprKind::Block(ref block, _) = arm.body.kind; + if let ExprKind::Block(block, _) = arm.body.kind; if is_panic_block(block); then { // `Err(_)` or `Err(_e)` arm with `panic!` found @@ -1143,9 +1143,7 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) // If the block contains only a `panic!` macro (as expression or statement) fn is_panic_block(block: &Block<'_>) -> bool { match (&block.expr, block.stmts.len(), block.stmts.first()) { - (&Some(ref exp), 0, _) => { - is_expn_of(exp.span, "panic").is_some() && is_expn_of(exp.span, "unreachable").is_none() - }, + (&Some(exp), 0, _) => is_expn_of(exp.span, "panic").is_some() && is_expn_of(exp.span, "unreachable").is_none(), (&None, 1, Some(stmt)) => { is_expn_of(stmt.span, "panic").is_some() && is_expn_of(stmt.span, "unreachable").is_none() }, @@ -1156,7 +1154,7 @@ fn is_panic_block(block: &Block<'_>) -> bool { fn check_match_ref_pats(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) { if has_only_ref_pats(arms) { let mut suggs = Vec::with_capacity(arms.len() + 1); - let (title, msg) = if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, ref inner) = ex.kind { + let (title, msg) = if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = ex.kind { let span = ex.span.source_callsite(); suggs.push((span, Sugg::hir_with_macro_callsite(cx, inner, "..").to_string())); ( @@ -1173,7 +1171,7 @@ fn check_match_ref_pats(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], e }; suggs.extend(arms.iter().filter_map(|a| { - if let PatKind::Ref(ref refp, _) = a.pat.kind { + if let PatKind::Ref(refp, _) = a.pat.kind { Some((a.pat.span, snippet(cx, refp.span, "..").to_string())) } else { None @@ -1242,7 +1240,7 @@ fn check_match_as_ref(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], exp fn check_wild_in_or_pats(cx: &LateContext<'_>, arms: &[Arm<'_>]) { for arm in arms { - if let PatKind::Or(ref fields) = arm.pat.kind { + if let PatKind::Or(fields) = arm.pat.kind { // look for multiple fields in this arm that contains at least one Wild pattern if fields.len() > 1 && fields.iter().any(is_wild) { span_lint_and_help( @@ -1308,7 +1306,7 @@ fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr // strip potential borrows (#6503), but only if the type is a reference let mut ex_new = ex; if let ExprKind::AddrOf(BorrowKind::Ref, .., ex_inner) = ex.kind { - if let ty::Ref(..) = cx.typeck_results().expr_ty(&ex_inner).kind() { + if let ty::Ref(..) = cx.typeck_results().expr_ty(ex_inner).kind() { ex_new = ex_inner; } }; @@ -1385,7 +1383,7 @@ fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[A let matched_vars = ex.span; let bind_names = arms[0].pat.span; - let match_body = remove_blocks(&arms[0].body); + let match_body = remove_blocks(arms[0].body); let mut snippet_body = if match_body.span.from_expansion() { Sugg::hir_with_macro_callsite(cx, match_body, "..").to_string() } else { @@ -1396,13 +1394,13 @@ fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[A match match_body.kind { ExprKind::Block(block, _) => { // macro + expr_ty(body) == () - if block.span.from_expansion() && cx.typeck_results().expr_ty(&match_body).is_unit() { + if block.span.from_expansion() && cx.typeck_results().expr_ty(match_body).is_unit() { snippet_body.push(';'); } }, _ => { // expr_ty(body) == () - if cx.typeck_results().expr_ty(&match_body).is_unit() { + if cx.typeck_results().expr_ty(match_body).is_unit() { snippet_body.push(';'); } }, @@ -1502,10 +1500,7 @@ fn opt_parent_let<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option<&'a Local<' fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) -> Vec> { arms.iter() .flat_map(|arm| { - if let Arm { - ref pat, guard: None, .. - } = *arm - { + if let Arm { pat, guard: None, .. } = *arm { if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind { let lhs = match lhs { Some(lhs) => constant(cx, cx.typeck_results(), lhs)?.0, @@ -1525,7 +1520,7 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) }); } - if let PatKind::Lit(ref value) = pat.kind { + if let PatKind::Lit(value) = pat.kind { let value = constant(cx, cx.typeck_results(), value)?.0; return Some(SpannedRange { span: pat.span, @@ -1572,8 +1567,8 @@ fn type_ranges(ranges: &[SpannedRange]) -> TypedRanges { fn is_unit_expr(expr: &Expr<'_>) -> bool { match expr.kind { - ExprKind::Tup(ref v) if v.is_empty() => true, - ExprKind::Block(ref b, _) if b.stmts.is_empty() && b.expr.is_none() => true, + ExprKind::Tup(v) if v.is_empty() => true, + ExprKind::Block(b, _) if b.stmts.is_empty() && b.expr.is_none() => true, _ => false, } } @@ -1586,14 +1581,14 @@ fn is_none_arm(arm: &Arm<'_>) -> bool { // Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`) fn is_ref_some_arm(arm: &Arm<'_>) -> Option { if_chain! { - if let PatKind::TupleStruct(ref path, ref pats, _) = arm.pat.kind; + if let PatKind::TupleStruct(ref path, pats, _) = arm.pat.kind; if pats.len() == 1 && match_qpath(path, &paths::OPTION_SOME); if let PatKind::Binding(rb, .., ident, _) = pats[0].kind; if rb == BindingAnnotation::Ref || rb == BindingAnnotation::RefMut; - if let ExprKind::Call(ref e, ref args) = remove_blocks(&arm.body).kind; + if let ExprKind::Call(e, args) = remove_blocks(arm.body).kind; if let ExprKind::Path(ref some_path) = e.kind; if match_qpath(some_path, &paths::OPTION_SOME) && args.len() == 1; - if let ExprKind::Path(QPath::Resolved(_, ref path2)) = args[0].kind; + if let ExprKind::Path(QPath::Resolved(_, path2)) = args[0].kind; if path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name; then { return Some(rb) @@ -1685,7 +1680,7 @@ where (&Kind::End(a, _), &Kind::Start(b, _)) if a != Bound::Included(b) => (), _ => { // skip if the range `a` is completely included into the range `b` - if let Ordering::Equal | Ordering::Less = a.cmp(&b) { + if let Ordering::Equal | Ordering::Less = a.cmp(b) { let kind_a = Kind::End(a.range().node.1, a.range()); let kind_b = Kind::End(b.range().node.1, b.range()); if let Ordering::Equal | Ordering::Greater = kind_a.cmp(&kind_b) { @@ -1737,7 +1732,7 @@ mod redundant_pattern_match { kind = &inner.kind; } let good_method = match kind { - PatKind::TupleStruct(ref path, ref patterns, _) if patterns.len() == 1 => { + PatKind::TupleStruct(ref path, patterns, _) if patterns.len() == 1 => { if let PatKind::Wild = patterns[0].kind { if match_qpath(path, &paths::RESULT_OK) { "is_ok()" @@ -1818,8 +1813,8 @@ mod redundant_pattern_match { let found_good_method = match node_pair { ( - PatKind::TupleStruct(ref path_left, ref patterns_left, _), - PatKind::TupleStruct(ref path_right, ref patterns_right, _), + PatKind::TupleStruct(ref path_left, patterns_left, _), + PatKind::TupleStruct(ref path_right, patterns_right, _), ) if patterns_left.len() == 1 && patterns_right.len() == 1 => { if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].kind, &patterns_right[0].kind) { find_good_method_for_match( @@ -1846,8 +1841,8 @@ mod redundant_pattern_match { None } }, - (PatKind::TupleStruct(ref path_left, ref patterns, _), PatKind::Path(ref path_right)) - | (PatKind::Path(ref path_left), PatKind::TupleStruct(ref path_right, ref patterns, _)) + (PatKind::TupleStruct(ref path_left, patterns, _), PatKind::Path(ref path_right)) + | (PatKind::Path(ref path_left), PatKind::TupleStruct(ref path_right, patterns, _)) if patterns.len() == 1 => { if let PatKind::Wild = patterns[0].kind { @@ -1969,10 +1964,10 @@ fn test_overlapping() { /// Implementation of `MATCH_SAME_ARMS`. fn lint_match_arms<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) { - if let ExprKind::Match(_, ref arms, MatchSource::Normal) = expr.kind { + if let ExprKind::Match(_, arms, MatchSource::Normal) = expr.kind { let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 { let mut h = SpanlessHash::new(cx); - h.hash_expr(&arm.body); + h.hash_expr(arm.body); h.finish() }; @@ -2008,7 +2003,7 @@ fn lint_match_arms<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) { (min_index..=max_index).all(|index| arms[index].guard.is_none()) && SpanlessEq::new(cx) .expr_fallback(eq_fallback) - .eq_expr(&lhs.body, &rhs.body) + .eq_expr(lhs.body, rhs.body) // these checks could be removed to allow unused bindings && bindings_eq(lhs.pat, local_map.keys().copied().collect()) && bindings_eq(rhs.pat, local_map.values().copied().collect()) diff --git a/clippy_lints/src/mem_discriminant.rs b/clippy_lints/src/mem_discriminant.rs index 7895ba9f1e0..a735c616f6e 100644 --- a/clippy_lints/src/mem_discriminant.rs +++ b/clippy_lints/src/mem_discriminant.rs @@ -34,7 +34,7 @@ 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(ref func, ref func_args) = expr.kind; + 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(); @@ -59,7 +59,7 @@ impl<'tcx> LateLintPass<'tcx> for MemDiscriminant { let mut derefs_needed = ptr_depth; let mut cur_expr = param; while derefs_needed > 0 { - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref inner_expr) = cur_expr.kind { + if let ExprKind::AddrOf(BorrowKind::Ref, _, inner_expr) = cur_expr.kind { derefs_needed -= 1; cur_expr = inner_expr; } else { diff --git a/clippy_lints/src/mem_forget.rs b/clippy_lints/src/mem_forget.rs index c13802e3953..a28cb5f32fe 100644 --- a/clippy_lints/src/mem_forget.rs +++ b/clippy_lints/src/mem_forget.rs @@ -28,7 +28,7 @@ declare_lint_pass!(MemForget => [MEM_FORGET]); impl<'tcx> LateLintPass<'tcx> for MemForget { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if let ExprKind::Call(ref path_expr, ref args) = e.kind { + if let ExprKind::Call(path_expr, args) = e.kind { if let ExprKind::Path(ref qpath) = path_expr.kind { if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id() { if match_def_path(cx, def_id, &paths::MEM_FORGET) { diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index 0418c616efa..e1d351aee45 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -109,14 +109,14 @@ fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &E // argument's type. All that's left is to get // replacee's path. let replaced_path = match dest.kind { - ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, ref replaced) => { - if let ExprKind::Path(QPath::Resolved(None, ref replaced_path)) = replaced.kind { + ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, replaced) => { + if let ExprKind::Path(QPath::Resolved(None, replaced_path)) = replaced.kind { replaced_path } else { return; } }, - ExprKind::Path(QPath::Resolved(None, ref replaced_path)) => replaced_path, + ExprKind::Path(QPath::Resolved(None, replaced_path)) => replaced_path, _ => return, }; @@ -161,7 +161,7 @@ fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<' } if_chain! { - if let ExprKind::Call(ref repl_func, ref repl_args) = src.kind; + if let ExprKind::Call(repl_func, repl_args) = src.kind; if repl_args.is_empty(); 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(); @@ -214,7 +214,7 @@ fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath< .iter() .any(|symbol| is_diagnostic_assoc_item(cx, def_id, *symbol)) { - if let QPath::TypeRelative(_, ref method) = path { + if let QPath::TypeRelative(_, method) = path { if method.ident.name == sym::new { return true; } @@ -226,7 +226,7 @@ fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath< fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) { if_chain! { - if let ExprKind::Call(ref repl_func, _) = src.kind; + if let ExprKind::Call(repl_func, _) = src.kind; if !in_external_macro(cx.tcx.sess, expr_span); 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(); @@ -273,11 +273,11 @@ impl<'tcx> LateLintPass<'tcx> for MemReplace { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { // Check that `expr` is a call to `mem::replace()` - if let ExprKind::Call(ref func, ref func_args) = expr.kind; + if let ExprKind::Call(func, func_args) = expr.kind; 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_REPLACE); - if let [dest, src] = &**func_args; + if let [dest, src] = func_args; then { check_replace_option_with_none(cx, src, dest, expr.span); check_replace_with_uninit(cx, src, dest, expr.span); diff --git a/clippy_lints/src/methods/bind_instead_of_map.rs b/clippy_lints/src/methods/bind_instead_of_map.rs index 46d4c674648..287bff886bf 100644 --- a/clippy_lints/src/methods/bind_instead_of_map.rs +++ b/clippy_lints/src/methods/bind_instead_of_map.rs @@ -71,7 +71,7 @@ pub(crate) trait BindInsteadOfMap { closure_args_span: Span, ) -> bool { if_chain! { - if let hir::ExprKind::Call(ref some_expr, [inner_expr]) = closure_expr.kind; + if let hir::ExprKind::Call(some_expr, [inner_expr]) = closure_expr.kind; if let hir::ExprKind::Path(QPath::Resolved(_, path)) = some_expr.kind; if Self::is_variant(cx, path.res); if !contains_return(inner_expr); @@ -107,7 +107,7 @@ pub(crate) trait BindInsteadOfMap { let can_sugg: bool = find_all_ret_expressions(cx, closure_expr, |ret_expr| { if_chain! { if !in_macro(ret_expr.span); - if let hir::ExprKind::Call(ref func_path, [arg]) = ret_expr.kind; + if let hir::ExprKind::Call(func_path, [arg]) = ret_expr.kind; if let hir::ExprKind::Path(QPath::Resolved(_, path)) = func_path.kind; if Self::is_variant(cx, path.res); if !contains_return(arg); diff --git a/clippy_lints/src/methods/chars_cmp.rs b/clippy_lints/src/methods/chars_cmp.rs index c668fe52781..514c4118765 100644 --- a/clippy_lints/src/methods/chars_cmp.rs +++ b/clippy_lints/src/methods/chars_cmp.rs @@ -19,7 +19,7 @@ pub(super) fn check( ) -> bool { if_chain! { if let Some(args) = method_chain_args(info.chain, chain_methods); - if let hir::ExprKind::Call(ref fun, ref arg_char) = info.other.kind; + if let hir::ExprKind::Call(fun, arg_char) = info.other.kind; if arg_char.len() == 1; if let hir::ExprKind::Path(ref qpath) = fun.kind; if let Some(segment) = single_segment_path(qpath); diff --git a/clippy_lints/src/methods/expect_fun_call.rs b/clippy_lints/src/methods/expect_fun_call.rs index e7bffa66b3f..03cb41697d5 100644 --- a/clippy_lints/src/methods/expect_fun_call.rs +++ b/clippy_lints/src/methods/expect_fun_call.rs @@ -100,9 +100,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_span: Spa applicability: &mut Applicability, ) -> Vec { if_chain! { - if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, ref format_arg) = a.kind; - if let hir::ExprKind::Match(ref format_arg_expr, _, _) = format_arg.kind; - if let hir::ExprKind::Tup(ref format_arg_expr_tup) = format_arg_expr.kind; + if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, format_arg) = a.kind; + if let hir::ExprKind::Match(format_arg_expr, _, _) = format_arg.kind; + if let hir::ExprKind::Tup(format_arg_expr_tup) = format_arg_expr.kind; then { format_arg_expr_tup @@ -155,7 +155,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_span: Spa if block.stmts.len() == 1; if let hir::StmtKind::Local(local) = &block.stmts[0].kind; if let Some(arg_root) = &local.init; - if let hir::ExprKind::Call(ref inner_fun, ref inner_args) = arg_root.kind; + if let hir::ExprKind::Call(inner_fun, inner_args) = arg_root.kind; if is_expn_of(inner_fun.span, "format").is_some() && inner_args.len() == 1; if let hir::ExprKind::Call(_, format_args) = &inner_args[0].kind; then { diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs index 45d1ed953b4..35fae450eeb 100644 --- a/clippy_lints/src/methods/filter_map.rs +++ b/clippy_lints/src/methods/filter_map.rs @@ -19,7 +19,7 @@ use super::OPTION_FILTER_MAP; fn is_method<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Symbol) -> bool { match &expr.kind { - hir::ExprKind::Path(QPath::TypeRelative(_, ref mname)) => mname.ident.name == method_name, + hir::ExprKind::Path(QPath::TypeRelative(_, mname)) => mname.ident.name == method_name, hir::ExprKind::Path(QPath::Resolved(_, segments)) => { segments.segments.last().unwrap().ident.name == method_name }, @@ -28,7 +28,7 @@ fn is_method<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Sy let closure_expr = remove_blocks(&body.value); let arg_id = body.params[0].pat.hir_id; match closure_expr.kind { - hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, _, ref args, _) => { + hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, _, args, _) => { if_chain! { if ident.name == method_name; if let hir::ExprKind::Path(path) = &args[0].kind; @@ -61,7 +61,7 @@ fn lint_filter_some_map_unwrap( methods_span: Span, ) { let iterator = is_trait_method(cx, expr, sym::Iterator); - let option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&filter_recv), sym::option_type); + let option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(filter_recv), sym::option_type); if (iterator || option) && is_option_filter_map(cx, filter_arg, map_arg) { let msg = "`filter` for `Some` followed by `unwrap`"; let help = "consider using `flatten` instead"; diff --git a/clippy_lints/src/methods/flat_map_identity.rs b/clippy_lints/src/methods/flat_map_identity.rs index 664885a2f0e..dd613d0cd63 100644 --- a/clippy_lints/src/methods/flat_map_identity.rs +++ b/clippy_lints/src/methods/flat_map_identity.rs @@ -35,7 +35,7 @@ pub(super) fn check<'tcx>( let body = cx.tcx.hir().body(*body_id); if let hir::PatKind::Binding(_, _, binding_ident, _) = body.params[0].pat.kind; - if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = body.value.kind; + if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = body.value.kind; if path.segments.len() == 1; if path.segments[0].ident.name == binding_ident.name; diff --git a/clippy_lints/src/methods/implicit_clone.rs b/clippy_lints/src/methods/implicit_clone.rs index 04461ad5c3a..1211e2f2bf7 100644 --- a/clippy_lints/src/methods/implicit_clone.rs +++ b/clippy_lints/src/methods/implicit_clone.rs @@ -13,7 +13,7 @@ use clippy_utils::is_diagnostic_assoc_item; pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, trait_diagnostic: Symbol) { if_chain! { if let ExprKind::MethodCall(method_path, _, [arg], _) = &expr.kind; - let return_type = cx.typeck_results().expr_ty(&expr); + let return_type = cx.typeck_results().expr_ty(expr); let input_type = cx.typeck_results().expr_ty(arg).peel_refs(); if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if let Some(ty_name) = input_type.ty_adt_def().map(|adt_def| cx.tcx.item_name(adt_def.did)); diff --git a/clippy_lints/src/methods/iter_next_slice.rs b/clippy_lints/src/methods/iter_next_slice.rs index dab0a43a096..a49851de38e 100644 --- a/clippy_lints/src/methods/iter_next_slice.rs +++ b/clippy_lints/src/methods/iter_next_slice.rs @@ -27,7 +27,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, cal if derefs_to_slice(cx, caller_expr, cx.typeck_results().expr_ty(caller_expr)).is_some() { // caller is a Slice if_chain! { - if let hir::ExprKind::Index(ref caller_var, ref index_expr) = &caller_expr.kind; + if let hir::ExprKind::Index(caller_var, index_expr) = &caller_expr.kind; if let Some(higher::Range { start: Some(start_expr), end: None, limits: ast::RangeLimits::HalfOpen }) = higher::range(index_expr); if let hir::ExprKind::Lit(ref start_lit) = &start_expr.kind; diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index fa6323b56e1..b1ade5addd6 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1740,10 +1740,10 @@ impl<'tcx> LateLintPass<'tcx> for Methods { check_methods(cx, expr, self.msrv.as_ref()); match expr.kind { - hir::ExprKind::Call(ref func, ref args) => { + hir::ExprKind::Call(func, args) => { from_iter_instead_of_collect::check(cx, expr, args, &func.kind); }, - hir::ExprKind::MethodCall(ref method_call, ref method_span, ref args, _) => { + hir::ExprKind::MethodCall(method_call, ref method_span, args, _) => { or_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args); expect_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args); clone_on_copy::check(cx, expr, method_call.ident.name, args); @@ -1753,9 +1753,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { into_iter_on_ref::check(cx, expr, *method_span, method_call.ident.name, args); single_char_pattern::check(cx, expr, method_call.ident.name, args); }, - hir::ExprKind::Binary(op, ref lhs, ref rhs) - if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => - { + hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => { let mut info = BinaryExprInfo { expr, chain: lhs, @@ -1763,7 +1761,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { eq: op.node == hir::BinOpKind::Eq, }; lint_binary_expr_with_method_call(cx, &mut info); - } + }, _ => (), } } @@ -1781,7 +1779,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { let implements_trait = matches!(item.kind, hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. })); if_chain! { if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind; - if let Some(first_arg) = iter_input_pats(&sig.decl, cx.tcx.hir().body(id)).next(); + if let Some(first_arg) = iter_input_pats(sig.decl, cx.tcx.hir().body(id)).next(); let method_sig = cx.tcx.fn_sig(impl_item.def_id); let method_sig = cx.tcx.erase_late_bound_regions(method_sig); @@ -1801,7 +1799,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { method_config.output_type.matches(&sig.decl.output) && method_config.self_kind.matches(cx, self_ty, first_arg_ty) && fn_header_equals(method_config.fn_header, sig.header) && - method_config.lifetime_param_cond(&impl_item) + method_config.lifetime_param_cond(impl_item) { span_lint_and_help( cx, @@ -2272,10 +2270,10 @@ impl OutType { let is_unit = |ty: &hir::Ty<'_>| matches!(ty.kind, hir::TyKind::Tup(&[])); match (self, ty) { (Self::Unit, &hir::FnRetTy::DefaultReturn(_)) => true, - (Self::Unit, &hir::FnRetTy::Return(ref ty)) if is_unit(ty) => true, - (Self::Bool, &hir::FnRetTy::Return(ref ty)) if is_bool(ty) => true, - (Self::Any, &hir::FnRetTy::Return(ref ty)) if !is_unit(ty) => true, - (Self::Ref, &hir::FnRetTy::Return(ref ty)) => matches!(ty.kind, hir::TyKind::Rptr(_, _)), + (Self::Unit, &hir::FnRetTy::Return(ty)) if is_unit(ty) => true, + (Self::Bool, &hir::FnRetTy::Return(ty)) if is_bool(ty) => true, + (Self::Any, &hir::FnRetTy::Return(ty)) if !is_unit(ty) => true, + (Self::Ref, &hir::FnRetTy::Return(ty)) => matches!(ty.kind, hir::TyKind::Rptr(_, _)), _ => false, } } diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs index 1367a0c21d8..7e9c8fa829d 100644 --- a/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/clippy_lints/src/methods/option_as_ref_deref.rs @@ -77,10 +77,10 @@ pub(super) fn check<'tcx>( } } }, - hir::ExprKind::AddrOf(hir::BorrowKind::Ref, m, ref inner) if same_mutability(m) => { + hir::ExprKind::AddrOf(hir::BorrowKind::Ref, m, inner) if same_mutability(m) => { if_chain! { - if let hir::ExprKind::Unary(hir::UnOp::Deref, ref inner1) = inner.kind; - if let hir::ExprKind::Unary(hir::UnOp::Deref, ref inner2) = inner1.kind; + if let hir::ExprKind::Unary(hir::UnOp::Deref, inner1) = inner.kind; + if let hir::ExprKind::Unary(hir::UnOp::Deref, inner2) = inner1.kind; then { path_to_local_id(inner2, closure_body.params[0].pat.hir_id) } else { diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index 89dedc5f0d8..df89da5d3e0 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -86,7 +86,7 @@ pub(super) fn check<'tcx>( (&paths::RESULT, true, &["or", "unwrap_or"], "else"), ]; - if let hir::ExprKind::MethodCall(ref path, _, ref args, _) = &arg.kind { + if let hir::ExprKind::MethodCall(path, _, args, _) = &arg.kind { if path.ident.as_str() == "len" { let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs(); @@ -105,7 +105,7 @@ pub(super) fn check<'tcx>( if KNOW_TYPES.iter().any(|k| k.2.contains(&name)); if is_lazyness_candidate(cx, arg); - if !contains_return(&arg); + if !contains_return(arg); let self_ty = cx.typeck_results().expr_ty(self_expr); @@ -158,7 +158,7 @@ pub(super) fn check<'tcx>( if args.len() == 2 { match args[1].kind { - hir::ExprKind::Call(ref fun, ref or_args) => { + hir::ExprKind::Call(fun, or_args) => { let or_has_args = !or_args.is_empty(); if !check_unwrap_or_default(cx, name, fun, &args[0], &args[1], or_has_args, expr.span) { let fun_span = if or_has_args { None } else { Some(fun.span) }; diff --git a/clippy_lints/src/methods/search_is_some.rs b/clippy_lints/src/methods/search_is_some.rs index 8a94d7f4155..ecec6fc3bb7 100644 --- a/clippy_lints/src/methods/search_is_some.rs +++ b/clippy_lints/src/methods/search_is_some.rs @@ -45,7 +45,7 @@ pub(super) fn check<'tcx>( then { if let hir::PatKind::Ref(..) = closure_arg.pat.kind { Some(search_snippet.replacen('&', "", 1)) - } else if let PatKind::Binding(_, _, ident, _) = strip_pat_refs(&closure_arg.pat).kind { + } else if let PatKind::Binding(_, _, ident, _) = strip_pat_refs(closure_arg.pat).kind { let name = &*ident.name.as_str(); Some(search_snippet.replace(&format!("*{}", name), name)) } else { @@ -108,8 +108,8 @@ pub(super) fn check<'tcx>( } }; if_chain! { - if is_string_or_str_slice(&search_recv); - if is_string_or_str_slice(&search_arg); + if is_string_or_str_slice(search_recv); + if is_string_or_str_slice(search_arg); then { let msg = format!("called `{}()` after calling `find()` on a string", option_check_method); match option_check_method { diff --git a/clippy_lints/src/methods/uninit_assumed_init.rs b/clippy_lints/src/methods/uninit_assumed_init.rs index 3cc1912b15a..0ae65c0c01d 100644 --- a/clippy_lints/src/methods/uninit_assumed_init.rs +++ b/clippy_lints/src/methods/uninit_assumed_init.rs @@ -10,7 +10,7 @@ use super::UNINIT_ASSUMED_INIT; /// lint for `MaybeUninit::uninit().assume_init()` (we already have the latter) pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { if_chain! { - if let hir::ExprKind::Call(ref callee, ref args) = recv.kind; + if let hir::ExprKind::Call(callee, args) = recv.kind; if args.is_empty(); if let hir::ExprKind::Path(ref path) = callee.kind; if match_qpath(path, &paths::MEM_MAYBEUNINIT_UNINIT); @@ -28,9 +28,9 @@ 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(ref component, _) => is_maybe_uninit_ty_valid(cx, component), - ty::Tuple(ref types) => types.types().all(|ty| is_maybe_uninit_ty_valid(cx, ty)), - ty::Adt(ref adt, _) => match_def_path(cx, adt.did, &paths::MEM_MAYBEUNINIT), + 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/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index 8e637e12393..0f28bfdf09e 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -20,9 +20,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr< let mutates_arg = mutated_variables(&body.value, cx).map_or(true, |used_mutably| used_mutably.contains(&arg_id)); - let (mut found_mapping, mut found_filtering) = check_expression(&cx, arg_id, &body.value); + let (mut found_mapping, mut found_filtering) = check_expression(cx, arg_id, &body.value); - let mut return_visitor = ReturnVisitor::new(&cx, arg_id); + let mut return_visitor = ReturnVisitor::new(cx, arg_id); return_visitor.visit_expr(&body.value); found_mapping |= return_visitor.found_mapping; found_filtering |= return_visitor.found_filtering; @@ -52,7 +52,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr< // returns (found_mapping, found_filtering) fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tcx hir::Expr<'_>) -> (bool, bool) { match &expr.kind { - hir::ExprKind::Call(ref func, ref args) => { + hir::ExprKind::Call(func, args) => { if let hir::ExprKind::Path(ref path) = func.kind { if match_qpath(path, &paths::OPTION_SOME) { if path_to_local_id(&args[0], arg_id) { @@ -65,22 +65,22 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc } (true, true) }, - hir::ExprKind::Block(ref block, _) => block + hir::ExprKind::Block(block, _) => block .expr .as_ref() - .map_or((false, false), |expr| check_expression(cx, arg_id, &expr)), + .map_or((false, false), |expr| check_expression(cx, arg_id, expr)), hir::ExprKind::Match(_, arms, _) => { let mut found_mapping = false; let mut found_filtering = false; for arm in *arms { - let (m, f) = check_expression(cx, arg_id, &arm.body); + let (m, f) = check_expression(cx, arg_id, arm.body); found_mapping |= m; found_filtering |= f; } (found_mapping, found_filtering) }, // There must be an else_arm or there will be a type error - hir::ExprKind::If(_, ref if_arm, Some(ref else_arm)) => { + hir::ExprKind::If(_, if_arm, Some(else_arm)) => { let if_check = check_expression(cx, arg_id, if_arm); let else_check = check_expression(cx, arg_id, else_arm); (if_check.0 | else_check.0, if_check.1 | else_check.1) diff --git a/clippy_lints/src/methods/unnecessary_fold.rs b/clippy_lints/src/methods/unnecessary_fold.rs index 7c16470348f..75517c48a21 100644 --- a/clippy_lints/src/methods/unnecessary_fold.rs +++ b/clippy_lints/src/methods/unnecessary_fold.rs @@ -34,13 +34,13 @@ pub(super) fn check( let closure_expr = remove_blocks(&closure_body.value); // Check if the closure body is of the form `acc some_expr(x)` - if let hir::ExprKind::Binary(ref bin_op, ref left_expr, ref right_expr) = closure_expr.kind; + if let hir::ExprKind::Binary(ref bin_op, left_expr, right_expr) = closure_expr.kind; if bin_op.node == op; // Extract the names of the two arguments to the closure if let [param_a, param_b] = closure_body.params; - if let PatKind::Binding(_, first_arg_id, ..) = strip_pat_refs(¶m_a.pat).kind; - if let PatKind::Binding(_, second_arg_id, second_arg_ident, _) = strip_pat_refs(¶m_b.pat).kind; + if let PatKind::Binding(_, first_arg_id, ..) = strip_pat_refs(param_a.pat).kind; + if let PatKind::Binding(_, second_arg_id, second_arg_ident, _) = strip_pat_refs(param_b.pat).kind; if path_to_local_id(left_expr, first_arg_id); if replacement_has_args || path_to_local_id(right_expr, second_arg_id); diff --git a/clippy_lints/src/methods/utils.rs b/clippy_lints/src/methods/utils.rs index ac6b55396da..f6bf37e08b9 100644 --- a/clippy_lints/src/methods/utils.rs +++ b/clippy_lints/src/methods/utils.rs @@ -26,7 +26,7 @@ pub(super) fn derefs_to_slice<'tcx>( } } - if let hir::ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind { + if let hir::ExprKind::MethodCall(path, _, args, _) = expr.kind { if path.ident.name == sym::iter && may_slice(cx, cx.typeck_results().expr_ty(&args[0])) { Some(&args[0]) } else { diff --git a/clippy_lints/src/methods/zst_offset.rs b/clippy_lints/src/methods/zst_offset.rs index 0489d0f6fcf..866cf616679 100644 --- a/clippy_lints/src/methods/zst_offset.rs +++ b/clippy_lints/src/methods/zst_offset.rs @@ -8,7 +8,7 @@ use super::ZST_OFFSET; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { if_chain! { - if let ty::RawPtr(ty::TypeAndMut { ref ty, .. }) = cx.typeck_results().expr_ty(recv).kind(); + if let ty::RawPtr(ty::TypeAndMut { ty, .. }) = cx.typeck_results().expr_ty(recv).kind(); if let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty)); if layout.is_zst(); then { diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index 776f4c7b741..45948f4d926 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -67,7 +67,7 @@ enum MinMax { fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Constant, &'a Expr<'a>)> { match expr.kind { - ExprKind::Call(ref path, ref args) => { + ExprKind::Call(path, args) => { if let ExprKind::Path(ref qpath) = path.kind { cx.typeck_results() .qpath_res(qpath, path.hir_id) @@ -85,7 +85,7 @@ fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Cons None } }, - ExprKind::MethodCall(ref path, _, ref args, _) => { + ExprKind::MethodCall(path, _, args, _) => { if_chain! { if let [obj, _] = args; if cx.typeck_results().expr_ty(obj).is_floating_point() || match_trait_method(cx, expr, &paths::ORD); diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index c23643cb2f5..afced5a5ce5 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -304,9 +304,9 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { if_chain! { if !in_external_macro(cx.tcx.sess, stmt.span); - if let StmtKind::Local(ref local) = stmt.kind; + if let StmtKind::Local(local) = stmt.kind; if let PatKind::Binding(an, .., name, None) = local.pat.kind; - if let Some(ref init) = local.init; + if let Some(init) = local.init; if !higher::is_from_for_desugar(local); if an == BindingAnnotation::Ref || an == BindingAnnotation::RefMut; then { @@ -322,7 +322,7 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { } else { ("", sugg_init.addr()) }; - let tyopt = if let Some(ref ty) = local.ty { + let tyopt = if let Some(ty) = local.ty { format!(": &{mutopt}{ty}", mutopt=mutopt, ty=snippet(cx, ty.span, "..")) } else { String::new() @@ -350,8 +350,8 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { } }; if_chain! { - if let StmtKind::Semi(ref expr) = stmt.kind; - if let ExprKind::Binary(ref binop, ref a, ref b) = expr.kind; + if let StmtKind::Semi(expr) = stmt.kind; + if let ExprKind::Binary(ref binop, a, b) = expr.kind; if binop.node == BinOpKind::And || binop.node == BinOpKind::Or; if let Some(sugg) = Sugg::hir_opt(cx, a); then { @@ -378,11 +378,11 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { match expr.kind { - ExprKind::Cast(ref e, ref ty) => { + ExprKind::Cast(e, ty) => { check_cast(cx, expr.span, e, ty); return; }, - ExprKind::Binary(ref cmp, ref left, ref right) => { + ExprKind::Binary(ref cmp, left, right) => { check_binary(cx, expr, cmp, left, right); return; }, @@ -501,12 +501,12 @@ fn is_allowed<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { // Return true if `expr` is the result of `signum()` invoked on a float value. fn is_signum(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { // The negation of a signum is still a signum - if let ExprKind::Unary(UnOp::Neg, ref child_expr) = expr.kind { - return is_signum(cx, &child_expr); + if let ExprKind::Unary(UnOp::Neg, child_expr) = expr.kind { + return is_signum(cx, child_expr); } if_chain! { - if let ExprKind::MethodCall(ref method_name, _, ref expressions, _) = expr.kind; + if let ExprKind::MethodCall(method_name, _, expressions, _) = expr.kind; if sym!(signum) == method_name.ident.name; // Check that the receiver of the signum() is a float (expressions[0] is the receiver of // the method call) @@ -552,7 +552,7 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: } let (arg_ty, snip) = match expr.kind { - ExprKind::MethodCall(.., ref args, _) if args.len() == 1 => { + ExprKind::MethodCall(.., args, _) if args.len() == 1 => { if_chain!( if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if is_diagnostic_assoc_item(cx, expr_def_id, sym::ToString) @@ -564,7 +564,7 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: } ) }, - ExprKind::Call(ref path, ref v) if v.len() == 1 => { + ExprKind::Call(path, v) if v.len() == 1 => { if let ExprKind::Path(ref path) = path.kind { if match_qpath(path, &["String", "from_str"]) || match_qpath(path, &["String", "from"]) { (cx.typeck_results().expr_ty(&v[0]), snippet(cx, v[0].span, "..")) @@ -649,7 +649,7 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: /// of what it means for an expression to be "used". fn is_used(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { get_parent_expr(cx, expr).map_or(true, |parent| match parent.kind { - ExprKind::Assign(_, ref rhs, _) | ExprKind::AssignOp(_, _, ref rhs) => SpanlessEq::new(cx).eq_expr(rhs, expr), + ExprKind::Assign(_, rhs, _) | ExprKind::AssignOp(_, _, rhs) => SpanlessEq::new(cx).eq_expr(rhs, expr), _ => is_used(cx, parent), }) } diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index 23554669d97..0dc02431ad5 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -138,7 +138,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { let mir = cx.tcx.optimized_mir(def_id); - if let Err((span, err)) = is_min_const_fn(cx.tcx, &mir) { + if let Err((span, err)) = is_min_const_fn(cx.tcx, mir) { if rustc_mir::const_eval::is_min_const_fn(cx.tcx, def_id.to_def_id()) { cx.tcx.sess.span_err(span, &err); } diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index dd4488f3f02..041fe64a1a9 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -96,7 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { let attrs = cx.tcx.hir().attrs(it.hir_id()); check_missing_inline_attrs(cx, attrs, it.span, desc); }, - hir::ItemKind::Trait(ref _is_auto, ref _unsafe, ref _generics, ref _bounds, trait_items) => { + hir::ItemKind::Trait(ref _is_auto, ref _unsafe, ref _generics, _bounds, trait_items) => { // note: we need to check if the trait is exported so we can't use // `LateLintPass::check_trait_item` here. for tit in trait_items { diff --git a/clippy_lints/src/mut_key.rs b/clippy_lints/src/mut_key.rs index 41bd07bcf1e..ed7b9cd62dc 100644 --- a/clippy_lints/src/mut_key.rs +++ b/clippy_lints/src/mut_key.rs @@ -58,21 +58,21 @@ declare_lint_pass!(MutableKeyType => [ MUTABLE_KEY_TYPE ]); impl<'tcx> LateLintPass<'tcx> for MutableKeyType { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { if let hir::ItemKind::Fn(ref sig, ..) = item.kind { - check_sig(cx, item.hir_id(), &sig.decl); + check_sig(cx, item.hir_id(), sig.decl); } } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'tcx>) { if let hir::ImplItemKind::Fn(ref sig, ..) = item.kind { if trait_ref_of_method(cx, item.hir_id()).is_none() { - check_sig(cx, item.hir_id(), &sig.decl); + check_sig(cx, item.hir_id(), sig.decl); } } } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'tcx>) { if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind { - check_sig(cx, item.hir_id(), &sig.decl); + check_sig(cx, item.hir_id(), sig.decl); } } diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index ef33e41a5fa..4b9c51d0c16 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -62,7 +62,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> { // Let's ignore the generated code. intravisit::walk_expr(self, arg); intravisit::walk_expr(self, body); - } else if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, ref e) = expr.kind { + } else if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, e) = expr.kind { if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, _) = e.kind { span_lint( self.cx, @@ -85,7 +85,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> { if let hir::TyKind::Rptr( _, hir::MutTy { - ty: ref pty, + ty: pty, mutbl: hir::Mutability::Mut, }, ) = ty.kind diff --git a/clippy_lints/src/mut_reference.rs b/clippy_lints/src/mut_reference.rs index 0c09ddb8073..b85cc4b9548 100644 --- a/clippy_lints/src/mut_reference.rs +++ b/clippy_lints/src/mut_reference.rs @@ -32,7 +32,7 @@ declare_lint_pass!(UnnecessaryMutPassed => [UNNECESSARY_MUT_PASSED]); impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { match e.kind { - ExprKind::Call(ref fn_expr, ref arguments) => { + ExprKind::Call(fn_expr, arguments) => { if let ExprKind::Path(ref path) = fn_expr.kind { check_arguments( cx, @@ -43,7 +43,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed { ); } }, - ExprKind::MethodCall(ref path, _, ref arguments, _) => { + ExprKind::MethodCall(path, _, arguments, _) => { let def_id = cx.typeck_results().type_dependent_def_id(e.hir_id).unwrap(); let substs = cx.typeck_results().node_substs(e.hir_id); let method_type = cx.tcx.type_of(def_id).subst(cx.tcx, substs); diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs index db7b3423ad9..96a58d1410f 100644 --- a/clippy_lints/src/needless_bool.rs +++ b/clippy_lints/src/needless_bool.rs @@ -71,7 +71,7 @@ declare_lint_pass!(NeedlessBool => [NEEDLESS_BOOL]); impl<'tcx> LateLintPass<'tcx> for NeedlessBool { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { use self::Expression::{Bool, RetBool}; - if let ExprKind::If(ref pred, ref then_block, Some(ref else_expr)) = e.kind { + if let ExprKind::If(pred, then_block, Some(else_expr)) = e.kind { let reduce = |ret, not| { let mut applicability = Applicability::MachineApplicable; let snip = Sugg::hir_with_applicability(cx, pred, "", &mut applicability); @@ -81,7 +81,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool { snip = snip.make_return(); } - if parent_node_is_if_expr(&e, &cx) { + if parent_node_is_if_expr(e, cx) { snip = snip.blockify() } @@ -95,7 +95,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool { applicability, ); }; - if let ExprKind::Block(ref then_block, _) = then_block.kind { + if let ExprKind::Block(then_block, _) = then_block.kind { match (fetch_bool_block(then_block), fetch_bool_expr(else_expr)) { (RetBool(true), RetBool(true)) | (Bool(true), Bool(true)) => { span_lint( @@ -225,7 +225,7 @@ fn check_comparison<'a, 'tcx>( ) { use self::Expression::{Bool, Other}; - if let ExprKind::Binary(op, ref left_side, ref right_side) = e.kind { + if let ExprKind::Binary(op, left_side, right_side) = e.kind { let (l_ty, r_ty) = ( cx.typeck_results().expr_ty(left_side), cx.typeck_results().expr_ty(right_side), @@ -237,7 +237,7 @@ fn check_comparison<'a, 'tcx>( let mut applicability = Applicability::MachineApplicable; if let BinOpKind::Eq = op.node { - let expression_info = one_side_is_unary_not(&left_side, &right_side); + let expression_info = one_side_is_unary_not(left_side, right_side); if expression_info.one_side_is_unary_not { span_lint_and_sugg( cx, @@ -324,9 +324,9 @@ fn fetch_bool_block(block: &Block<'_>) -> Expression { match (&*block.stmts, block.expr.as_ref()) { (&[], Some(e)) => fetch_bool_expr(&**e), (&[ref e], None) => { - if let StmtKind::Semi(ref e) = e.kind { + if let StmtKind::Semi(e) = e.kind { if let ExprKind::Ret(_) = e.kind { - fetch_bool_expr(&**e) + fetch_bool_expr(e) } else { Expression::Other } @@ -340,7 +340,7 @@ fn fetch_bool_block(block: &Block<'_>) -> Expression { fn fetch_bool_expr(expr: &Expr<'_>) -> Expression { match expr.kind { - ExprKind::Block(ref block, _) => fetch_bool_block(block), + ExprKind::Block(block, _) => fetch_bool_block(block), ExprKind::Lit(ref lit_ptr) => { if let LitKind::Bool(value) = lit_ptr.node { Expression::Bool(value) @@ -348,7 +348,7 @@ fn fetch_bool_expr(expr: &Expr<'_>) -> Expression { Expression::Other } }, - ExprKind::Ret(Some(ref expr)) => match fetch_bool_expr(expr) { + ExprKind::Ret(Some(expr)) => match fetch_bool_expr(expr) { Expression::Bool(value) => Expression::RetBool(value), _ => Expression::Other, }, diff --git a/clippy_lints/src/needless_borrow.rs b/clippy_lints/src/needless_borrow.rs index 79d84da2dfc..eef3c16730b 100644 --- a/clippy_lints/src/needless_borrow.rs +++ b/clippy_lints/src/needless_borrow.rs @@ -48,7 +48,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow { if e.span.from_expansion() || self.derived_item.is_some() { return; } - if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, ref inner) = e.kind { + if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = e.kind { if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(inner).kind() { for adj3 in cx.typeck_results().expr_adjustments(e).windows(3) { if let [Adjustment { diff --git a/clippy_lints/src/needless_borrowed_ref.rs b/clippy_lints/src/needless_borrowed_ref.rs index 7fbffe04a3f..0e976b130eb 100644 --- a/clippy_lints/src/needless_borrowed_ref.rs +++ b/clippy_lints/src/needless_borrowed_ref.rs @@ -51,7 +51,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef { if_chain! { // Only lint immutable refs, because `&mut ref T` may be useful. - if let PatKind::Ref(ref sub_pat, Mutability::Not) = pat.kind; + if let PatKind::Ref(sub_pat, Mutability::Not) = pat.kind; // Check sub_pat got a `ref` keyword (excluding `ref mut`). if let PatKind::Binding(BindingAnnotation::Ref, .., spanned_name, _) = sub_pat.kind; diff --git a/clippy_lints/src/needless_for_each.rs b/clippy_lints/src/needless_for_each.rs index 2ea871990f1..079b6642d58 100644 --- a/clippy_lints/src/needless_for_each.rs +++ b/clippy_lints/src/needless_for_each.rs @@ -70,7 +70,7 @@ impl LateLintPass<'_> for NeedlessForEach { ExprKind::Array(..) | ExprKind::Call(..) | ExprKind::Path(..) ); // Checks the type of the `iter` method receiver is NOT a user defined type. - if has_iter_method(cx, cx.typeck_results().expr_ty(&iter_recv)).is_some(); + if has_iter_method(cx, cx.typeck_results().expr_ty(iter_recv)).is_some(); // Skip the lint if the body is not block because this is simpler than `for` loop. // e.g. `v.iter().for_each(f)` is simpler and clearer than using `for` loop. if let ExprKind::Closure(_, _, body_id, ..) = for_each_arg.kind; diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 7370ba39922..780e2241293 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -208,7 +208,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { if is_type_diagnostic_item(cx, ty, sym::vec_type); if let Some(clone_spans) = get_spans(cx, Some(body.id()), idx, &[("clone", ".to_owned()")]); - if let TyKind::Path(QPath::Resolved(_, ref path)) = input.kind; + if let TyKind::Path(QPath::Resolved(_, path)) = input.kind; if let Some(elem_ty) = path.segments.iter() .find(|seg| seg.ident.name == sym::Vec) .and_then(|ps| ps.args.as_ref()) diff --git a/clippy_lints/src/needless_update.rs b/clippy_lints/src/needless_update.rs index e93de8a252a..8f325404deb 100644 --- a/clippy_lints/src/needless_update.rs +++ b/clippy_lints/src/needless_update.rs @@ -49,7 +49,7 @@ declare_lint_pass!(NeedlessUpdate => [NEEDLESS_UPDATE]); impl<'tcx> LateLintPass<'tcx> for NeedlessUpdate { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Struct(_, ref fields, Some(ref base)) = expr.kind { + if let ExprKind::Struct(_, fields, Some(base)) = expr.kind { let ty = cx.typeck_results().expr_ty(expr); if let ty::Adt(def, _) = ty.kind() { if fields.len() == def.non_enum_variant().fields.len() diff --git a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs index 4b935c7b906..0704173a011 100644 --- a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs +++ b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs @@ -51,8 +51,8 @@ impl<'tcx> LateLintPass<'tcx> for NoNegCompOpForPartialOrd { if_chain! { if !in_external_macro(cx.sess(), expr.span); - if let ExprKind::Unary(UnOp::Not, ref inner) = expr.kind; - if let ExprKind::Binary(ref op, ref left, _) = inner.kind; + if let ExprKind::Unary(UnOp::Not, inner) = expr.kind; + if let ExprKind::Binary(ref op, left, _) = inner.kind; if let BinOpKind::Le | BinOpKind::Ge | BinOpKind::Lt | BinOpKind::Gt = op.node; then { diff --git a/clippy_lints/src/neg_multiply.rs b/clippy_lints/src/neg_multiply.rs index 7b00879251f..34fd012572f 100644 --- a/clippy_lints/src/neg_multiply.rs +++ b/clippy_lints/src/neg_multiply.rs @@ -28,12 +28,12 @@ declare_lint_pass!(NegMultiply => [NEG_MULTIPLY]); #[allow(clippy::match_same_arms)] impl<'tcx> LateLintPass<'tcx> for NegMultiply { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if let ExprKind::Binary(ref op, ref left, ref right) = e.kind { + if let ExprKind::Binary(ref op, left, right) = e.kind { if BinOpKind::Mul == op.node { match (&left.kind, &right.kind) { (&ExprKind::Unary(..), &ExprKind::Unary(..)) => {}, - (&ExprKind::Unary(UnOp::Neg, ref lit), _) => check_mul(cx, e.span, lit, right), - (_, &ExprKind::Unary(UnOp::Neg, ref lit)) => check_mul(cx, e.span, lit, left), + (&ExprKind::Unary(UnOp::Neg, lit), _) => check_mul(cx, e.span, lit, right), + (_, &ExprKind::Unary(UnOp::Neg, lit)) => check_mul(cx, e.span, lit, left), _ => {}, } } diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index 83953a16bc8..cfcaf509471 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -51,23 +51,21 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { match expr.kind { ExprKind::Lit(..) | ExprKind::Closure(..) => true, ExprKind::Path(..) => !has_drop(cx, cx.typeck_results().expr_ty(expr)), - ExprKind::Index(ref a, ref b) | ExprKind::Binary(_, ref a, ref b) => { - has_no_effect(cx, a) && has_no_effect(cx, b) - }, - ExprKind::Array(ref v) | ExprKind::Tup(ref v) => v.iter().all(|val| has_no_effect(cx, val)), - ExprKind::Repeat(ref inner, _) - | ExprKind::Cast(ref inner, _) - | ExprKind::Type(ref inner, _) - | ExprKind::Unary(_, ref inner) - | ExprKind::Field(ref inner, _) - | ExprKind::AddrOf(_, _, ref inner) - | ExprKind::Box(ref inner) => has_no_effect(cx, inner), - ExprKind::Struct(_, ref fields, ref base) => { + ExprKind::Index(a, b) | ExprKind::Binary(_, a, b) => has_no_effect(cx, a) && has_no_effect(cx, b), + ExprKind::Array(v) | ExprKind::Tup(v) => v.iter().all(|val| has_no_effect(cx, val)), + ExprKind::Repeat(inner, _) + | ExprKind::Cast(inner, _) + | ExprKind::Type(inner, _) + | ExprKind::Unary(_, inner) + | ExprKind::Field(inner, _) + | ExprKind::AddrOf(_, _, inner) + | ExprKind::Box(inner) => has_no_effect(cx, inner), + ExprKind::Struct(_, fields, ref base) => { !has_drop(cx, cx.typeck_results().expr_ty(expr)) - && fields.iter().all(|field| has_no_effect(cx, &field.expr)) + && fields.iter().all(|field| has_no_effect(cx, field.expr)) && base.as_ref().map_or(true, |base| has_no_effect(cx, base)) }, - ExprKind::Call(ref callee, ref args) => { + ExprKind::Call(callee, args) => { if let ExprKind::Path(ref qpath) = callee.kind { let res = cx.qpath_res(qpath, callee.hir_id); match res { @@ -81,7 +79,7 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { false } }, - ExprKind::Block(ref block, _) => { + ExprKind::Block(block, _) => { block.stmts.is_empty() && block.expr.as_ref().map_or(false, |expr| has_no_effect(cx, expr)) }, _ => false, @@ -92,7 +90,7 @@ 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(ref expr) = stmt.kind { + if let StmtKind::Semi(expr) = stmt.kind { if has_no_effect(cx, expr) { span_lint(cx, NO_EFFECT, stmt.span, "statement with no effect"); } else if let Some(reduced) = reduce_expression(cx, expr) { @@ -127,26 +125,26 @@ fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option Some(vec![&**a, &**b]), - ExprKind::Binary(ref binop, ref a, ref b) if binop.node != BinOpKind::And && binop.node != BinOpKind::Or => { - Some(vec![&**a, &**b]) + ExprKind::Index(a, b) => Some(vec![a, b]), + ExprKind::Binary(ref binop, a, b) if binop.node != BinOpKind::And && binop.node != BinOpKind::Or => { + Some(vec![a, b]) }, - ExprKind::Array(ref v) | ExprKind::Tup(ref v) => Some(v.iter().collect()), - ExprKind::Repeat(ref inner, _) - | ExprKind::Cast(ref inner, _) - | ExprKind::Type(ref inner, _) - | ExprKind::Unary(_, ref inner) - | ExprKind::Field(ref inner, _) - | ExprKind::AddrOf(_, _, ref inner) - | ExprKind::Box(ref inner) => reduce_expression(cx, inner).or_else(|| Some(vec![inner])), - ExprKind::Struct(_, ref fields, ref base) => { + ExprKind::Array(v) | ExprKind::Tup(v) => Some(v.iter().collect()), + ExprKind::Repeat(inner, _) + | ExprKind::Cast(inner, _) + | ExprKind::Type(inner, _) + | ExprKind::Unary(_, inner) + | ExprKind::Field(inner, _) + | ExprKind::AddrOf(_, _, inner) + | ExprKind::Box(inner) => reduce_expression(cx, inner).or_else(|| Some(vec![inner])), + ExprKind::Struct(_, fields, ref base) => { if has_drop(cx, cx.typeck_results().expr_ty(expr)) { None } else { Some(fields.iter().map(|f| &f.expr).chain(base).map(Deref::deref).collect()) } }, - ExprKind::Call(ref callee, ref args) => { + ExprKind::Call(callee, args) => { if let ExprKind::Path(ref qpath) = callee.kind { let res = cx.qpath_res(qpath, callee.hir_id); match res { @@ -161,7 +159,7 @@ fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option { + ExprKind::Block(block, _) => { if block.stmts.is_empty() { block.expr.as_ref().and_then(|e| { match block.rules { diff --git a/clippy_lints/src/non_octal_unix_permissions.rs b/clippy_lints/src/non_octal_unix_permissions.rs index 6d45e7bc6cf..a83daea97bf 100644 --- a/clippy_lints/src/non_octal_unix_permissions.rs +++ b/clippy_lints/src/non_octal_unix_permissions.rs @@ -44,7 +44,7 @@ impl LateLintPass<'_> for NonOctalUnixPermissions { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { match &expr.kind { ExprKind::MethodCall(path, _, [func, param], _) => { - let obj_ty = cx.typeck_results().expr_ty(&func).peel_refs(); + let obj_ty = cx.typeck_results().expr_ty(func).peel_refs(); if_chain! { if (path.ident.name == sym!(mode) @@ -65,7 +65,7 @@ impl LateLintPass<'_> for NonOctalUnixPermissions { } } }, - ExprKind::Call(ref func, [param]) => { + ExprKind::Call(func, [param]) => { if_chain! { if let ExprKind::Path(ref path) = func.kind; if let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id(); diff --git a/clippy_lints/src/open_options.rs b/clippy_lints/src/open_options.rs index c61dff4b8e0..9efe45336bf 100644 --- a/clippy_lints/src/open_options.rs +++ b/clippy_lints/src/open_options.rs @@ -31,7 +31,7 @@ declare_lint_pass!(OpenOptions => [NONSENSICAL_OPEN_OPTIONS]); impl<'tcx> LateLintPass<'tcx> for OpenOptions { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if let ExprKind::MethodCall(ref path, _, ref arguments, _) = e.kind { + if let ExprKind::MethodCall(path, _, arguments, _) = e.kind { let obj_ty = cx.typeck_results().expr_ty(&arguments[0]).peel_refs(); if path.ident.name == sym!(open) && match_type(cx, obj_ty, &paths::OPEN_OPTIONS) { let mut options = Vec::new(); @@ -59,7 +59,7 @@ enum OpenOption { } fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec<(OpenOption, Argument)>) { - if let ExprKind::MethodCall(ref path, _, ref arguments, _) = argument.kind { + if let ExprKind::MethodCall(path, _, arguments, _) = argument.kind { let obj_ty = cx.typeck_results().expr_ty(&arguments[0]).peel_refs(); // Only proceed if this is a call on some object of type std::fs::OpenOptions diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index a76a4a33f1f..1b9120ae45f 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -66,9 +66,9 @@ declare_lint_pass!(OptionIfLetElse => [OPTION_IF_LET_ELSE]); /// Returns true iff the given expression is the result of calling `Result::ok` fn is_result_ok(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool { - if let ExprKind::MethodCall(ref path, _, &[ref receiver], _) = &expr.kind { + if let ExprKind::MethodCall(path, _, &[ref receiver], _) = &expr.kind { path.ident.name.as_str() == "ok" - && is_type_diagnostic_item(cx, &cx.typeck_results().expr_ty(&receiver), sym::result_type) + && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver), sym::result_type) } else { false } @@ -97,9 +97,9 @@ fn extract_body_from_arm<'a>(arm: &'a Arm<'a>) -> Option<&'a Expr<'a>> { ) = &arm.body.kind { if let [] = statements { - Some(&expr) + Some(expr) } else { - Some(&arm.body) + Some(arm.body) } } else { None diff --git a/clippy_lints/src/overflow_check_conditional.rs b/clippy_lints/src/overflow_check_conditional.rs index cf667c6e805..e222782c2cc 100644 --- a/clippy_lints/src/overflow_check_conditional.rs +++ b/clippy_lints/src/overflow_check_conditional.rs @@ -31,11 +31,11 @@ impl<'tcx> LateLintPass<'tcx> for OverflowCheckConditional { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let eq = |l, r| SpanlessEq::new(cx).eq_path_segment(l, r); if_chain! { - if let ExprKind::Binary(ref op, ref first, ref second) = expr.kind; - if let ExprKind::Binary(ref op2, ref ident1, ref ident2) = first.kind; - if let ExprKind::Path(QPath::Resolved(_, ref path1)) = ident1.kind; - if let ExprKind::Path(QPath::Resolved(_, ref path2)) = ident2.kind; - if let ExprKind::Path(QPath::Resolved(_, ref path3)) = second.kind; + if let ExprKind::Binary(ref op, first, second) = expr.kind; + if let ExprKind::Binary(ref op2, ident1, ident2) = first.kind; + if let ExprKind::Path(QPath::Resolved(_, path1)) = ident1.kind; + if let ExprKind::Path(QPath::Resolved(_, path2)) = ident2.kind; + if let ExprKind::Path(QPath::Resolved(_, path3)) = second.kind; if eq(&path1.segments[0], &path3.segments[0]) || eq(&path2.segments[0], &path3.segments[0]); if cx.typeck_results().expr_ty(ident1).is_integral(); if cx.typeck_results().expr_ty(ident2).is_integral(); @@ -56,11 +56,11 @@ impl<'tcx> LateLintPass<'tcx> for OverflowCheckConditional { } if_chain! { - if let ExprKind::Binary(ref op, ref first, ref second) = expr.kind; - if let ExprKind::Binary(ref op2, ref ident1, ref ident2) = second.kind; - if let ExprKind::Path(QPath::Resolved(_, ref path1)) = ident1.kind; - if let ExprKind::Path(QPath::Resolved(_, ref path2)) = ident2.kind; - if let ExprKind::Path(QPath::Resolved(_, ref path3)) = first.kind; + if let ExprKind::Binary(ref op, first, second) = expr.kind; + if let ExprKind::Binary(ref op2, ident1, ident2) = second.kind; + if let ExprKind::Path(QPath::Resolved(_, path1)) = ident1.kind; + if let ExprKind::Path(QPath::Resolved(_, path2)) = ident2.kind; + if let ExprKind::Path(QPath::Resolved(_, path3)) = first.kind; if eq(&path1.segments[0], &path3.segments[0]) || eq(&path2.segments[0], &path3.segments[0]); if cx.typeck_results().expr_ty(ident1).is_integral(); if cx.typeck_results().expr_ty(ident2).is_integral(); diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index 9a5b1c3b944..c86a847b2ee 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -141,11 +141,11 @@ impl<'tcx> PassByRefOrValue { }; if_chain! { - if !output_lts.contains(&input_lt); + if !output_lts.contains(input_lt); if is_copy(cx, ty); if let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes()); if size <= self.ref_min_size; - if let hir::TyKind::Rptr(_, MutTy { ty: ref decl_ty, .. }) = input.kind; + if let hir::TyKind::Rptr(_, MutTy { ty: decl_ty, .. }) = input.kind; then { let value_type = if is_self_ty(decl_ty) { "self".into() diff --git a/clippy_lints/src/path_buf_push_overwrite.rs b/clippy_lints/src/path_buf_push_overwrite.rs index 95ffae28d8c..00245926381 100644 --- a/clippy_lints/src/path_buf_push_overwrite.rs +++ b/clippy_lints/src/path_buf_push_overwrite.rs @@ -45,7 +45,7 @@ declare_lint_pass!(PathBufPushOverwrite => [PATH_BUF_PUSH_OVERWRITE]); impl<'tcx> LateLintPass<'tcx> for PathBufPushOverwrite { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { - if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind; + if let ExprKind::MethodCall(path, _, args, _) = expr.kind; if path.ident.name == sym!(push); if args.len() == 2; if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]).peel_refs(), sym::PathBuf); diff --git a/clippy_lints/src/pattern_type_mismatch.rs b/clippy_lints/src/pattern_type_mismatch.rs index 4550b367da4..8c198cecd6a 100644 --- a/clippy_lints/src/pattern_type_mismatch.rs +++ b/clippy_lints/src/pattern_type_mismatch.rs @@ -86,7 +86,7 @@ declare_lint_pass!(PatternTypeMismatch => [PATTERN_TYPE_MISMATCH]); impl<'tcx> LateLintPass<'tcx> for PatternTypeMismatch { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - if let StmtKind::Local(ref local) = stmt.kind { + if let StmtKind::Local(local) = stmt.kind { if let Some(init) = &local.init { if let Some(init_ty) = cx.typeck_results().node_type_opt(init.hir_id) { let pat = &local.pat; @@ -104,7 +104,7 @@ impl<'tcx> LateLintPass<'tcx> for PatternTypeMismatch { } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Match(ref expr, arms, source) = expr.kind { + if let ExprKind::Match(expr, arms, source) = expr.kind { match source { MatchSource::Normal | MatchSource::IfLetDesugar { .. } | MatchSource::WhileLetDesugar => { if let Some(expr_ty) = cx.typeck_results().node_type_opt(expr.hir_id) { @@ -135,7 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for PatternTypeMismatch { ) { if let Some(fn_sig) = cx.typeck_results().liberated_fn_sigs().get(hir_id) { for (param, ty) in body.params.iter().zip(fn_sig.inputs().iter()) { - apply_lint(cx, ¶m.pat, ty, DerefPossible::Impossible); + apply_lint(cx, param.pat, ty, DerefPossible::Impossible); } } } @@ -187,7 +187,7 @@ fn find_first_mismatch<'tcx>( ty: Ty<'tcx>, level: Level, ) -> Option<(Span, Mutability, Level)> { - if let PatKind::Ref(ref sub_pat, _) = pat.kind { + if let PatKind::Ref(sub_pat, _) = pat.kind { if let TyKind::Ref(_, sub_ty, _) = ty.kind() { return find_first_mismatch(cx, sub_pat, sub_ty, Level::Lower); } @@ -199,8 +199,8 @@ fn find_first_mismatch<'tcx>( } } - if let PatKind::Struct(ref qpath, ref field_pats, _) = pat.kind { - if let TyKind::Adt(ref adt_def, ref substs_ref) = ty.kind() { + if let PatKind::Struct(ref qpath, field_pats, _) = pat.kind { + if let TyKind::Adt(adt_def, substs_ref) = ty.kind() { if let Some(variant) = get_variant(adt_def, qpath) { let field_defs = &variant.fields; return find_first_mismatch_in_struct(cx, field_pats, field_defs, substs_ref); @@ -208,8 +208,8 @@ fn find_first_mismatch<'tcx>( } } - if let PatKind::TupleStruct(ref qpath, ref pats, _) = pat.kind { - if let TyKind::Adt(ref adt_def, ref substs_ref) = ty.kind() { + if let PatKind::TupleStruct(ref qpath, pats, _) = pat.kind { + if let TyKind::Adt(adt_def, substs_ref) = ty.kind() { if let Some(variant) = get_variant(adt_def, qpath) { let field_defs = &variant.fields; let ty_iter = field_defs.iter().map(|field_def| field_def.ty(cx.tcx, substs_ref)); @@ -218,7 +218,7 @@ fn find_first_mismatch<'tcx>( } } - if let PatKind::Tuple(ref pats, _) = pat.kind { + if let PatKind::Tuple(pats, _) = pat.kind { if let TyKind::Tuple(..) = ty.kind() { return find_first_mismatch_in_tuple(cx, pats, ty.tuple_fields()); } diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 6e530d0ffb0..09fcdb5faf8 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -124,7 +124,7 @@ declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF]); impl<'tcx> LateLintPass<'tcx> for Ptr { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if let ItemKind::Fn(ref sig, _, body_id) = item.kind { - check_fn(cx, &sig.decl, item.hir_id(), Some(body_id)); + check_fn(cx, sig.decl, item.hir_id(), Some(body_id)); } } @@ -136,7 +136,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { return; // ignore trait impls } } - check_fn(cx, &sig.decl, item.hir_id(), Some(body_id)); + check_fn(cx, sig.decl, item.hir_id(), Some(body_id)); } } @@ -147,12 +147,12 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { } else { None }; - check_fn(cx, &sig.decl, item.hir_id(), body_id); + check_fn(cx, sig.decl, item.hir_id(), body_id); } } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Binary(ref op, ref l, ref r) = expr.kind { + if let ExprKind::Binary(ref op, l, r) = expr.kind { if (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) && (is_null_path(l) || is_null_path(r)) { span_lint( cx, @@ -262,10 +262,10 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: } } else if match_type(cx, ty, &paths::COW) { if_chain! { - if let TyKind::Rptr(_, MutTy { ref ty, ..} ) = arg.kind; - if let TyKind::Path(QPath::Resolved(None, ref pp)) = ty.kind; + if let TyKind::Rptr(_, MutTy { ty, ..} ) = arg.kind; + if let TyKind::Path(QPath::Resolved(None, pp)) = ty.kind; if let [ref bx] = *pp.segments; - if let Some(ref params) = bx.args; + if let Some(params) = bx.args; if !params.parenthesized; if let Some(inner) = params.args.iter().find_map(|arg| match arg { GenericArg::Type(ty) => Some(ty), @@ -289,7 +289,7 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: } } - if let FnRetTy::Return(ref ty) = decl.output { + if let FnRetTy::Return(ty) = decl.output { if let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty) { let mut immutables = vec![]; for (_, ref mutbl, ref argspan) in decl @@ -322,8 +322,8 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: fn get_only_generic_arg_snippet(cx: &LateContext<'_>, arg: &Ty<'_>) -> Option { if_chain! { - if let TyKind::Path(QPath::Resolved(_, ref path)) = walk_ptrs_hir_ty(arg).kind; - if let Some(&PathSegment{args: Some(ref parameters), ..}) = path.segments.last(); + if let TyKind::Path(QPath::Resolved(_, path)) = walk_ptrs_hir_ty(arg).kind; + if let Some(&PathSegment{args: Some(parameters), ..}) = path.segments.last(); let types: Vec<_> = parameters.args.iter().filter_map(|arg| match arg { GenericArg::Type(ty) => Some(ty), _ => None, @@ -346,7 +346,7 @@ fn get_rptr_lm<'tcx>(ty: &'tcx Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, } fn is_null_path(expr: &Expr<'_>) -> bool { - if let ExprKind::Call(ref pathexp, ref args) = expr.kind { + if let ExprKind::Call(pathexp, args) = expr.kind { if args.is_empty() { if let ExprKind::Path(ref path) = pathexp.kind { return match_qpath(path, &paths::PTR_NULL) || match_qpath(path, &paths::PTR_NULL_MUT); diff --git a/clippy_lints/src/ptr_eq.rs b/clippy_lints/src/ptr_eq.rs index 5796c59c8b3..77cfa3f6b17 100644 --- a/clippy_lints/src/ptr_eq.rs +++ b/clippy_lints/src/ptr_eq.rs @@ -46,11 +46,11 @@ impl LateLintPass<'_> for PtrEq { return; } - if let ExprKind::Binary(ref op, ref left, ref right) = expr.kind { + if let ExprKind::Binary(ref op, left, right) = expr.kind { if BinOpKind::Eq == op.node { let (left, right) = match (expr_as_cast_to_usize(cx, left), expr_as_cast_to_usize(cx, right)) { (Some(lhs), Some(rhs)) => (lhs, rhs), - _ => (&**left, &**right), + _ => (left, right), }; if_chain! { @@ -79,7 +79,7 @@ impl LateLintPass<'_> for PtrEq { // E.g., `foo as *const _ as usize` returns `foo as *const _`. fn expr_as_cast_to_usize<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { if cx.typeck_results().expr_ty(cast_expr) == cx.tcx.types.usize { - if let ExprKind::Cast(ref expr, _) = cast_expr.kind { + if let ExprKind::Cast(expr, _) = cast_expr.kind { return Some(expr); } } @@ -90,7 +90,7 @@ fn expr_as_cast_to_usize<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_> // E.g., `foo as *const _` returns `foo`. fn expr_as_cast_to_raw_pointer<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { if cx.typeck_results().expr_ty(cast_expr).is_unsafe_ptr() { - if let ExprKind::Cast(ref expr, _) = cast_expr.kind { + if let ExprKind::Cast(expr, _) = cast_expr.kind { return Some(expr); } } diff --git a/clippy_lints/src/ptr_offset_with_cast.rs b/clippy_lints/src/ptr_offset_with_cast.rs index c04b4255256..afb198f4955 100644 --- a/clippy_lints/src/ptr_offset_with_cast.rs +++ b/clippy_lints/src/ptr_offset_with_cast.rs @@ -78,8 +78,8 @@ impl<'tcx> LateLintPass<'tcx> for PtrOffsetWithCast { // If the given expression is a cast from a usize, return the lhs of the cast fn expr_as_cast_from_usize<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { - if let ExprKind::Cast(ref cast_lhs_expr, _) = expr.kind { - if is_expr_ty_usize(cx, &cast_lhs_expr) { + if let ExprKind::Cast(cast_lhs_expr, _) = expr.kind { + if is_expr_ty_usize(cx, cast_lhs_expr) { return Some(cast_lhs_expr); } } @@ -92,7 +92,7 @@ fn expr_as_ptr_offset_call<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Method)> { - if let ExprKind::MethodCall(ref path_segment, _, ref args, _) = expr.kind { + if let ExprKind::MethodCall(path_segment, _, args, _) = expr.kind { if is_expr_ty_raw_ptr(cx, &args[0]) { if path_segment.ident.name == sym::offset { return Some((&args[0], &args[1], Method::Offset)); diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index 2054255a7c9..6d720f43851 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -148,14 +148,14 @@ impl QuestionMark { fn expression_returns_none(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool { match expression.kind { - ExprKind::Block(ref block, _) => { + ExprKind::Block(block, _) => { if let Some(return_expression) = Self::return_expression(block) { - return Self::expression_returns_none(cx, &return_expression); + return Self::expression_returns_none(cx, return_expression); } false }, - ExprKind::Ret(Some(ref expr)) => Self::expression_returns_none(cx, expr), + ExprKind::Ret(Some(expr)) => Self::expression_returns_none(cx, expr), ExprKind::Path(ref qp) => { if let Res::Def(DefKind::Ctor(def::CtorOf::Variant, def::CtorKind::Const), def_id) = cx.qpath_res(qp, expression.hir_id) @@ -174,7 +174,7 @@ impl QuestionMark { if_chain! { if block.stmts.len() == 1; if let Some(expr) = block.stmts.iter().last(); - if let StmtKind::Semi(ref expr) = expr.kind; + if let StmtKind::Semi(expr) = expr.kind; if let ExprKind::Ret(Some(ret_expr)) = expr.kind; then { diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 79692abb6ac..1c3c125e579 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -183,10 +183,10 @@ impl_lint_pass!(Ranges => [ impl<'tcx> LateLintPass<'tcx> for Ranges { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { match expr.kind { - ExprKind::MethodCall(ref path, _, ref args, _) => { + ExprKind::MethodCall(path, _, args, _) => { check_range_zip_with_len(cx, path, args, expr.span); }, - ExprKind::Binary(ref op, ref l, ref r) => { + ExprKind::Binary(ref op, l, r) => { if meets_msrv(self.msrv.as_ref(), &MANUAL_RANGE_CONTAINS_MSRV) { check_possible_range_contains(cx, op.node, l, r, expr); } @@ -287,7 +287,7 @@ fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<' } fn check_range_bounds(cx: &LateContext<'_>, ex: &Expr<'_>) -> Option<(Constant, Ident, Span, Span, Ordering, bool)> { - if let ExprKind::Binary(ref op, ref l, ref r) = ex.kind { + if let ExprKind::Binary(ref op, l, r) = ex.kind { let (inclusive, ordering) = match op.node { BinOpKind::Gt => (false, Ordering::Greater), BinOpKind::Ge => (true, Ordering::Greater), @@ -324,18 +324,18 @@ fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args: if path.ident.as_str() == "zip"; if let [iter, zip_arg] = args; // `.iter()` call - if let ExprKind::MethodCall(ref iter_path, _, ref iter_args, _) = iter.kind; + if let ExprKind::MethodCall(iter_path, _, iter_args, _) = iter.kind; if iter_path.ident.name == sym::iter; // range expression in `.zip()` call: `0..x.len()` if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::range(zip_arg); if is_integer_const(cx, start, 0); // `.len()` call - if let ExprKind::MethodCall(ref len_path, _, ref len_args, _) = end.kind; + if let ExprKind::MethodCall(len_path, _, len_args, _) = end.kind; if len_path.ident.name == sym!(len) && len_args.len() == 1; // `.iter()` and `.len()` called on same `Path` - if let ExprKind::Path(QPath::Resolved(_, ref iter_path)) = iter_args[0].kind; - if let ExprKind::Path(QPath::Resolved(_, ref len_path)) = len_args[0].kind; - if SpanlessEq::new(cx).eq_path_segments(&iter_path.segments, &len_path.segments); + if let ExprKind::Path(QPath::Resolved(_, iter_path)) = iter_args[0].kind; + if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_args[0].kind; + if SpanlessEq::new(cx).eq_path_segments(iter_path.segments, len_path.segments); then { span_lint(cx, RANGE_ZIP_WITH_LEN, @@ -508,8 +508,8 @@ fn y_plus_one<'t>(cx: &LateContext<'_>, expr: &'t Expr<'_>) -> Option<&'t Expr<' Spanned { node: BinOpKind::Add, .. }, - ref lhs, - ref rhs, + lhs, + rhs, ) => { if is_integer_const(cx, lhs, 1) { Some(rhs) @@ -529,8 +529,8 @@ fn y_minus_one<'t>(cx: &LateContext<'_>, expr: &'t Expr<'_>) -> Option<&'t Expr< Spanned { node: BinOpKind::Sub, .. }, - ref lhs, - ref rhs, + lhs, + rhs, ) if is_integer_const(cx, rhs, 1) => Some(lhs), _ => None, } diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 9656ee64c81..19650c41b84 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -94,7 +94,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { .into_results_cursor(mir); let mut possible_borrower = { let mut vis = PossibleBorrowerVisitor::new(cx, mir); - vis.visit_body(&mir); + vis.visit_body(mir); vis.into_map(cx, maybe_storage_live_result) }; @@ -126,7 +126,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { continue; } - if let ty::Adt(ref def, _) = arg_ty.kind() { + if let ty::Adt(def, _) = arg_ty.kind() { if match_def_path(cx, def.did, &paths::MEM_MANUALLY_DROP) { continue; } @@ -206,7 +206,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { clone_consumed_or_mutated: true, } } else { - let clone_usage = visit_clone_usage(local, ret_local, &mir, bb); + let clone_usage = visit_clone_usage(local, ret_local, mir, bb); if clone_usage.cloned_used && clone_usage.clone_consumed_or_mutated { // cloned value is used, and the clone is modified or moved continue; @@ -426,7 +426,7 @@ fn visit_clone_usage(cloned: mir::Local, clone: mir::Local, mir: &mir::Body<'_>, // TODO: Actually check for mutation of non-temporaries. clone_consumed_or_mutated: mir.local_kind(clone) != mir::LocalKind::Temp, }; - traversal::ReversePostorder::new(&mir, bb) + traversal::ReversePostorder::new(mir, bb) .skip(1) .fold(init, |usage, (tbb, tdata)| { // Short-circuit @@ -588,7 +588,7 @@ impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'tcx> { // If the call returns something with lifetimes, // let's conservatively assume the returned value contains lifetime of all the arguments. // For example, given `let y: Foo<'a> = foo(x)`, `y` is considered to be a possible borrower of `x`. - if ContainsRegion.visit_ty(&self.body.local_decls[*dest].ty).is_continue() { + if ContainsRegion.visit_ty(self.body.local_decls[*dest].ty).is_continue() { return; } diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index 5429d389610..92921bedf4d 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -111,8 +111,8 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { if_chain! { - if let hir::ExprKind::Call(ref closure, _) = expr.kind; - if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = closure.kind; + if let hir::ExprKind::Call(closure, _) = expr.kind; + if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = closure.kind; if self.path.segments[0].ident == path.segments[0].ident; if self.path.res == path.res; then { @@ -133,14 +133,14 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { for w in block.stmts.windows(2) { if_chain! { - if let hir::StmtKind::Local(ref local) = w[0].kind; - if let Option::Some(ref t) = local.init; + if let hir::StmtKind::Local(local) = w[0].kind; + if let Option::Some(t) = local.init; if let hir::ExprKind::Closure(..) = t.kind; if let hir::PatKind::Binding(_, _, ident, _) = local.pat.kind; - if let hir::StmtKind::Semi(ref second) = w[1].kind; - if let hir::ExprKind::Assign(_, ref call, _) = second.kind; - if let hir::ExprKind::Call(ref closure, _) = call.kind; - if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = closure.kind; + if let hir::StmtKind::Semi(second) = w[1].kind; + if let hir::ExprKind::Assign(_, call, _) = second.kind; + if let hir::ExprKind::Call(closure, _) = call.kind; + if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = closure.kind; if ident == path.segments[0].ident; if count_closure_usage(cx, block, path) == 1; then { diff --git a/clippy_lints/src/ref_option_ref.rs b/clippy_lints/src/ref_option_ref.rs index 0922cfa494e..0cf4e0ce7fe 100644 --- a/clippy_lints/src/ref_option_ref.rs +++ b/clippy_lints/src/ref_option_ref.rs @@ -44,7 +44,7 @@ impl<'tcx> LateLintPass<'tcx> for RefOptionRef { if let Some(def_id) = res.opt_def_id(); if cx.tcx.is_diagnostic_item(sym::option_type, def_id); - if let Some(ref params) = last_path_segment(qpath).args ; + if let Some(params) = last_path_segment(qpath).args ; if !params.parenthesized; if let Some(inner_ty) = params.args.iter().find_map(|arg| match arg { GenericArg::Type(inner_ty) => Some(inner_ty), diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index 1cc332de894..4b5306de58e 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -60,7 +60,7 @@ impl_lint_pass!(Regex => [INVALID_REGEX, TRIVIAL_REGEX]); impl<'tcx> LateLintPass<'tcx> for Regex { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { - if let ExprKind::Call(ref fun, ref args) = expr.kind; + if let ExprKind::Call(fun, args) = expr.kind; if let ExprKind::Path(ref qpath) = fun.kind; if args.len() == 1; if let Some(def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id(); @@ -134,7 +134,7 @@ fn is_trivial_regex(s: ®ex_syntax::hir::Hir) -> Option<&'static str> { fn check_set<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { if_chain! { - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref expr) = expr.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, _, expr) = expr.kind; if let ExprKind::Array(exprs) = expr.kind; then { for expr in exprs { diff --git a/clippy_lints/src/repeat_once.rs b/clippy_lints/src/repeat_once.rs index 63e5ec69e66..560a5e7c920 100644 --- a/clippy_lints/src/repeat_once.rs +++ b/clippy_lints/src/repeat_once.rs @@ -45,10 +45,10 @@ 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 let Some(Constant::Int(1)) = constant_context(cx, cx.typeck_results()).expr(count); if !in_macro(receiver.span); then { - let ty = cx.typeck_results().expr_ty(&receiver).peel_refs(); + let ty = cx.typeck_results().expr_ty(receiver).peel_refs(); if ty.is_str() { span_lint_and_sugg( cx, diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 8995ae431ad..af772cf4a14 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -102,7 +102,7 @@ impl<'tcx> LateLintPass<'tcx> for Return { err.span_label(local.span, "unnecessary `let` binding"); if let Some(mut snippet) = snippet_opt(cx, initexpr.span) { - if !cx.typeck_results().expr_adjustments(&retexpr).is_empty() { + if !cx.typeck_results().expr_adjustments(retexpr).is_empty() { snippet.push_str(" as _"); } err.multipart_suggestion( @@ -143,7 +143,7 @@ impl<'tcx> LateLintPass<'tcx> for Return { check_final_expr(cx, &body.value, Some(body.value.span), replacement) }, FnKind::ItemFn(..) | FnKind::Method(..) => { - if let ExprKind::Block(ref block, _) = body.value.kind { + if let ExprKind::Block(block, _) = body.value.kind { check_block_return(cx, block); } }, @@ -160,7 +160,7 @@ fn check_block_return<'tcx>(cx: &LateContext<'tcx>, block: &Block<'tcx>) { check_final_expr(cx, expr, Some(expr.span), RetReplacement::Empty); } else if let Some(stmt) = block.stmts.iter().last() { match stmt.kind { - StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => { + StmtKind::Expr(expr) | StmtKind::Semi(expr) => { check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty); }, _ => (), @@ -192,11 +192,11 @@ fn check_final_expr<'tcx>( } }, // a whole block? check it! - ExprKind::Block(ref block, _) => { + ExprKind::Block(block, _) => { check_block_return(cx, block); }, ExprKind::If(_, then, else_clause_opt) => { - if let ExprKind::Block(ref ifblock, _) = then.kind { + if let ExprKind::Block(ifblock, _) = then.kind { check_block_return(cx, ifblock); } if let Some(else_clause) = else_clause_opt { @@ -207,16 +207,16 @@ fn check_final_expr<'tcx>( // an if/if let expr, check both exprs // note, if without else is going to be a type checking error anyways // (except for unit type functions) so we don't match it - ExprKind::Match(_, ref arms, source) => match source { + ExprKind::Match(_, arms, source) => match source { MatchSource::Normal => { for arm in arms.iter() { - check_final_expr(cx, &arm.body, Some(arm.body.span), RetReplacement::Block); + check_final_expr(cx, arm.body, Some(arm.body.span), RetReplacement::Block); } }, MatchSource::IfLetDesugar { contains_else_clause: true, } => { - if let ExprKind::Block(ref ifblock, _) = arms[0].body.kind { + if let ExprKind::Block(ifblock, _) = arms[0].body.kind { check_block_return(cx, ifblock); } check_final_expr(cx, arms[1].body, None, RetReplacement::Empty); diff --git a/clippy_lints/src/semicolon_if_nothing_returned.rs b/clippy_lints/src/semicolon_if_nothing_returned.rs index f61af15fbed..553987a426b 100644 --- a/clippy_lints/src/semicolon_if_nothing_returned.rs +++ b/clippy_lints/src/semicolon_if_nothing_returned.rs @@ -51,7 +51,7 @@ impl LateLintPass<'_> for SemicolonIfNothingReturned { return; } - let sugg = sugg::Sugg::hir_with_macro_callsite(cx, &expr, ".."); + let sugg = sugg::Sugg::hir_with_macro_callsite(cx, expr, ".."); let suggestion = format!("{0};", sugg); span_lint_and_sugg( cx, diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index 612d2fd84cb..d6101bd5e36 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -130,12 +130,12 @@ fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>, bindings: & let len = bindings.len(); for stmt in block.stmts { match stmt.kind { - StmtKind::Local(ref local) => check_local(cx, local, bindings), - StmtKind::Expr(ref e) | StmtKind::Semi(ref e) => check_expr(cx, e, bindings), + StmtKind::Local(local) => check_local(cx, local, bindings), + StmtKind::Expr(e) | StmtKind::Semi(e) => check_expr(cx, e, bindings), StmtKind::Item(..) => {}, } } - if let Some(ref o) = block.expr { + if let Some(o) = block.expr { check_expr(cx, o, bindings); } bindings.truncate(len); @@ -149,16 +149,16 @@ fn check_local<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>, bindings: & return; } let Local { - ref pat, + pat, ref ty, ref init, span, .. } = *local; - if let Some(ref t) = *ty { + if let Some(t) = *ty { check_ty(cx, t, bindings) } - if let Some(ref o) = *init { + if let Some(o) = *init { check_expr(cx, o, bindings); check_pat(cx, pat, Some(o), span, bindings); } else { @@ -196,34 +196,34 @@ fn check_pat<'tcx>( bindings.push((name, ident.span)); } } - if let Some(ref p) = *inner { + 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(_, ref efields, _) = init_struct.kind { + 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); + check_pat(cx, field.pat, efield, span, bindings); } } else { for field in pfields { - check_pat(cx, &field.pat, init, span, bindings); + check_pat(cx, field.pat, init, span, bindings); } } } else { for field in pfields { - check_pat(cx, &field.pat, None, span, bindings); + check_pat(cx, field.pat, None, span, bindings); } } }, PatKind::Tuple(inner, _) => { if let Some(init_tup) = init { - if let ExprKind::Tup(ref tup) = init_tup.kind { + 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); } @@ -238,10 +238,10 @@ fn check_pat<'tcx>( } } }, - PatKind::Box(ref inner) => { + PatKind::Box(inner) => { if let Some(initp) = init { - if let ExprKind::Box(ref inner_init) = initp.kind { - check_pat(cx, inner, Some(&**inner_init), span, bindings); + 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); } @@ -249,7 +249,7 @@ fn check_pat<'tcx>( check_pat(cx, inner, init, span, bindings); } }, - PatKind::Ref(ref inner, _) => check_pat(cx, inner, init, span, bindings), + PatKind::Ref(inner, _) => check_pat(cx, inner, init, span, bindings), // PatVec(Vec>, Option>, Vec>), _ => (), } @@ -323,11 +323,10 @@ fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, bindings: &mut return; } match expr.kind { - ExprKind::Unary(_, ref e) - | ExprKind::Field(ref e, _) - | ExprKind::AddrOf(_, _, ref e) - | ExprKind::Box(ref e) => check_expr(cx, e, bindings), - ExprKind::Block(ref block, _) | ExprKind::Loop(ref block, ..) => check_block(cx, block, bindings), + 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) => { @@ -335,18 +334,18 @@ fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, bindings: &mut check_expr(cx, e, bindings) } }, - ExprKind::If(ref cond, ref then, ref otherwise) => { + ExprKind::If(cond, then, ref otherwise) => { check_expr(cx, cond, bindings); - check_expr(cx, &**then, bindings); - if let Some(ref o) = *otherwise { + check_expr(cx, then, bindings); + if let Some(o) = *otherwise { check_expr(cx, o, bindings); } }, - ExprKind::Match(ref init, arms, _) => { + 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); + 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 { @@ -357,7 +356,7 @@ fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, bindings: &mut }, } } - check_expr(cx, &arm.body, bindings); + check_expr(cx, arm.body, bindings); bindings.truncate(len); } }, @@ -367,14 +366,12 @@ fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, bindings: &mut fn check_ty<'tcx>(cx: &LateContext<'tcx>, ty: &'tcx Ty<'_>, bindings: &mut Vec<(Symbol, Span)>) { match ty.kind { - TyKind::Slice(ref sty) => check_ty(cx, sty, bindings), - TyKind::Array(ref fty, ref anon_const) => { + 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: ref mty, .. }) | TyKind::Rptr(_, MutTy { ty: ref mty, .. }) => { - check_ty(cx, mty, 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) @@ -387,12 +384,12 @@ fn check_ty<'tcx>(cx: &LateContext<'tcx>, ty: &'tcx Ty<'_>, bindings: &mut Vec<( fn is_self_shadow(name: Symbol, expr: &Expr<'_>) -> bool { match expr.kind { - ExprKind::Box(ref inner) | ExprKind::AddrOf(_, _, ref inner) => is_self_shadow(name, inner), - ExprKind::Block(ref block, _) => { + 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, ref inner) => (UnOp::Deref == op) && is_self_shadow(name, inner), - ExprKind::Path(QPath::Resolved(_, ref path)) => path_eq_name(name, path), + ExprKind::Unary(op, inner) => (UnOp::Deref == op) && is_self_shadow(name, inner), + ExprKind::Path(QPath::Resolved(_, path)) => path_eq_name(name, path), _ => false, } } diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index d55a83f1613..8cf89ae456e 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -70,14 +70,14 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // Matches initialization on reassignements. For example: `vec = Vec::with_capacity(100)` if_chain! { - if let ExprKind::Assign(ref left, ref right, _) = expr.kind; + if let ExprKind::Assign(left, right, _) = expr.kind; // Extract variable name - if let ExprKind::Path(QPath::Resolved(_, ref path)) = left.kind; + if let ExprKind::Path(QPath::Resolved(_, path)) = left.kind; if let Some(variable_name) = path.segments.get(0); // Extract len argument - if let Some(ref len_arg) = Self::is_vec_with_capacity(right); + if let Some(len_arg) = Self::is_vec_with_capacity(right); then { let vi = VecAllocation { @@ -94,10 +94,10 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { // Matches statements which initializes vectors. For example: `let mut vec = Vec::with_capacity(10)` if_chain! { - if let StmtKind::Local(ref local) = stmt.kind; + if let StmtKind::Local(local) = stmt.kind; if let PatKind::Binding(BindingAnnotation::Mutable, .., variable_name, None) = local.pat.kind; - if let Some(ref init) = local.init; - if let Some(ref len_arg) = Self::is_vec_with_capacity(init); + if let Some(init) = local.init; + if let Some(len_arg) = Self::is_vec_with_capacity(init); then { let vi = VecAllocation { @@ -117,7 +117,7 @@ impl SlowVectorInit { /// of the first argument of `with_capacity` call if it matches or `None` if it does not. fn is_vec_with_capacity<'tcx>(expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { if_chain! { - if let ExprKind::Call(ref func, ref args) = expr.kind; + if let ExprKind::Call(func, args) = expr.kind; if let ExprKind::Path(ref path) = func.kind; if match_qpath(path, &["Vec", "with_capacity"]); if args.len() == 1; @@ -208,11 +208,11 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { fn search_slow_extend_filling(&mut self, expr: &'tcx Expr<'_>) { if_chain! { if self.initialization_found; - if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind; + if let ExprKind::MethodCall(path, _, args, _) = expr.kind; if let ExprKind::Path(ref qpath_subj) = args[0].kind; - if match_qpath(&qpath_subj, &[&*self.vec_alloc.variable_name.as_str()]); + if match_qpath(qpath_subj, &[&*self.vec_alloc.variable_name.as_str()]); if path.ident.name == sym!(extend); - if let Some(ref extend_arg) = args.get(1); + if let Some(extend_arg) = args.get(1); if self.is_repeat_take(extend_arg); then { @@ -225,11 +225,11 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { fn search_slow_resize_filling(&mut self, expr: &'tcx Expr<'_>) { if_chain! { if self.initialization_found; - if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind; + if let ExprKind::MethodCall(path, _, args, _) = expr.kind; if let ExprKind::Path(ref qpath_subj) = args[0].kind; - if match_qpath(&qpath_subj, &[&*self.vec_alloc.variable_name.as_str()]); + if match_qpath(qpath_subj, &[&*self.vec_alloc.variable_name.as_str()]); if path.ident.name == sym!(resize); - if let (Some(ref len_arg), Some(fill_arg)) = (args.get(1), args.get(2)); + if let (Some(len_arg), Some(fill_arg)) = (args.get(1), args.get(2)); // Check that is filled with 0 if let ExprKind::Lit(ref lit) = fill_arg.kind; @@ -247,15 +247,15 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { /// Returns `true` if give expression is `repeat(0).take(...)` fn is_repeat_take(&self, expr: &Expr<'_>) -> bool { if_chain! { - if let ExprKind::MethodCall(ref take_path, _, ref take_args, _) = expr.kind; + if let ExprKind::MethodCall(take_path, _, take_args, _) = expr.kind; if take_path.ident.name == sym!(take); // Check that take is applied to `repeat(0)` - if let Some(ref repeat_expr) = take_args.get(0); + if let Some(repeat_expr) = take_args.get(0); if Self::is_repeat_zero(repeat_expr); // Check that len expression is equals to `with_capacity` expression - if let Some(ref len_arg) = take_args.get(1); + if let Some(len_arg) = take_args.get(1); if SpanlessEq::new(self.cx).eq_expr(len_arg, self.vec_alloc.len_expr); then { @@ -269,10 +269,10 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { /// Returns `true` if given expression is `repeat(0)` fn is_repeat_zero(expr: &Expr<'_>) -> bool { if_chain! { - if let ExprKind::Call(ref fn_expr, ref repeat_args) = expr.kind; + if let ExprKind::Call(fn_expr, repeat_args) = expr.kind; if let ExprKind::Path(ref qpath_repeat) = fn_expr.kind; - if match_qpath(&qpath_repeat, &["repeat"]); - if let Some(ref repeat_arg) = repeat_args.get(0); + if match_qpath(qpath_repeat, &["repeat"]); + if let Some(repeat_arg) = repeat_args.get(0); if let ExprKind::Lit(ref lit) = repeat_arg.kind; if let LitKind::Int(0, _) = lit.node; @@ -291,7 +291,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VectorInitializationVisitor<'a, 'tcx> { fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) { if self.initialization_found { match stmt.kind { - StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => { + StmtKind::Expr(expr) | StmtKind::Semi(expr) => { self.search_slow_extend_filling(expr); self.search_slow_resize_filling(expr); }, @@ -306,7 +306,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VectorInitializationVisitor<'a, 'tcx> { fn visit_block(&mut self, block: &'tcx Block<'_>) { if self.initialization_found { - if let Some(ref s) = block.stmts.get(0) { + if let Some(s) = block.stmts.get(0) { self.visit_stmt(s) } diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 99ca7ef77a5..9d91b53e1bb 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -119,7 +119,7 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd { Spanned { node: BinOpKind::Add, .. }, - ref left, + left, _, ) = e.kind { @@ -127,7 +127,7 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd { if !is_allowed(cx, STRING_ADD_ASSIGN, e.hir_id) { let parent = get_parent_expr(cx, e); if let Some(p) = parent { - if let ExprKind::Assign(ref target, _, _) = p.kind { + if let ExprKind::Assign(target, _, _) = p.kind { // avoid duplicate matches if SpanlessEq::new(cx).eq_expr(target, left) { return; @@ -142,7 +142,7 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd { "you added something to a string. Consider using `String::push_str()` instead", ); } - } else if let ExprKind::Assign(ref target, ref src, _) = e.kind { + } else if let ExprKind::Assign(target, src, _) = e.kind { if is_string(cx, target) && is_add(cx, src, target) { span_lint( cx, @@ -166,10 +166,10 @@ fn is_add(cx: &LateContext<'_>, src: &Expr<'_>, target: &Expr<'_>) -> bool { Spanned { node: BinOpKind::Add, .. }, - ref left, + left, _, ) => SpanlessEq::new(cx).eq_expr(target, left), - ExprKind::Block(ref block, _) => { + ExprKind::Block(block, _) => { block.stmts.is_empty() && block.expr.as_ref().map_or(false, |expr| is_add(cx, expr, target)) }, _ => false, @@ -210,8 +210,8 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { if let Some(args) = match_function_call(cx, e, &paths::STR_FROM_UTF8); // Find string::as_bytes - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref args) = args[0].kind; - if let ExprKind::Index(ref left, ref right) = args.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, _, args) = args[0].kind; + if let ExprKind::Index(left, right) = args.kind; let (method_names, expressions, _) = method_calls(left, 1); if method_names.len() == 1; if expressions.len() == 1; diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs index 46f423204a2..cb2237e5312 100644 --- a/clippy_lints/src/suspicious_operation_groupings.rs +++ b/clippy_lints/src/suspicious_operation_groupings.rs @@ -225,7 +225,7 @@ fn attempt_to_emit_no_difference_lint( emit_suggestion( cx, binop.span, - replace_left_sugg(cx, &binop, &sugg, &mut applicability), + replace_left_sugg(cx, binop, &sugg, &mut applicability), applicability, ); return; @@ -247,7 +247,7 @@ fn attempt_to_emit_no_difference_lint( emit_suggestion( cx, binop.span, - replace_right_sugg(cx, &binop, &sugg, &mut applicability), + replace_right_sugg(cx, binop, &sugg, &mut applicability), applicability, ); return; @@ -276,8 +276,8 @@ fn ident_swap_sugg( location: IdentLocation, applicability: &mut Applicability, ) -> Option { - let left_ident = get_ident(&binop.left, location)?; - let right_ident = get_ident(&binop.right, location)?; + let left_ident = get_ident(binop.left, location)?; + let right_ident = get_ident(binop.right, location)?; let sugg = match ( paired_identifiers.contains(&left_ident), @@ -293,8 +293,7 @@ fn ident_swap_sugg( // ends up duplicating a clause, the `logic_bug` lint // should catch it. - let right_suggestion = - suggestion_with_swapped_ident(cx, &binop.right, location, left_ident, applicability)?; + let right_suggestion = suggestion_with_swapped_ident(cx, binop.right, location, left_ident, applicability)?; replace_right_sugg(cx, binop, &right_suggestion, applicability) }, @@ -302,15 +301,14 @@ fn ident_swap_sugg( // We haven't seen a pair involving the left one, so // it's probably what is wanted. - let right_suggestion = - suggestion_with_swapped_ident(cx, &binop.right, location, left_ident, applicability)?; + let right_suggestion = suggestion_with_swapped_ident(cx, binop.right, location, left_ident, applicability)?; replace_right_sugg(cx, binop, &right_suggestion, applicability) }, (true, false) => { // We haven't seen a pair involving the right one, so // it's probably what is wanted. - let left_suggestion = suggestion_with_swapped_ident(cx, &binop.left, location, right_ident, applicability)?; + let left_suggestion = suggestion_with_swapped_ident(cx, binop.left, location, right_ident, applicability)?; replace_left_sugg(cx, binop, &left_suggestion, applicability) }, diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index 14519eaa962..19967e2c970 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -78,26 +78,26 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) { for w in block.stmts.windows(3) { if_chain! { // let t = foo(); - if let StmtKind::Local(ref tmp) = w[0].kind; - if let Some(ref tmp_init) = tmp.init; + if let StmtKind::Local(tmp) = w[0].kind; + if let Some(tmp_init) = tmp.init; if let PatKind::Binding(.., ident, None) = tmp.pat.kind; // foo() = bar(); - if let StmtKind::Semi(ref first) = w[1].kind; - if let ExprKind::Assign(ref lhs1, ref rhs1, _) = first.kind; + if let StmtKind::Semi(first) = w[1].kind; + if let ExprKind::Assign(lhs1, rhs1, _) = first.kind; // bar() = t; - if let StmtKind::Semi(ref second) = w[2].kind; - if let ExprKind::Assign(ref lhs2, ref rhs2, _) = second.kind; - if let ExprKind::Path(QPath::Resolved(None, ref rhs2)) = rhs2.kind; + if let StmtKind::Semi(second) = w[2].kind; + if let ExprKind::Assign(lhs2, rhs2, _) = second.kind; + if let ExprKind::Path(QPath::Resolved(None, rhs2)) = rhs2.kind; if rhs2.segments.len() == 1; if ident.name == rhs2.segments[0].ident.name; if eq_expr_value(cx, tmp_init, lhs1); if eq_expr_value(cx, rhs1, lhs2); then { - if let ExprKind::Field(ref lhs1, _) = lhs1.kind { - if let ExprKind::Field(ref lhs2, _) = lhs2.kind { + if let ExprKind::Field(lhs1, _) = lhs1.kind { + if let ExprKind::Field(lhs2, _) = lhs2.kind { if lhs1.hir_id.owner == lhs2.hir_id.owner { return; } @@ -192,8 +192,8 @@ enum Slice<'a> { /// Checks if both expressions are index operations into "slice-like" types. fn check_for_slice<'a>(cx: &LateContext<'_>, lhs1: &'a Expr<'_>, lhs2: &'a Expr<'_>) -> Slice<'a> { - if let ExprKind::Index(ref lhs1, ref idx1) = lhs1.kind { - if let ExprKind::Index(ref lhs2, ref idx2) = lhs2.kind { + if let ExprKind::Index(lhs1, idx1) = lhs1.kind { + if let ExprKind::Index(lhs2, idx2) = lhs2.kind { if eq_expr_value(cx, lhs1, lhs2) { let ty = cx.typeck_results().expr_ty(lhs1).peel_refs(); @@ -217,11 +217,11 @@ fn check_for_slice<'a>(cx: &LateContext<'_>, lhs1: &'a Expr<'_>, lhs2: &'a Expr< fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) { for w in block.stmts.windows(2) { if_chain! { - if let StmtKind::Semi(ref first) = w[0].kind; - if let StmtKind::Semi(ref second) = w[1].kind; + if let StmtKind::Semi(first) = w[0].kind; + if let StmtKind::Semi(second) = w[1].kind; if !differing_macro_contexts(first.span, second.span); - if let ExprKind::Assign(ref lhs0, ref rhs0, _) = first.kind; - if let ExprKind::Assign(ref lhs1, ref rhs1, _) = second.kind; + if let ExprKind::Assign(lhs0, rhs0, _) = first.kind; + if let ExprKind::Assign(lhs1, rhs1, _) = second.kind; if eq_expr_value(cx, lhs0, rhs1); if eq_expr_value(cx, lhs1, rhs0); then { diff --git a/clippy_lints/src/tabs_in_doc_comments.rs b/clippy_lints/src/tabs_in_doc_comments.rs index 88bd2feaadd..a0492a88f91 100644 --- a/clippy_lints/src/tabs_in_doc_comments.rs +++ b/clippy_lints/src/tabs_in_doc_comments.rs @@ -86,7 +86,7 @@ impl TabsInDocComments { impl EarlyLintPass for TabsInDocComments { fn check_attribute(&mut self, cx: &EarlyContext<'_>, attribute: &ast::Attribute) { - Self::warn_if_tabs_in_doc(cx, &attribute); + Self::warn_if_tabs_in_doc(cx, attribute); } } diff --git a/clippy_lints/src/to_string_in_display.rs b/clippy_lints/src/to_string_in_display.rs index 42ec14c31b5..ae05a8da37b 100644 --- a/clippy_lints/src/to_string_in_display.rs +++ b/clippy_lints/src/to_string_in_display.rs @@ -92,7 +92,7 @@ impl LateLintPass<'_> for ToStringInDisplay { if_chain! { if self.in_display_impl; if let Some(self_hir_id) = self.self_hir_id; - if let ExprKind::MethodCall(ref path, _, args, _) = expr.kind; + if let ExprKind::MethodCall(path, _, args, _) = expr.kind; if path.ident.name == sym!(to_string); if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if is_diagnostic_assoc_item(cx, expr_def_id, sym::ToString); diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 3ff27c3bcf4..b0589b0512e 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -107,7 +107,7 @@ impl TraitBounds { if let WherePredicate::BoundPredicate(ref p) = bound; if p.bounds.len() as u64 <= self.max_trait_bounds; if !in_macro(p.span); - let h = hash(&p.bounded_ty); + let h = hash(p.bounded_ty); if let Some(ref v) = map.insert(h, p.bounds.iter().collect::>()); then { @@ -170,7 +170,7 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { if_chain! { if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate; if !in_macro(bound_predicate.span); - if let TyKind::Path(QPath::Resolved(_, Path { ref segments, .. })) = bound_predicate.bounded_ty.kind; + if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind; if let Some(segment) = segments.first(); if let Some(trait_resolutions_direct) = map.get(&segment.ident); then { diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index 47d58bd30db..86ac916df6c 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -325,7 +325,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { #[allow(clippy::similar_names, clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { if_chain! { - if let ExprKind::Call(ref path_expr, ref args) = e.kind; + if let ExprKind::Call(path_expr, args) = e.kind; if let ExprKind::Path(ref qpath) = path_expr.kind; if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id(); if match_def_path(cx, def_id, &paths::TRANSMUTE); diff --git a/clippy_lints/src/transmute/transmute_float_to_int.rs b/clippy_lints/src/transmute/transmute_float_to_int.rs index 1a6124e9ddb..3aa3c393ba5 100644 --- a/clippy_lints/src/transmute/transmute_float_to_int.rs +++ b/clippy_lints/src/transmute/transmute_float_to_int.rs @@ -30,7 +30,7 @@ pub(super) fn check<'tcx>( let mut arg = sugg::Sugg::hir(cx, expr, ".."); if let ExprKind::Unary(UnOp::Neg, inner_expr) = &expr.kind { - expr = &inner_expr; + expr = inner_expr; } if_chain! { diff --git a/clippy_lints/src/transmute/utils.rs b/clippy_lints/src/transmute/utils.rs index c6d0d63b0b5..f359b606e45 100644 --- a/clippy_lints/src/transmute/utils.rs +++ b/clippy_lints/src/transmute/utils.rs @@ -16,7 +16,7 @@ use rustc_typeck::check::{cast::CastCheck, FnCtxt, Inherited}; pub(super) fn get_type_snippet(cx: &LateContext<'_>, path: &QPath<'_>, to_ref_ty: Ty<'_>) -> String { let seg = last_path_segment(path); if_chain! { - if let Some(ref params) = seg.args; + if let Some(params) = seg.args; if !params.parenthesized; if let Some(to_ty) = params.args.iter().filter_map(|arg| match arg { GenericArg::Type(ty) => Some(ty), diff --git a/clippy_lints/src/transmuting_null.rs b/clippy_lints/src/transmuting_null.rs index d42cdde110e..0be05d3e0cf 100644 --- a/clippy_lints/src/transmuting_null.rs +++ b/clippy_lints/src/transmuting_null.rs @@ -37,7 +37,7 @@ impl<'tcx> LateLintPass<'tcx> for TransmutingNull { } if_chain! { - if let ExprKind::Call(ref func, ref args) = expr.kind; + if let ExprKind::Call(func, args) = expr.kind; if let ExprKind::Path(ref path) = func.kind; if match_qpath(path, &paths::STD_MEM_TRANSMUTE); if args.len() == 1; @@ -58,7 +58,7 @@ impl<'tcx> LateLintPass<'tcx> for TransmutingNull { // Catching: // `std::mem::transmute(0 as *const i32)` if_chain! { - if let ExprKind::Cast(ref inner_expr, ref _cast_ty) = args[0].kind; + if let ExprKind::Cast(inner_expr, _cast_ty) = args[0].kind; if let ExprKind::Lit(ref lit) = inner_expr.kind; if let LitKind::Int(0, _) = lit.node; then { @@ -69,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for TransmutingNull { // Catching: // `std::mem::transmute(std::ptr::null::())` if_chain! { - if let ExprKind::Call(ref func1, ref args1) = args[0].kind; + if let ExprKind::Call(func1, args1) = args[0].kind; if let ExprKind::Path(ref path1) = func1.kind; if match_qpath(path1, &paths::STD_PTR_NULL); if args1.is_empty(); diff --git a/clippy_lints/src/try_err.rs b/clippy_lints/src/try_err.rs index e4799790d35..23a1953ffac 100644 --- a/clippy_lints/src/try_err.rs +++ b/clippy_lints/src/try_err.rs @@ -60,13 +60,13 @@ impl<'tcx> LateLintPass<'tcx> for TryErr { // }; if_chain! { if !in_external_macro(cx.tcx.sess, expr.span); - if let ExprKind::Match(ref match_arg, _, MatchSource::TryDesugar) = expr.kind; - if let ExprKind::Call(ref match_fun, ref try_args) = match_arg.kind; + if let ExprKind::Match(match_arg, _, MatchSource::TryDesugar) = expr.kind; + if let ExprKind::Call(match_fun, try_args) = match_arg.kind; if let ExprKind::Path(ref match_fun_path) = match_fun.kind; if matches!(match_fun_path, QPath::LangItem(LangItem::TryIntoResult, _)); - if let Some(ref try_arg) = try_args.get(0); - if let ExprKind::Call(ref err_fun, ref err_args) = try_arg.kind; - if let Some(ref err_arg) = err_args.get(0); + if let Some(try_arg) = try_args.get(0); + if let ExprKind::Call(err_fun, err_args) = try_arg.kind; + if let Some(err_arg) = err_args.get(0); if let ExprKind::Path(ref err_fun_path) = err_fun.kind; if match_qpath(err_fun_path, &paths::RESULT_ERR); if let Some(return_ty) = find_return_type(cx, &expr.kind); @@ -123,9 +123,9 @@ impl<'tcx> LateLintPass<'tcx> for TryErr { /// Finds function return type by examining return expressions in match arms. fn find_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> Option> { - if let ExprKind::Match(_, ref arms, MatchSource::TryDesugar) = expr { + if let ExprKind::Match(_, arms, MatchSource::TryDesugar) = expr { for arm in arms.iter() { - if let ExprKind::Ret(Some(ref ret)) = arm.body.kind { + if let ExprKind::Ret(Some(ret)) = arm.body.kind { return Some(cx.typeck_results().expr_ty(ret)); } } diff --git a/clippy_lints/src/types/borrowed_box.rs b/clippy_lints/src/types/borrowed_box.rs index d68c6db4e23..1425d8f3f37 100644 --- a/clippy_lints/src/types/borrowed_box.rs +++ b/clippy_lints/src/types/borrowed_box.rs @@ -19,9 +19,9 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m if_chain! { if let Some(def_id) = def.opt_def_id(); if Some(def_id) == cx.tcx.lang_items().owned_box(); - if let QPath::Resolved(None, ref path) = *qpath; + if let QPath::Resolved(None, path) = *qpath; if let [ref bx] = *path.segments; - if let Some(ref params) = bx.args; + if let Some(params) = bx.args; if !params.parenthesized; if let Some(inner) = params.args.iter().find_map(|arg| match arg { GenericArg::Type(ty) => Some(ty), @@ -86,11 +86,11 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m // Returns true if given type is `Any` trait. fn is_any_trait(t: &hir::Ty<'_>) -> bool { if_chain! { - if let TyKind::TraitObject(ref traits, ..) = t.kind; + if let TyKind::TraitObject(traits, ..) = t.kind; if !traits.is_empty(); // Only Send/Sync can be used as additional traits, so it is enough to // check only the first trait. - if match_path(&traits[0].trait_ref.path, &paths::ANY_TRAIT); + if match_path(traits[0].trait_ref.path, &paths::ANY_TRAIT); then { return true; } diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 12e1eba2ca6..d9b47a699dc 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -278,9 +278,7 @@ impl<'tcx> LateLintPass<'tcx> for Types { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { match item.kind { - ItemKind::Static(ref ty, _, _) | ItemKind::Const(ref ty, _) => { - self.check_ty(cx, ty, CheckTyContext::default()) - }, + ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _) => self.check_ty(cx, ty, CheckTyContext::default()), // functions, enums, structs, impls and traits are covered _ => (), } @@ -288,7 +286,7 @@ impl<'tcx> LateLintPass<'tcx> for Types { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { match item.kind { - ImplItemKind::Const(ref ty, _) | ImplItemKind::TyAlias(ref ty) => self.check_ty( + ImplItemKind::Const(ty, _) | ImplItemKind::TyAlias(ty) => self.check_ty( cx, ty, CheckTyContext { @@ -302,21 +300,21 @@ impl<'tcx> LateLintPass<'tcx> for Types { } fn check_field_def(&mut self, cx: &LateContext<'_>, field: &hir::FieldDef<'_>) { - self.check_ty(cx, &field.ty, CheckTyContext::default()); + self.check_ty(cx, field.ty, CheckTyContext::default()); } fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) { match item.kind { - TraitItemKind::Const(ref ty, _) | TraitItemKind::Type(_, Some(ref ty)) => { + TraitItemKind::Const(ty, _) | TraitItemKind::Type(_, Some(ty)) => { self.check_ty(cx, ty, CheckTyContext::default()) }, - TraitItemKind::Fn(ref sig, _) => self.check_fn_decl(cx, &sig.decl, CheckTyContext::default()), + TraitItemKind::Fn(ref sig, _) => self.check_fn_decl(cx, sig.decl, CheckTyContext::default()), TraitItemKind::Type(..) => (), } } fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) { - if let Some(ref ty) = local.ty { + if let Some(ty) = local.ty { self.check_ty( cx, ty, @@ -342,7 +340,7 @@ impl Types { self.check_ty(cx, input, context); } - if let FnRetTy::Return(ref ty) = decl.output { + if let FnRetTy::Return(ty) = decl.output { self.check_ty(cx, ty, context); } } @@ -383,7 +381,7 @@ impl Types { } } match *qpath { - QPath::Resolved(Some(ref ty), ref p) => { + QPath::Resolved(Some(ty), p) => { context.is_nested_call = true; self.check_ty(cx, ty, context); for ty in p.segments.iter().flat_map(|seg| { @@ -398,7 +396,7 @@ impl Types { self.check_ty(cx, ty, context); } }, - QPath::Resolved(None, ref p) => { + QPath::Resolved(None, p) => { context.is_nested_call = true; for ty in p.segments.iter().flat_map(|seg| { seg.args @@ -412,10 +410,10 @@ impl Types { self.check_ty(cx, ty, context); } }, - QPath::TypeRelative(ref ty, ref seg) => { + QPath::TypeRelative(ty, seg) => { context.is_nested_call = true; self.check_ty(cx, ty, context); - if let Some(ref params) = seg.args { + if let Some(params) = seg.args { for ty in params.args.iter().filter_map(|arg| match arg { GenericArg::Type(ty) => Some(ty), _ => None, @@ -430,10 +428,10 @@ impl Types { TyKind::Rptr(ref lt, ref mut_ty) => { context.is_nested_call = true; if !borrowed_box::check(cx, hir_ty, lt, mut_ty) { - self.check_ty(cx, &mut_ty.ty, context); + self.check_ty(cx, mut_ty.ty, context); } }, - TyKind::Slice(ref ty) | TyKind::Array(ref ty, _) | TyKind::Ptr(MutTy { ref ty, .. }) => { + TyKind::Slice(ty) | TyKind::Array(ty, _) | TyKind::Ptr(MutTy { ty, .. }) => { context.is_nested_call = true; self.check_ty(cx, ty, context) }, diff --git a/clippy_lints/src/types/type_complexity.rs b/clippy_lints/src/types/type_complexity.rs index 9a4e9da3e2b..d8c4b67520d 100644 --- a/clippy_lints/src/types/type_complexity.rs +++ b/clippy_lints/src/types/type_complexity.rs @@ -48,9 +48,9 @@ impl<'tcx> Visitor<'tcx> for TypeComplexityVisitor { TyKind::Path(..) | TyKind::Slice(..) | TyKind::Tup(..) | TyKind::Array(..) => (10 * self.nest, 1), // function types bring a lot of overhead - TyKind::BareFn(ref bare) if bare.abi == Abi::Rust => (50 * self.nest, 1), + TyKind::BareFn(bare) if bare.abi == Abi::Rust => (50 * self.nest, 1), - TyKind::TraitObject(ref param_bounds, _, _) => { + TyKind::TraitObject(param_bounds, _, _) => { let has_lifetime_parameters = param_bounds.iter().any(|bound| { bound .bound_generic_params diff --git a/clippy_lints/src/types/utils.rs b/clippy_lints/src/types/utils.rs index 45f891ed718..0fa75f8f0a9 100644 --- a/clippy_lints/src/types/utils.rs +++ b/clippy_lints/src/types/utils.rs @@ -7,7 +7,7 @@ use rustc_span::source_map::Span; pub(super) fn match_borrows_parameter(_cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option { let last = last_path_segment(qpath); if_chain! { - if let Some(ref params) = last.args; + if let Some(params) = last.args; if !params.parenthesized; if let Some(ty) = params.args.iter().find_map(|arg| match arg { GenericArg::Type(ty) => Some(ty), diff --git a/clippy_lints/src/types/vec_box.rs b/clippy_lints/src/types/vec_box.rs index d2c373db261..7a444174626 100644 --- a/clippy_lints/src/types/vec_box.rs +++ b/clippy_lints/src/types/vec_box.rs @@ -22,7 +22,7 @@ pub(super) fn check( if cx.tcx.is_diagnostic_item(sym::vec_type, def_id) { if_chain! { // Get the _ part of Vec<_> - if let Some(ref last) = last_path_segment(qpath).args; + if let Some(last) = last_path_segment(qpath).args; if let Some(ty) = last.args.iter().find_map(|arg| match arg { GenericArg::Type(ty) => Some(ty), _ => None, @@ -33,7 +33,7 @@ pub(super) fn check( if let Some(def_id) = res.opt_def_id(); if Some(def_id) == cx.tcx.lang_items().owned_box(); // At this point, we know ty is Box, now get T - if let Some(ref last) = last_path_segment(ty_qpath).args; + if let Some(last) = last_path_segment(ty_qpath).args; if let Some(boxed_ty) = last.args.iter().find_map(|arg| match arg { GenericArg::Type(ty) => Some(ty), _ => None, diff --git a/clippy_lints/src/undropped_manually_drops.rs b/clippy_lints/src/undropped_manually_drops.rs index b6749069176..f4f5e1233e3 100644 --- a/clippy_lints/src/undropped_manually_drops.rs +++ b/clippy_lints/src/undropped_manually_drops.rs @@ -35,7 +35,7 @@ declare_lint_pass!(UndroppedManuallyDrops => [UNDROPPED_MANUALLY_DROPS]); impl LateLintPass<'tcx> for UndroppedManuallyDrops { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let Some(ref args) = match_function_call(cx, expr, &paths::DROP) { + if let Some(args) = match_function_call(cx, expr, &paths::DROP) { let ty = cx.typeck_results().expr_ty(&args[0]); if is_type_lang_item(cx, ty, lang_items::LangItem::ManuallyDrop) { span_lint_and_help( diff --git a/clippy_lints/src/unit_return_expecting_ord.rs b/clippy_lints/src/unit_return_expecting_ord.rs index cdc65abe47c..47b95b18ffb 100644 --- a/clippy_lints/src/unit_return_expecting_ord.rs +++ b/clippy_lints/src/unit_return_expecting_ord.rs @@ -138,7 +138,7 @@ fn check_arg<'tcx>(cx: &LateContext<'tcx>, arg: &'tcx Expr<'tcx>) -> Option<(Spa impl<'tcx> LateLintPass<'tcx> for UnitReturnExpectingOrd { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if let ExprKind::MethodCall(_, _, ref args, _) = expr.kind { + if let ExprKind::MethodCall(_, _, args, _) = expr.kind { let arg_indices = get_args_to_check(cx, expr); for (i, trait_name) in arg_indices { if i < args.len() { diff --git a/clippy_lints/src/unit_types/let_unit_value.rs b/clippy_lints/src/unit_types/let_unit_value.rs index 8698a718bbd..fad647dfb26 100644 --- a/clippy_lints/src/unit_types/let_unit_value.rs +++ b/clippy_lints/src/unit_types/let_unit_value.rs @@ -9,8 +9,8 @@ use rustc_middle::lint::in_external_macro; use super::LET_UNIT_VALUE; pub(super) fn check(cx: &LateContext<'_>, stmt: &Stmt<'_>) { - if let StmtKind::Local(ref local) = stmt.kind { - if cx.typeck_results().pat_ty(&local.pat).is_unit() { + if let StmtKind::Local(local) = stmt.kind { + if cx.typeck_results().pat_ty(local.pat).is_unit() { if in_external_macro(cx.sess(), stmt.span) || local.pat.span.from_expansion() { return; } diff --git a/clippy_lints/src/unit_types/unit_arg.rs b/clippy_lints/src/unit_types/unit_arg.rs index f77d811c283..57be2d2f674 100644 --- a/clippy_lints/src/unit_types/unit_arg.rs +++ b/clippy_lints/src/unit_types/unit_arg.rs @@ -54,7 +54,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { fn is_questionmark_desugar_marked_call(expr: &Expr<'_>) -> bool { use rustc_span::hygiene::DesugaringKind; - if let ExprKind::Call(ref callee, _) = expr.kind { + if let ExprKind::Call(callee, _) = expr.kind { callee.span.is_desugaring(DesugaringKind::QuestionMark) } else { false diff --git a/clippy_lints/src/unit_types/unit_cmp.rs b/clippy_lints/src/unit_types/unit_cmp.rs index b3077dec5d8..85257f3113c 100644 --- a/clippy_lints/src/unit_types/unit_cmp.rs +++ b/clippy_lints/src/unit_types/unit_cmp.rs @@ -9,7 +9,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { if expr.span.from_expansion() { if let Some(callee) = expr.span.source_callee() { if let ExpnKind::Macro(MacroKind::Bang, symbol) = callee.kind { - if let ExprKind::Binary(ref cmp, ref left, _) = expr.kind { + if let ExprKind::Binary(ref cmp, left, _) = expr.kind { let op = cmp.node; if op.is_comparison() && cx.typeck_results().expr_ty(left).is_unit() { let result = match &*symbol.as_str() { @@ -34,7 +34,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { return; } - if let ExprKind::Binary(ref cmp, ref left, _) = expr.kind { + if let ExprKind::Binary(ref cmp, left, _) = expr.kind { let op = cmp.node; if op.is_comparison() && cx.typeck_results().expr_ty(left).is_unit() { let result = match op { diff --git a/clippy_lints/src/unit_types/utils.rs b/clippy_lints/src/unit_types/utils.rs index 4e194a05e8d..9a3750b2356 100644 --- a/clippy_lints/src/unit_types/utils.rs +++ b/clippy_lints/src/unit_types/utils.rs @@ -1,5 +1,5 @@ use rustc_hir::{Expr, ExprKind}; pub(super) fn is_unit_literal(expr: &Expr<'_>) -> bool { - matches!(expr.kind, ExprKind::Tup(ref slice) if slice.is_empty()) + matches!(expr.kind, ExprKind::Tup(slice) if slice.is_empty()) } diff --git a/clippy_lints/src/unnamed_address.rs b/clippy_lints/src/unnamed_address.rs index 9ceb2c3ffe2..9cca05b1f1a 100644 --- a/clippy_lints/src/unnamed_address.rs +++ b/clippy_lints/src/unnamed_address.rs @@ -77,7 +77,7 @@ impl LateLintPass<'_> for UnnamedAddress { } if_chain! { - if let ExprKind::Binary(binop, ref left, ref right) = expr.kind; + if let ExprKind::Binary(binop, left, right) = expr.kind; if is_comparison(binop.node); if is_trait_ptr(cx, left) && is_trait_ptr(cx, right); then { @@ -93,7 +93,7 @@ impl LateLintPass<'_> for UnnamedAddress { } if_chain! { - if let ExprKind::Call(ref func, [ref _left, ref _right]) = expr.kind; + if let ExprKind::Call(func, [ref _left, ref _right]) = expr.kind; 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::PTR_EQ) || @@ -114,7 +114,7 @@ impl LateLintPass<'_> for UnnamedAddress { } if_chain! { - if let ExprKind::Binary(binop, ref left, ref right) = expr.kind; + if let ExprKind::Binary(binop, left, right) = expr.kind; if is_comparison(binop.node); if cx.typeck_results().expr_ty_adjusted(left).is_fn_ptr(); if cx.typeck_results().expr_ty_adjusted(right).is_fn_ptr(); diff --git a/clippy_lints/src/unnecessary_sort_by.rs b/clippy_lints/src/unnecessary_sort_by.rs index e23bab5eba0..03711eb5b65 100644 --- a/clippy_lints/src/unnecessary_sort_by.rs +++ b/clippy_lints/src/unnecessary_sort_by.rs @@ -187,15 +187,15 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { if method_path.ident.name == sym::cmp; then { let (closure_body, closure_arg, reverse) = if mirrored_exprs( - &cx, - &left_expr, - &left_ident, - &right_expr, - &right_ident + cx, + left_expr, + left_ident, + right_expr, + right_ident ) { - (Sugg::hir(cx, &left_expr, "..").to_string(), left_ident.name.to_string(), false) - } else if mirrored_exprs(&cx, &left_expr, &right_ident, &right_expr, &left_ident) { - (Sugg::hir(cx, &left_expr, "..").to_string(), right_ident.name.to_string(), true) + (Sugg::hir(cx, left_expr, "..").to_string(), left_ident.name.to_string(), false) + } else if mirrored_exprs(cx, left_expr, right_ident, right_expr, left_ident) { + (Sugg::hir(cx, left_expr, "..").to_string(), right_ident.name.to_string(), true) } else { return None; }; diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index c2be457e9dc..5bb417cb1be 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -103,7 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { if_chain! { if !in_macro(ret_expr.span); // Check if a function call. - if let ExprKind::Call(ref func, ref args) = ret_expr.kind; + if let ExprKind::Call(func, args) = ret_expr.kind; // Get the Path of the function call. if let ExprKind::Path(ref qpath) = func.kind; // Check if OPTION_SOME or RESULT_OK, depending on return type. diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index 9990052e114..024ab03fd41 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -36,13 +36,13 @@ declare_lint_pass!(UnusedIoAmount => [UNUSED_IO_AMOUNT]); impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) { let expr = match s.kind { - hir::StmtKind::Semi(ref expr) | hir::StmtKind::Expr(ref expr) => &**expr, + hir::StmtKind::Semi(expr) | hir::StmtKind::Expr(expr) => expr, _ => return, }; match expr.kind { - hir::ExprKind::Match(ref res, _, _) if is_try(expr).is_some() => { - if let hir::ExprKind::Call(ref func, ref args) = res.kind { + hir::ExprKind::Match(res, _, _) if is_try(expr).is_some() => { + if let hir::ExprKind::Call(func, args) = res.kind { if matches!( func.kind, hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::TryIntoResult, _)) @@ -54,7 +54,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { } }, - hir::ExprKind::MethodCall(ref path, _, ref args, _) => match &*path.ident.as_str() { + hir::ExprKind::MethodCall(path, _, args, _) => match &*path.ident.as_str() { "expect" | "unwrap" | "unwrap_or" | "unwrap_or_else" => { check_method_call(cx, &args[0], expr); }, @@ -67,7 +67,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { } fn check_method_call(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) { - if let hir::ExprKind::MethodCall(ref path, _, _, _) = call.kind { + if let hir::ExprKind::MethodCall(path, _, _, _) = call.kind { let symbol = &*path.ident.as_str(); let read_trait = match_trait_method(cx, call, &paths::IO_READ); let write_trait = match_trait_method(cx, call, &paths::IO_WRITE); diff --git a/clippy_lints/src/unused_unit.rs b/clippy_lints/src/unused_unit.rs index 329ea49024b..ce2d0b3ab2f 100644 --- a/clippy_lints/src/unused_unit.rs +++ b/clippy_lints/src/unused_unit.rs @@ -45,7 +45,7 @@ impl EarlyLintPass for UnusedUnit { fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) { if_chain! { - if let Some(ref stmt) = block.stmts.last(); + if let Some(stmt) = block.stmts.last(); if let ast::StmtKind::Expr(ref expr) = stmt.kind; if is_unit_expr(expr) && !stmt.span.from_expansion(); then { diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index fb29acca18a..d4efee56eff 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -166,8 +166,8 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { } else { // find `unwrap[_err]()` calls: if_chain! { - if let ExprKind::MethodCall(ref method_name, _, ref args, _) = expr.kind; - if let ExprKind::Path(QPath::Resolved(None, ref path)) = args[0].kind; + if let ExprKind::MethodCall(method_name, _, args, _) = expr.kind; + if let ExprKind::Path(QPath::Resolved(None, path)) = args[0].kind; if [sym::unwrap, sym!(unwrap_err)].contains(&method_name.ident.name); let call_to_unwrap = method_name.ident.name == sym::unwrap; if let Some(unwrappable) = self.unwrappables.iter() diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 116cb8b1e1c..c6a3c58a9a2 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -104,7 +104,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { of_trait, .. }) => { - let should_check = if let TyKind::Path(QPath::Resolved(_, ref item_path)) = hir_self_ty.kind { + let should_check = if let TyKind::Path(QPath::Resolved(_, item_path)) = hir_self_ty.kind { let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args; parameters.as_ref().map_or(true, |params| { !params.parenthesized && !params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_))) @@ -197,7 +197,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { for (impl_hir_ty, trait_sem_ty) in impl_inputs_outputs.zip(trait_method_sig.inputs_and_output) { if trait_sem_ty.walk().any(|inner| inner == self_ty.into()) { let mut visitor = SkipTyCollector::default(); - visitor.visit_ty(&impl_hir_ty); + visitor.visit_ty(impl_hir_ty); types_to_skip.extend(visitor.types_to_skip); } } @@ -333,7 +333,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { // unit enum variants (`Enum::A`) ExprKind::Path(qpath) => { if expr_ty_matches(cx, expr, self_ty) { - span_lint_on_qpath_resolved(cx, &qpath, true); + span_lint_on_qpath_resolved(cx, qpath, true); } }, _ => (), diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index d893b271a20..7edb280be73 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -53,17 +53,17 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { } match e.kind { - ExprKind::Match(_, ref arms, MatchSource::TryDesugar) => { + ExprKind::Match(_, arms, MatchSource::TryDesugar) => { let e = match arms[0].body.kind { - ExprKind::Ret(Some(ref e)) | ExprKind::Break(_, Some(ref e)) => e, + ExprKind::Ret(Some(e)) | ExprKind::Break(_, Some(e)) => e, _ => return, }; - if let ExprKind::Call(_, ref args) = e.kind { + if let ExprKind::Call(_, args) = e.kind { self.try_desugar_arm.push(args[0].hir_id); } }, - ExprKind::MethodCall(ref name, .., ref args, _) => { + ExprKind::MethodCall(name, .., args, _) => { if match_trait_method(cx, e, &paths::INTO) && &*name.ident.as_str() == "into" { let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(&args[0]); @@ -82,7 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { } if match_trait_method(cx, e, &paths::INTO_ITERATOR) && name.ident.name == sym::into_iter { if let Some(parent_expr) = get_parent_expr(cx, e) { - if let ExprKind::MethodCall(ref parent_name, ..) = parent_expr.kind { + if let ExprKind::MethodCall(parent_name, ..) = parent_expr.kind { if parent_name.ident.name != sym::into_iter { return; } @@ -124,7 +124,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { } }, - ExprKind::Call(ref path, ref args) => { + ExprKind::Call(path, args) => { if_chain! { if args.len() == 1; if let ExprKind::Path(ref qpath) = path.kind; diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index a92c987014f..e70f8a09ebe 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -203,13 +203,13 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { print!(" if let ExprKind::"); let current = format!("{}.kind", self.current); match expr.kind { - ExprKind::Box(ref inner) => { + ExprKind::Box(inner) => { let inner_pat = self.next("inner"); println!("Box(ref {}) = {};", inner_pat, current); self.current = inner_pat; self.visit_expr(inner); }, - ExprKind::Array(ref elements) => { + ExprKind::Array(elements) => { let elements_pat = self.next("elements"); println!("Array(ref {}) = {};", elements_pat, current); println!(" if {}.len() == {};", elements_pat, elements.len()); @@ -218,7 +218,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.visit_expr(element); } }, - ExprKind::Call(ref func, ref args) => { + ExprKind::Call(func, args) => { let func_pat = self.next("func"); let args_pat = self.next("args"); println!("Call(ref {}, ref {}) = {};", func_pat, args_pat, current); @@ -230,14 +230,14 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.visit_expr(arg); } }, - ExprKind::MethodCall(ref _method_name, ref _generics, ref _args, ref _fn_span) => { + ExprKind::MethodCall(_method_name, ref _generics, _args, ref _fn_span) => { println!( "MethodCall(ref method_name, ref generics, ref args, ref fn_span) = {};", current ); println!(" // unimplemented: `ExprKind::MethodCall` is not further destructured at the moment"); }, - ExprKind::Tup(ref elements) => { + ExprKind::Tup(elements) => { let elements_pat = self.next("elements"); println!("Tup(ref {}) = {};", elements_pat, current); println!(" if {}.len() == {};", elements_pat, elements.len()); @@ -246,7 +246,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.visit_expr(element); } }, - ExprKind::Binary(ref op, ref left, ref right) => { + ExprKind::Binary(ref op, left, right) => { let op_pat = self.next("op"); let left_pat = self.next("left"); let right_pat = self.next("right"); @@ -260,7 +260,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.current = right_pat; self.visit_expr(right); }, - ExprKind::Unary(ref op, ref inner) => { + ExprKind::Unary(ref op, inner) => { let inner_pat = self.next("inner"); println!("Unary(UnOp::{:?}, ref {}) = {};", op, inner_pat, current); self.current = inner_pat; @@ -296,7 +296,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { }, } }, - ExprKind::Cast(ref expr, ref ty) => { + ExprKind::Cast(expr, ty) => { let cast_pat = self.next("expr"); let cast_ty = self.next("cast_ty"); let qp_label = self.next("qp"); @@ -310,13 +310,13 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.current = cast_pat; self.visit_expr(expr); }, - ExprKind::Type(ref expr, ref _ty) => { + ExprKind::Type(expr, _ty) => { let cast_pat = self.next("expr"); println!("Type(ref {}, _) = {};", cast_pat, current); self.current = cast_pat; self.visit_expr(expr); }, - ExprKind::Loop(ref body, _, desugaring, _) => { + ExprKind::Loop(body, _, desugaring, _) => { let body_pat = self.next("body"); let des = loop_desugaring_name(desugaring); let label_pat = self.next("label"); @@ -324,10 +324,10 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.current = body_pat; self.visit_block(body); }, - ExprKind::If(ref cond, ref then, ref opt_else) => { + ExprKind::If(cond, then, ref opt_else) => { let cond_pat = self.next("cond"); let then_pat = self.next("then"); - if let Some(ref else_) = *opt_else { + if let Some(else_) = *opt_else { let else_pat = self.next("else_"); println!( "If(ref {}, ref {}, Some(ref {})) = {};", @@ -343,7 +343,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.current = then_pat; self.visit_expr(then); }, - ExprKind::Match(ref expr, ref arms, desugaring) => { + ExprKind::Match(expr, arms, desugaring) => { let des = desugaring_name(desugaring); let expr_pat = self.next("expr"); let arms_pat = self.next("arms"); @@ -353,18 +353,18 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { println!(" if {}.len() == {};", arms_pat, arms.len()); for (i, arm) in arms.iter().enumerate() { self.current = format!("{}[{}].body", arms_pat, i); - self.visit_expr(&arm.body); + self.visit_expr(arm.body); if let Some(ref guard) = arm.guard { let guard_pat = self.next("guard"); println!(" if let Some(ref {}) = {}[{}].guard;", guard_pat, arms_pat, i); match guard { - hir::Guard::If(ref if_expr) => { + hir::Guard::If(if_expr) => { let if_expr_pat = self.next("expr"); println!(" if let Guard::If(ref {}) = {};", if_expr_pat, guard_pat); self.current = if_expr_pat; self.visit_expr(if_expr); }, - hir::Guard::IfLet(ref if_let_pat, ref if_let_expr) => { + hir::Guard::IfLet(if_let_pat, if_let_expr) => { let if_let_pat_pat = self.next("pat"); let if_let_expr_pat = self.next("expr"); println!( @@ -379,26 +379,26 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { } } self.current = format!("{}[{}].pat", arms_pat, i); - self.visit_pat(&arm.pat); + self.visit_pat(arm.pat); } }, - ExprKind::Closure(ref _capture_clause, ref _func, _, _, _) => { + ExprKind::Closure(ref _capture_clause, _func, _, _, _) => { println!("Closure(ref capture_clause, ref func, _, _, _) = {};", current); println!(" // unimplemented: `ExprKind::Closure` is not further destructured at the moment"); }, - ExprKind::Yield(ref sub, _) => { + ExprKind::Yield(sub, _) => { let sub_pat = self.next("sub"); println!("Yield(ref sub) = {};", current); self.current = sub_pat; self.visit_expr(sub); }, - ExprKind::Block(ref block, _) => { + ExprKind::Block(block, _) => { let block_pat = self.next("block"); println!("Block(ref {}) = {};", block_pat, current); self.current = block_pat; self.visit_block(block); }, - ExprKind::Assign(ref target, ref value, _) => { + ExprKind::Assign(target, value, _) => { let target_pat = self.next("target"); let value_pat = self.next("value"); println!( @@ -410,7 +410,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.current = value_pat; self.visit_expr(value); }, - ExprKind::AssignOp(ref op, ref target, ref value) => { + ExprKind::AssignOp(ref op, target, value) => { let op_pat = self.next("op"); let target_pat = self.next("target"); let value_pat = self.next("value"); @@ -424,7 +424,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.current = value_pat; self.visit_expr(value); }, - ExprKind::Field(ref object, ref field_ident) => { + ExprKind::Field(object, ref field_ident) => { let obj_pat = self.next("object"); let field_name_pat = self.next("field_name"); println!("Field(ref {}, ref {}) = {};", obj_pat, field_name_pat, current); @@ -432,7 +432,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.current = obj_pat; self.visit_expr(object); }, - ExprKind::Index(ref object, ref index) => { + ExprKind::Index(object, index) => { let object_pat = self.next("object"); let index_pat = self.next("index"); println!("Index(ref {}, ref {}) = {};", object_pat, index_pat, current); @@ -447,7 +447,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.current = path_pat; self.print_qpath(path); }, - ExprKind::AddrOf(kind, mutability, ref inner) => { + ExprKind::AddrOf(kind, mutability, inner) => { let inner_pat = self.next("inner"); println!( "AddrOf(BorrowKind::{:?}, Mutability::{:?}, ref {}) = {};", @@ -458,7 +458,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { }, ExprKind::Break(ref _destination, ref opt_value) => { let destination_pat = self.next("destination"); - if let Some(ref value) = *opt_value { + if let Some(value) = *opt_value { let value_pat = self.next("value"); println!("Break(ref {}, Some(ref {})) = {};", destination_pat, value_pat, current); self.current = value_pat; @@ -474,7 +474,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { // FIXME: implement label printing }, ExprKind::Ret(ref opt_value) => { - if let Some(ref value) = *opt_value { + if let Some(value) = *opt_value { let value_pat = self.next("value"); println!("Ret(Some(ref {})) = {};", value_pat, current); self.current = value_pat; @@ -491,10 +491,10 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { println!("LlvmInlineAsm(_) = {};", current); println!(" // unimplemented: `ExprKind::LlvmInlineAsm` is not further destructured at the moment"); }, - ExprKind::Struct(ref path, ref fields, ref opt_base) => { + ExprKind::Struct(path, fields, ref opt_base) => { let path_pat = self.next("path"); let fields_pat = self.next("fields"); - if let Some(ref base) = *opt_base { + if let Some(base) = *opt_base { let base_pat = self.next("base"); println!( "Struct(ref {}, ref {}, Some(ref {})) = {};", @@ -516,7 +516,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.current = value_pat; }, // FIXME: compute length (needs type info) - ExprKind::Repeat(ref value, _) => { + ExprKind::Repeat(value, _) => { let value_pat = self.next("value"); println!("Repeat(ref {}, _) = {};", value_pat, current); println!("// unimplemented: repeat count check"); @@ -526,7 +526,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { ExprKind::Err => { println!("Err = {}", current); }, - ExprKind::DropTemps(ref expr) => { + ExprKind::DropTemps(expr) => { let expr_pat = self.next("expr"); println!("DropTemps(ref {}) = {};", expr_pat, current); self.current = expr_pat; @@ -560,7 +560,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { BindingAnnotation::RefMut => "BindingAnnotation::RefMut", }; let name_pat = self.next("name"); - if let Some(ref sub) = *sub { + if let Some(sub) = *sub { let sub_pat = self.next("sub"); println!( "Binding({}, _, {}, Some(ref {})) = {};", @@ -573,7 +573,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { } println!(" if {}.as_str() == \"{}\";", name_pat, ident.as_str()); }, - PatKind::Struct(ref path, ref fields, ignore) => { + PatKind::Struct(ref path, fields, ignore) => { let path_pat = self.next("path"); let fields_pat = self.next("fields"); println!( @@ -585,13 +585,13 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { println!(" if {}.len() == {};", fields_pat, fields.len()); println!(" // unimplemented: field checks"); }, - PatKind::Or(ref fields) => { + PatKind::Or(fields) => { let fields_pat = self.next("fields"); println!("Or(ref {}) = {};", fields_pat, current); println!(" if {}.len() == {};", fields_pat, fields.len()); println!(" // unimplemented: field checks"); }, - PatKind::TupleStruct(ref path, ref fields, skip_pos) => { + PatKind::TupleStruct(ref path, fields, skip_pos) => { let path_pat = self.next("path"); let fields_pat = self.next("fields"); println!( @@ -609,25 +609,25 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.current = path_pat; self.print_qpath(path); }, - PatKind::Tuple(ref fields, skip_pos) => { + PatKind::Tuple(fields, skip_pos) => { let fields_pat = self.next("fields"); println!("Tuple(ref {}, {:?}) = {};", fields_pat, skip_pos, current); println!(" if {}.len() == {};", fields_pat, fields.len()); println!(" // unimplemented: field checks"); }, - PatKind::Box(ref pat) => { + PatKind::Box(pat) => { let pat_pat = self.next("pat"); println!("Box(ref {}) = {};", pat_pat, current); self.current = pat_pat; self.visit_pat(pat); }, - PatKind::Ref(ref pat, muta) => { + PatKind::Ref(pat, muta) => { let pat_pat = self.next("pat"); println!("Ref(ref {}, Mutability::{:?}) = {};", pat_pat, muta, current); self.current = pat_pat; self.visit_pat(pat); }, - PatKind::Lit(ref lit_expr) => { + PatKind::Lit(lit_expr) => { let lit_expr_pat = self.next("lit_expr"); println!("Lit(ref {}) = {}", lit_expr_pat, current); self.current = lit_expr_pat; @@ -645,10 +645,10 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { self.current = end_pat; walk_list!(self, visit_expr, end); }, - PatKind::Slice(ref start, ref middle, ref end) => { + PatKind::Slice(start, ref middle, end) => { let start_pat = self.next("start"); let end_pat = self.next("end"); - if let Some(ref middle) = middle { + if let Some(middle) = middle { let middle_pat = self.next("middle"); println!( "Slice(ref {}, Some(ref {}), ref {}) = {};", @@ -678,17 +678,17 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { let current = format!("{}.kind", self.current); match s.kind { // A local (let) binding: - StmtKind::Local(ref local) => { + StmtKind::Local(local) => { let local_pat = self.next("local"); println!("Local(ref {}) = {};", local_pat, current); - if let Some(ref init) = local.init { + if let Some(init) = local.init { let init_pat = self.next("init"); println!(" if let Some(ref {}) = {}.init;", init_pat, local_pat); self.current = init_pat; self.visit_expr(init); } self.current = format!("{}.pat", local_pat); - self.visit_pat(&local.pat); + self.visit_pat(local.pat); }, // An item binding: StmtKind::Item(_) => { @@ -696,7 +696,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { }, // Expr without trailing semi-colon (must have unit type): - StmtKind::Expr(ref e) => { + StmtKind::Expr(e) => { let e_pat = self.next("e"); println!("Expr(ref {}, _) = {}", e_pat, current); self.current = e_pat; @@ -704,7 +704,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { }, // Expr with trailing semi-colon (may have any type): - StmtKind::Semi(ref e) => { + StmtKind::Semi(e) => { let e_pat = self.next("e"); println!("Semi(ref {}, _) = {}", e_pat, current); self.current = e_pat; @@ -752,7 +752,7 @@ fn loop_desugaring_name(des: hir::LoopSource) -> &'static str { fn print_path(path: &QPath<'_>, first: &mut bool) { match *path { - QPath::Resolved(_, ref path) => { + QPath::Resolved(_, path) => { for segment in path.segments { if *first { *first = false; @@ -762,7 +762,7 @@ fn print_path(path: &QPath<'_>, first: &mut bool) { print!("{:?}", segment.ident.as_str()); } }, - QPath::TypeRelative(ref ty, ref segment) => match ty.kind { + QPath::TypeRelative(ty, segment) => match ty.kind { hir::TyKind::Path(ref inner_path) => { print_path(inner_path, first); if *first { diff --git a/clippy_lints/src/utils/inspector.rs b/clippy_lints/src/utils/inspector.rs index 6fd3c9d7dec..32d34e8d31e 100644 --- a/clippy_lints/src/utils/inspector.rs +++ b/clippy_lints/src/utils/inspector.rs @@ -47,7 +47,7 @@ impl<'tcx> LateLintPass<'tcx> for DeepCodeInspector { match item.vis.node { hir::VisibilityKind::Public => println!("public"), hir::VisibilityKind::Crate(_) => println!("visible crate wide"), - hir::VisibilityKind::Restricted { ref path, .. } => println!( + hir::VisibilityKind::Restricted { path, .. } => println!( "visible in module `{}`", rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(path, false)) ), @@ -99,13 +99,13 @@ impl<'tcx> LateLintPass<'tcx> for DeepCodeInspector { if !has_attr(cx.sess(), cx.tcx.hir().attrs(arm.hir_id)) { return; } - print_pat(cx, &arm.pat, 1); + print_pat(cx, arm.pat, 1); if let Some(ref guard) = arm.guard { println!("guard:"); print_guard(cx, guard, 1); } println!("body:"); - print_expr(cx, &arm.body, 1); + print_expr(cx, arm.body, 1); } fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'_>) { @@ -113,17 +113,17 @@ impl<'tcx> LateLintPass<'tcx> for DeepCodeInspector { return; } match stmt.kind { - hir::StmtKind::Local(ref local) => { + hir::StmtKind::Local(local) => { println!("local variable of type {}", cx.typeck_results().node_type(local.hir_id)); println!("pattern:"); - print_pat(cx, &local.pat, 0); - if let Some(ref e) = local.init { + print_pat(cx, local.pat, 0); + if let Some(e) = local.init { println!("init expression:"); print_expr(cx, e, 0); } }, hir::StmtKind::Item(_) => println!("item decl"), - hir::StmtKind::Expr(ref e) | hir::StmtKind::Semi(ref e) => print_expr(cx, e, 0), + hir::StmtKind::Expr(e) | hir::StmtKind::Semi(e) => print_expr(cx, e, 0), } } // fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx @@ -151,7 +151,7 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { cx.typeck_results().adjustments().get(expr.hir_id) ); match expr.kind { - hir::ExprKind::Box(ref e) => { + hir::ExprKind::Box(e) => { println!("{}Box", ind); print_expr(cx, e, indent + 1); }, @@ -161,7 +161,7 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { print_expr(cx, e, indent + 1); } }, - hir::ExprKind::Call(ref func, args) => { + hir::ExprKind::Call(func, args) => { println!("{}Call", ind); println!("{}function:", ind); print_expr(cx, func, indent + 1); @@ -170,7 +170,7 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { print_expr(cx, arg, indent + 1); } }, - hir::ExprKind::MethodCall(ref path, _, args, _) => { + hir::ExprKind::MethodCall(path, _, args, _) => { println!("{}MethodCall", ind); println!("{}method name: {}", ind, path.ident.name); for arg in args { @@ -183,7 +183,7 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { print_expr(cx, e, indent + 1); } }, - hir::ExprKind::Binary(op, ref lhs, ref rhs) => { + hir::ExprKind::Binary(op, lhs, rhs) => { println!("{}Binary", ind); println!("{}op: {:?}", ind, op.node); println!("{}lhs:", ind); @@ -191,7 +191,7 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { println!("{}rhs:", ind); print_expr(cx, rhs, indent + 1); }, - hir::ExprKind::Unary(op, ref inner) => { + hir::ExprKind::Unary(op, inner) => { println!("{}Unary", ind); println!("{}op: {:?}", ind, op); print_expr(cx, inner, indent + 1); @@ -200,12 +200,12 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { println!("{}Lit", ind); println!("{}{:?}", ind, lit); }, - hir::ExprKind::Cast(ref e, ref target) => { + hir::ExprKind::Cast(e, target) => { println!("{}Cast", ind); print_expr(cx, e, indent + 1); println!("{}target type: {:?}", ind, target); }, - hir::ExprKind::Type(ref e, ref target) => { + hir::ExprKind::Type(e, target) => { println!("{}Type", ind); print_expr(cx, e, indent + 1); println!("{}target type: {:?}", ind, target); @@ -213,16 +213,16 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { hir::ExprKind::Loop(..) => { println!("{}Loop", ind); }, - hir::ExprKind::If(ref cond, _, ref else_opt) => { + hir::ExprKind::If(cond, _, ref else_opt) => { println!("{}If", ind); println!("{}condition:", ind); print_expr(cx, cond, indent + 1); - if let Some(ref els) = *else_opt { + if let Some(els) = *else_opt { println!("{}else:", ind); print_expr(cx, els, indent + 1); } }, - hir::ExprKind::Match(ref cond, _, ref source) => { + hir::ExprKind::Match(cond, _, ref source) => { println!("{}Match", ind); println!("{}condition:", ind); print_expr(cx, cond, indent + 1); @@ -232,21 +232,21 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { println!("{}Closure", ind); println!("{}clause: {:?}", ind, clause); }, - hir::ExprKind::Yield(ref sub, _) => { + hir::ExprKind::Yield(sub, _) => { println!("{}Yield", ind); print_expr(cx, sub, indent + 1); }, hir::ExprKind::Block(_, _) => { println!("{}Block", ind); }, - hir::ExprKind::Assign(ref lhs, ref rhs, _) => { + hir::ExprKind::Assign(lhs, rhs, _) => { println!("{}Assign", ind); println!("{}lhs:", ind); print_expr(cx, lhs, indent + 1); println!("{}rhs:", ind); print_expr(cx, rhs, indent + 1); }, - hir::ExprKind::AssignOp(ref binop, ref lhs, ref rhs) => { + hir::ExprKind::AssignOp(ref binop, lhs, rhs) => { println!("{}AssignOp", ind); println!("{}op: {:?}", ind, binop.node); println!("{}lhs:", ind); @@ -254,31 +254,31 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { println!("{}rhs:", ind); print_expr(cx, rhs, indent + 1); }, - hir::ExprKind::Field(ref e, ident) => { + hir::ExprKind::Field(e, ident) => { println!("{}Field", ind); println!("{}field name: {}", ind, ident.name); println!("{}struct expr:", ind); print_expr(cx, e, indent + 1); }, - hir::ExprKind::Index(ref arr, ref idx) => { + hir::ExprKind::Index(arr, idx) => { println!("{}Index", ind); println!("{}array expr:", ind); print_expr(cx, arr, indent + 1); println!("{}index expr:", ind); print_expr(cx, idx, indent + 1); }, - hir::ExprKind::Path(hir::QPath::Resolved(ref ty, ref path)) => { + hir::ExprKind::Path(hir::QPath::Resolved(ref ty, path)) => { println!("{}Resolved Path, {:?}", ind, ty); println!("{}path: {:?}", ind, path); }, - hir::ExprKind::Path(hir::QPath::TypeRelative(ref ty, ref seg)) => { + hir::ExprKind::Path(hir::QPath::TypeRelative(ty, seg)) => { println!("{}Relative Path, {:?}", ind, ty); println!("{}seg: {:?}", ind, seg); }, hir::ExprKind::Path(hir::QPath::LangItem(lang_item, ..)) => { println!("{}Lang Item Path, {:?}", ind, lang_item.name()); }, - hir::ExprKind::AddrOf(kind, ref muta, ref e) => { + hir::ExprKind::AddrOf(kind, ref muta, e) => { println!("{}AddrOf", ind); println!("kind: {:?}", kind); println!("mutability: {:?}", muta); @@ -286,18 +286,18 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { }, hir::ExprKind::Break(_, ref e) => { println!("{}Break", ind); - if let Some(ref e) = *e { + if let Some(e) = *e { print_expr(cx, e, indent + 1); } }, hir::ExprKind::Continue(_) => println!("{}Again", ind), hir::ExprKind::Ret(ref e) => { println!("{}Ret", ind); - if let Some(ref e) = *e { + if let Some(e) = *e { print_expr(cx, e, indent + 1); } }, - hir::ExprKind::InlineAsm(ref asm) => { + hir::ExprKind::InlineAsm(asm) => { println!("{}InlineAsm", ind); println!("{}template: {}", ind, InlineAsmTemplatePiece::to_string(asm.template)); println!("{}options: {:?}", ind, asm.options); @@ -322,7 +322,7 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { } } }, - hir::ExprKind::LlvmInlineAsm(ref asm) => { + hir::ExprKind::LlvmInlineAsm(asm) => { let inputs = &asm.inputs_exprs; let outputs = &asm.outputs_exprs; println!("{}LlvmInlineAsm", ind); @@ -335,14 +335,14 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { print_expr(cx, e, indent + 1); } }, - hir::ExprKind::Struct(ref path, fields, ref base) => { + hir::ExprKind::Struct(path, fields, ref base) => { println!("{}Struct", ind); println!("{}path: {:?}", ind, path); for field in fields { println!("{}field \"{}\":", ind, field.ident.name); - print_expr(cx, &field.expr, indent + 1); + print_expr(cx, field.expr, indent + 1); } - if let Some(ref base) = *base { + if let Some(base) = *base { println!("{}base:", ind); print_expr(cx, base, indent + 1); } @@ -352,7 +352,7 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { println!("{}anon_const:", ind); print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1); }, - hir::ExprKind::Repeat(ref val, ref anon_const) => { + hir::ExprKind::Repeat(val, ref anon_const) => { println!("{}Repeat", ind); println!("{}value:", ind); print_expr(cx, val, indent + 1); @@ -362,7 +362,7 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { hir::ExprKind::Err => { println!("{}Err", ind); }, - hir::ExprKind::DropTemps(ref e) => { + hir::ExprKind::DropTemps(e) => { println!("{}DropTemps", ind); print_expr(cx, e, indent + 1); }, @@ -375,7 +375,7 @@ fn print_item(cx: &LateContext<'_>, item: &hir::Item<'_>) { match item.vis.node { hir::VisibilityKind::Public => println!("public"), hir::VisibilityKind::Crate(_) => println!("visible crate wide"), - hir::VisibilityKind::Restricted { ref path, .. } => println!( + hir::VisibilityKind::Restricted { path, .. } => println!( "visible in module `{}`", rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(path, false)) ), @@ -395,7 +395,7 @@ fn print_item(cx: &LateContext<'_>, item: &hir::Item<'_>) { println!("weird extern crate without a crate id"); } }, - hir::ItemKind::Use(ref path, ref kind) => println!("{:?}, {:?}", path, kind), + hir::ItemKind::Use(path, ref kind) => println!("{:?}, {:?}", path, kind), hir::ItemKind::Static(..) => println!("static item of type {:#?}", cx.tcx.type_of(did)), hir::ItemKind::Const(..) => println!("const item of type {:#?}", cx.tcx.type_of(did)), hir::ItemKind::Fn(..) => { @@ -404,7 +404,7 @@ fn print_item(cx: &LateContext<'_>, item: &hir::Item<'_>) { }, hir::ItemKind::Mod(..) => println!("module"), hir::ItemKind::ForeignMod { abi, .. } => println!("foreign module with abi: {}", abi), - hir::ItemKind::GlobalAsm(ref asm) => println!("global asm: {:?}", asm), + hir::ItemKind::GlobalAsm(asm) => println!("global asm: {:?}", asm), hir::ItemKind::TyAlias(..) => { println!("type alias for {:?}", cx.tcx.type_of(did)); }, @@ -454,7 +454,7 @@ fn print_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, indent: usize) { println!("{}Binding", ind); println!("{}mode: {:?}", ind, mode); println!("{}name: {}", ind, ident.name); - if let Some(ref inner) = *inner { + if let Some(inner) = *inner { println!("{}inner:", ind); print_pat(cx, inner, indent + 1); } @@ -479,7 +479,7 @@ fn print_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, indent: usize) { if field.is_shorthand { println!("{} in shorthand notation", ind); } - print_pat(cx, &field.pat, indent + 1); + print_pat(cx, field.pat, indent + 1); } }, hir::PatKind::TupleStruct(ref path, fields, opt_dots_position) => { @@ -496,11 +496,11 @@ fn print_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, indent: usize) { print_pat(cx, field, indent + 1); } }, - hir::PatKind::Path(hir::QPath::Resolved(ref ty, ref path)) => { + hir::PatKind::Path(hir::QPath::Resolved(ref ty, path)) => { println!("{}Resolved Path, {:?}", ind, ty); println!("{}path: {:?}", ind, path); }, - hir::PatKind::Path(hir::QPath::TypeRelative(ref ty, ref seg)) => { + hir::PatKind::Path(hir::QPath::TypeRelative(ty, seg)) => { println!("{}Relative Path, {:?}", ind, ty); println!("{}seg: {:?}", ind, seg); }, @@ -516,16 +516,16 @@ fn print_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, indent: usize) { print_pat(cx, field, indent + 1); } }, - hir::PatKind::Box(ref inner) => { + hir::PatKind::Box(inner) => { println!("{}Box", ind); print_pat(cx, inner, indent + 1); }, - hir::PatKind::Ref(ref inner, ref muta) => { + hir::PatKind::Ref(inner, ref muta) => { println!("{}Ref", ind); println!("{}mutability: {:?}", ind, muta); print_pat(cx, inner, indent + 1); }, - hir::PatKind::Lit(ref e) => { + hir::PatKind::Lit(e) => { println!("{}Lit", ind); print_expr(cx, e, indent + 1); }, @@ -549,7 +549,7 @@ fn print_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, indent: usize) { print_pat(cx, pat, indent + 1); } println!("i:"); - if let Some(ref pat) = *range { + if let Some(pat) = *range { print_pat(cx, pat, indent + 1); } println!("[y, z]:"); diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index d5a13c135b1..cf8039d6059 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -353,12 +353,12 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { return; } - if let hir::ItemKind::Static(ref ty, Mutability::Not, body_id) = item.kind { + if let hir::ItemKind::Static(ty, Mutability::Not, body_id) = item.kind { if is_lint_ref_type(cx, ty) { let expr = &cx.tcx.hir().body(body_id).value; if_chain! { - if let ExprKind::AddrOf(_, _, ref inner_exp) = expr.kind; - if let ExprKind::Struct(_, ref fields, _) = inner_exp.kind; + if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind; + if let ExprKind::Struct(_, fields, _) = inner_exp.kind; let field = fields .iter() .find(|f| f.ident.as_str() == "desc") @@ -385,7 +385,7 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { { if let hir::ItemKind::Impl(hir::Impl { of_trait: None, - items: ref impl_item_refs, + items: impl_item_refs, .. }) = item.kind { @@ -437,7 +437,7 @@ fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &Ty<'_>) -> bool { if let TyKind::Rptr( _, MutTy { - ty: ref inner, + ty: inner, mutbl: Mutability::Not, }, ) = ty.kind @@ -498,7 +498,7 @@ impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions { } if_chain! { - if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind; + if let ExprKind::MethodCall(path, _, args, _) = expr.kind; let fn_name = path.ident; if let Some(sugg) = self.map.get(&*fn_name.as_str()); let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs(); @@ -577,7 +577,7 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { } if_chain! { - if let ExprKind::Call(ref func, ref and_then_args) = expr.kind; + if let ExprKind::Call(func, and_then_args) = expr.kind; if let ExprKind::Path(ref path) = func.kind; if match_qpath(path, &["span_lint_and_then"]); if and_then_args.len() == 5; @@ -587,7 +587,7 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { let stmts = &block.stmts; if stmts.len() == 1 && block.expr.is_none(); if let StmtKind::Semi(only_expr) = &stmts[0].kind; - if let ExprKind::MethodCall(ref ps, _, ref span_call_args, _) = &only_expr.kind; + if let ExprKind::MethodCall(ps, _, span_call_args, _) = &only_expr.kind; then { let and_then_snippets = get_and_then_snippets(cx, and_then_args); let mut sle = SpanlessEq::new(cx).deny_side_effects(); @@ -762,7 +762,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem { // Check if this is a call to utils::match_type() if let ExprKind::Call(fn_path, [context, ty, ty_path]) = expr.kind; if let ExprKind::Path(fn_qpath) = &fn_path.kind; - if match_qpath(&fn_qpath, &["utils", "match_type"]); + if match_qpath(fn_qpath, &["utils", "match_type"]); // Extract the path to the matched type if let Some(segments) = path_to_matched_type(cx, ty_path); let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect(); diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index bc2eb88114e..febd4b6ff7b 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -49,7 +49,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { if_chain! { if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(expr).kind(); if let ty::Slice(..) = ty.kind(); - if let ExprKind::AddrOf(BorrowKind::Ref, mutability, ref addressee) = expr.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, mutability, addressee) = expr.kind; if let Some(vec_args) = higher::vec_macro(cx, addressee); then { self.check_vec_macro(cx, &vec_args, mutability, expr.span); diff --git a/clippy_lints/src/vec_resize_to_zero.rs b/clippy_lints/src/vec_resize_to_zero.rs index e035d3c5cad..5540e87405f 100644 --- a/clippy_lints/src/vec_resize_to_zero.rs +++ b/clippy_lints/src/vec_resize_to_zero.rs @@ -30,7 +30,7 @@ declare_lint_pass!(VecResizeToZero => [VEC_RESIZE_TO_ZERO]); impl<'tcx> LateLintPass<'tcx> for VecResizeToZero { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { - if let hir::ExprKind::MethodCall(path_segment, _, ref args, _) = expr.kind; + if let hir::ExprKind::MethodCall(path_segment, _, args, _) = expr.kind; if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if match_def_path(cx, method_def_id, &paths::VEC_RESIZE) && args.len() == 3; if let ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = args[1].kind; diff --git a/clippy_lints/src/zero_div_zero.rs b/clippy_lints/src/zero_div_zero.rs index 3b4890ad560..350b1cf25ff 100644 --- a/clippy_lints/src/zero_div_zero.rs +++ b/clippy_lints/src/zero_div_zero.rs @@ -31,7 +31,7 @@ impl<'tcx> LateLintPass<'tcx> for ZeroDiv { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // check for instances of 0.0/0.0 if_chain! { - if let ExprKind::Binary(ref op, ref left, ref right) = expr.kind; + if let ExprKind::Binary(ref op, left, right) = expr.kind; if let BinOpKind::Div = op.node; // TODO - constant_simple does not fold many operations involving floats. // That's probably fine for this lint - it's pretty unlikely that someone would diff --git a/clippy_lints/src/zero_sized_map_values.rs b/clippy_lints/src/zero_sized_map_values.rs index 2abd033e2a0..f93f0047f51 100644 --- a/clippy_lints/src/zero_sized_map_values.rs +++ b/clippy_lints/src/zero_sized_map_values.rs @@ -50,7 +50,7 @@ impl LateLintPass<'_> for ZeroSizedMapValues { if !in_trait_impl(cx, hir_ty.hir_id); let ty = ty_from_hir_ty(cx, hir_ty); if is_type_diagnostic_item(cx, ty, sym::hashmap_type) || match_type(cx, ty, &paths::BTREEMAP); - if let Adt(_, ref substs) = ty.kind(); + if let Adt(_, substs) = ty.kind(); let ty = substs.type_at(1); // Do this to prevent `layout_of` crashing, being unable to fully normalize `ty`. if is_normalizable(cx, cx.param_env, ty); diff --git a/src/driver.rs b/src/driver.rs index b6aed862e89..fa0c5f01430 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -99,17 +99,17 @@ impl rustc_driver::Callbacks for ClippyCallbacks { config.parse_sess_created = Some(Box::new(move |parse_sess| { track_clippy_args(parse_sess, &clippy_args_var); })); - config.register_lints = Some(Box::new(move |sess, mut lint_store| { + config.register_lints = Some(Box::new(move |sess, lint_store| { // technically we're ~guaranteed that this is none but might as well call anything that // is there already. Certainly it can't hurt. if let Some(previous) = &previous { (previous)(sess, lint_store); } - let conf = clippy_lints::read_conf(&[], &sess); - clippy_lints::register_plugins(&mut lint_store, &sess, &conf); - clippy_lints::register_pre_expansion_lints(&mut lint_store); - clippy_lints::register_renamed(&mut lint_store); + let conf = clippy_lints::read_conf(&[], sess); + clippy_lints::register_plugins(lint_store, sess, &conf); + clippy_lints::register_pre_expansion_lints(lint_store); + clippy_lints::register_renamed(lint_store); })); // FIXME: #4825; This is required, because Clippy lints that are based on MIR have to be @@ -191,7 +191,7 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { ]; for note in &xs { - handler.note_without_error(¬e); + handler.note_without_error(note); } // If backtraces are enabled, also print the query stack diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 0594663786c..f4d354f0bf9 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -98,7 +98,7 @@ fn default_config() -> compiletest::Config { fn run_mode(cfg: &mut compiletest::Config) { cfg.mode = TestMode::Ui; cfg.src_base = Path::new("tests").join("ui"); - compiletest::run_tests(&cfg); + compiletest::run_tests(cfg); } fn run_internal_tests(cfg: &mut compiletest::Config) { @@ -108,7 +108,7 @@ fn run_internal_tests(cfg: &mut compiletest::Config) { } cfg.mode = TestMode::Ui; cfg.src_base = Path::new("tests").join("ui-internal"); - compiletest::run_tests(&cfg); + compiletest::run_tests(cfg); } fn run_ui_toml(config: &mut compiletest::Config) { @@ -136,7 +136,7 @@ fn run_ui_toml(config: &mut compiletest::Config) { base: config.src_base.clone(), relative_dir: dir_path.file_name().unwrap().into(), }; - let test_name = compiletest::make_test_name(&config, &paths); + let test_name = compiletest::make_test_name(config, &paths); let index = tests .iter() .position(|test| test.desc.name == test_name) @@ -150,10 +150,10 @@ fn run_ui_toml(config: &mut compiletest::Config) { config.mode = TestMode::Ui; config.src_base = Path::new("tests").join("ui-toml").canonicalize().unwrap(); - let tests = compiletest::make_tests(&config); + let tests = compiletest::make_tests(config); let manifest_dir = var("CARGO_MANIFEST_DIR").unwrap_or_default(); - let res = run_tests(&config, tests); + let res = run_tests(config, tests); set_var("CARGO_MANIFEST_DIR", &manifest_dir); match res { Ok(true) => {}, @@ -221,7 +221,7 @@ fn run_ui_cargo(config: &mut compiletest::Config) { base: config.src_base.clone(), relative_dir: src_path.strip_prefix(&config.src_base).unwrap().into(), }; - let test_name = compiletest::make_test_name(&config, &paths); + let test_name = compiletest::make_test_name(config, &paths); let index = tests .iter() .position(|test| test.desc.name == test_name) @@ -240,11 +240,11 @@ fn run_ui_cargo(config: &mut compiletest::Config) { config.mode = TestMode::Ui; config.src_base = Path::new("tests").join("ui-cargo").canonicalize().unwrap(); - let tests = compiletest::make_tests(&config); + let tests = compiletest::make_tests(config); let current_dir = env::current_dir().unwrap(); let conf_dir = var("CLIPPY_CONF_DIR").unwrap_or_default(); - let res = run_tests(&config, &config.filters, tests); + let res = run_tests(config, &config.filters, tests); env::set_current_dir(current_dir).unwrap(); set_var("CLIPPY_CONF_DIR", conf_dir); diff --git a/tests/ui/borrow_interior_mutable_const/others.rs b/tests/ui/borrow_interior_mutable_const/others.rs index ea25729d11d..4327f12c37c 100644 --- a/tests/ui/borrow_interior_mutable_const/others.rs +++ b/tests/ui/borrow_interior_mutable_const/others.rs @@ -1,5 +1,9 @@ #![warn(clippy::borrow_interior_mutable_const)] -#![allow(clippy::declare_interior_mutable_const, clippy::ref_in_deref)] +#![allow( + clippy::declare_interior_mutable_const, + clippy::ref_in_deref, + clippy::needless_borrow +)] #![allow(const_item_mutation)] use std::borrow::Cow; diff --git a/tests/ui/borrow_interior_mutable_const/others.stderr b/tests/ui/borrow_interior_mutable_const/others.stderr index 9a908cf30e9..f146b97cf61 100644 --- a/tests/ui/borrow_interior_mutable_const/others.stderr +++ b/tests/ui/borrow_interior_mutable_const/others.stderr @@ -1,5 +1,5 @@ error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:54:5 + --> $DIR/others.rs:58:5 | LL | ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability | ^^^^^^ @@ -8,7 +8,7 @@ LL | ATOMIC.store(1, Ordering::SeqCst); //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:55:16 + --> $DIR/others.rs:59:16 | LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutability | ^^^^^^ @@ -16,7 +16,7 @@ LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); //~ ERROR interior mutabi = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:58:22 + --> $DIR/others.rs:62:22 | LL | let _once_ref = &ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -24,7 +24,7 @@ LL | let _once_ref = &ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:59:25 + --> $DIR/others.rs:63:25 | LL | let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -32,7 +32,7 @@ LL | let _once_ref_2 = &&ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:60:27 + --> $DIR/others.rs:64:27 | LL | let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -40,7 +40,7 @@ LL | let _once_ref_4 = &&&&ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:61:26 + --> $DIR/others.rs:65:26 | LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability | ^^^^^^^^^ @@ -48,7 +48,7 @@ LL | let _once_mut = &mut ONCE_INIT; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:72:14 + --> $DIR/others.rs:76:14 | LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -56,7 +56,7 @@ LL | let _ = &ATOMIC_TUPLE; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:73:14 + --> $DIR/others.rs:77:14 | LL | let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | let _ = &ATOMIC_TUPLE.0; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:74:19 + --> $DIR/others.rs:78:19 | LL | let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL | let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:75:14 + --> $DIR/others.rs:79:14 | LL | let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -80,7 +80,7 @@ LL | let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:76:13 + --> $DIR/others.rs:80:13 | LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -88,7 +88,7 @@ LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR interior mu = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:82:13 + --> $DIR/others.rs:86:13 | LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability | ^^^^^^^^^^^^ @@ -96,7 +96,7 @@ LL | let _ = ATOMIC_TUPLE.0[0]; //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:87:5 + --> $DIR/others.rs:91:5 | LL | CELL.set(2); //~ ERROR interior mutability | ^^^^ @@ -104,7 +104,7 @@ LL | CELL.set(2); //~ ERROR interior mutability = help: assign this const to a local or static variable, and use the variable here error: a `const` item with interior mutability should not be borrowed - --> $DIR/others.rs:88:16 + --> $DIR/others.rs:92:16 | LL | assert_eq!(CELL.get(), 6); //~ ERROR interior mutability | ^^^^ diff --git a/tests/ui/collapsible_match2.rs b/tests/ui/collapsible_match2.rs index 8372a212477..542e6948427 100644 --- a/tests/ui/collapsible_match2.rs +++ b/tests/ui/collapsible_match2.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::needless_borrow +)] fn lint_cases(opt_opt: Option>, res_opt: Result, String>) { // if guards on outer match diff --git a/tests/ui/collapsible_match2.stderr b/tests/ui/collapsible_match2.stderr index c8a445ef369..ffef32d1fde 100644 --- a/tests/ui/collapsible_match2.stderr +++ b/tests/ui/collapsible_match2.stderr @@ -1,5 +1,5 @@ error: unnecessary nested match - --> $DIR/collapsible_match2.rs:8:34 + --> $DIR/collapsible_match2.rs:13:34 | LL | Ok(val) if make() => 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_match2.rs:8:16 + --> $DIR/collapsible_match2.rs:13:16 | LL | Ok(val) if make() => match val { | ^^^ replace this binding @@ -18,7 +18,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: unnecessary nested match - --> $DIR/collapsible_match2.rs:15:24 + --> $DIR/collapsible_match2.rs:20:24 | LL | Ok(val) => match val { | ________________________^ @@ -28,7 +28,7 @@ LL | | }, | |_____________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match2.rs:15:16 + --> $DIR/collapsible_match2.rs:20:16 | LL | Ok(val) => match val { | ^^^ replace this binding @@ -36,7 +36,7 @@ LL | Some(n) => foo(n), | ^^^^^^^ with this pattern error: unnecessary nested match - --> $DIR/collapsible_match2.rs:29:29 + --> $DIR/collapsible_match2.rs:34:29 | LL | $pat => match $e { | _____________________________^ @@ -49,7 +49,7 @@ LL | mac!(res_opt => Ok(val), val => Some(n), foo(n)); | ------------------------------------------------- in this macro invocation | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match2.rs:41:28 + --> $DIR/collapsible_match2.rs:46:28 | LL | mac!(res_opt => Ok(val), val => Some(n), foo(n)); | ^^^ ^^^^^^^ with this pattern @@ -58,7 +58,7 @@ LL | mac!(res_opt => Ok(val), val => Some(n), foo(n)); = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error: unnecessary nested match - --> $DIR/collapsible_match2.rs:46:20 + --> $DIR/collapsible_match2.rs:51:20 | LL | Some(s) => match *s { | ____________________^ @@ -68,7 +68,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match2.rs:46:14 + --> $DIR/collapsible_match2.rs:51:14 | LL | Some(s) => match *s { | ^ replace this binding @@ -76,7 +76,7 @@ LL | [n] => foo(n), | ^^^ with this pattern error: unnecessary nested match - --> $DIR/collapsible_match2.rs:55:24 + --> $DIR/collapsible_match2.rs:60:24 | LL | Some(ref s) => match &*s { | ________________________^ @@ -86,7 +86,7 @@ LL | | }, | |_________^ | help: the outer pattern can be modified to include the inner pattern - --> $DIR/collapsible_match2.rs:55:14 + --> $DIR/collapsible_match2.rs:60:14 | LL | Some(ref s) => match &*s { | ^^^^^ replace this binding diff --git a/tests/ui/escape_analysis.rs b/tests/ui/escape_analysis.rs index d26f48fc68f..13e2b6c7a2e 100644 --- a/tests/ui/escape_analysis.rs +++ b/tests/ui/escape_analysis.rs @@ -101,7 +101,7 @@ fn warn_match() { let x = box A; match &x { // not moved - ref y => (), + y => (), } } @@ -111,7 +111,7 @@ fn nowarn_large_array() { let x = box [1; 10000]; match &x { // not moved - ref y => (), + y => (), } } diff --git a/tests/ui/implicit_clone.rs b/tests/ui/implicit_clone.rs index 19101522163..cef71cf79d7 100644 --- a/tests/ui/implicit_clone.rs +++ b/tests/ui/implicit_clone.rs @@ -66,7 +66,7 @@ fn main() { let _ = vec.to_vec(); let vec_ref = &vec; - let _ = return_owned_from_slice(&vec_ref); + let _ = return_owned_from_slice(vec_ref); let _ = vec_ref.to_owned(); let _ = vec_ref.to_vec(); diff --git a/tests/ui/into_iter_on_ref.fixed b/tests/ui/into_iter_on_ref.fixed index 7f92d0dbdc9..b77f17944d8 100644 --- a/tests/ui/into_iter_on_ref.fixed +++ b/tests/ui/into_iter_on_ref.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![allow(clippy::useless_vec)] +#![allow(clippy::useless_vec, clippy::needless_borrow)] #![warn(clippy::into_iter_on_ref)] struct X; diff --git a/tests/ui/into_iter_on_ref.rs b/tests/ui/into_iter_on_ref.rs index 416056d3fdb..3854bb05af8 100644 --- a/tests/ui/into_iter_on_ref.rs +++ b/tests/ui/into_iter_on_ref.rs @@ -1,5 +1,5 @@ // run-rustfix -#![allow(clippy::useless_vec)] +#![allow(clippy::useless_vec, clippy::needless_borrow)] #![warn(clippy::into_iter_on_ref)] struct X; diff --git a/tests/ui/ref_option_ref.rs b/tests/ui/ref_option_ref.rs index b2c275d68af..2df45c927d7 100644 --- a/tests/ui/ref_option_ref.rs +++ b/tests/ui/ref_option_ref.rs @@ -9,7 +9,7 @@ static THRESHOLD: i32 = 10; static REF_THRESHOLD: &Option<&i32> = &Some(&THRESHOLD); const CONST_THRESHOLD: &i32 = &10; -const REF_CONST: &Option<&i32> = &Some(&CONST_THRESHOLD); +const REF_CONST: &Option<&i32> = &Some(CONST_THRESHOLD); type RefOptRefU32<'a> = &'a Option<&'a u32>; type RefOptRef<'a, T> = &'a Option<&'a T>; diff --git a/tests/ui/ref_option_ref.stderr b/tests/ui/ref_option_ref.stderr index 4e7fc800061..b61334758e8 100644 --- a/tests/ui/ref_option_ref.stderr +++ b/tests/ui/ref_option_ref.stderr @@ -9,7 +9,7 @@ LL | static REF_THRESHOLD: &Option<&i32> = &Some(&THRESHOLD); error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>` --> $DIR/ref_option_ref.rs:12:18 | -LL | const REF_CONST: &Option<&i32> = &Some(&CONST_THRESHOLD); +LL | const REF_CONST: &Option<&i32> = &Some(CONST_THRESHOLD); | ^^^^^^^^^^^^^ help: try: `Option<&i32>` error: since `&` implements the `Copy` trait, `&Option<&T>` can be simplified to `Option<&T>` diff --git a/tests/ui/stable_sort_primitive.fixed b/tests/ui/stable_sort_primitive.fixed index 8f8f5665931..f5f18169df2 100644 --- a/tests/ui/stable_sort_primitive.fixed +++ b/tests/ui/stable_sort_primitive.fixed @@ -20,7 +20,7 @@ fn main() { // Negative examples: behavior changes if made unstable let mut vec = vec![1, 3, 2]; vec.sort_by_key(|i| i / 2); - vec.sort_by(|a, b| (a + b).cmp(&b)); + vec.sort_by(|&a, &b| (a + b).cmp(&b)); // negative examples - Not of a primitive type let mut vec_of_complex = vec![String::from("hello"), String::from("world!")]; vec_of_complex.sort(); diff --git a/tests/ui/stable_sort_primitive.rs b/tests/ui/stable_sort_primitive.rs index f9bd9779067..8149c5638e0 100644 --- a/tests/ui/stable_sort_primitive.rs +++ b/tests/ui/stable_sort_primitive.rs @@ -20,7 +20,7 @@ fn main() { // Negative examples: behavior changes if made unstable let mut vec = vec![1, 3, 2]; vec.sort_by_key(|i| i / 2); - vec.sort_by(|a, b| (a + b).cmp(&b)); + vec.sort_by(|&a, &b| (a + b).cmp(&b)); // negative examples - Not of a primitive type let mut vec_of_complex = vec![String::from("hello"), String::from("world!")]; vec_of_complex.sort(); -- cgit 1.4.1-3-g733a5 From 6b5778eb176deda52a3960cc2029bc93f1424c35 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 26 Mar 2021 22:28:59 -0400 Subject: Fix `explicit_into_iter_loop` Only lint when `into_iter` is an implementation of `IntoIterator` Minor cleanups --- clippy_lints/src/loops/explicit_into_iter_loop.rs | 13 +++++----- clippy_lints/src/loops/explicit_iter_loop.rs | 10 ++++---- clippy_lints/src/loops/mod.rs | 29 ++++++++++------------- tests/ui/for_loop_fixable.fixed | 26 ++++++++++++++++++++ tests/ui/for_loop_fixable.rs | 26 ++++++++++++++++++++ 5 files changed, 77 insertions(+), 27 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/loops/explicit_into_iter_loop.rs b/clippy_lints/src/loops/explicit_into_iter_loop.rs index 4871a031187..c7a28f42ea1 100644 --- a/clippy_lints/src/loops/explicit_into_iter_loop.rs +++ b/clippy_lints/src/loops/explicit_into_iter_loop.rs @@ -1,24 +1,25 @@ use super::EXPLICIT_INTO_ITER_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{match_trait_method, paths}; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::TyS; -pub(super) fn check(cx: &LateContext<'_>, args: &'hir [Expr<'hir>], arg: &Expr<'_>) { - let receiver_ty = cx.typeck_results().expr_ty(&args[0]); - let receiver_ty_adjusted = cx.typeck_results().expr_ty_adjusted(&args[0]); - if !TyS::same_type(receiver_ty, receiver_ty_adjusted) { +pub(super) fn check(cx: &LateContext<'_>, self_arg: &'hir Expr<'hir>, call_expr: &Expr<'_>) { + let self_ty = cx.typeck_results().expr_ty(self_arg); + let self_ty_adjusted = cx.typeck_results().expr_ty_adjusted(self_arg); + if !(TyS::same_type(self_ty, self_ty_adjusted) && match_trait_method(cx, call_expr, &paths::INTO_ITERATOR)) { return; } let mut applicability = Applicability::MachineApplicable; - let object = snippet_with_applicability(cx, args[0].span, "_", &mut applicability); + let object = snippet_with_applicability(cx, self_arg.span, "_", &mut applicability); span_lint_and_sugg( cx, EXPLICIT_INTO_ITER_LOOP, - arg.span, + call_expr.span, "it is more concise to loop over containers instead of using explicit \ iteration methods", "to write this more concisely, try", diff --git a/clippy_lints/src/loops/explicit_iter_loop.rs b/clippy_lints/src/loops/explicit_iter_loop.rs index 92aa2beb66d..ce02ad013be 100644 --- a/clippy_lints/src/loops/explicit_iter_loop.rs +++ b/clippy_lints/src/loops/explicit_iter_loop.rs @@ -9,12 +9,12 @@ use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty, TyS}; use rustc_span::sym; -pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], arg: &Expr<'_>, method_name: &str) { +pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, arg: &Expr<'_>, method_name: &str) { let should_lint = match method_name { - "iter" | "iter_mut" => is_ref_iterable_type(cx, &args[0]), + "iter" | "iter_mut" => is_ref_iterable_type(cx, self_arg), "into_iter" if match_trait_method(cx, arg, &paths::INTO_ITERATOR) => { - let receiver_ty = cx.typeck_results().expr_ty(&args[0]); - let receiver_ty_adjusted = cx.typeck_results().expr_ty_adjusted(&args[0]); + let receiver_ty = cx.typeck_results().expr_ty(self_arg); + let receiver_ty_adjusted = cx.typeck_results().expr_ty_adjusted(self_arg); let ref_receiver_ty = cx.tcx.mk_ref( cx.tcx.lifetimes.re_erased, ty::TypeAndMut { @@ -32,7 +32,7 @@ pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], arg: &Expr<'_>, met } let mut applicability = Applicability::MachineApplicable; - let object = snippet_with_applicability(cx, args[0].span, "_", &mut applicability); + let object = snippet_with_applicability(cx, self_arg.span, "_", &mut applicability); let muta = if method_name == "iter_mut" { "mut " } else { "" }; span_lint_and_sugg( cx, diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index 28acefd51fe..a4bc3e6bd10 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -602,22 +602,19 @@ fn check_for_loop<'tcx>( fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>, expr: &Expr<'_>) { let mut next_loop_linted = false; // whether or not ITER_NEXT_LOOP lint was used - if let ExprKind::MethodCall(method, _, args, _) = arg.kind { - // just the receiver, no arguments - if args.len() == 1 { - let method_name = &*method.ident.as_str(); - // check for looping over x.iter() or x.iter_mut(), could use &x or &mut x - match method_name { - "iter" | "iter_mut" => explicit_iter_loop::check(cx, args, arg, method_name), - "into_iter" => { - explicit_iter_loop::check(cx, args, arg, method_name); - explicit_into_iter_loop::check(cx, args, arg); - }, - "next" => { - next_loop_linted = iter_next_loop::check(cx, arg, expr); - }, - _ => {}, - } + if let ExprKind::MethodCall(method, _, [self_arg], _) = arg.kind { + let method_name = &*method.ident.as_str(); + // check for looping over x.iter() or x.iter_mut(), could use &x or &mut x + match method_name { + "iter" | "iter_mut" => explicit_iter_loop::check(cx, self_arg, arg, method_name), + "into_iter" => { + explicit_iter_loop::check(cx, self_arg, arg, method_name); + explicit_into_iter_loop::check(cx, self_arg, arg); + }, + "next" => { + next_loop_linted = iter_next_loop::check(cx, arg, expr); + }, + _ => {}, } } diff --git a/tests/ui/for_loop_fixable.fixed b/tests/ui/for_loop_fixable.fixed index 249a88a0b39..f44928d4083 100644 --- a/tests/ui/for_loop_fixable.fixed +++ b/tests/ui/for_loop_fixable.fixed @@ -281,3 +281,29 @@ mod issue_4958 { for _ in rr.into_iter() {} } } + +// explicit_into_iter_loop +#[warn(clippy::explicit_into_iter_loop)] +mod issue_6900 { + struct S; + impl S { + #[allow(clippy::should_implement_trait)] + pub fn into_iter(self) -> I { + unimplemented!() + } + } + + struct I(T); + impl Iterator for I { + type Item = T; + fn next(&mut self) -> Option { + unimplemented!() + } + } + + fn f() { + for _ in S.into_iter::() { + unimplemented!() + } + } +} diff --git a/tests/ui/for_loop_fixable.rs b/tests/ui/for_loop_fixable.rs index 306d85a6351..5b1eb3ee4dc 100644 --- a/tests/ui/for_loop_fixable.rs +++ b/tests/ui/for_loop_fixable.rs @@ -281,3 +281,29 @@ mod issue_4958 { for _ in rr.into_iter() {} } } + +// explicit_into_iter_loop +#[warn(clippy::explicit_into_iter_loop)] +mod issue_6900 { + struct S; + impl S { + #[allow(clippy::should_implement_trait)] + pub fn into_iter(self) -> I { + unimplemented!() + } + } + + struct I(T); + impl Iterator for I { + type Item = T; + fn next(&mut self) -> Option { + unimplemented!() + } + } + + fn f() { + for _ in S.into_iter::() { + unimplemented!() + } + } +} -- cgit 1.4.1-3-g733a5 From 4f7fc11ef17e3afb0175b965995cad2f9d7ab210 Mon Sep 17 00:00:00 2001 From: boxdot Date: Sun, 18 Oct 2020 13:42:09 +0200 Subject: Add invalid null pointer usage lint. --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 3 + clippy_lints/src/ptr.rs | 95 +++++++++++++++-- clippy_lints/src/size_of_in_element_count.rs | 4 +- clippy_utils/src/paths.rs | 13 ++- tests/ui/invalid_null_ptr_usage.fixed | 49 +++++++++ tests/ui/invalid_null_ptr_usage.rs | 49 +++++++++ tests/ui/invalid_null_ptr_usage.stderr | 154 +++++++++++++++++++++++++++ 8 files changed, 353 insertions(+), 15 deletions(-) create mode 100644 tests/ui/invalid_null_ptr_usage.fixed create mode 100644 tests/ui/invalid_null_ptr_usage.rs create mode 100644 tests/ui/invalid_null_ptr_usage.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 73997192ae0..715f4702e95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2267,6 +2267,7 @@ Released 2018-09-13 [`into_iter_on_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_array [`into_iter_on_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_ref [`invalid_atomic_ordering`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_atomic_ordering +[`invalid_null_ptr_usage`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_null_ptr_usage [`invalid_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_ref [`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex [`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index cd92b551abd..2fb8bbcd1f7 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -902,6 +902,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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, @@ -1671,6 +1672,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), @@ -2010,6 +2012,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 09fcdb5faf8..2ab1e958ec8 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -4,7 +4,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_the use clippy_utils::ptr::get_spans; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{is_type_diagnostic_item, match_type, walk_ptrs_hir_ty}; -use clippy_utils::{is_allowed, match_qpath, paths}; +use clippy_utils::{is_allowed, match_def_path, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{ @@ -15,6 +15,7 @@ use rustc_lint::{LateContext, LateLintPass}; 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 rustc_span::{sym, MultiSpan}; use std::borrow::Cow; @@ -94,7 +95,7 @@ declare_clippy_lint! { /// ``` pub CMP_NULL, style, - "comparing a pointer to a null pointer, suggesting to use `.is_null()` instead." + "comparing a pointer to a null pointer, suggesting to use `.is_null()` instead" } declare_clippy_lint! { @@ -119,7 +120,28 @@ declare_clippy_lint! { "fns that create mutable refs from immutable ref args" } -declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF]); +declare_clippy_lint! { + /// **What it does:** This lint checks for invalid usages of `ptr::null`. + /// + /// **Why is this bad?** This causes undefined behavior. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```ignore + /// // Bad. Undefined behavior + /// unsafe { std::slice::from_raw_parts(ptr::null(), 0); } + /// ``` + /// + /// // Good + /// unsafe { std::slice::from_raw_parts(NonNull::dangling().as_ptr(), 0); } + /// ``` + pub INVALID_NULL_PTR_USAGE, + correctness, + "invalid usage of a null pointer, suggesting `NonNull::dangling()` instead" +} + +declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF, INVALID_NULL_PTR_USAGE]); impl<'tcx> LateLintPass<'tcx> for Ptr { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { @@ -153,7 +175,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Binary(ref op, l, r) = expr.kind { - if (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) && (is_null_path(l) || is_null_path(r)) { + if (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) && (is_null_path(cx, l) || is_null_path(cx, r)) { span_lint( cx, CMP_NULL, @@ -161,6 +183,55 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { "comparing with null is better expressed by the `.is_null()` method", ); } + } else { + check_invalid_ptr_usage(cx, expr); + } + } +} + +fn check_invalid_ptr_usage<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + // (fn_path, arg_indices) - `arg_indices` are the `arg` positions where null would cause U.B. + const INVALID_NULL_PTR_USAGE_TABLE: [(&[&str], &[usize]); 16] = [ + (&paths::SLICE_FROM_RAW_PARTS, &[0]), + (&paths::SLICE_FROM_RAW_PARTS_MUT, &[0]), + (&paths::PTR_COPY, &[0, 1]), + (&paths::PTR_COPY_NONOVERLAPPING, &[0, 1]), + (&paths::PTR_READ, &[0]), + (&paths::PTR_READ_UNALIGNED, &[0]), + (&paths::PTR_READ_VOLATILE, &[0]), + (&paths::PTR_REPLACE, &[0]), + (&paths::PTR_SLICE_FROM_RAW_PARTS, &[0]), + (&paths::PTR_SLICE_FROM_RAW_PARTS_MUT, &[0]), + (&paths::PTR_SWAP, &[0, 1]), + (&paths::PTR_SWAP_NONOVERLAPPING, &[0, 1]), + (&paths::PTR_WRITE, &[0]), + (&paths::PTR_WRITE_UNALIGNED, &[0]), + (&paths::PTR_WRITE_VOLATILE, &[0]), + (&paths::PTR_WRITE_BYTES, &[0]), + ]; + + if_chain! { + if let ExprKind::Call(ref fun, ref args) = expr.kind; + if let ExprKind::Path(ref qpath) = fun.kind; + if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id(); + let fun_def_path = cx.get_def_path(fun_def_id).into_iter().map(Symbol::to_ident_string).collect::>(); + if let Some(&(_, arg_indices)) = INVALID_NULL_PTR_USAGE_TABLE + .iter() + .find(|&&(fn_path, _)| fn_path == fun_def_path); + then { + for &arg_idx in arg_indices { + if let Some(arg) = args.get(arg_idx).filter(|arg| is_null_path(cx, arg)) { + span_lint_and_sugg( + cx, + INVALID_NULL_PTR_USAGE, + arg.span, + "pointer must be non-null", + "change this to", + "core::ptr::NonNull::dangling().as_ptr()".to_string(), + Applicability::MachineApplicable, + ); + } + } } } } @@ -345,13 +416,15 @@ fn get_rptr_lm<'tcx>(ty: &'tcx Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, } } -fn is_null_path(expr: &Expr<'_>) -> bool { - if let ExprKind::Call(pathexp, args) = expr.kind { - if args.is_empty() { - if let ExprKind::Path(ref path) = pathexp.kind { - return match_qpath(path, &paths::PTR_NULL) || match_qpath(path, &paths::PTR_NULL_MUT); - } +fn is_null_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + if_chain! { + if let ExprKind::Call(path, []) = expr.kind; + if let ExprKind::Path(ref qpath) = path.kind; + if let Some(fn_def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id(); + then { + match_def_path(cx, fn_def_id, &paths::PTR_NULL) || match_def_path(cx, fn_def_id, &paths::PTR_NULL_MUT) + } else { + false } } - false } diff --git a/clippy_lints/src/size_of_in_element_count.rs b/clippy_lints/src/size_of_in_element_count.rs index 09e00866815..cd2bdec1707 100644 --- a/clippy_lints/src/size_of_in_element_count.rs +++ b/clippy_lints/src/size_of_in_element_count.rs @@ -65,8 +65,8 @@ fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, inverted: bool) fn get_pointee_ty_and_count_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> { const FUNCTIONS: [&[&str]; 8] = [ - &paths::COPY_NONOVERLAPPING, - &paths::COPY, + &paths::PTR_COPY_NONOVERLAPPING, + &paths::PTR_COPY, &paths::WRITE_BYTES, &paths::PTR_SWAP_NONOVERLAPPING, &paths::PTR_SLICE_FROM_RAW_PARTS, diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 3b4c4070c0e..5da9c624ac4 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -18,8 +18,6 @@ pub const BTREESET: [&str; 5] = ["alloc", "collections", "btree", "set", "BTreeS pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"]; pub const CMP_MAX: [&str; 3] = ["core", "cmp", "max"]; pub const CMP_MIN: [&str; 3] = ["core", "cmp", "min"]; -pub const COPY: [&str; 4] = ["core", "intrinsics", "", "copy_nonoverlapping"]; -pub const COPY_NONOVERLAPPING: [&str; 4] = ["core", "intrinsics", "", "copy"]; pub const COW: [&str; 3] = ["alloc", "borrow", "Cow"]; pub const CSTRING_AS_C_STR: [&str; 5] = ["std", "ffi", "c_str", "CString", "as_c_str"]; pub const DEFAULT_TRAIT: [&str; 3] = ["core", "default", "Default"]; @@ -100,12 +98,23 @@ pub const PERMISSIONS_FROM_MODE: [&str; 7] = ["std", "sys", "unix", "ext", "fs", 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"]; +pub const PTR_COPY: [&str; 4] = ["core", "intrinsics", "", "copy"]; +pub const PTR_COPY_NONOVERLAPPING: [&str; 4] = ["core", "intrinsics", "", "copy_nonoverlapping"]; pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"]; pub const PTR_NULL: [&str; 3] = ["core", "ptr", "null"]; pub const PTR_NULL_MUT: [&str; 3] = ["core", "ptr", "null_mut"]; pub const PTR_SLICE_FROM_RAW_PARTS: [&str; 3] = ["core", "ptr", "slice_from_raw_parts"]; pub const PTR_SLICE_FROM_RAW_PARTS_MUT: [&str; 3] = ["core", "ptr", "slice_from_raw_parts_mut"]; pub const PTR_SWAP_NONOVERLAPPING: [&str; 3] = ["core", "ptr", "swap_nonoverlapping"]; +pub const PTR_READ: [&str; 3] = ["core", "ptr", "read"]; +pub const PTR_READ_UNALIGNED: [&str; 3] = ["core", "ptr", "read_unaligned"]; +pub const PTR_READ_VOLATILE: [&str; 3] = ["core", "ptr", "read_volatile"]; +pub const PTR_REPLACE: [&str; 3] = ["core", "ptr", "replace"]; +pub const PTR_SWAP: [&str; 3] = ["core", "ptr", "swap"]; +pub const PTR_WRITE: [&str; 3] = ["core", "ptr", "write"]; +pub const PTR_WRITE_BYTES: [&str; 3] = ["core", "intrinsics", "write_bytes"]; +pub const PTR_WRITE_UNALIGNED: [&str; 3] = ["core", "ptr", "write_unaligned"]; +pub const PTR_WRITE_VOLATILE: [&str; 3] = ["core", "ptr", "write_volatile"]; pub const PUSH_STR: [&str; 4] = ["alloc", "string", "String", "push_str"]; pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"]; pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"]; diff --git a/tests/ui/invalid_null_ptr_usage.fixed b/tests/ui/invalid_null_ptr_usage.fixed new file mode 100644 index 00000000000..4f5322ebf20 --- /dev/null +++ b/tests/ui/invalid_null_ptr_usage.fixed @@ -0,0 +1,49 @@ +// run-rustfix + +fn main() { + unsafe { + let _slice: &[usize] = std::slice::from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0); + let _slice: &[usize] = std::slice::from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0); + + let _slice: &[usize] = std::slice::from_raw_parts_mut(core::ptr::NonNull::dangling().as_ptr(), 0); + + std::ptr::copy::(core::ptr::NonNull::dangling().as_ptr(), std::ptr::NonNull::dangling().as_ptr(), 0); + std::ptr::copy::(std::ptr::NonNull::dangling().as_ptr(), core::ptr::NonNull::dangling().as_ptr(), 0); + + std::ptr::copy_nonoverlapping::(core::ptr::NonNull::dangling().as_ptr(), std::ptr::NonNull::dangling().as_ptr(), 0); + std::ptr::copy_nonoverlapping::(std::ptr::NonNull::dangling().as_ptr(), core::ptr::NonNull::dangling().as_ptr(), 0); + + struct A; // zero sized struct + assert_eq!(std::mem::size_of::(), 0); + + let _a: A = std::ptr::read(core::ptr::NonNull::dangling().as_ptr()); + let _a: A = std::ptr::read(core::ptr::NonNull::dangling().as_ptr()); + + let _a: A = std::ptr::read_unaligned(core::ptr::NonNull::dangling().as_ptr()); + let _a: A = std::ptr::read_unaligned(core::ptr::NonNull::dangling().as_ptr()); + + let _a: A = std::ptr::read_volatile(core::ptr::NonNull::dangling().as_ptr()); + let _a: A = std::ptr::read_volatile(core::ptr::NonNull::dangling().as_ptr()); + + let _a: A = std::ptr::replace(core::ptr::NonNull::dangling().as_ptr(), A); + + let _slice: *const [usize] = std::ptr::slice_from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0); + let _slice: *const [usize] = std::ptr::slice_from_raw_parts(core::ptr::NonNull::dangling().as_ptr(), 0); + + let _slice: *const [usize] = std::ptr::slice_from_raw_parts_mut(core::ptr::NonNull::dangling().as_ptr(), 0); + + std::ptr::swap::(core::ptr::NonNull::dangling().as_ptr(), &mut A); + std::ptr::swap::(&mut A, core::ptr::NonNull::dangling().as_ptr()); + + std::ptr::swap_nonoverlapping::(core::ptr::NonNull::dangling().as_ptr(), &mut A, 0); + std::ptr::swap_nonoverlapping::(&mut A, core::ptr::NonNull::dangling().as_ptr(), 0); + + std::ptr::write(core::ptr::NonNull::dangling().as_ptr(), A); + + std::ptr::write_unaligned(core::ptr::NonNull::dangling().as_ptr(), A); + + std::ptr::write_volatile(core::ptr::NonNull::dangling().as_ptr(), A); + + std::ptr::write_bytes::(core::ptr::NonNull::dangling().as_ptr(), 42, 0); + } +} diff --git a/tests/ui/invalid_null_ptr_usage.rs b/tests/ui/invalid_null_ptr_usage.rs new file mode 100644 index 00000000000..ae51c52d8af --- /dev/null +++ b/tests/ui/invalid_null_ptr_usage.rs @@ -0,0 +1,49 @@ +// run-rustfix + +fn main() { + unsafe { + let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null(), 0); + let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null_mut(), 0); + + let _slice: &[usize] = std::slice::from_raw_parts_mut(std::ptr::null_mut(), 0); + + std::ptr::copy::(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0); + std::ptr::copy::(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0); + + std::ptr::copy_nonoverlapping::(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0); + std::ptr::copy_nonoverlapping::(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0); + + struct A; // zero sized struct + assert_eq!(std::mem::size_of::(), 0); + + let _a: A = std::ptr::read(std::ptr::null()); + let _a: A = std::ptr::read(std::ptr::null_mut()); + + let _a: A = std::ptr::read_unaligned(std::ptr::null()); + let _a: A = std::ptr::read_unaligned(std::ptr::null_mut()); + + let _a: A = std::ptr::read_volatile(std::ptr::null()); + let _a: A = std::ptr::read_volatile(std::ptr::null_mut()); + + let _a: A = std::ptr::replace(std::ptr::null_mut(), A); + + let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null(), 0); + let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null_mut(), 0); + + let _slice: *const [usize] = std::ptr::slice_from_raw_parts_mut(std::ptr::null_mut(), 0); + + std::ptr::swap::(std::ptr::null_mut(), &mut A); + std::ptr::swap::(&mut A, std::ptr::null_mut()); + + std::ptr::swap_nonoverlapping::(std::ptr::null_mut(), &mut A, 0); + std::ptr::swap_nonoverlapping::(&mut A, std::ptr::null_mut(), 0); + + std::ptr::write(std::ptr::null_mut(), A); + + std::ptr::write_unaligned(std::ptr::null_mut(), A); + + std::ptr::write_volatile(std::ptr::null_mut(), A); + + std::ptr::write_bytes::(std::ptr::null_mut(), 42, 0); + } +} diff --git a/tests/ui/invalid_null_ptr_usage.stderr b/tests/ui/invalid_null_ptr_usage.stderr new file mode 100644 index 00000000000..532c36abe51 --- /dev/null +++ b/tests/ui/invalid_null_ptr_usage.stderr @@ -0,0 +1,154 @@ +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:5:59 + | +LL | let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null(), 0); + | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + | + = note: `#[deny(clippy::invalid_null_ptr_usage)]` on by default + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:6:59 + | +LL | let _slice: &[usize] = std::slice::from_raw_parts(std::ptr::null_mut(), 0); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:8:63 + | +LL | let _slice: &[usize] = std::slice::from_raw_parts_mut(std::ptr::null_mut(), 0); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:10:33 + | +LL | std::ptr::copy::(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0); + | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:11:73 + | +LL | std::ptr::copy::(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:13:48 + | +LL | std::ptr::copy_nonoverlapping::(std::ptr::null(), std::ptr::NonNull::dangling().as_ptr(), 0); + | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:14:88 + | +LL | std::ptr::copy_nonoverlapping::(std::ptr::NonNull::dangling().as_ptr(), std::ptr::null_mut(), 0); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:19:36 + | +LL | let _a: A = std::ptr::read(std::ptr::null()); + | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:20:36 + | +LL | let _a: A = std::ptr::read(std::ptr::null_mut()); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:22:46 + | +LL | let _a: A = std::ptr::read_unaligned(std::ptr::null()); + | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:23:46 + | +LL | let _a: A = std::ptr::read_unaligned(std::ptr::null_mut()); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:25:45 + | +LL | let _a: A = std::ptr::read_volatile(std::ptr::null()); + | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:26:45 + | +LL | let _a: A = std::ptr::read_volatile(std::ptr::null_mut()); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:28:39 + | +LL | let _a: A = std::ptr::replace(std::ptr::null_mut(), A); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:30:69 + | +LL | let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null(), 0); + | ^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:31:69 + | +LL | let _slice: *const [usize] = std::ptr::slice_from_raw_parts(std::ptr::null_mut(), 0); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:33:73 + | +LL | let _slice: *const [usize] = std::ptr::slice_from_raw_parts_mut(std::ptr::null_mut(), 0); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:35:29 + | +LL | std::ptr::swap::(std::ptr::null_mut(), &mut A); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:36:37 + | +LL | std::ptr::swap::(&mut A, std::ptr::null_mut()); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:38:44 + | +LL | std::ptr::swap_nonoverlapping::(std::ptr::null_mut(), &mut A, 0); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:39:52 + | +LL | std::ptr::swap_nonoverlapping::(&mut A, std::ptr::null_mut(), 0); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:41:25 + | +LL | std::ptr::write(std::ptr::null_mut(), A); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:43:35 + | +LL | std::ptr::write_unaligned(std::ptr::null_mut(), A); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:45:34 + | +LL | std::ptr::write_volatile(std::ptr::null_mut(), A); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: pointer must be non-null + --> $DIR/invalid_null_ptr_usage.rs:47:40 + | +LL | std::ptr::write_bytes::(std::ptr::null_mut(), 42, 0); + | ^^^^^^^^^^^^^^^^^^^^ help: change this to: `core::ptr::NonNull::dangling().as_ptr()` + +error: aborting due to 25 previous errors + -- cgit 1.4.1-3-g733a5 From 012f9d47b24dc5e92be2d83bf798ca332847e68a Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Fri, 9 Apr 2021 10:09:06 -0400 Subject: Use `register_renamed` instead of `register_removed` for uplifted lints This still applies the lint, and also adds a structured suggestion to rename it. --- CHANGELOG.md | 29 +++++++---------- clippy_lints/src/deprecated_lints.rs | 63 ------------------------------------ clippy_lints/src/lib.rs | 37 ++++++--------------- tests/ui/deprecated.stderr | 28 ++++++++-------- 4 files changed, 34 insertions(+), 123 deletions(-) (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 715f4702e95..7885bdfb12c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -179,7 +179,7 @@ Current stable, released 2021-03-25 * Replace [`find_map`] with [`manual_find_map`] [#6591](https://github.com/rust-lang/rust-clippy/pull/6591) -* [`unknown_clippy_lints`] Now integrated in the `unknown_lints` rustc lint +* `unknown_clippy_lints` Now integrated in the `unknown_lints` rustc lint [#6653](https://github.com/rust-lang/rust-clippy/pull/6653) ### Enhancements @@ -280,7 +280,7 @@ Released 2021-02-11 * Previously deprecated [`str_to_string`] and [`string_to_string`] have been un-deprecated as `restriction` lints [#6333](https://github.com/rust-lang/rust-clippy/pull/6333) -* Deprecate [`panic_params`] lint. This is now available in rustc as `panic_fmt` +* Deprecate `panic_params` lint. This is now available in rustc as `non_fmt_panic` [#6351](https://github.com/rust-lang/rust-clippy/pull/6351) * Move [`map_err_ignore`] to `restriction` [#6416](https://github.com/rust-lang/rust-clippy/pull/6416) @@ -419,7 +419,7 @@ Released 2020-12-31 [#6037](https://github.com/rust-lang/rust-clippy/pull/6037) * Rename `zero_width_space` to [`invisible_characters`] [#6105](https://github.com/rust-lang/rust-clippy/pull/6105) -* Deprecate [`drop_bounds`] (uplifted) +* Deprecate `drop_bounds` (uplifted) [#6111](https://github.com/rust-lang/rust-clippy/pull/6111) * Move [`string_lit_as_bytes`] to `nursery` [#6117](https://github.com/rust-lang/rust-clippy/pull/6117) @@ -1018,7 +1018,7 @@ Released 2020-03-12 [#5015](https://github.com/rust-lang/rust-clippy/pull/5015) * Move [`range_plus_one`] to pedantic group [#5057](https://github.com/rust-lang/rust-clippy/pull/5057) * Move [`debug_assert_with_mut_call`] to nursery group [#5106](https://github.com/rust-lang/rust-clippy/pull/5106) -* Deprecate [`unused_label`] [#4930](https://github.com/rust-lang/rust-clippy/pull/4930) +* Deprecate `unused_label` [#4930](https://github.com/rust-lang/rust-clippy/pull/4930) ### Enhancements @@ -1046,7 +1046,7 @@ Released 2020-03-12 * [`wildcard_enum_match_arm`] [#4934](https://github.com/rust-lang/rust-clippy/pull/4934) * [`cognitive_complexity`] [#4935](https://github.com/rust-lang/rust-clippy/pull/4935) * [`decimal_literal_representation`] [#4956](https://github.com/rust-lang/rust-clippy/pull/4956) -* [`unknown_clippy_lints`] [#4963](https://github.com/rust-lang/rust-clippy/pull/4963) +* `unknown_clippy_lints` [#4963](https://github.com/rust-lang/rust-clippy/pull/4963) * [`explicit_into_iter_loop`] [#4978](https://github.com/rust-lang/rust-clippy/pull/4978) * [`useless_attribute`] [#5022](https://github.com/rust-lang/rust-clippy/pull/5022) * [`if_let_some_result`] [#5032](https://github.com/rust-lang/rust-clippy/pull/5032) @@ -1080,7 +1080,7 @@ Released 2020-01-30 [Inside Rust Blog](https://blog.rust-lang.org/inside-rust/2019/11/04/Clippy-removes-plugin-interface.html) for details [#4714](https://github.com/rust-lang/rust-clippy/pull/4714) * Move [`use_self`] to nursery group [#4863](https://github.com/rust-lang/rust-clippy/pull/4863) -* Deprecate [`into_iter_on_array`] [#4788](https://github.com/rust-lang/rust-clippy/pull/4788) +* Deprecate `into_iter_on_array` [#4788](https://github.com/rust-lang/rust-clippy/pull/4788) * Expand [`string_lit_as_bytes`] to also trigger when literal has escapes [#4808](https://github.com/rust-lang/rust-clippy/pull/4808) * Fix false positive in `comparison_chain` [#4842](https://github.com/rust-lang/rust-clippy/pull/4842) @@ -1282,7 +1282,7 @@ Released 2019-05-20 [1fac380..37f5c1e](https://github.com/rust-lang/rust-clippy/compare/1fac380...37f5c1e) -* New lint: [`drop_bounds`] to detect `T: Drop` bounds +* New lint: `drop_bounds` to detect `T: Drop` bounds * Split [`redundant_closure`] into [`redundant_closure`] and [`redundant_closure_for_method_calls`] [#4110](https://github.com/rust-lang/rust-clippy/pull/4101) * Rename `cyclomatic_complexity` to [`cognitive_complexity`], start work on making lint more practical for Rust code * Move [`get_unwrap`] to the restriction category @@ -1375,7 +1375,7 @@ Released 2019-01-17 * New lints: [`slow_vector_initialization`], [`mem_discriminant_non_enum`], [`redundant_clone`], [`wildcard_dependencies`], - [`into_iter_on_ref`], [`into_iter_on_array`], [`deprecated_cfg_attr`], + [`into_iter_on_ref`], `into_iter_on_array`, [`deprecated_cfg_attr`], [`mem_discriminant_non_enum`], [`cargo_common_metadata`] * Add support for `u128` and `i128` to integer related lints * Add float support to `mistyped_literal_suffixes` @@ -1649,7 +1649,7 @@ Released 2018-09-13 ## 0.0.166 * Rustup to *rustc 1.22.0-nightly (b7960878b 2017-10-18)* -* New lints: [`explicit_write`], `identity_conversion`, [`implicit_hasher`], [`invalid_ref`], [`option_map_or_none`], +* New lints: [`explicit_write`], `identity_conversion`, [`implicit_hasher`], `invalid_ref`, [`option_map_or_none`], [`range_minus_one`], [`range_plus_one`], [`transmute_int_to_bool`], [`transmute_int_to_char`], [`transmute_int_to_float`] @@ -2037,7 +2037,7 @@ Released 2018-09-13 ## 0.0.64 — 2016-04-26 * Rustup to *rustc 1.10.0-nightly (645dd013a 2016-04-24)* -* New lints: [`temporary_cstring_as_ptr`], [`unsafe_removed_from_name`], and [`mem_forget`] +* New lints: `temporary_cstring_as_ptr`, [`unsafe_removed_from_name`], and [`mem_forget`] ## 0.0.63 — 2016-04-08 * Rustup to *rustc 1.9.0-nightly (7979dd608 2016-04-07)* @@ -2091,7 +2091,7 @@ Released 2018-09-13 ## 0.0.49 — 2016-03-09 * Update to *rustc 1.9.0-nightly (eabfc160f 2016-03-08)* -* New lints: [`overflow_check_conditional`], [`unused_label`], [`new_without_default`] +* New lints: [`overflow_check_conditional`], `unused_label`, [`new_without_default`] ## 0.0.48 — 2016-03-07 * Fixed: ICE in [`needless_range_loop`] with globals @@ -2178,7 +2178,6 @@ Released 2018-09-13 [`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use [`double_neg`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_neg [`double_parens`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_parens -[`drop_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_bounds [`drop_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_copy [`drop_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_ref [`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument @@ -2264,11 +2263,9 @@ Released 2018-09-13 [`int_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#int_plus_one [`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic [`integer_division`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division -[`into_iter_on_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_array [`into_iter_on_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_ref [`invalid_atomic_ordering`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_atomic_ordering [`invalid_null_ptr_usage`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_null_ptr_usage -[`invalid_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_ref [`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex [`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons [`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters @@ -2403,7 +2400,6 @@ Released 2018-09-13 [`overflow_check_conditional`]: https://rust-lang.github.io/rust-clippy/master/index.html#overflow_check_conditional [`panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic [`panic_in_result_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_in_result_fn -[`panic_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_params [`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap [`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl [`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite @@ -2489,7 +2485,6 @@ Released 2018-09-13 [`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting [`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments [`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment -[`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr [`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 [`todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo @@ -2518,7 +2513,6 @@ Released 2018-09-13 [`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 -[`unknown_clippy_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#unknown_clippy_lints [`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast [`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map [`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold @@ -2542,7 +2536,6 @@ Released 2018-09-13 [`unstable_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_slice [`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect [`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount -[`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label [`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self [`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit [`unusual_byte_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_groupings diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index 89088c533ed..66e9f8c8af7 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -93,15 +93,6 @@ declare_deprecated_lint! { "the replacement suggested by this lint had substantially different behavior" } -declare_deprecated_lint! { - /// **What it does:** Nothing. This lint has been deprecated. - /// - /// **Deprecation reason:** This lint has been superseded by the warn-by-default - /// `invalid_value` rustc lint. - pub INVALID_REF, - "superseded by rustc lint `invalid_value`" -} - declare_deprecated_lint! { /// **What it does:** Nothing. This lint has been deprecated. /// @@ -110,24 +101,6 @@ declare_deprecated_lint! { "`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint" } -declare_deprecated_lint! { - /// **What it does:** Nothing. This lint has been deprecated. - /// - /// **Deprecation reason:** This lint has been uplifted to rustc and is now called - /// `array_into_iter`. - pub INTO_ITER_ON_ARRAY, - "this lint has been uplifted to rustc and is now called `array_into_iter`" -} - -declare_deprecated_lint! { - /// **What it does:** Nothing. This lint has been deprecated. - /// - /// **Deprecation reason:** This lint has been uplifted to rustc and is now called - /// `unused_labels`. - pub UNUSED_LABEL, - "this lint has been uplifted to rustc and is now called `unused_labels`" -} - declare_deprecated_lint! { /// **What it does:** Nothing. This lint has been deprecated. /// @@ -144,42 +117,6 @@ declare_deprecated_lint! { "the regex! macro has been removed from the regex crate in 2018" } -declare_deprecated_lint! { - /// **What it does:** Nothing. This lint has been deprecated. - /// - /// **Deprecation reason:** This lint has been uplifted to rustc and is now called - /// `drop_bounds`. - pub DROP_BOUNDS, - "this lint has been uplifted to rustc and is now called `drop_bounds`" -} - -declare_deprecated_lint! { - /// **What it does:** Nothing. This lint has been deprecated. - /// - /// **Deprecation reason:** This lint has been uplifted to rustc and is now called - /// `temporary_cstring_as_ptr`. - pub TEMPORARY_CSTRING_AS_PTR, - "this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr`" -} - -declare_deprecated_lint! { - /// **What it does:** Nothing. This lint has been deprecated. - /// - /// **Deprecation reason:** This lint has been uplifted to rustc and is now called - /// `panic_fmt`. - pub PANIC_PARAMS, - "this lint has been uplifted to rustc and is now called `panic_fmt`" -} - -declare_deprecated_lint! { - /// **What it does:** Nothing. This lint has been deprecated. - /// - /// **Deprecation reason:** This lint has been integrated into the `unknown_lints` - /// rustc lint. - pub UNKNOWN_CLIPPY_LINTS, - "this lint has been integrated into the `unknown_lints` rustc lint" -} - declare_deprecated_lint! { /// **What it does:** Nothing. This lint has been deprecated. /// diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index b066643b568..e97f363a793 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -493,22 +493,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: "clippy::unsafe_vector_initialization", "the replacement suggested by this lint had substantially different behavior", ); - store.register_removed( - "clippy::invalid_ref", - "superseded by rustc lint `invalid_value`", - ); 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::into_iter_on_array", - "this lint has been uplifted to rustc and is now called `array_into_iter`", - ); - store.register_removed( - "clippy::unused_label", - "this lint has been uplifted to rustc and is now called `unused_labels`", - ); store.register_removed( "clippy::replace_consts", "associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants", @@ -517,22 +505,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: "clippy::regex_macro", "the regex! macro has been removed from the regex crate in 2018", ); - store.register_removed( - "clippy::drop_bounds", - "this lint has been uplifted to rustc and is now called `drop_bounds`", - ); - store.register_removed( - "clippy::temporary_cstring_as_ptr", - "this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr`", - ); - store.register_removed( - "clippy::panic_params", - "this lint has been uplifted to rustc and is now called `panic_fmt`", - ); - store.register_removed( - "clippy::unknown_clippy_lints", - "this lint has been integrated into the `unknown_lints` rustc lint", - ); store.register_removed( "clippy::find_map", "this lint has been replaced by `manual_find_map`, a more specific lint", @@ -2155,6 +2127,15 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) { ls.register_renamed("clippy::identity_conversion", "clippy::useless_conversion"); ls.register_renamed("clippy::zero_width_space", "clippy::invisible_characters"); ls.register_renamed("clippy::single_char_push_str", "clippy::single_char_add_str"); + + // uplifted lints + ls.register_renamed("clippy::invalid_ref", "invalid_value"); + ls.register_renamed("clippy::into_iter_on_array", "array_into_iter"); + ls.register_renamed("clippy::unused_label", "unused_labels"); + ls.register_renamed("clippy::drop_bounds", "drop_bounds"); + ls.register_renamed("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr"); + ls.register_renamed("clippy::panic_params", "non_fmt_panic"); + ls.register_renamed("clippy::unknown_clippy_lints", "unknown_lints"); } // only exists to let the dogfood integration test works. diff --git a/tests/ui/deprecated.stderr b/tests/ui/deprecated.stderr index 64efcd18f88..1c92fb6ff04 100644 --- a/tests/ui/deprecated.stderr +++ b/tests/ui/deprecated.stderr @@ -24,23 +24,23 @@ error: lint `clippy::unused_collect` has been removed: `collect` has been marked LL | #[warn(clippy::unused_collect)] | ^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::invalid_ref` has been removed: superseded by rustc lint `invalid_value` +error: lint `clippy::invalid_ref` has been renamed to `invalid_value` --> $DIR/deprecated.rs:5:8 | LL | #[warn(clippy::invalid_ref)] - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` -error: lint `clippy::into_iter_on_array` has been removed: this lint has been uplifted to rustc and is now called `array_into_iter` +error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` --> $DIR/deprecated.rs:6:8 | LL | #[warn(clippy::into_iter_on_array)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` -error: lint `clippy::unused_label` has been removed: this lint has been uplifted to rustc and is now called `unused_labels` +error: lint `clippy::unused_label` has been renamed to `unused_labels` --> $DIR/deprecated.rs:7:8 | LL | #[warn(clippy::unused_label)] - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` error: lint `clippy::regex_macro` has been removed: the regex! macro has been removed from the regex crate in 2018 --> $DIR/deprecated.rs:8:8 @@ -48,29 +48,29 @@ error: lint `clippy::regex_macro` has been removed: the regex! macro has been re LL | #[warn(clippy::regex_macro)] | ^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::drop_bounds` has been removed: this lint has been uplifted to rustc and is now called `drop_bounds` +error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` --> $DIR/deprecated.rs:9:8 | LL | #[warn(clippy::drop_bounds)] - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` -error: lint `clippy::temporary_cstring_as_ptr` has been removed: this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr` +error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` --> $DIR/deprecated.rs:10:8 | LL | #[warn(clippy::temporary_cstring_as_ptr)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` -error: lint `clippy::panic_params` has been removed: this lint has been uplifted to rustc and is now called `panic_fmt` +error: lint `clippy::panic_params` has been renamed to `non_fmt_panic` --> $DIR/deprecated.rs:11:8 | LL | #[warn(clippy::panic_params)] - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panic` -error: lint `clippy::unknown_clippy_lints` has been removed: this lint has been integrated into the `unknown_lints` rustc lint +error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` --> $DIR/deprecated.rs:12:8 | LL | #[warn(clippy::unknown_clippy_lints)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` error: lint `clippy::find_map` has been removed: this lint has been replaced by `manual_find_map`, a more specific lint --> $DIR/deprecated.rs:13:8 -- cgit 1.4.1-3-g733a5 From 297e84f3f4b7ff3c2648e65b0f2c144982cfad63 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sat, 10 Apr 2021 10:26:53 +0200 Subject: Handle imports which are nested directly --- clippy_lints/src/single_component_path_imports.rs | 73 +++++++++++++++------- .../single_component_path_imports_nested_first.rs | 17 +++++ ...ngle_component_path_imports_nested_first.stderr | 25 ++++++++ 3 files changed, 92 insertions(+), 23 deletions(-) create mode 100644 tests/ui/single_component_path_imports_nested_first.rs create mode 100644 tests/ui/single_component_path_imports_nested_first.stderr (limited to 'tests') diff --git a/clippy_lints/src/single_component_path_imports.rs b/clippy_lints/src/single_component_path_imports.rs index adf0d7998f8..6104103580e 100644 --- a/clippy_lints/src/single_component_path_imports.rs +++ b/clippy_lints/src/single_component_path_imports.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::in_macro; use rustc_ast::{ptr::P, Crate, Item, ItemKind, ModKind, UseTreeKind}; use rustc_errors::Applicability; @@ -66,15 +66,27 @@ fn check_mod(cx: &EarlyContext<'_>, items: &[P]) { for single_use in &single_use_usages { if !imports_reused_with_self.contains(&single_use.0) { - span_lint_and_sugg( - cx, - SINGLE_COMPONENT_PATH_IMPORTS, - single_use.1, - "this import is redundant", - "remove it entirely", - String::new(), - Applicability::MachineApplicable, - ); + let can_suggest = single_use.2; + if can_suggest { + span_lint_and_sugg( + cx, + SINGLE_COMPONENT_PATH_IMPORTS, + single_use.1, + "this import is redundant", + "remove it entirely", + String::new(), + Applicability::MachineApplicable, + ); + } else { + span_lint_and_help( + cx, + SINGLE_COMPONENT_PATH_IMPORTS, + single_use.1, + "this import is redundant", + None, + "remove this import", + ); + } } } } @@ -83,7 +95,7 @@ fn track_uses( cx: &EarlyContext<'_>, item: &Item, imports_reused_with_self: &mut Vec, - single_use_usages: &mut Vec<(Symbol, Span)>, + single_use_usages: &mut Vec<(Symbol, Span, bool)>, ) { if in_macro(item.span) || item.vis.kind.is_pub() { return; @@ -100,25 +112,40 @@ fn track_uses( if segments.len() == 1 { if let UseTreeKind::Simple(None, _, _) = use_tree.kind { let ident = &segments[0].ident; - single_use_usages.push((ident.name, item.span)); + single_use_usages.push((ident.name, item.span, true)); } return; } - // keep track of `use self::some_module` usages - if segments[0].ident.name == kw::SelfLower { - // simple case such as `use self::module::SomeStruct` - if segments.len() > 1 { - imports_reused_with_self.push(segments[1].ident.name); - return; - } - - // nested case such as `use self::{module1::Struct1, module2::Struct2}` + if segments.is_empty() { + // keep track of `use {some_module, some_other_module};` usages if let UseTreeKind::Nested(trees) = &use_tree.kind { for tree in trees { let segments = &tree.0.prefix.segments; - if !segments.is_empty() { - imports_reused_with_self.push(segments[0].ident.name); + if segments.len() == 1 { + if let UseTreeKind::Simple(None, _, _) = tree.0.kind { + let ident = &segments[0].ident; + single_use_usages.push((ident.name, tree.0.span, false)); + } + } + } + } + } else { + // keep track of `use self::some_module` usages + if segments[0].ident.name == kw::SelfLower { + // simple case such as `use self::module::SomeStruct` + if segments.len() > 1 { + imports_reused_with_self.push(segments[1].ident.name); + return; + } + + // nested case such as `use self::{module1::Struct1, module2::Struct2}` + if let UseTreeKind::Nested(trees) = &use_tree.kind { + for tree in trees { + let segments = &tree.0.prefix.segments; + if !segments.is_empty() { + imports_reused_with_self.push(segments[0].ident.name); + } } } } diff --git a/tests/ui/single_component_path_imports_nested_first.rs b/tests/ui/single_component_path_imports_nested_first.rs new file mode 100644 index 00000000000..94117061b27 --- /dev/null +++ b/tests/ui/single_component_path_imports_nested_first.rs @@ -0,0 +1,17 @@ +// edition:2018 +#![warn(clippy::single_component_path_imports)] +#![allow(unused_imports)] + +use regex; +use serde as edres; +pub use serde; + +fn main() { + regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap(); +} + +mod root_nested_use_mod { + use {regex, serde}; + #[allow(dead_code)] + fn root_nested_use_mod() {} +} diff --git a/tests/ui/single_component_path_imports_nested_first.stderr b/tests/ui/single_component_path_imports_nested_first.stderr new file mode 100644 index 00000000000..0c3256c1ce4 --- /dev/null +++ b/tests/ui/single_component_path_imports_nested_first.stderr @@ -0,0 +1,25 @@ +error: this import is redundant + --> $DIR/single_component_path_imports_nested_first.rs:14:10 + | +LL | use {regex, serde}; + | ^^^^^ + | + = note: `-D clippy::single-component-path-imports` implied by `-D warnings` + = help: remove this import + +error: this import is redundant + --> $DIR/single_component_path_imports_nested_first.rs:14:17 + | +LL | use {regex, serde}; + | ^^^^^ + | + = help: remove this import + +error: this import is redundant + --> $DIR/single_component_path_imports_nested_first.rs:5:1 + | +LL | use regex; + | ^^^^^^^^^^ help: remove it entirely + +error: aborting due to 3 previous errors + -- cgit 1.4.1-3-g733a5 From 8b9331b49dd86cbc1ec655f6a6f5f9d74ca8d901 Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Sat, 10 Apr 2021 14:43:52 +0200 Subject: Fix rustfmt error / Add comment for tab character --- tests/ui/crashes/ice-5835.rs | 5 +++-- tests/ui/crashes/ice-5835.stderr | 14 ++------------ 2 files changed, 5 insertions(+), 14 deletions(-) (limited to 'tests') diff --git a/tests/ui/crashes/ice-5835.rs b/tests/ui/crashes/ice-5835.rs index 209a5b1eb09..5e99cb432b6 100644 --- a/tests/ui/crashes/ice-5835.rs +++ b/tests/ui/crashes/ice-5835.rs @@ -1,7 +1,8 @@ -#![rustfmt::skip] - +#[rustfmt::skip] pub struct Foo { /// 位 + /// ^ Do not remove this tab character. + /// It was required to trigger the ICE. pub bar: u8, } diff --git a/tests/ui/crashes/ice-5835.stderr b/tests/ui/crashes/ice-5835.stderr index e286bc580ad..c972bcb60a0 100644 --- a/tests/ui/crashes/ice-5835.stderr +++ b/tests/ui/crashes/ice-5835.stderr @@ -1,20 +1,10 @@ -error[E0658]: custom inner attributes are unstable - --> $DIR/ice-5835.rs:1:4 - | -LL | #![rustfmt::skip] - | ^^^^^^^^^^^^^ - | - = note: see issue #54726 for more information - = help: add `#![feature(custom_inner_attributes)]` to the crate attributes to enable - error: using tabs in doc comments is not recommended - --> $DIR/ice-5835.rs:4:10 + --> $DIR/ice-5835.rs:3:10 | LL | /// 位 | ^^^^ help: consider using four spaces per tab | = note: `-D clippy::tabs-in-doc-comments` implied by `-D warnings` -error: aborting due to 2 previous errors +error: aborting due to previous error -For more information about this error, try `rustc --explain E0658`. -- cgit 1.4.1-3-g733a5 From 43e6c656ba0f221caace290b8a8deace2abd89cd Mon Sep 17 00:00:00 2001 From: daxpedda Date: Sat, 10 Apr 2021 17:38:04 +0200 Subject: Remove `debug_assert` from `panic_in_result_fn` --- clippy_lints/src/panic_in_result_fn.rs | 3 -- tests/ui/panic_in_result_fn_debug_assertions.rs | 16 +++--- .../ui/panic_in_result_fn_debug_assertions.stderr | 57 ---------------------- 3 files changed, 9 insertions(+), 67 deletions(-) delete mode 100644 tests/ui/panic_in_result_fn_debug_assertions.stderr (limited to 'tests') diff --git a/clippy_lints/src/panic_in_result_fn.rs b/clippy_lints/src/panic_in_result_fn.rs index d32b937b209..791a2bf0798 100644 --- a/clippy_lints/src/panic_in_result_fn.rs +++ b/clippy_lints/src/panic_in_result_fn.rs @@ -61,9 +61,6 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir "assert", "assert_eq", "assert_ne", - "debug_assert", - "debug_assert_eq", - "debug_assert_ne", ], body, ); diff --git a/tests/ui/panic_in_result_fn_debug_assertions.rs b/tests/ui/panic_in_result_fn_debug_assertions.rs index b60c79f97c8..1f8485b8cd8 100644 --- a/tests/ui/panic_in_result_fn_debug_assertions.rs +++ b/tests/ui/panic_in_result_fn_debug_assertions.rs @@ -1,43 +1,45 @@ #![warn(clippy::panic_in_result_fn)] #![allow(clippy::unnecessary_wraps)] +// debug_assert should never trigger the `panic_in_result_fn` lint + struct A; impl A { - fn result_with_debug_assert_with_message(x: i32) -> Result // should emit lint + fn result_with_debug_assert_with_message(x: i32) -> Result { debug_assert!(x == 5, "wrong argument"); Ok(true) } - fn result_with_debug_assert_eq(x: i32) -> Result // should emit lint + fn result_with_debug_assert_eq(x: i32) -> Result { debug_assert_eq!(x, 5); Ok(true) } - fn result_with_debug_assert_ne(x: i32) -> Result // should emit lint + fn result_with_debug_assert_ne(x: i32) -> Result { debug_assert_ne!(x, 1); Ok(true) } - fn other_with_debug_assert_with_message(x: i32) // should not emit lint + fn other_with_debug_assert_with_message(x: i32) { debug_assert!(x == 5, "wrong argument"); } - fn other_with_debug_assert_eq(x: i32) // should not emit lint + fn other_with_debug_assert_eq(x: i32) { debug_assert_eq!(x, 5); } - fn other_with_debug_assert_ne(x: i32) // should not emit lint + fn other_with_debug_assert_ne(x: i32) { debug_assert_ne!(x, 1); } - fn result_without_banned_functions() -> Result // should not emit lint + fn result_without_banned_functions() -> Result { let debug_assert = "debug_assert!"; println!("No {}", debug_assert); diff --git a/tests/ui/panic_in_result_fn_debug_assertions.stderr b/tests/ui/panic_in_result_fn_debug_assertions.stderr deleted file mode 100644 index ec18e89698c..00000000000 --- a/tests/ui/panic_in_result_fn_debug_assertions.stderr +++ /dev/null @@ -1,57 +0,0 @@ -error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` - --> $DIR/panic_in_result_fn_debug_assertions.rs:7:5 - | -LL | / fn result_with_debug_assert_with_message(x: i32) -> Result // should emit lint -LL | | { -LL | | debug_assert!(x == 5, "wrong argument"); -LL | | Ok(true) -LL | | } - | |_____^ - | - = note: `-D clippy::panic-in-result-fn` implied by `-D warnings` - = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing -note: return Err() instead of panicking - --> $DIR/panic_in_result_fn_debug_assertions.rs:9:9 - | -LL | debug_assert!(x == 5, "wrong argument"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` - --> $DIR/panic_in_result_fn_debug_assertions.rs:13:5 - | -LL | / fn result_with_debug_assert_eq(x: i32) -> Result // should emit lint -LL | | { -LL | | debug_assert_eq!(x, 5); -LL | | Ok(true) -LL | | } - | |_____^ - | - = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing -note: return Err() instead of panicking - --> $DIR/panic_in_result_fn_debug_assertions.rs:15:9 - | -LL | debug_assert_eq!(x, 5); - | ^^^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result` - --> $DIR/panic_in_result_fn_debug_assertions.rs:19:5 - | -LL | / fn result_with_debug_assert_ne(x: i32) -> Result // should emit lint -LL | | { -LL | | debug_assert_ne!(x, 1); -LL | | Ok(true) -LL | | } - | |_____^ - | - = help: `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing -note: return Err() instead of panicking - --> $DIR/panic_in_result_fn_debug_assertions.rs:21:9 - | -LL | debug_assert_ne!(x, 1); - | ^^^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 3 previous errors - -- cgit 1.4.1-3-g733a5 From 8f4417faf24e18359a344945ff1072b54f354f77 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Sat, 10 Apr 2021 17:45:55 +0200 Subject: Fix rustfmt --- tests/ui/panic_in_result_fn_debug_assertions.rs | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) (limited to 'tests') diff --git a/tests/ui/panic_in_result_fn_debug_assertions.rs b/tests/ui/panic_in_result_fn_debug_assertions.rs index 1f8485b8cd8..c4fcd7e7094 100644 --- a/tests/ui/panic_in_result_fn_debug_assertions.rs +++ b/tests/ui/panic_in_result_fn_debug_assertions.rs @@ -6,41 +6,34 @@ struct A; impl A { - fn result_with_debug_assert_with_message(x: i32) -> Result - { + fn result_with_debug_assert_with_message(x: i32) -> Result { debug_assert!(x == 5, "wrong argument"); Ok(true) } - fn result_with_debug_assert_eq(x: i32) -> Result - { + fn result_with_debug_assert_eq(x: i32) -> Result { debug_assert_eq!(x, 5); Ok(true) } - fn result_with_debug_assert_ne(x: i32) -> Result - { + fn result_with_debug_assert_ne(x: i32) -> Result { debug_assert_ne!(x, 1); Ok(true) } - fn other_with_debug_assert_with_message(x: i32) - { + fn other_with_debug_assert_with_message(x: i32) { debug_assert!(x == 5, "wrong argument"); } - fn other_with_debug_assert_eq(x: i32) - { + fn other_with_debug_assert_eq(x: i32) { debug_assert_eq!(x, 5); } - fn other_with_debug_assert_ne(x: i32) - { + fn other_with_debug_assert_ne(x: i32) { debug_assert_ne!(x, 1); } - fn result_without_banned_functions() -> Result - { + fn result_without_banned_functions() -> Result { let debug_assert = "debug_assert!"; println!("No {}", debug_assert); Ok(true) -- cgit 1.4.1-3-g733a5 From cb14e7ebf40bb6edf9307813b341a9119800ef49 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Sat, 10 Apr 2021 23:37:18 +0200 Subject: Fix false-positive `debug_assert` in `panic` --- clippy_lints/src/panic_unimplemented.rs | 2 +- tests/ui/panicking_macros.rs | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/panic_unimplemented.rs b/clippy_lints/src/panic_unimplemented.rs index 1e946858947..1a680e7607e 100644 --- a/clippy_lints/src/panic_unimplemented.rs +++ b/clippy_lints/src/panic_unimplemented.rs @@ -74,7 +74,7 @@ declare_lint_pass!(PanicUnimplemented => [UNIMPLEMENTED, UNREACHABLE, TODO, PANI impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if match_panic_call(cx, expr).is_some() { + if match_panic_call(cx, expr).is_some() && is_expn_of(expr.span, "debug_assert").is_none() { let span = get_outer_span(expr); if is_expn_of(expr.span, "unimplemented").is_some() { span_lint( diff --git a/tests/ui/panicking_macros.rs b/tests/ui/panicking_macros.rs index 77fcb8dfd02..93b236f7473 100644 --- a/tests/ui/panicking_macros.rs +++ b/tests/ui/panicking_macros.rs @@ -1,5 +1,5 @@ #![warn(clippy::unimplemented, clippy::unreachable, clippy::todo, clippy::panic)] -#![allow(clippy::assertions_on_constants)] +#![allow(clippy::assertions_on_constants, clippy::eq_op)] extern crate core; @@ -43,6 +43,18 @@ fn core_versions() { unreachable!(); } +fn debug_assert() { + debug_assert!(true); + debug_assert_eq!(true, true); + debug_assert_ne!(true, false); +} + +fn debug_assert_msg() { + debug_assert!(true, "test"); + debug_assert_eq!(true, true, "test"); + debug_assert_ne!(true, false, "test"); +} + fn main() { panic(); todo(); -- cgit 1.4.1-3-g733a5 From a45faf66f3f35f644be2e121a1cd478bb2b908e0 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 9 Apr 2021 16:25:39 -0500 Subject: Deprecate filter_map --- clippy_lints/src/deprecated_lints.rs | 9 ++++++ clippy_lints/src/lib.rs | 6 ++-- clippy_lints/src/methods/filter_flat_map.rs | 18 ----------- clippy_lints/src/methods/filter_map_flat_map.rs | 18 ----------- clippy_lints/src/methods/filter_map_map.rs | 17 ----------- clippy_lints/src/methods/mod.rs | 40 +------------------------ clippy_utils/src/attrs.rs | 1 - lintcheck/src/main.rs | 2 +- tests/ui/deprecated.rs | 1 + tests/ui/deprecated.stderr | 8 ++++- tests/ui/filter_methods.rs | 25 ---------------- tests/ui/filter_methods.stderr | 39 ------------------------ 12 files changed, 23 insertions(+), 161 deletions(-) delete mode 100644 clippy_lints/src/methods/filter_flat_map.rs delete mode 100644 clippy_lints/src/methods/filter_map_flat_map.rs delete mode 100644 clippy_lints/src/methods/filter_map_map.rs delete mode 100644 tests/ui/filter_methods.rs delete mode 100644 tests/ui/filter_methods.stderr (limited to 'tests') diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index 89088c533ed..6f61229e133 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -188,3 +188,12 @@ declare_deprecated_lint! { pub FIND_MAP, "this lint has been replaced by `manual_find_map`, a more specific lint" } + +declare_deprecated_lint! { + /// **What it does:** Nothing. This lint has been deprecated. + /// + /// **Deprecation reason:** This lint has been replaced by `manual_filter_map`, a + /// more specific lint. + pub FILTER_MAP, + "this lint has been replaced by `manual_filter_map`, a more specific lint" +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index b066643b568..03454271da5 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -537,6 +537,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: "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", + ); // end deprecated lints, do not remove this comment, it’s used in `update_lints` // begin register lints, do not remove this comment, it’s used in `update_lints` @@ -788,7 +792,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: methods::EXPECT_FUN_CALL, methods::EXPECT_USED, methods::FILETYPE_IS_FILE, - methods::FILTER_MAP, methods::FILTER_MAP_IDENTITY, methods::FILTER_MAP_NEXT, methods::FILTER_NEXT, @@ -1404,7 +1407,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS), LintId::of(matches::MATCH_WILD_ERR_ARM), LintId::of(matches::SINGLE_MATCH_ELSE), - LintId::of(methods::FILTER_MAP), LintId::of(methods::FILTER_MAP_NEXT), LintId::of(methods::IMPLICIT_CLONE), LintId::of(methods::INEFFICIENT_TO_STRING), diff --git a/clippy_lints/src/methods/filter_flat_map.rs b/clippy_lints/src/methods/filter_flat_map.rs deleted file mode 100644 index 1588eec8882..00000000000 --- a/clippy_lints/src/methods/filter_flat_map.rs +++ /dev/null @@ -1,18 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::is_trait_method; -use rustc_hir as hir; -use rustc_lint::LateContext; -use rustc_span::sym; - -use super::FILTER_MAP; - -/// lint use of `filter().flat_map()` for `Iterators` -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - // lint if caller of `.filter().flat_map()` is an Iterator - if is_trait_method(cx, expr, sym::Iterator) { - let msg = "called `filter(..).flat_map(..)` on an `Iterator`"; - let hint = "this is more succinctly expressed by calling `.flat_map(..)` \ - and filtering by returning `iter::empty()`"; - span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint); - } -} diff --git a/clippy_lints/src/methods/filter_map_flat_map.rs b/clippy_lints/src/methods/filter_map_flat_map.rs deleted file mode 100644 index 741b1e7e361..00000000000 --- a/clippy_lints/src/methods/filter_map_flat_map.rs +++ /dev/null @@ -1,18 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::is_trait_method; -use rustc_hir as hir; -use rustc_lint::LateContext; -use rustc_span::sym; - -use super::FILTER_MAP; - -/// lint use of `filter_map().flat_map()` for `Iterators` -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - // lint if caller of `.filter_map().flat_map()` is an Iterator - if is_trait_method(cx, expr, sym::Iterator) { - let msg = "called `filter_map(..).flat_map(..)` on an `Iterator`"; - let hint = "this is more succinctly expressed by calling `.flat_map(..)` \ - and filtering by returning `iter::empty()`"; - span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint); - } -} diff --git a/clippy_lints/src/methods/filter_map_map.rs b/clippy_lints/src/methods/filter_map_map.rs deleted file mode 100644 index 713bbf25837..00000000000 --- a/clippy_lints/src/methods/filter_map_map.rs +++ /dev/null @@ -1,17 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::is_trait_method; -use rustc_hir as hir; -use rustc_lint::LateContext; -use rustc_span::sym; - -use super::FILTER_MAP; - -/// lint use of `filter_map().map()` for `Iterators` -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - // lint if caller of `.filter_map().map()` is an Iterator - if is_trait_method(cx, expr, sym::Iterator) { - let msg = "called `filter_map(..).map(..)` on an `Iterator`"; - let hint = "this is more succinctly expressed by only calling `.filter_map(..)` instead"; - span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint); - } -} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index b1ade5addd6..fc18849b07c 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -11,11 +11,8 @@ mod clone_on_ref_ptr; mod expect_fun_call; mod expect_used; mod filetype_is_file; -mod filter_flat_map; mod filter_map; -mod filter_map_flat_map; mod filter_map_identity; -mod filter_map_map; mod filter_map_next; mod filter_next; mod flat_map_identity; @@ -472,35 +469,6 @@ declare_clippy_lint! { "using combinations of `flatten` and `map` which can usually be written as a single method call" } -declare_clippy_lint! { - /// **What it does:** Checks for usage of `_.filter(_).map(_)`, - /// `_.filter(_).flat_map(_)`, `_.filter_map(_).flat_map(_)` and similar. - /// - /// **Why is this bad?** Readability, this can be written more concisely as - /// `_.filter_map(_)`. - /// - /// **Known problems:** Often requires a condition + Option/Iterator creation - /// inside the closure. - /// - /// **Example:** - /// ```rust - /// let vec = vec![1]; - /// - /// // Bad - /// vec.iter().filter(|x| **x == 0).map(|x| *x * 2); - /// - /// // Good - /// vec.iter().filter_map(|x| if *x == 0 { - /// Some(*x * 2) - /// } else { - /// None - /// }); - /// ``` - pub FILTER_MAP, - pedantic, - "using combinations of `filter`, `map`, `filter_map` and `flat_map` which can usually be written as a single method call" -} - declare_clippy_lint! { /// **What it does:** Checks for usage of `_.filter(_).map(_)` that can be written more simply /// as `filter_map(_)`. @@ -1677,7 +1645,6 @@ impl_lint_pass!(Methods => [ SEARCH_IS_SOME, FILTER_NEXT, SKIP_WHILE_NEXT, - FILTER_MAP, FILTER_MAP_IDENTITY, MANUAL_FILTER_MAP, MANUAL_FIND_MAP, @@ -1965,11 +1932,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio unnecessary_filter_map::check(cx, expr, arg); filter_map_identity::check(cx, expr, arg, span); }, - ("flat_map", [flm_arg]) => match method_call!(recv) { - Some(("filter", [_, _], _)) => filter_flat_map::check(cx, expr), - Some(("filter_map", [_, _], _)) => filter_map_flat_map::check(cx, expr), - _ => flat_map_identity::check(cx, expr, flm_arg, span), - }, + ("flat_map", [flm_arg]) => flat_map_identity::check(cx, expr, flm_arg, span), ("flatten", []) => { if let Some(("map", [recv, map_arg], _)) = method_call!(recv) { map_flatten::check(cx, expr, recv, map_arg); @@ -1993,7 +1956,6 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio ("filter", [f_arg]) => { filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false) }, - ("filter_map", [_]) => filter_map_map::check(cx, expr), ("find", [f_arg]) => filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, true), _ => {}, } diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index 7ec8452bf4c..5e44c7b3ee0 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -151,7 +151,6 @@ pub fn is_proc_macro(sess: &Session, attrs: &[ast::Attribute]) -> bool { /// Return true if the attributes contain `#[doc(hidden)]` pub fn is_doc_hidden(attrs: &[ast::Attribute]) -> bool { - #[allow(clippy::filter_map)] attrs .iter() .filter(|attr| attr.has_name(sym::doc)) diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index 2041aed2b97..bfb0c3b3f74 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -5,7 +5,7 @@ // When a new lint is introduced, we can search the results for new warnings and check for false // positives. -#![allow(clippy::filter_map, clippy::collapsible_else_if)] +#![allow(clippy::collapsible_else_if)] use std::ffi::OsStr; use std::process::Command; diff --git a/tests/ui/deprecated.rs b/tests/ui/deprecated.rs index fc444c0bea7..dbf0b03af76 100644 --- a/tests/ui/deprecated.rs +++ b/tests/ui/deprecated.rs @@ -11,5 +11,6 @@ #[warn(clippy::panic_params)] #[warn(clippy::unknown_clippy_lints)] #[warn(clippy::find_map)] +#[warn(clippy::filter_map)] fn main() {} diff --git a/tests/ui/deprecated.stderr b/tests/ui/deprecated.stderr index 64efcd18f88..aa3ed28d40b 100644 --- a/tests/ui/deprecated.stderr +++ b/tests/ui/deprecated.stderr @@ -78,11 +78,17 @@ error: lint `clippy::find_map` has been removed: this lint has been replaced by LL | #[warn(clippy::find_map)] | ^^^^^^^^^^^^^^^^ +error: lint `clippy::filter_map` has been removed: this lint has been replaced by `manual_filter_map`, a more specific lint + --> $DIR/deprecated.rs:14:8 + | +LL | #[warn(clippy::filter_map)] + | ^^^^^^^^^^^^^^^^^^ + error: lint `clippy::unstable_as_slice` has been removed: `Vec::as_slice` has been stabilized in 1.7 --> $DIR/deprecated.rs:1:8 | LL | #[warn(clippy::unstable_as_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 14 previous errors +error: aborting due to 15 previous errors diff --git a/tests/ui/filter_methods.rs b/tests/ui/filter_methods.rs deleted file mode 100644 index 51450241619..00000000000 --- a/tests/ui/filter_methods.rs +++ /dev/null @@ -1,25 +0,0 @@ -#![warn(clippy::all, clippy::pedantic)] -#![allow(clippy::clippy::let_underscore_drop)] -#![allow(clippy::missing_docs_in_private_items)] - -fn main() { - let _: Vec<_> = vec![5; 6].into_iter().filter(|&x| x == 0).map(|x| x * 2).collect(); - - let _: Vec<_> = vec![5_i8; 6] - .into_iter() - .filter(|&x| x == 0) - .flat_map(|x| x.checked_mul(2)) - .collect(); - - let _: Vec<_> = vec![5_i8; 6] - .into_iter() - .filter_map(|x| x.checked_mul(2)) - .flat_map(|x| x.checked_mul(2)) - .collect(); - - let _: Vec<_> = vec![5_i8; 6] - .into_iter() - .filter_map(|x| x.checked_mul(2)) - .map(|x| x.checked_mul(2)) - .collect(); -} diff --git a/tests/ui/filter_methods.stderr b/tests/ui/filter_methods.stderr deleted file mode 100644 index c7b4f28be3a..00000000000 --- a/tests/ui/filter_methods.stderr +++ /dev/null @@ -1,39 +0,0 @@ -error: called `filter(..).flat_map(..)` on an `Iterator` - --> $DIR/filter_methods.rs:8:21 - | -LL | let _: Vec<_> = vec![5_i8; 6] - | _____________________^ -LL | | .into_iter() -LL | | .filter(|&x| x == 0) -LL | | .flat_map(|x| x.checked_mul(2)) - | |_______________________________________^ - | - = note: `-D clippy::filter-map` implied by `-D warnings` - = help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()` - -error: called `filter_map(..).flat_map(..)` on an `Iterator` - --> $DIR/filter_methods.rs:14:21 - | -LL | let _: Vec<_> = vec![5_i8; 6] - | _____________________^ -LL | | .into_iter() -LL | | .filter_map(|x| x.checked_mul(2)) -LL | | .flat_map(|x| x.checked_mul(2)) - | |_______________________________________^ - | - = help: this is more succinctly expressed by calling `.flat_map(..)` and filtering by returning `iter::empty()` - -error: called `filter_map(..).map(..)` on an `Iterator` - --> $DIR/filter_methods.rs:20:21 - | -LL | let _: Vec<_> = vec![5_i8; 6] - | _____________________^ -LL | | .into_iter() -LL | | .filter_map(|x| x.checked_mul(2)) -LL | | .map(|x| x.checked_mul(2)) - | |__________________________________^ - | - = help: this is more succinctly expressed by only calling `.filter_map(..)` instead - -error: aborting due to 3 previous errors - -- cgit 1.4.1-3-g733a5 From 3ce6f0d022014ba13ed328a372c1ac1f37d9045e Mon Sep 17 00:00:00 2001 From: ThibsG Date: Sun, 11 Apr 2021 13:29:08 +0200 Subject: Fix FP in `wrong_self_convention` lint --- clippy_lints/src/methods/wrong_self_convention.rs | 8 ++++++++ tests/ui/wrong_self_convention2.rs | 12 ++++++++++++ 2 files changed, 20 insertions(+) (limited to 'tests') diff --git a/clippy_lints/src/methods/wrong_self_convention.rs b/clippy_lints/src/methods/wrong_self_convention.rs index 1e0de249a91..6e2bcb113c2 100644 --- a/clippy_lints/src/methods/wrong_self_convention.rs +++ b/clippy_lints/src/methods/wrong_self_convention.rs @@ -102,6 +102,14 @@ pub(super) fn check<'tcx>( .iter() .all(|conv| conv.check(cx, self_ty, item_name, implements_trait, is_trait_item)) }) { + // don't lint if it implements a trait but not willing to check `Copy` types conventions (see #7032) + if implements_trait + && !conventions + .iter() + .any(|conv| matches!(conv, Convention::IsSelfTypeCopy(_))) + { + return; + } if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) { let suggestion = { if conventions.len() > 1 { diff --git a/tests/ui/wrong_self_convention2.rs b/tests/ui/wrong_self_convention2.rs index 8b42aa59e13..ae3a740d405 100644 --- a/tests/ui/wrong_self_convention2.rs +++ b/tests/ui/wrong_self_convention2.rs @@ -30,3 +30,15 @@ mod issue6983 { } } } + +mod issue7032 { + trait Foo { + fn from_usize(x: usize) -> Self; + } + // don't trigger + impl Foo for usize { + fn from_usize(x: usize) -> Self { + x + } + } +} -- cgit 1.4.1-3-g733a5 From 0218a3b12f3aca97fc018bdb1e2bf9710c75fa2b Mon Sep 17 00:00:00 2001 From: Takayuki Date: Mon, 12 Apr 2021 21:37:19 +0900 Subject: add tests for a false negative on `needless_return` --- tests/ui/needless_return.fixed | 103 ++++++++++++++++++++++++++++ tests/ui/needless_return.rs | 103 ++++++++++++++++++++++++++++ tests/ui/needless_return.stderr | 146 ++++++++++++++++++++++++++++++++++------ 3 files changed, 333 insertions(+), 19 deletions(-) (limited to 'tests') diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index 82d95cc041f..70ee6e9b31a 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -1,4 +1,5 @@ // run-rustfix +// edition:2018 #![allow(unused)] #![allow( @@ -125,6 +126,108 @@ mod issue6501 { } } +async fn async_test_end_of_fn() -> bool { + if true { + // no error! + return true; + } + true +} + +async fn async_test_no_semicolon() -> bool { + true +} + +async fn async_test_if_block() -> bool { + if true { + true + } else { + false + } +} + +async fn async_test_match(x: bool) -> bool { + match x { + true => false, + false => { + true + }, + } +} + +async fn async_test_closure() { + let _ = || { + true + }; + let _ = || true; +} + +async fn async_test_macro_call() -> i32 { + return the_answer!(); +} + +async fn async_test_void_fun() { + +} + +async fn async_test_void_if_fun(b: bool) { + if b { + + } else { + + } +} + +async fn async_test_void_match(x: u32) { + match x { + 0 => (), + _ => {}, + } +} + +async fn async_read_line() -> String { + use std::io::BufRead; + let stdin = ::std::io::stdin(); + return stdin.lock().lines().next().unwrap().unwrap(); +} + +async fn async_borrows_but_not_last(value: bool) -> String { + if value { + use std::io::BufRead; + let stdin = ::std::io::stdin(); + let _a = stdin.lock().lines().next().unwrap().unwrap(); + String::from("test") + } else { + String::new() + } +} + +async fn async_test_return_in_macro() { + // This will return and the macro below won't be executed. Removing the `return` from the macro + // will change semantics. + needed_return!(10); + needed_return!(0); +} + +mod async_issue6501 { + async fn foo(bar: Result<(), ()>) { + bar.unwrap_or_else(|_| {}) + } + + async fn async_test_closure() { + let _ = || { + + }; + let _ = || {}; + } + + struct Foo; + #[allow(clippy::unnecessary_lazy_evaluations)] + async fn bar(res: Result) -> Foo { + res.unwrap_or_else(|_| Foo) + } +} + fn main() { let _ = test_end_of_fn(); let _ = test_no_semicolon(); diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index 8a471f802e1..d0da17cf862 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -1,4 +1,5 @@ // run-rustfix +// edition:2018 #![allow(unused)] #![allow( @@ -125,6 +126,108 @@ mod issue6501 { } } +async fn async_test_end_of_fn() -> bool { + if true { + // no error! + return true; + } + return true; +} + +async fn async_test_no_semicolon() -> bool { + return true; +} + +async fn async_test_if_block() -> bool { + if true { + return true; + } else { + return false; + } +} + +async fn async_test_match(x: bool) -> bool { + match x { + true => return false, + false => { + return true; + }, + } +} + +async fn async_test_closure() { + let _ = || { + return true; + }; + let _ = || return true; +} + +async fn async_test_macro_call() -> i32 { + return the_answer!(); +} + +async fn async_test_void_fun() { + return; +} + +async fn async_test_void_if_fun(b: bool) { + if b { + return; + } else { + return; + } +} + +async fn async_test_void_match(x: u32) { + match x { + 0 => (), + _ => return, + } +} + +async fn async_read_line() -> String { + use std::io::BufRead; + let stdin = ::std::io::stdin(); + return stdin.lock().lines().next().unwrap().unwrap(); +} + +async fn async_borrows_but_not_last(value: bool) -> String { + if value { + use std::io::BufRead; + let stdin = ::std::io::stdin(); + let _a = stdin.lock().lines().next().unwrap().unwrap(); + return String::from("test"); + } else { + return String::new(); + } +} + +async fn async_test_return_in_macro() { + // This will return and the macro below won't be executed. Removing the `return` from the macro + // will change semantics. + needed_return!(10); + needed_return!(0); +} + +mod async_issue6501 { + async fn foo(bar: Result<(), ()>) { + bar.unwrap_or_else(|_| return) + } + + async fn async_test_closure() { + let _ = || { + return; + }; + let _ = || return; + } + + struct Foo; + #[allow(clippy::unnecessary_lazy_evaluations)] + async fn bar(res: Result) -> Foo { + res.unwrap_or_else(|_| return Foo) + } +} + fn main() { let _ = test_end_of_fn(); let _ = test_no_semicolon(); diff --git a/tests/ui/needless_return.stderr b/tests/ui/needless_return.stderr index 075db22456f..44b29361802 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:23:5 + --> $DIR/needless_return.rs:24:5 | LL | return true; | ^^^^^^^^^^^^ help: remove `return`: `true` @@ -7,106 +7,214 @@ LL | return true; = note: `-D clippy::needless-return` implied by `-D warnings` error: unneeded `return` statement - --> $DIR/needless_return.rs:27:5 + --> $DIR/needless_return.rs:28:5 | LL | return true; | ^^^^^^^^^^^^ help: remove `return`: `true` error: unneeded `return` statement - --> $DIR/needless_return.rs:32:9 + --> $DIR/needless_return.rs:33:9 | LL | return true; | ^^^^^^^^^^^^ help: remove `return`: `true` error: unneeded `return` statement - --> $DIR/needless_return.rs:34:9 + --> $DIR/needless_return.rs:35:9 | LL | return false; | ^^^^^^^^^^^^^ help: remove `return`: `false` error: unneeded `return` statement - --> $DIR/needless_return.rs:40:17 + --> $DIR/needless_return.rs:41:17 | LL | true => return false, | ^^^^^^^^^^^^ help: remove `return`: `false` error: unneeded `return` statement - --> $DIR/needless_return.rs:42:13 + --> $DIR/needless_return.rs:43:13 | LL | return true; | ^^^^^^^^^^^^ help: remove `return`: `true` error: unneeded `return` statement - --> $DIR/needless_return.rs:49:9 + --> $DIR/needless_return.rs:50:9 | LL | return true; | ^^^^^^^^^^^^ help: remove `return`: `true` error: unneeded `return` statement - --> $DIR/needless_return.rs:51:16 + --> $DIR/needless_return.rs:52:16 | LL | let _ = || return true; | ^^^^^^^^^^^ help: remove `return`: `true` error: unneeded `return` statement - --> $DIR/needless_return.rs:59:5 + --> $DIR/needless_return.rs:60:5 | LL | return; | ^^^^^^^ help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:64:9 + --> $DIR/needless_return.rs:65:9 | LL | return; | ^^^^^^^ help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:66:9 + --> $DIR/needless_return.rs:67:9 | LL | return; | ^^^^^^^ help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:73:14 + --> $DIR/needless_return.rs:74:14 | LL | _ => return, | ^^^^^^ help: replace `return` with an empty block: `{}` error: unneeded `return` statement - --> $DIR/needless_return.rs:88:9 + --> $DIR/needless_return.rs:89:9 | LL | return String::from("test"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")` error: unneeded `return` statement - --> $DIR/needless_return.rs:90:9 + --> $DIR/needless_return.rs:91:9 | LL | return String::new(); | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()` error: unneeded `return` statement - --> $DIR/needless_return.rs:111:32 + --> $DIR/needless_return.rs:112:32 | LL | bar.unwrap_or_else(|_| return) | ^^^^^^ help: replace `return` with an empty block: `{}` error: unneeded `return` statement - --> $DIR/needless_return.rs:116:13 + --> $DIR/needless_return.rs:117:13 | LL | return; | ^^^^^^^ help: remove `return` error: unneeded `return` statement - --> $DIR/needless_return.rs:118:20 + --> $DIR/needless_return.rs:119:20 | LL | let _ = || return; | ^^^^^^ help: replace `return` with an empty block: `{}` error: unneeded `return` statement - --> $DIR/needless_return.rs:124:32 + --> $DIR/needless_return.rs:125:32 | LL | res.unwrap_or_else(|_| return Foo) | ^^^^^^^^^^ help: remove `return`: `Foo` -error: aborting due to 18 previous errors +error: unneeded `return` statement + --> $DIR/needless_return.rs:134:5 + | +LL | return true; + | ^^^^^^^^^^^^ help: remove `return`: `true` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:138:5 + | +LL | return true; + | ^^^^^^^^^^^^ help: remove `return`: `true` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:143:9 + | +LL | return true; + | ^^^^^^^^^^^^ help: remove `return`: `true` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:145:9 + | +LL | return false; + | ^^^^^^^^^^^^^ help: remove `return`: `false` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:151:17 + | +LL | true => return false, + | ^^^^^^^^^^^^ help: remove `return`: `false` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:153:13 + | +LL | return true; + | ^^^^^^^^^^^^ help: remove `return`: `true` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:160:9 + | +LL | return true; + | ^^^^^^^^^^^^ help: remove `return`: `true` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:162:16 + | +LL | let _ = || return true; + | ^^^^^^^^^^^ help: remove `return`: `true` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:170:5 + | +LL | return; + | ^^^^^^^ help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:175:9 + | +LL | return; + | ^^^^^^^ help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:177:9 + | +LL | return; + | ^^^^^^^ help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:184:14 + | +LL | _ => return, + | ^^^^^^ help: replace `return` with an empty block: `{}` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:199:9 + | +LL | return String::from("test"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:201:9 + | +LL | return String::new(); + | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:214:32 + | +LL | bar.unwrap_or_else(|_| return) + | ^^^^^^ help: replace `return` with an empty block: `{}` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:219:13 + | +LL | return; + | ^^^^^^^ help: remove `return` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:221:20 + | +LL | let _ = || return; + | ^^^^^^ help: replace `return` with an empty block: `{}` + +error: unneeded `return` statement + --> $DIR/needless_return.rs:227:32 + | +LL | res.unwrap_or_else(|_| return Foo) + | ^^^^^^^^^^ help: remove `return`: `Foo` + +error: aborting due to 36 previous errors -- cgit 1.4.1-3-g733a5 From e6c67ad2bfd7bb9b4777bb95aea7c4ce70e2dd8a Mon Sep 17 00:00:00 2001 From: Takayuki Date: Mon, 12 Apr 2021 21:58:34 +0900 Subject: fix limit_stderr_length error --- tests/ui/needless_return.fixed | 29 +---------------------------- tests/ui/needless_return.rs | 29 +---------------------------- tests/ui/needless_return.stderr | 26 +------------------------- 3 files changed, 3 insertions(+), 81 deletions(-) (limited to 'tests') diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index 70ee6e9b31a..5c4fd466c04 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -203,35 +203,8 @@ async fn async_borrows_but_not_last(value: bool) -> String { } async fn async_test_return_in_macro() { - // This will return and the macro below won't be executed. Removing the `return` from the macro - // will change semantics. needed_return!(10); needed_return!(0); } -mod async_issue6501 { - async fn foo(bar: Result<(), ()>) { - bar.unwrap_or_else(|_| {}) - } - - async fn async_test_closure() { - let _ = || { - - }; - let _ = || {}; - } - - struct Foo; - #[allow(clippy::unnecessary_lazy_evaluations)] - async fn bar(res: Result) -> Foo { - res.unwrap_or_else(|_| Foo) - } -} - -fn main() { - let _ = test_end_of_fn(); - let _ = test_no_semicolon(); - let _ = test_if_block(); - let _ = test_match(true); - test_closure(); -} +fn main() {} diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index d0da17cf862..34811db7413 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -203,35 +203,8 @@ async fn async_borrows_but_not_last(value: bool) -> String { } async fn async_test_return_in_macro() { - // This will return and the macro below won't be executed. Removing the `return` from the macro - // will change semantics. needed_return!(10); needed_return!(0); } -mod async_issue6501 { - async fn foo(bar: Result<(), ()>) { - bar.unwrap_or_else(|_| return) - } - - async fn async_test_closure() { - let _ = || { - return; - }; - let _ = || return; - } - - struct Foo; - #[allow(clippy::unnecessary_lazy_evaluations)] - async fn bar(res: Result) -> Foo { - res.unwrap_or_else(|_| return Foo) - } -} - -fn main() { - let _ = test_end_of_fn(); - let _ = test_no_semicolon(); - let _ = test_if_block(); - let _ = test_match(true); - test_closure(); -} +fn main() {} diff --git a/tests/ui/needless_return.stderr b/tests/ui/needless_return.stderr index 44b29361802..74dda971fda 100644 --- a/tests/ui/needless_return.stderr +++ b/tests/ui/needless_return.stderr @@ -192,29 +192,5 @@ error: unneeded `return` statement LL | return String::new(); | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()` -error: unneeded `return` statement - --> $DIR/needless_return.rs:214:32 - | -LL | bar.unwrap_or_else(|_| return) - | ^^^^^^ help: replace `return` with an empty block: `{}` - -error: unneeded `return` statement - --> $DIR/needless_return.rs:219:13 - | -LL | return; - | ^^^^^^^ help: remove `return` - -error: unneeded `return` statement - --> $DIR/needless_return.rs:221:20 - | -LL | let _ = || return; - | ^^^^^^ help: replace `return` with an empty block: `{}` - -error: unneeded `return` statement - --> $DIR/needless_return.rs:227:32 - | -LL | res.unwrap_or_else(|_| return Foo) - | ^^^^^^^^^^ help: remove `return`: `Foo` - -error: aborting due to 36 previous errors +error: aborting due to 32 previous errors -- cgit 1.4.1-3-g733a5 From 26a1989041393116c001f17d0323d6b4fc989dd9 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Tue, 13 Apr 2021 17:13:49 +1200 Subject: Fix a FP in `missing_const_for_fn` where a function that calls a standard library function whose constness is unstable is considered as being able to be a const function --- clippy_lints/src/missing_const_for_fn.rs | 2 +- clippy_utils/src/lib.rs | 1 + clippy_utils/src/qualify_min_const_fn.rs | 40 +++++++++++++++++++--- tests/ui/missing_const_for_fn/auxiliary/helper.rs | 8 +++++ tests/ui/missing_const_for_fn/cant_be_const.rs | 19 ++++++++++ tests/ui/missing_const_for_fn/could_be_const.rs | 10 ++++++ .../ui/missing_const_for_fn/could_be_const.stderr | 26 +++++++++----- 7 files changed, 92 insertions(+), 14 deletions(-) create mode 100644 tests/ui/missing_const_for_fn/auxiliary/helper.rs (limited to 'tests') diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index 0dc02431ad5..93b7a897405 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -138,7 +138,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { let mir = cx.tcx.optimized_mir(def_id); - if let Err((span, err)) = is_min_const_fn(cx.tcx, mir) { + if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, self.msrv.as_ref()) { if rustc_mir::const_eval::is_min_const_fn(cx.tcx, def_id.to_def_id()) { cx.tcx.sess.span_err(span, &err); } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index e99ffbe269b..8c20f8a0fbc 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -9,6 +9,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_errors; extern crate rustc_hir; diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index b52cbf31e35..b2ce58b597b 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -1,3 +1,8 @@ +// This code used to be a part of `rustc` but moved to Clippy as a result of +// https://github.com/rust-lang/rust/issues/76618. Because of that, it contains unused code and some +// of terminologies might not be relevant in the context of Clippy. Note that its behavior might +// differ from the time of `rustc` even if the name stays the same. + use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_middle::mir::{ @@ -6,6 +11,7 @@ use rustc_middle::mir::{ }; use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt}; +use rustc_semver::RustcVersion; use rustc_span::symbol::sym; use rustc_span::Span; use rustc_target::spec::abi::Abi::RustIntrinsic; @@ -13,7 +19,7 @@ use std::borrow::Cow; type McfResult = Result<(), (Span, Cow<'static, str>)>; -pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>) -> McfResult { +pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option<&RustcVersion>) -> McfResult { let def_id = body.source.def_id(); let mut current = def_id; loop { @@ -70,7 +76,7 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>) -> McfResult { )?; for bb in body.basic_blocks() { - check_terminator(tcx, body, bb.terminator())?; + check_terminator(tcx, body, bb.terminator(), msrv)?; for stmt in &bb.statements { check_statement(tcx, body, def_id, stmt)?; } @@ -268,7 +274,12 @@ fn check_place(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'t Ok(()) } -fn check_terminator(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Terminator<'tcx>) -> McfResult { +fn check_terminator( + tcx: TyCtxt<'tcx>, + body: &'a Body<'tcx>, + terminator: &Terminator<'tcx>, + msrv: Option<&RustcVersion>, +) -> McfResult { let span = terminator.source_info.span; match &terminator.kind { TerminatorKind::FalseEdge { .. } @@ -305,7 +316,7 @@ fn check_terminator(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Termin } => { let fn_ty = func.ty(body, tcx); if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() { - if !rustc_mir::const_eval::is_min_const_fn(tcx, fn_def_id) { + if !is_const_fn(tcx, fn_def_id, msrv) { return Err(( span, format!( @@ -350,3 +361,24 @@ fn check_terminator(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Termin TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())), } } + +fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<&RustcVersion>) -> bool { + rustc_mir::const_eval::is_const_fn(tcx, def_id) + && if let Some(const_stab) = tcx.lookup_const_stability(def_id) { + if let rustc_attr::StabilityLevel::Stable { since } = const_stab.level { + // Checking MSRV is manually necessary because `rustc` has no such concept. This entire + // function could be removed if `rustc` provided a MSRV-aware version of `is_const_fn`. + // as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262. + crate::meets_msrv( + msrv, + &RustcVersion::parse(&since.as_str()) + .expect("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted"), + ) + } else { + // `rustc_mir::const_eval::is_const_fn` should return false for unstably const functions. + unreachable!(); + } + } else { + true + } +} diff --git a/tests/ui/missing_const_for_fn/auxiliary/helper.rs b/tests/ui/missing_const_for_fn/auxiliary/helper.rs new file mode 100644 index 00000000000..7b9dc76b8f1 --- /dev/null +++ b/tests/ui/missing_const_for_fn/auxiliary/helper.rs @@ -0,0 +1,8 @@ +// This file provides a const function that is unstably const forever. + +#![feature(staged_api)] +#![stable(feature = "1", since = "1.0.0")] + +#[stable(feature = "1", since = "1.0.0")] +#[rustc_const_unstable(feature = "foo", issue = "none")] +pub const fn unstably_const_fn() {} diff --git a/tests/ui/missing_const_for_fn/cant_be_const.rs b/tests/ui/missing_const_for_fn/cant_be_const.rs index ba352ef9ee9..7cda1aaa3c2 100644 --- a/tests/ui/missing_const_for_fn/cant_be_const.rs +++ b/tests/ui/missing_const_for_fn/cant_be_const.rs @@ -2,9 +2,14 @@ //! compilation error. //! The .stderr output of this test should be empty. Otherwise it's a bug somewhere. +// aux-build:helper.rs + #![warn(clippy::missing_const_for_fn)] #![allow(incomplete_features)] #![feature(start, const_generics)] +#![feature(custom_inner_attributes)] + +extern crate helper; struct Game; @@ -101,3 +106,17 @@ fn const_generic_return(t: &[T]) -> &[T; N] { unsafe { &*p } } + +// Do not lint this because it calls a function whose constness is unstable. +fn unstably_const_fn() { + helper::unstably_const_fn() +} + +mod const_fn_stabilized_after_msrv { + #![clippy::msrv = "1.46.0"] + + // Do not lint this because `u8::is_ascii_digit` is stabilized as a const function in 1.47.0. + fn const_fn_stabilized_after_msrv(byte: u8) { + byte.is_ascii_digit(); + } +} diff --git a/tests/ui/missing_const_for_fn/could_be_const.rs b/tests/ui/missing_const_for_fn/could_be_const.rs index c6f44b7daa3..0accb516f5f 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.rs +++ b/tests/ui/missing_const_for_fn/could_be_const.rs @@ -1,6 +1,7 @@ #![warn(clippy::missing_const_for_fn)] #![allow(incomplete_features, clippy::let_and_return)] #![feature(const_generics)] +#![feature(custom_inner_attributes)] use std::mem::transmute; @@ -70,5 +71,14 @@ mod with_drop { } } +mod const_fn_stabilized_before_msrv { + #![clippy::msrv = "1.47.0"] + + // This could be const because `u8::is_ascii_digit` is a stable const function in 1.47. + fn const_fn_stabilized_before_msrv(byte: u8) { + byte.is_ascii_digit(); + } +} + // Should not be const fn main() {} diff --git a/tests/ui/missing_const_for_fn/could_be_const.stderr b/tests/ui/missing_const_for_fn/could_be_const.stderr index 74d32b8a1aa..63c211f39fa 100644 --- a/tests/ui/missing_const_for_fn/could_be_const.stderr +++ b/tests/ui/missing_const_for_fn/could_be_const.stderr @@ -1,5 +1,5 @@ error: this could be a `const fn` - --> $DIR/could_be_const.rs:13:5 + --> $DIR/could_be_const.rs:14:5 | LL | / pub fn new() -> Self { LL | | Self { guess: 42 } @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::missing-const-for-fn` implied by `-D warnings` error: this could be a `const fn` - --> $DIR/could_be_const.rs:17:5 + --> $DIR/could_be_const.rs:18:5 | LL | / fn const_generic_params<'a, T, const N: usize>(&self, b: &'a [T; N]) -> &'a [T; N] { LL | | b @@ -17,7 +17,7 @@ LL | | } | |_____^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:23:1 + --> $DIR/could_be_const.rs:24:1 | LL | / fn one() -> i32 { LL | | 1 @@ -25,7 +25,7 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:28:1 + --> $DIR/could_be_const.rs:29:1 | LL | / fn two() -> i32 { LL | | let abc = 2; @@ -34,7 +34,7 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:34:1 + --> $DIR/could_be_const.rs:35:1 | LL | / fn string() -> String { LL | | String::new() @@ -42,7 +42,7 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:39:1 + --> $DIR/could_be_const.rs:40:1 | LL | / unsafe fn four() -> i32 { LL | | 4 @@ -50,7 +50,7 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:44:1 + --> $DIR/could_be_const.rs:45:1 | LL | / fn generic(t: T) -> T { LL | | t @@ -58,12 +58,20 @@ LL | | } | |_^ error: this could be a `const fn` - --> $DIR/could_be_const.rs:67:9 + --> $DIR/could_be_const.rs:68:9 | LL | / pub fn b(self, a: &A) -> B { LL | | B LL | | } | |_________^ -error: aborting due to 8 previous errors +error: this could be a `const fn` + --> $DIR/could_be_const.rs:78:5 + | +LL | / fn const_fn_stabilized_before_msrv(byte: u8) { +LL | | byte.is_ascii_digit(); +LL | | } + | |_____^ + +error: aborting due to 9 previous errors -- cgit 1.4.1-3-g733a5 From cbde4f2c67667c717adecf3fac9df84954154fe0 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sat, 10 Apr 2021 14:45:51 +0200 Subject: parent_node_is_if_expr now also recognizes if let as parent if --- clippy_lints/src/copies.rs | 7 +++++ clippy_utils/src/lib.rs | 30 +++++++++++++++++----- tests/ui/branches_sharing_code/shared_at_bottom.rs | 14 ++++++++++ 3 files changed, 44 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 8b503c9a030..dfc79ac365b 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -290,6 +290,13 @@ fn lint_same_then_else<'tcx>( } } +/// The return tuple is structured as follows: +/// 1. The amount of equal statements from the start +/// 2. The amount of equal statements from the end +/// 3. An indication if the block expressions are the same. This will also be true if both are `None` +/// +/// This function can also trigger the `IF_SAME_THEN_ELSE` in which case it'll return `(0, 0, false)` +/// to aboard any further processing and avoid duplicate lint triggers. fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> (usize, usize, bool) { let mut start_eq = usize::MAX; let mut end_eq = usize::MAX; diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 8e1a2105b96..1ef5d9d32c5 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1212,13 +1212,29 @@ pub fn parent_node_is_if_expr(expr: &Expr<'_>, cx: &LateContext<'_>) -> bool { let map = cx.tcx.hir(); let parent_id = map.get_parent_node(expr.hir_id); let parent_node = map.get(parent_id); - matches!( - parent_node, - Node::Expr(Expr { - kind: ExprKind::If(_, _, _), - .. - }) - ) + + // Check for `if` + if_chain! { + if let Node::Expr(expr) = parent_node; + if let ExprKind::If(_, _, _) = expr.kind; + then { + return true; + } + } + + // Check for `if let` + if_chain! { + if let Node::Arm(arm) = parent_node; + let arm_parent_id = map.get_parent_node(arm.hir_id); + let arm_parent_node = map.get(arm_parent_id); + if let Node::Expr(expr) = arm_parent_node; + if let ExprKind::Match(_, _, MatchSource::IfLetDesugar { .. }) = expr.kind; + then { + return true; + } + } + + false } // Finds the `#[must_use]` attribute, if any diff --git a/tests/ui/branches_sharing_code/shared_at_bottom.rs b/tests/ui/branches_sharing_code/shared_at_bottom.rs index c389c243d44..ce2040bdeb8 100644 --- a/tests/ui/branches_sharing_code/shared_at_bottom.rs +++ b/tests/ui/branches_sharing_code/shared_at_bottom.rs @@ -206,4 +206,18 @@ fn fp_test() { } } +fn fp_if_let_issue7054() { + // This shouldn't trigger the lint + let string; + let _x = if let true = true { + "" + } else if true { + string = "x".to_owned(); + &string + } else { + string = "y".to_owned(); + &string + }; +} + fn main() {} -- cgit 1.4.1-3-g733a5 From 2992b19c82c0c5f96327b3be037777bf42862230 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Tue, 13 Apr 2021 22:55:47 +0200 Subject: Added inferred local type comparion to SpanlessEq --- clippy_lints/src/copies.rs | 9 ++--- clippy_utils/src/hir_utils.rs | 48 +++++++++++++++++++++++++ clippy_utils/src/lib.rs | 2 +- tests/ui/branches_sharing_code/shared_at_top.rs | 11 ++++++ 4 files changed, 65 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index dfc79ac365b..34ea014dd67 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -293,10 +293,11 @@ fn lint_same_then_else<'tcx>( /// The return tuple is structured as follows: /// 1. The amount of equal statements from the start /// 2. The amount of equal statements from the end -/// 3. An indication if the block expressions are the same. This will also be true if both are `None` +/// 3. An indication if the block expressions are the same. This will also be true if both are +/// `None` /// -/// This function can also trigger the `IF_SAME_THEN_ELSE` in which case it'll return `(0, 0, false)` -/// to aboard any further processing and avoid duplicate lint triggers. +/// This function can also trigger the `IF_SAME_THEN_ELSE` in which case it'll return `(0, 0, +/// false)` to aboard any further processing and avoid duplicate lint triggers. fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> (usize, usize, bool) { let mut start_eq = usize::MAX; let mut end_eq = usize::MAX; @@ -307,7 +308,7 @@ fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> (usize, // `SpanlessEq` now keeps track of the locals and is therefore context sensitive clippy#6752. // The comparison therefore needs to be done in a way that builds the correct context. - let mut evaluator = SpanlessEq::new(cx); + let mut evaluator = SpanlessEq::new(cx).enable_check_inferred_local_types(); let mut evaluator = evaluator.inter_expr(); let current_start_eq = count_eq(&mut l_stmts.iter(), &mut r_stmts.iter(), |l, r| evaluator.eq_stmt(l, r)); diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index f695f1a61e7..97db58ae12b 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -1,6 +1,7 @@ use crate::consts::{constant_context, constant_simple}; use crate::differing_macro_contexts; use crate::source::snippet_opt; +use if_chain::if_chain; use rustc_ast::ast::InlineAsmTemplatePiece; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_hir::def::Res; @@ -29,6 +30,30 @@ pub struct SpanlessEq<'a, 'tcx> { maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>, allow_side_effects: bool, expr_fallback: Option, &Expr<'_>) -> bool + 'a>>, + /// This adds an additional type comparison to locals that insures that even the + /// inferred of the value is the same. + /// + /// **Example** + /// * Context 1 + /// ```ignore + /// let vec = Vec::new(); + /// vec.push("A string"); + /// ``` + /// + /// * Context 2 + /// ```ignore + /// let vec = Vec::new(); + /// vec.push(0); // An integer + /// ``` + /// + /// Only comparing the first local definition would usually return that they are + /// equal, since they are identical. However, they are different due to the context + /// as they have different inferred types. + /// + /// This option enables or disables the specific check of the inferred type. + /// + /// Note: This check will only be done if `self.maybe_typeck_results` is `Some()`. + check_inferred_local_types: bool, } impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { @@ -38,6 +63,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { maybe_typeck_results: cx.maybe_typeck_results(), allow_side_effects: true, expr_fallback: None, + check_inferred_local_types: false, } } @@ -56,6 +82,13 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { } } + pub fn enable_check_inferred_local_types(self) -> Self { + Self { + check_inferred_local_types: true, + ..self + } + } + /// Use this method to wrap comparisons that may involve inter-expression context. /// See `self.locals`. pub fn inter_expr(&mut self) -> HirEqInterExpr<'_, 'a, 'tcx> { @@ -96,6 +129,21 @@ impl HirEqInterExpr<'_, '_, '_> { pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool { match (&left.kind, &right.kind) { (&StmtKind::Local(ref l), &StmtKind::Local(ref r)) => { + // See `SpanlessEq::check_inferred_local_types` for an explication of this check + if_chain! { + if l.ty.is_none() && r.ty.is_none(); + if self.inner.check_inferred_local_types; + if let Some(tcx) = self.inner.maybe_typeck_results; + + // Check the inferred types + let l_ty = tcx.pat_ty(&l.pat); + let r_ty = tcx.pat_ty(&r.pat); + if l_ty != r_ty; + then { + return false; + } + } + // eq_pat adds the HirIds to the locals map. We therefor call it last to make sure that // these only get added if the init and type is equal. both(&l.init, &r.init, |l, r| self.eq_expr(l, r)) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 1ef5d9d32c5..280bde73ed7 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1212,7 +1212,7 @@ pub fn parent_node_is_if_expr(expr: &Expr<'_>, cx: &LateContext<'_>) -> bool { let map = cx.tcx.hir(); let parent_id = map.get_parent_node(expr.hir_id); let parent_node = map.get(parent_id); - + // Check for `if` if_chain! { if let Node::Expr(expr) = parent_node; diff --git a/tests/ui/branches_sharing_code/shared_at_top.rs b/tests/ui/branches_sharing_code/shared_at_top.rs index e65bcfd7873..51a46481399 100644 --- a/tests/ui/branches_sharing_code/shared_at_top.rs +++ b/tests/ui/branches_sharing_code/shared_at_top.rs @@ -100,4 +100,15 @@ fn check_if_same_than_else_mask() { } } +#[allow(clippy::vec_init_then_push)] +fn pf_local_with_inferred_type_issue7053() { + if true { + let mut v = Vec::new(); + v.push(0); + } else { + let mut v = Vec::new(); + v.push(""); + }; +} + fn main() {} -- cgit 1.4.1-3-g733a5 From ce5e927713ddd32096f4e73f7a2c339cc8163d82 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 25 Mar 2021 09:25:04 -0400 Subject: Improve `map_entry` lint Fix false positives where the map is used before inserting into the map. Fix false positives where two insertions happen. Suggest using `if let Entry::Vacant(e) = _.entry(_)` when `or_insert` might be a semantic change --- clippy_lints/src/entry.rs | 489 ++++++++++++++++++++++++++++++---------- clippy_utils/src/lib.rs | 125 ++++++++-- clippy_utils/src/paths.rs | 4 + clippy_utils/src/source.rs | 9 + tests/ui/entry.fixed | 101 +++++++++ tests/ui/entry.rs | 103 +++++++++ tests/ui/entry.stderr | 171 ++++++++++++++ tests/ui/entry_fixable.fixed | 15 -- tests/ui/entry_fixable.rs | 17 -- tests/ui/entry_fixable.stderr | 12 - tests/ui/entry_unfixable.rs | 59 ++--- tests/ui/entry_unfixable.stderr | 57 ----- 12 files changed, 882 insertions(+), 280 deletions(-) create mode 100644 tests/ui/entry.fixed create mode 100644 tests/ui/entry.rs create mode 100644 tests/ui/entry.stderr delete mode 100644 tests/ui/entry_fixable.fixed delete mode 100644 tests/ui/entry_fixable.rs delete mode 100644 tests/ui/entry_fixable.stderr delete mode 100644 tests/ui/entry_unfixable.stderr (limited to 'tests') diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index a815df1691a..ca01d0a7f87 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -1,17 +1,18 @@ -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability}; -use clippy_utils::ty::{is_type_diagnostic_item, match_type}; -use clippy_utils::SpanlessEq; -use clippy_utils::{get_item_name, paths}; -use if_chain::if_chain; +use clippy_utils::{ + diagnostics::span_lint_and_sugg, + is_expr_final_block_expr, is_expr_used_or_unified, match_def_path, paths, peel_hir_expr_while, + source::{snippet_indent, snippet_with_applicability, snippet_with_context}, + SpanlessEq, +}; use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; -use rustc_hir::{BorrowKind, Expr, ExprKind, UnOp}; +use rustc_hir::{ + intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor}, + Expr, ExprKind, Guard, Local, Stmt, StmtKind, UnOp, +}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Span; -use rustc_span::sym; +use rustc_span::{Span, SyntaxContext, DUMMY_SP}; +use std::fmt::Write; declare_clippy_lint! { /// **What it does:** Checks for uses of `contains_key` + `insert` on `HashMap` @@ -19,15 +20,14 @@ declare_clippy_lint! { /// /// **Why is this bad?** Using `entry` is more efficient. /// - /// **Known problems:** Some false negatives, eg.: + /// **Known problems:** The suggestion may have type inference errors in some cases. e.g. /// ```rust - /// # use std::collections::HashMap; - /// # let mut map = HashMap::new(); - /// # let v = 1; - /// # let k = 1; - /// if !map.contains_key(&k) { - /// map.insert(k.clone(), v); - /// } + /// let mut map = std::collections::HashMap::new(); + /// let _ = if !map.contains_key(&0) { + /// map.insert(0, 0) + /// } else { + /// None + /// }; /// ``` /// /// **Example:** @@ -56,132 +56,379 @@ declare_clippy_lint! { declare_lint_pass!(HashMapPass => [MAP_ENTRY]); impl<'tcx> LateLintPass<'tcx> for HashMapPass { + #[allow(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::If(check, then_block, ref else_block) = expr.kind { - if let ExprKind::Unary(UnOp::Not, check) = check.kind { - if let Some((ty, map, key)) = check_cond(cx, check) { - // in case of `if !m.contains_key(&k) { m.insert(k, v); }` - // we can give a better error message - let sole_expr = { - else_block.is_none() - && if let ExprKind::Block(then_block, _) = then_block.kind { - (then_block.expr.is_some() as usize) + then_block.stmts.len() == 1 - } else { - true - } - // XXXManishearth we can also check for if/else blocks containing `None`. - }; + let (cond_expr, then_expr, else_expr) = match expr.kind { + ExprKind::If(c, t, e) => (c, t, e), + _ => return, + }; + let (map_ty, contains_expr) = match try_parse_contains(cx, cond_expr) { + Some(x) => x, + None => return, + }; - let mut visitor = InsertVisitor { - cx, - span: expr.span, - ty, - map, - key, - sole_expr, - }; + let then_search = match find_insert_calls(cx, &contains_expr, then_expr) { + Some(x) => x, + None => return, + }; - walk_expr(&mut visitor, then_block); - } - } else if let Some(else_block) = *else_block { - if let Some((ty, map, key)) = check_cond(cx, check) { - let mut visitor = InsertVisitor { - cx, - span: expr.span, - ty, - map, - key, - sole_expr: false, + let mut app = Applicability::MachineApplicable; + let map_str = snippet_with_context(cx, contains_expr.map.span, contains_expr.call_ctxt, "..", &mut app).0; + let key_str = snippet_with_context(cx, contains_expr.key.span, contains_expr.call_ctxt, "..", &mut app).0; + let sugg = if !contains_expr.negated || else_expr.is_some() || then_search.insertions.is_empty() { + return; + } else { + // if .. { insert } + match then_search.as_single_insertion() { + Some(insertion) if !insertion.value.can_have_side_effects() => { + format!( + "{}.entry({}).or_insert({});", + map_str, + key_str, + snippet_with_context(cx, insertion.value.span, insertion.call.span.ctxt(), "..", &mut app).0, + ) + }, + _ => { + let (body_str, entry_kind) = if contains_expr.negated { + (then_search.snippet_vacant(cx, then_expr.span, &mut app), "Vacant(e)") + } else { + ( + then_search.snippet_occupied(cx, then_expr.span, &mut app), + "Occupied(mut e)", + ) }; - - walk_expr(&mut visitor, else_block); - } + format!( + "if let {}::{} = {}.entry({}) {}", + map_ty.entry_path(), + entry_kind, + map_str, + key_str, + body_str, + ) + }, } + }; + + span_lint_and_sugg( + cx, + MAP_ENTRY, + expr.span, + &format!("usage of `contains_key` followed by `insert` on a `{}`", map_ty.name()), + "try this", + sugg, + app, + ); + } +} + +#[derive(Clone, Copy)] +enum MapType { + Hash, + BTree, +} +impl MapType { + fn name(self) -> &'static str { + match self { + Self::Hash => "HashMap", + Self::BTree => "BTreeMap", + } + } + fn entry_path(self) -> &'staic str { + match self { + Self::Hash => "std::collections::hash_map::Entry", + Self::BTree => "std::collections::btree_map::Entry", } } } -fn check_cond<'a>(cx: &LateContext<'_>, check: &'a Expr<'a>) -> Option<(&'static str, &'a Expr<'a>, &'a Expr<'a>)> { - if_chain! { - if let ExprKind::MethodCall(path, _, params, _) = check.kind; - if params.len() >= 2; - if path.ident.name == sym!(contains_key); - if let ExprKind::AddrOf(BorrowKind::Ref, _, key) = params[1].kind; - then { - let map = ¶ms[0]; - let obj_ty = cx.typeck_results().expr_ty(map).peel_refs(); - - return if match_type(cx, obj_ty, &paths::BTREEMAP) { - Some(("BTreeMap", map, key)) - } - else if is_type_diagnostic_item(cx, obj_ty, sym::hashmap_type) { - Some(("HashMap", map, key)) - } - else { - None +struct ContainsExpr<'tcx> { + negated: bool, + map: &'tcx Expr<'tcx>, + key: &'tcx Expr<'tcx>, + call_ctxt: SyntaxContext, +} +fn try_parse_contains(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(MapType, ContainsExpr<'tcx>)> { + let mut negated = false; + let expr = peel_hir_expr_while(expr, |e| match e.kind { + ExprKind::Unary(UnOp::Not, e) => { + negated = !negated; + Some(e) + }, + _ => None, + }); + match expr.kind { + ExprKind::MethodCall( + _, + _, + [map, Expr { + kind: ExprKind::AddrOf(_, _, key), + span: key_span, + .. + }], + _, + ) if key_span.ctxt() == expr.span.ctxt() => { + let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?; + let expr = ContainsExpr { + negated, + map, + key, + call_ctxt: expr.span.ctxt(), }; + if match_def_path(cx, id, &paths::BTREEMAP_CONTAINS_KEY) { + Some((MapType::BTree, expr)) + } else if match_def_path(cx, id, &paths::HASHMAP_CONTAINS_KEY) { + Some((MapType::Hash, expr)) + } else { + None + } + }, + _ => None, + } +} + +struct InsertExpr<'tcx> { + map: &'tcx Expr<'tcx>, + key: &'tcx Expr<'tcx>, + value: &'tcx Expr<'tcx>, +} +fn try_parse_insert(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option> { + if let ExprKind::MethodCall(_, _, [map, key, value], _) = expr.kind { + let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?; + if match_def_path(cx, id, &paths::BTREEMAP_INSERT) || match_def_path(cx, id, &paths::HASHMAP_INSERT) { + Some(InsertExpr { map, key, value }) + } else { + None } + } else { + None } +} - None +#[derive(Clone, Copy)] +struct Insertion<'tcx> { + call: &'tcx Expr<'tcx>, + value: &'tcx Expr<'tcx>, } -struct InsertVisitor<'a, 'tcx, 'b> { - cx: &'a LateContext<'tcx>, - span: Span, - ty: &'static str, - map: &'b Expr<'b>, - key: &'b Expr<'b>, - sole_expr: bool, +// This visitor needs to do a multiple things: +// * Find all usages of the map. Only insertions into the map which share the same key are +// permitted. All others will prevent the lint. +// * Determine if the final statement executed is an insertion. This is needed to use `insert_with`. +// * Determine if there's any sub-expression that can't be placed in a closure. +// * Determine if there's only a single insert statement. This is needed to give better suggestions. + +#[allow(clippy::struct_excessive_bools)] +struct InsertSearcher<'cx, 'i, 'tcx> { + cx: &'cx LateContext<'tcx>, + /// The map expression used in the contains call. + map: &'tcx Expr<'tcx>, + /// The key expression used in the contains call. + key: &'tcx Expr<'tcx>, + /// The context of the top level block. All insert calls must be in the same context. + ctxt: SyntaxContext, + /// Whether this expression can use the entry api. + can_use_entry: bool, + // A single insert expression has a slightly different suggestion. + is_single_insert: bool, + is_map_used: bool, + insertions: &'i mut Vec>, +} +impl<'tcx> InsertSearcher<'_, '_, 'tcx> { + /// Visit the expression as a branch in control flow. Multiple insert calls can be used, but + /// only if they are on separate code paths. This will return whether the map was used in the + /// given expression. + fn visit_cond_arm(&mut self, e: &'tcx Expr<'_>) -> bool { + let is_map_used = self.is_map_used; + self.visit_expr(e); + let res = self.is_map_used; + self.is_map_used = is_map_used; + res + } } +impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, '_, 'tcx> { + type Map = ErasedMap<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } -impl<'a, 'tcx, 'b> Visitor<'tcx> for InsertVisitor<'a, 'tcx, 'b> { - type Map = Map<'tcx>; + fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) { + match stmt.kind { + StmtKind::Semi(e) | StmtKind::Expr(e) => self.visit_expr(e), + StmtKind::Local(Local { init: Some(e), .. }) => { + self.is_single_insert = false; + self.visit_expr(e); + }, + _ => { + self.is_single_insert = false; + }, + } + } fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if_chain! { - if let ExprKind::MethodCall(path, _, params, _) = expr.kind; - if params.len() == 3; - if path.ident.name == sym!(insert); - if get_item_name(self.cx, self.map) == get_item_name(self.cx, ¶ms[0]); - if SpanlessEq::new(self.cx).eq_expr(self.key, ¶ms[1]); - if snippet_opt(self.cx, self.map.span) == snippet_opt(self.cx, params[0].span); - then { - span_lint_and_then(self.cx, MAP_ENTRY, self.span, - &format!("usage of `contains_key` followed by `insert` on a `{}`", self.ty), |diag| { - if self.sole_expr { - let mut app = Applicability::MachineApplicable; - let help = format!("{}.entry({}).or_insert({});", - snippet_with_applicability(self.cx, self.map.span, "map", &mut app), - snippet_with_applicability(self.cx, params[1].span, "..", &mut app), - snippet_with_applicability(self.cx, params[2].span, "..", &mut app)); - - diag.span_suggestion( - self.span, - "consider using", - help, - Applicability::MachineApplicable, // snippet - ); + if !self.can_use_entry { + return; + } + + match try_parse_insert(self.cx, expr) { + Some(insert_expr) if SpanlessEq::new(self.cx).eq_expr(self.map, insert_expr.map) => { + // Multiple inserts, inserts with a different key, and inserts from a macro can't use the entry api. + if self.is_map_used + || !SpanlessEq::new(self.cx).eq_expr(self.key, insert_expr.key) + || expr.span.ctxt() != self.ctxt + { + self.can_use_entry = false; + return; + } + + self.insertions.push(Insertion { + call: expr, + value: insert_expr.value, + }); + self.is_map_used = true; + + // The value doesn't affect whether there is only a single insert expression. + let is_single_insert = self.is_single_insert; + self.visit_expr(insert_expr.value); + self.is_single_insert = is_single_insert; + }, + _ if SpanlessEq::new(self.cx).eq_expr(self.map, expr) => { + self.is_map_used = true; + }, + _ => match expr.kind { + ExprKind::If(cond_expr, then_expr, Some(else_expr)) => { + self.is_single_insert = false; + self.visit_expr(cond_expr); + // Each branch may contain it's own insert expression. + let mut is_map_used = self.visit_cond_arm(then_expr); + is_map_used |= self.visit_cond_arm(else_expr); + self.is_map_used = is_map_used; + }, + ExprKind::Match(scrutinee_expr, arms, _) => { + self.is_single_insert = false; + self.visit_expr(scrutinee_expr); + // Each branch may contain it's own insert expression. + let mut is_map_used = self.is_map_used; + for arm in arms { + if let Some(Guard::If(guard) | Guard::IfLet(_, guard)) = arm.guard { + self.visit_expr(guard) + } + is_map_used |= self.visit_cond_arm(arm.body); } - else { - let help = format!("consider using `{}.entry({})`", - snippet(self.cx, self.map.span, "map"), - snippet(self.cx, params[1].span, "..")); - - diag.span_label( - self.span, - &help, - ); + self.is_map_used = is_map_used; + }, + ExprKind::Loop(block, ..) => { + // Don't allow insertions inside of a loop. + let insertions_len = self.insertions.len(); + self.visit_block(block); + if self.insertions.len() != insertions_len { + self.can_use_entry = false; } - }); - } + }, + ExprKind::Block(block, _) => self.visit_block(block), + ExprKind::InlineAsm(_) | ExprKind::LlvmInlineAsm(_) => { + self.can_use_entry = false; + }, + _ => { + self.is_single_insert = false; + walk_expr(self, expr); + }, + }, } + } +} - if !self.sole_expr { - walk_expr(self, expr); +struct InsertSearchResults<'tcx> { + insertions: Vec>, + is_single_insert: bool, +} +impl InsertSearchResults<'tcx> { + fn as_single_insertion(&self) -> Option> { + self.is_single_insert.then(|| self.insertions[0]) + } + + fn snippet_occupied(&self, cx: &LateContext<'_>, mut span: Span, app: &mut Applicability) -> String { + let ctxt = span.ctxt(); + let mut res = String::new(); + for insertion in self.insertions.iter() { + res.push_str(&snippet_with_applicability( + cx, + span.until(insertion.call.span), + "..", + app, + )); + if is_expr_used_or_unified(cx.tcx, insertion.call) { + res.push_str("Some(e.insert("); + res.push_str(&snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0); + res.push_str("))"); + } else { + res.push_str("e.insert("); + res.push_str(&snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0); + res.push(')'); + } + span = span.trim_start(insertion.call.span).unwrap_or(DUMMY_SP); } + res.push_str(&snippet_with_applicability(cx, span, "..", app)); + res } - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None + + fn snippet_vacant(&self, cx: &LateContext<'_>, mut span: Span, app: &mut Applicability) -> String { + let ctxt = span.ctxt(); + let mut res = String::new(); + for insertion in self.insertions.iter() { + res.push_str(&snippet_with_applicability( + cx, + span.until(insertion.call.span), + "..", + app, + )); + if is_expr_used_or_unified(cx.tcx, insertion.call) { + if is_expr_final_block_expr(cx.tcx, insertion.call) { + let _ = write!( + res, + "e.insert({});\n{}None", + snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0, + snippet_indent(cx, insertion.call.span).as_deref().unwrap_or(""), + ); + } else { + let _ = write!( + res, + "{{ e.insert({}); None }}", + snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0, + ); + } + } else { + let _ = write!( + res, + "e.insert({})", + snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0, + ); + } + span = span.trim_start(insertion.call.span).unwrap_or(DUMMY_SP); + } + res.push_str(&snippet_with_applicability(cx, span, "..", app)); + res } } +fn find_insert_calls( + cx: &LateContext<'tcx>, + contains_expr: &ContainsExpr<'tcx>, + expr: &'tcx Expr<'_>, +) -> Option> { + let mut insertions = Vec::new(); + let mut s = InsertSearcher { + cx, + map: contains_expr.map, + key: contains_expr.key, + ctxt: expr.span.ctxt(), + insertions: &mut insertions, + is_map_used: false, + can_use_entry: true, + is_single_insert: true, + }; + s.visit_expr(expr); + let is_single_insert = s.is_single_insert; + s.can_use_entry.then(|| InsertSearchResults { + insertions, + is_single_insert, + }) +} diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 8e1a2105b96..0abee49f40c 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -63,9 +63,9 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::LangItem::{ResultErr, ResultOk}; use rustc_hir::{ - def, Arm, BindingAnnotation, Block, Body, Constness, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl, ImplItem, - ImplItemKind, Item, ItemKind, LangItem, MatchSource, Node, Param, Pat, PatKind, Path, PathSegment, QPath, - TraitItem, TraitItemKind, TraitRef, TyKind, + def, Arm, BindingAnnotation, Block, Body, Constness, Destination, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl, + ImplItem, ImplItemKind, Item, ItemKind, LangItem, MatchSource, Node, Param, Pat, PatKind, Path, PathSegment, QPath, + Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, }; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::exports::Export; @@ -1245,6 +1245,82 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { did.map_or(false, |did| must_use_attr(&cx.tcx.get_attrs(did)).is_some()) } +pub fn get_expr_use_node(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option> { + let map = tcx.hir(); + let mut child_id = expr.hir_id; + let mut iter = map.parent_iter(child_id); + loop { + match iter.next() { + None => break None, + Some((id, Node::Block(_))) => child_id = id, + Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id, + Some((_, Node::Expr(expr))) => match expr.kind { + ExprKind::Break( + Destination { + target_id: Ok(dest), .. + }, + _, + ) => { + iter = map.parent_iter(dest); + child_id = dest; + }, + ExprKind::DropTemps(_) | ExprKind::Block(..) => child_id = expr.hir_id, + ExprKind::If(control_expr, ..) | ExprKind::Match(control_expr, ..) + if control_expr.hir_id != child_id => + { + child_id = expr.hir_id + }, + _ => break Some(Node::Expr(expr)), + }, + Some((_, node)) => break Some(node), + } + } +} + +pub fn is_expr_used(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { + !matches!( + get_expr_use_node(tcx, expr), + Some(Node::Stmt(Stmt { + kind: StmtKind::Expr(_) | StmtKind::Semi(_), + .. + })) + ) +} + +pub fn get_expr_use_or_unification_node(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option> { + let map = tcx.hir(); + let mut child_id = expr.hir_id; + let mut iter = map.parent_iter(child_id); + loop { + match iter.next() { + None => break None, + Some((id, Node::Block(_))) => child_id = id, + Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id, + Some((_, Node::Expr(expr))) => match expr.kind { + ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id, + ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id, + ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None, + _ => break Some(Node::Expr(expr)), + }, + Some((_, node)) => break Some(node), + } + } +} + +pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { + !matches!( + get_expr_use_or_unification_node(tcx, expr), + None | Some(Node::Stmt(Stmt { + kind: StmtKind::Expr(_) | StmtKind::Semi(_), + .. + })) + ) +} + +pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { + matches!(get_parent_node(tcx, expr.hir_id), Some(Node::Block(..))) +} + pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool { cx.tcx.hir().attrs(hir::CRATE_HIR_ID).iter().any(|attr| { if let ast::AttrKind::Normal(ref attr, _) = attr.kind { @@ -1414,28 +1490,43 @@ pub fn peel_hir_pat_refs(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) { peel(pat, 0) } +/// Peels of expressions while the given closure returns `Some`. +pub fn peel_hir_expr_while<'tcx>( + mut expr: &'tcx Expr<'tcx>, + mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>, +) -> &'tcx Expr<'tcx> { + while let Some(e) = f(expr) { + expr = e; + } + expr +} + /// Peels off up to the given number of references on the expression. Returns the underlying /// expression and the number of references removed. pub fn peel_n_hir_expr_refs(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) { - fn f(expr: &'a Expr<'a>, count: usize, target: usize) -> (&'a Expr<'a>, usize) { - match expr.kind { - ExprKind::AddrOf(_, _, expr) if count != target => f(expr, count + 1, target), - _ => (expr, count), - } - } - f(expr, 0, count) + let mut remaining = count; + let e = peel_hir_expr_while(expr, |e| match e.kind { + ExprKind::AddrOf(BorrowKind::Ref, _, e) if remaining != 0 => { + remaining -= 1; + Some(e) + }, + _ => None, + }); + (e, count - remaining) } /// Peels off all references on the expression. Returns the underlying expression and the number of /// references removed. pub fn peel_hir_expr_refs(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) { - fn f(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) { - match expr.kind { - ExprKind::AddrOf(BorrowKind::Ref, _, expr) => f(expr, count + 1), - _ => (expr, count), - } - } - f(expr, 0) + let mut count = 0; + let e = peel_hir_expr_while(expr, |e| match e.kind { + ExprKind::AddrOf(BorrowKind::Ref, _, e) => { + count += 1; + Some(e) + }, + _ => None, + }); + (e, count) } #[macro_export] diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index ed8915f59e1..8066f6c223e 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -13,7 +13,9 @@ pub(super) const BEGIN_PANIC_FMT: [&str; 3] = ["std", "panicking", "begin_panic_ pub const BINARY_HEAP: [&str; 4] = ["alloc", "collections", "binary_heap", "BinaryHeap"]; pub const BORROW_TRAIT: [&str; 3] = ["core", "borrow", "Borrow"]; pub const BTREEMAP: [&str; 5] = ["alloc", "collections", "btree", "map", "BTreeMap"]; +pub const BTREEMAP_CONTAINS_KEY: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "contains_key"]; pub const BTREEMAP_ENTRY: [&str; 6] = ["alloc", "collections", "btree", "map", "entry", "Entry"]; +pub const BTREEMAP_INSERT: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "insert"]; pub const BTREESET: [&str; 5] = ["alloc", "collections", "btree", "set", "BTreeSet"]; pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"]; pub const CMP_MAX: [&str; 3] = ["core", "cmp", "max"]; @@ -45,7 +47,9 @@ pub const FROM_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "From pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; pub const HASH: [&str; 3] = ["core", "hash", "Hash"]; pub const HASHMAP: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"]; +pub const HASHMAP_CONTAINS_KEY: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "contains_key"]; pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"]; +pub const HASHMAP_INSERT: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "insert"]; pub const HASHSET: [&str; 5] = ["std", "collections", "hash", "set", "HashSet"]; #[cfg(feature = "internal-lints")] pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"]; diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index 2d794d48dc5..53180d1f9f5 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -66,6 +66,15 @@ pub fn indent_of(cx: &T, span: Span) -> Option { snippet_opt(cx, line_span(cx, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace())) } +/// Gets a snippet of the indentation of the line of a span +pub fn snippet_indent(cx: &T, span: Span) -> Option { + snippet_opt(cx, line_span(cx, span)).map(|mut s| { + let len = s.len() - s.trim_start().len(); + s.truncate(len); + s + }) +} + // If the snippet is empty, it's an attribute that was inserted during macro // expansion and we want to ignore those, because they could come from external // sources that the user has no control over. diff --git a/tests/ui/entry.fixed b/tests/ui/entry.fixed new file mode 100644 index 00000000000..60371c9833c --- /dev/null +++ b/tests/ui/entry.fixed @@ -0,0 +1,101 @@ +// run-rustfix + +#![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)] +#![warn(clippy::map_entry)] + +use std::collections::{BTreeMap, HashMap}; +use std::hash::Hash; + +macro_rules! m { + ($e:expr) => {{ $e }}; +} + +fn foo() {} + +fn hash_map(m: &mut HashMap, k: K, v: V, v2: V) { + m.entry(k).or_insert(v); + + if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { + if true { + e.insert(v); + } else { + e.insert(v2); + } + } + + if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { + if true { + e.insert(v); + } else { + e.insert(v2); + return; + } + } + + if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { + foo(); + e.insert(v); + } + + if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { + match 0 { + 1 if true => { + e.insert(v); + }, + _ => { + e.insert(v2); + }, + }; + } + + if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { + match 0 { + 0 => {}, + 1 => { + e.insert(v); + }, + _ => { + e.insert(v2); + }, + }; + } + + if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { + foo(); + match 0 { + 0 if false => { + e.insert(v); + }, + 1 => { + foo(); + e.insert(v); + }, + 2 | 3 => { + for _ in 0..2 { + foo(); + } + if true { + e.insert(v); + } else { + e.insert(v2); + }; + }, + _ => { + e.insert(v2); + }, + } + } + + if let std::collections::hash_map::Entry::Vacant(e) = m.entry(m!(k)) { + e.insert(m!(v)); + } +} + +fn btree_map(m: &mut BTreeMap, k: K, v: V, v2: V) { + if let std::collections::btree_map::Entry::Vacant(e) = m.entry(k) { + e.insert(v); + foo(); + } +} + +fn main() {} diff --git a/tests/ui/entry.rs b/tests/ui/entry.rs new file mode 100644 index 00000000000..4d3e241de78 --- /dev/null +++ b/tests/ui/entry.rs @@ -0,0 +1,103 @@ +// run-rustfix + +#![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)] +#![warn(clippy::map_entry)] + +use std::collections::{BTreeMap, HashMap}; +use std::hash::Hash; + +macro_rules! m { + ($e:expr) => {{ $e }}; +} + +fn foo() {} + +fn hash_map(m: &mut HashMap, k: K, v: V, v2: V) { + if !m.contains_key(&k) { + m.insert(k, v); + } + + if !m.contains_key(&k) { + if true { + m.insert(k, v); + } else { + m.insert(k, v2); + } + } + + if !m.contains_key(&k) { + if true { + m.insert(k, v); + } else { + m.insert(k, v2); + return; + } + } + + if !m.contains_key(&k) { + foo(); + m.insert(k, v); + } + + if !m.contains_key(&k) { + match 0 { + 1 if true => { + m.insert(k, v); + }, + _ => { + m.insert(k, v2); + }, + }; + } + + if !m.contains_key(&k) { + match 0 { + 0 => {}, + 1 => { + m.insert(k, v); + }, + _ => { + m.insert(k, v2); + }, + }; + } + + if !m.contains_key(&k) { + foo(); + match 0 { + 0 if false => { + m.insert(k, v); + }, + 1 => { + foo(); + m.insert(k, v); + }, + 2 | 3 => { + for _ in 0..2 { + foo(); + } + if true { + m.insert(k, v); + } else { + m.insert(k, v2); + }; + }, + _ => { + m.insert(k, v2); + }, + } + } + + if !m.contains_key(&m!(k)) { + m.insert(m!(k), m!(v)); + } +} + +fn btree_map(m: &mut BTreeMap, k: K, v: V, v2: V) { + if !m.contains_key(&k) { + m.insert(k, v); + foo(); + } +} + +fn main() {} diff --git a/tests/ui/entry.stderr b/tests/ui/entry.stderr new file mode 100644 index 00000000000..ab108ed6861 --- /dev/null +++ b/tests/ui/entry.stderr @@ -0,0 +1,171 @@ +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry.rs:16:5 + | +LL | / if !m.contains_key(&k) { +LL | | m.insert(k, v); +LL | | } + | |_____^ help: try this: `m.entry(k).or_insert(v);` + | + = note: `-D clippy::map-entry` implied by `-D warnings` + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry.rs:20:5 + | +LL | / if !m.contains_key(&k) { +LL | | if true { +LL | | m.insert(k, v); +LL | | } else { +LL | | m.insert(k, v2); +LL | | } +LL | | } + | |_____^ + | +help: try this + | +LL | if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { +LL | if true { +LL | e.insert(v); +LL | } else { +LL | e.insert(v2); +LL | } + ... + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry.rs:28:5 + | +LL | / if !m.contains_key(&k) { +LL | | if true { +LL | | m.insert(k, v); +LL | | } else { +... | +LL | | } +LL | | } + | |_____^ + | +help: try this + | +LL | if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { +LL | if true { +LL | e.insert(v); +LL | } else { +LL | e.insert(v2); +LL | return; + ... + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry.rs:37:5 + | +LL | / if !m.contains_key(&k) { +LL | | foo(); +LL | | m.insert(k, v); +LL | | } + | |_____^ + | +help: try this + | +LL | if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { +LL | foo(); +LL | e.insert(v); +LL | } + | + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry.rs:42:5 + | +LL | / if !m.contains_key(&k) { +LL | | match 0 { +LL | | 1 if true => { +LL | | m.insert(k, v); +... | +LL | | }; +LL | | } + | |_____^ + | +help: try this + | +LL | if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { +LL | match 0 { +LL | 1 if true => { +LL | e.insert(v); +LL | }, +LL | _ => { + ... + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry.rs:53:5 + | +LL | / if !m.contains_key(&k) { +LL | | match 0 { +LL | | 0 => {}, +LL | | 1 => { +... | +LL | | }; +LL | | } + | |_____^ + | +help: try this + | +LL | if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { +LL | match 0 { +LL | 0 => {}, +LL | 1 => { +LL | e.insert(v); +LL | }, + ... + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry.rs:65:5 + | +LL | / if !m.contains_key(&k) { +LL | | foo(); +LL | | match 0 { +LL | | 0 if false => { +... | +LL | | } +LL | | } + | |_____^ + | +help: try this + | +LL | if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { +LL | foo(); +LL | match 0 { +LL | 0 if false => { +LL | e.insert(v); +LL | }, + ... + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry.rs:91:5 + | +LL | / if !m.contains_key(&m!(k)) { +LL | | m.insert(m!(k), m!(v)); +LL | | } + | |_____^ + | +help: try this + | +LL | if let std::collections::hash_map::Entry::Vacant(e) = m.entry(m!(k)) { +LL | e.insert(m!(v)); +LL | } + | + +error: usage of `contains_key` followed by `insert` on a `BTreeMap` + --> $DIR/entry.rs:97:5 + | +LL | / if !m.contains_key(&k) { +LL | | m.insert(k, v); +LL | | foo(); +LL | | } + | |_____^ + | +help: try this + | +LL | if let std::collections::btree_map::Entry::Vacant(e) = m.entry(k) { +LL | e.insert(v); +LL | foo(); +LL | } + | + +error: aborting due to 9 previous errors + diff --git a/tests/ui/entry_fixable.fixed b/tests/ui/entry_fixable.fixed deleted file mode 100644 index dcdaae7e724..00000000000 --- a/tests/ui/entry_fixable.fixed +++ /dev/null @@ -1,15 +0,0 @@ -// run-rustfix - -#![allow(unused, clippy::needless_pass_by_value)] -#![warn(clippy::map_entry)] - -use std::collections::{BTreeMap, HashMap}; -use std::hash::Hash; - -fn foo() {} - -fn insert_if_absent0(m: &mut HashMap, k: K, v: V) { - m.entry(k).or_insert(v); -} - -fn main() {} diff --git a/tests/ui/entry_fixable.rs b/tests/ui/entry_fixable.rs deleted file mode 100644 index 55d5b21568d..00000000000 --- a/tests/ui/entry_fixable.rs +++ /dev/null @@ -1,17 +0,0 @@ -// run-rustfix - -#![allow(unused, clippy::needless_pass_by_value)] -#![warn(clippy::map_entry)] - -use std::collections::{BTreeMap, HashMap}; -use std::hash::Hash; - -fn foo() {} - -fn insert_if_absent0(m: &mut HashMap, k: K, v: V) { - if !m.contains_key(&k) { - m.insert(k, v); - } -} - -fn main() {} diff --git a/tests/ui/entry_fixable.stderr b/tests/ui/entry_fixable.stderr deleted file mode 100644 index 87403200ced..00000000000 --- a/tests/ui/entry_fixable.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry_fixable.rs:12:5 - | -LL | / if !m.contains_key(&k) { -LL | | m.insert(k, v); -LL | | } - | |_____^ help: consider using: `m.entry(k).or_insert(v);` - | - = note: `-D clippy::map-entry` implied by `-D warnings` - -error: aborting due to previous error - diff --git a/tests/ui/entry_unfixable.rs b/tests/ui/entry_unfixable.rs index f530fc023cf..beb2d5c97b1 100644 --- a/tests/ui/entry_unfixable.rs +++ b/tests/ui/entry_unfixable.rs @@ -4,50 +4,14 @@ use std::collections::{BTreeMap, HashMap}; use std::hash::Hash; -fn foo() {} - -fn insert_if_absent2(m: &mut HashMap, k: K, v: V) { - if !m.contains_key(&k) { - m.insert(k, v) - } else { - None - }; -} - -fn insert_if_present2(m: &mut HashMap, k: K, v: V) { - if m.contains_key(&k) { - None - } else { - m.insert(k, v) - }; -} - -fn insert_if_absent3(m: &mut HashMap, k: K, v: V) { - if !m.contains_key(&k) { - foo(); - m.insert(k, v) - } else { - None - }; -} - -fn insert_if_present3(m: &mut HashMap, k: K, v: V) { - if m.contains_key(&k) { - None - } else { - foo(); - m.insert(k, v) +macro_rules! m { + ($map:expr, $key:expr, $value:expr) => { + $map.insert($key, $value) }; + ($e:expr) => {{ $e }}; } -fn insert_in_btreemap(m: &mut BTreeMap, k: K, v: V) { - if !m.contains_key(&k) { - foo(); - m.insert(k, v) - } else { - None - }; -} +fn foo() {} // should not trigger fn insert_other_if_absent(m: &mut HashMap, k: K, o: K, v: V) { @@ -70,4 +34,17 @@ fn insert_from_different_map2(m: &mut HashMap, n: &mut Ha } } +fn insert_in_macro(m: &mut HashMap, k: K, v: V) { + if !m.contains_key(&k) { + m!(m, k, v); + } +} + +fn use_map_then_insert(m: &mut HashMap, k: K, v: V) { + if !m.contains_key(&k) { + let _ = m.len(); + m.insert(k, v); + } +} + fn main() {} diff --git a/tests/ui/entry_unfixable.stderr b/tests/ui/entry_unfixable.stderr deleted file mode 100644 index e58c8d22dc4..00000000000 --- a/tests/ui/entry_unfixable.stderr +++ /dev/null @@ -1,57 +0,0 @@ -error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry_unfixable.rs:10:5 - | -LL | / if !m.contains_key(&k) { -LL | | m.insert(k, v) -LL | | } else { -LL | | None -LL | | }; - | |_____^ consider using `m.entry(k)` - | - = note: `-D clippy::map-entry` implied by `-D warnings` - -error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry_unfixable.rs:18:5 - | -LL | / if m.contains_key(&k) { -LL | | None -LL | | } else { -LL | | m.insert(k, v) -LL | | }; - | |_____^ consider using `m.entry(k)` - -error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry_unfixable.rs:26:5 - | -LL | / if !m.contains_key(&k) { -LL | | foo(); -LL | | m.insert(k, v) -LL | | } else { -LL | | None -LL | | }; - | |_____^ consider using `m.entry(k)` - -error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry_unfixable.rs:35:5 - | -LL | / if m.contains_key(&k) { -LL | | None -LL | | } else { -LL | | foo(); -LL | | m.insert(k, v) -LL | | }; - | |_____^ consider using `m.entry(k)` - -error: usage of `contains_key` followed by `insert` on a `BTreeMap` - --> $DIR/entry_unfixable.rs:44:5 - | -LL | / if !m.contains_key(&k) { -LL | | foo(); -LL | | m.insert(k, v) -LL | | } else { -LL | | None -LL | | }; - | |_____^ consider using `m.entry(k)` - -error: aborting due to 5 previous errors - -- cgit 1.4.1-3-g733a5 From b63a5b56d6a37029970445ab5cbb77aeb5e19e84 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 25 Mar 2021 09:36:38 -0400 Subject: `map_entry` improvements Lint `if _.[!]contains_key(&_) { .. } else { .. }` so long as one of the branches contains an insertion. --- clippy_lints/src/entry.rs | 81 ++++++++++++++++++++++- tests/ui/entry_with_else.fixed | 73 +++++++++++++++++++++ tests/ui/entry_with_else.rs | 60 +++++++++++++++++ tests/ui/entry_with_else.stderr | 142 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 354 insertions(+), 2 deletions(-) create mode 100644 tests/ui/entry_with_else.fixed create mode 100644 tests/ui/entry_with_else.rs create mode 100644 tests/ui/entry_with_else.stderr (limited to 'tests') diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index ca01d0a7f87..3e1d70b2e40 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -1,7 +1,7 @@ use clippy_utils::{ diagnostics::span_lint_and_sugg, is_expr_final_block_expr, is_expr_used_or_unified, match_def_path, paths, peel_hir_expr_while, - source::{snippet_indent, snippet_with_applicability, snippet_with_context}, + source::{reindent_multiline, snippet_indent, snippet_with_applicability, snippet_with_context}, SpanlessEq, }; use rustc_errors::Applicability; @@ -75,7 +75,84 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { let mut app = Applicability::MachineApplicable; let map_str = snippet_with_context(cx, contains_expr.map.span, contains_expr.call_ctxt, "..", &mut app).0; let key_str = snippet_with_context(cx, contains_expr.key.span, contains_expr.call_ctxt, "..", &mut app).0; - let sugg = if !contains_expr.negated || else_expr.is_some() || then_search.insertions.is_empty() { + let sugg = if let Some(else_expr) = else_expr { + // if .. { .. } else { .. } + let else_search = match find_insert_calls(cx, &contains_expr, else_expr) { + Some(search) if !(then_search.insertions.is_empty() && search.insertions.is_empty()) => search, + _ => return, + }; + + if then_search.insertions.is_empty() || else_search.insertions.is_empty() { + // if .. { insert } else { .. } or if .. { .. } else { then } of + let (then_str, else_str, entry_kind) = if else_search.insertions.is_empty() { + if contains_expr.negated { + ( + then_search.snippet_vacant(cx, then_expr.span, &mut app), + snippet_with_applicability(cx, else_expr.span, "{ .. }", &mut app), + "Vacant(e)", + ) + } else { + ( + then_search.snippet_occupied(cx, then_expr.span, &mut app), + snippet_with_applicability(cx, else_expr.span, "{ .. }", &mut app), + "Occupied(mut e)", + ) + } + } else if contains_expr.negated { + ( + else_search.snippet_occupied(cx, else_expr.span, &mut app), + snippet_with_applicability(cx, then_expr.span, "{ .. }", &mut app), + "Occupied(mut e)", + ) + } else { + ( + else_search.snippet_vacant(cx, else_expr.span, &mut app), + snippet_with_applicability(cx, then_expr.span, "{ .. }", &mut app), + "Vacant(e)", + ) + }; + format!( + "if let {}::{} = {}.entry({}) {} else {}", + map_ty.entry_path(), + entry_kind, + map_str, + key_str, + then_str, + else_str, + ) + } else { + // if .. { insert } else { insert } + let (then_str, else_str, then_entry, else_entry) = if contains_expr.negated { + ( + then_search.snippet_vacant(cx, then_expr.span, &mut app), + else_search.snippet_occupied(cx, else_expr.span, &mut app), + "Vacant(e)", + "Occupied(mut e)", + ) + } else { + ( + then_search.snippet_occupied(cx, then_expr.span, &mut app), + else_search.snippet_vacant(cx, else_expr.span, &mut app), + "Occupied(mut e)", + "Vacant(e)", + ) + }; + let indent_str = snippet_indent(cx, expr.span); + let indent_str = indent_str.as_deref().unwrap_or(""); + format!( + "match {}.entry({}) {{\n{indent} {entry}::{} => {}\n\ + {indent} {entry}::{} => {}\n{indent}}}", + map_str, + key_str, + then_entry, + reindent_multiline(then_str.into(), true, Some(4 + indent_str.len())), + else_entry, + reindent_multiline(else_str.into(), true, Some(4 + indent_str.len())), + entry = map_ty.entry_path(), + indent = indent_str, + ) + } + } else if then_search.insertions.is_empty() || !contains_expr.negated { return; } else { // if .. { insert } diff --git a/tests/ui/entry_with_else.fixed b/tests/ui/entry_with_else.fixed new file mode 100644 index 00000000000..2332fa6313f --- /dev/null +++ b/tests/ui/entry_with_else.fixed @@ -0,0 +1,73 @@ +// run-rustfix + +#![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)] +#![warn(clippy::map_entry)] + +use std::collections::{BTreeMap, HashMap}; +use std::hash::Hash; + +macro_rules! m { + ($e:expr) => {{ $e }}; +} + +fn foo() {} + +fn insert_if_absent0(m: &mut HashMap, k: K, v: V, v2: V) { + match m.entry(k) { + std::collections::hash_map::Entry::Vacant(e) => { + e.insert(v); + } + std::collections::hash_map::Entry::Occupied(mut e) => { + e.insert(v2); + } + } + + match m.entry(k) { + std::collections::hash_map::Entry::Occupied(mut e) => { + e.insert(v); + } + std::collections::hash_map::Entry::Vacant(e) => { + e.insert(v2); + } + } + + if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { + e.insert(v); + } else { + foo(); + } + + if let std::collections::hash_map::Entry::Occupied(mut e) = m.entry(k) { + e.insert(v); + } else { + foo(); + } + + match m.entry(k) { + std::collections::hash_map::Entry::Vacant(e) => { + e.insert(v); + } + std::collections::hash_map::Entry::Occupied(mut e) => { + e.insert(v2); + } + } + + match m.entry(k) { + std::collections::hash_map::Entry::Occupied(mut e) => { + if true { Some(e.insert(v)) } else { Some(e.insert(v2)) } + } + std::collections::hash_map::Entry::Vacant(e) => { + e.insert(v); + None + } + }; + + if let std::collections::hash_map::Entry::Occupied(mut e) = m.entry(k) { + foo(); + Some(e.insert(v)) + } else { + None + }; +} + +fn main() {} diff --git a/tests/ui/entry_with_else.rs b/tests/ui/entry_with_else.rs new file mode 100644 index 00000000000..2ff0c038efe --- /dev/null +++ b/tests/ui/entry_with_else.rs @@ -0,0 +1,60 @@ +// run-rustfix + +#![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)] +#![warn(clippy::map_entry)] + +use std::collections::{BTreeMap, HashMap}; +use std::hash::Hash; + +macro_rules! m { + ($e:expr) => {{ $e }}; +} + +fn foo() {} + +fn insert_if_absent0(m: &mut HashMap, k: K, v: V, v2: V) { + if !m.contains_key(&k) { + m.insert(k, v); + } else { + m.insert(k, v2); + } + + if m.contains_key(&k) { + m.insert(k, v); + } else { + m.insert(k, v2); + } + + if !m.contains_key(&k) { + m.insert(k, v); + } else { + foo(); + } + + if !m.contains_key(&k) { + foo(); + } else { + m.insert(k, v); + } + + if !m.contains_key(&k) { + m.insert(k, v); + } else { + m.insert(k, v2); + } + + if m.contains_key(&k) { + if true { m.insert(k, v) } else { m.insert(k, v2) } + } else { + m.insert(k, v) + }; + + if m.contains_key(&k) { + foo(); + m.insert(k, v) + } else { + None + }; +} + +fn main() {} diff --git a/tests/ui/entry_with_else.stderr b/tests/ui/entry_with_else.stderr new file mode 100644 index 00000000000..6f62ff8d374 --- /dev/null +++ b/tests/ui/entry_with_else.stderr @@ -0,0 +1,142 @@ +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry_with_else.rs:16:5 + | +LL | / if !m.contains_key(&k) { +LL | | m.insert(k, v); +LL | | } else { +LL | | m.insert(k, v2); +LL | | } + | |_____^ + | + = note: `-D clippy::map-entry` implied by `-D warnings` +help: try this + | +LL | match m.entry(k) { +LL | std::collections::hash_map::Entry::Vacant(e) => { +LL | e.insert(v); +LL | } +LL | std::collections::hash_map::Entry::Occupied(mut e) => { +LL | e.insert(v2); + ... + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry_with_else.rs:22:5 + | +LL | / if m.contains_key(&k) { +LL | | m.insert(k, v); +LL | | } else { +LL | | m.insert(k, v2); +LL | | } + | |_____^ + | +help: try this + | +LL | match m.entry(k) { +LL | std::collections::hash_map::Entry::Occupied(mut e) => { +LL | e.insert(v); +LL | } +LL | std::collections::hash_map::Entry::Vacant(e) => { +LL | e.insert(v2); + ... + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry_with_else.rs:28:5 + | +LL | / if !m.contains_key(&k) { +LL | | m.insert(k, v); +LL | | } else { +LL | | foo(); +LL | | } + | |_____^ + | +help: try this + | +LL | if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { +LL | e.insert(v); +LL | } else { +LL | foo(); +LL | } + | + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry_with_else.rs:34:5 + | +LL | / if !m.contains_key(&k) { +LL | | foo(); +LL | | } else { +LL | | m.insert(k, v); +LL | | } + | |_____^ + | +help: try this + | +LL | if let std::collections::hash_map::Entry::Occupied(mut e) = m.entry(k) { +LL | e.insert(v); +LL | } else { +LL | foo(); +LL | } + | + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry_with_else.rs:40:5 + | +LL | / if !m.contains_key(&k) { +LL | | m.insert(k, v); +LL | | } else { +LL | | m.insert(k, v2); +LL | | } + | |_____^ + | +help: try this + | +LL | match m.entry(k) { +LL | std::collections::hash_map::Entry::Vacant(e) => { +LL | e.insert(v); +LL | } +LL | std::collections::hash_map::Entry::Occupied(mut e) => { +LL | e.insert(v2); + ... + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry_with_else.rs:46:5 + | +LL | / if m.contains_key(&k) { +LL | | if true { m.insert(k, v) } else { m.insert(k, v2) } +LL | | } else { +LL | | m.insert(k, v) +LL | | }; + | |_____^ + | +help: try this + | +LL | match m.entry(k) { +LL | std::collections::hash_map::Entry::Occupied(mut e) => { +LL | if true { Some(e.insert(v)) } else { Some(e.insert(v2)) } +LL | } +LL | std::collections::hash_map::Entry::Vacant(e) => { +LL | e.insert(v); + ... + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry_with_else.rs:52:5 + | +LL | / if m.contains_key(&k) { +LL | | foo(); +LL | | m.insert(k, v) +LL | | } else { +LL | | None +LL | | }; + | |_____^ + | +help: try this + | +LL | if let std::collections::hash_map::Entry::Occupied(mut e) = m.entry(k) { +LL | foo(); +LL | Some(e.insert(v)) +LL | } else { +LL | None +LL | }; + | + +error: aborting due to 7 previous errors + -- cgit 1.4.1-3-g733a5 From 3323ff71455c763a03353527d6203f90b53058fd Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 25 Mar 2021 09:47:56 -0400 Subject: `map_entry` improvements Suggest using `or_insert_with` when possible --- clippy_lints/src/entry.rs | 220 ++++++++++++++++++++++++++++++++--------- clippy_lints/src/manual_map.rs | 53 +--------- clippy_utils/src/lib.rs | 71 ++++++++++++- tests/ui/entry.fixed | 52 +++++----- tests/ui/entry.rs | 8 ++ tests/ui/entry.stderr | 65 +++++++----- 6 files changed, 322 insertions(+), 147 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index 3e1d70b2e40..0decb22a491 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -1,4 +1,5 @@ use clippy_utils::{ + can_move_expr_to_closure_no_visit, diagnostics::span_lint_and_sugg, is_expr_final_block_expr, is_expr_used_or_unified, match_def_path, paths, peel_hir_expr_while, source::{reindent_multiline, snippet_indent, snippet_with_applicability, snippet_with_context}, @@ -7,7 +8,7 @@ use clippy_utils::{ use rustc_errors::Applicability; use rustc_hir::{ intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor}, - Expr, ExprKind, Guard, Local, Stmt, StmtKind, UnOp, + Block, Expr, ExprKind, Guard, HirId, Local, Stmt, StmtKind, UnOp, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -78,13 +79,13 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { let sugg = if let Some(else_expr) = else_expr { // if .. { .. } else { .. } let else_search = match find_insert_calls(cx, &contains_expr, else_expr) { - Some(search) if !(then_search.insertions.is_empty() && search.insertions.is_empty()) => search, + Some(search) if !(then_search.edits.is_empty() && search.edits.is_empty()) => search, _ => return, }; - if then_search.insertions.is_empty() || else_search.insertions.is_empty() { + if then_search.edits.is_empty() || else_search.edits.is_empty() { // if .. { insert } else { .. } or if .. { .. } else { then } of - let (then_str, else_str, entry_kind) = if else_search.insertions.is_empty() { + let (then_str, else_str, entry_kind) = if else_search.edits.is_empty() { if contains_expr.negated { ( then_search.snippet_vacant(cx, then_expr.span, &mut app), @@ -152,37 +153,48 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { indent = indent_str, ) } - } else if then_search.insertions.is_empty() || !contains_expr.negated { + } else if then_search.edits.is_empty() { + // no insertions return; } else { // if .. { insert } - match then_search.as_single_insertion() { - Some(insertion) if !insertion.value.can_have_side_effects() => { - format!( - "{}.entry({}).or_insert({});", - map_str, - key_str, - snippet_with_context(cx, insertion.value.span, insertion.call.span.ctxt(), "..", &mut app).0, + if !then_search.allow_insert_closure { + let (body_str, entry_kind) = if contains_expr.negated { + (then_search.snippet_vacant(cx, then_expr.span, &mut app), "Vacant(e)") + } else { + ( + then_search.snippet_occupied(cx, then_expr.span, &mut app), + "Occupied(mut e)", ) - }, - _ => { - let (body_str, entry_kind) = if contains_expr.negated { - (then_search.snippet_vacant(cx, then_expr.span, &mut app), "Vacant(e)") + }; + format!( + "if let {}::{} = {}.entry({}) {}", + map_ty.entry_path(), + entry_kind, + map_str, + key_str, + body_str, + ) + } else if let Some(insertion) = then_search.as_single_insertion() { + let value_str = snippet_with_context(cx, insertion.value.span, then_expr.span.ctxt(), "..", &mut app).0; + if contains_expr.negated { + if insertion.value.can_have_side_effects() { + format!("{}.entry({}).or_insert_with(|| {});", map_str, key_str, value_str) } else { - ( - then_search.snippet_occupied(cx, then_expr.span, &mut app), - "Occupied(mut e)", - ) - }; - format!( - "if let {}::{} = {}.entry({}) {}", - map_ty.entry_path(), - entry_kind, - map_str, - key_str, - body_str, - ) - }, + format!("{}.entry({}).or_insert({});", map_str, key_str, value_str) + } + } else { + // Todo: if let Some(v) = map.get_mut(k) + return; + } + } else { + let block_str = then_search.snippet_closure(cx, then_expr.span, &mut app); + if contains_expr.negated { + format!("{}.entry({}).or_insert_with(|| {});", map_str, key_str, block_str) + } else { + // Todo: if let Some(v) = map.get_mut(k) + return; + } } }; @@ -281,6 +293,19 @@ fn try_parse_insert(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option { + /// A semicolon that needs to be removed. Used to create a closure for `insert_with`. + RemoveSemi(Span), + /// An insertion into the map. + Insertion(Insertion<'tcx>), +} +impl Edit<'tcx> { + fn as_insertion(self) -> Option> { + if let Self::Insertion(i) = self { Some(i) } else { None } + } +} #[derive(Clone, Copy)] struct Insertion<'tcx> { call: &'tcx Expr<'tcx>, @@ -303,12 +328,17 @@ struct InsertSearcher<'cx, 'i, 'tcx> { key: &'tcx Expr<'tcx>, /// The context of the top level block. All insert calls must be in the same context. ctxt: SyntaxContext, + /// Whether this expression can be safely moved into a closure. + allow_insert_closure: bool, /// Whether this expression can use the entry api. can_use_entry: bool, + /// Whether this expression is the final expression in this code path. This may be a statement. + in_tail_pos: bool, // A single insert expression has a slightly different suggestion. is_single_insert: bool, is_map_used: bool, - insertions: &'i mut Vec>, + edits: &'i mut Vec>, + loops: Vec, } impl<'tcx> InsertSearcher<'_, '_, 'tcx> { /// Visit the expression as a branch in control flow. Multiple insert calls can be used, but @@ -316,11 +346,22 @@ impl<'tcx> InsertSearcher<'_, '_, 'tcx> { /// given expression. fn visit_cond_arm(&mut self, e: &'tcx Expr<'_>) -> bool { let is_map_used = self.is_map_used; + let in_tail_pos = self.in_tail_pos; self.visit_expr(e); let res = self.is_map_used; self.is_map_used = is_map_used; + self.in_tail_pos = in_tail_pos; res } + + /// Visits an expression which is not itself in a tail position, but other sibling expressions + /// may be. e.g. if conditions + fn visit_non_tail_expr(&mut self, e: &'tcx Expr<'_>) { + let in_tail_pos = self.in_tail_pos; + self.in_tail_pos = false; + self.visit_expr(e); + self.in_tail_pos = in_tail_pos; + } } impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, '_, 'tcx> { type Map = ErasedMap<'tcx>; @@ -330,17 +371,63 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, '_, 'tcx> { fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) { match stmt.kind { - StmtKind::Semi(e) | StmtKind::Expr(e) => self.visit_expr(e), + StmtKind::Semi(e) => { + self.visit_expr(e); + + if self.in_tail_pos && self.allow_insert_closure { + // The spans are used to slice the top level expression into multiple parts. This requires that + // they all come from the same part of the source code. + if stmt.span.ctxt() == self.ctxt && e.span.ctxt() == self.ctxt { + self.edits + .push(Edit::RemoveSemi(stmt.span.trim_start(e.span).unwrap_or(DUMMY_SP))); + } else { + self.allow_insert_closure = false; + } + } + }, + StmtKind::Expr(e) => self.visit_expr(e), StmtKind::Local(Local { init: Some(e), .. }) => { + self.allow_insert_closure &= !self.in_tail_pos; + self.in_tail_pos = false; self.is_single_insert = false; self.visit_expr(e); }, _ => { + self.allow_insert_closure &= !self.in_tail_pos; self.is_single_insert = false; }, } } + fn visit_block(&mut self, block: &'tcx Block<'_>) { + // If the block is in a tail position, then the last expression (possibly a statement) is in the + // tail position. The rest, however, are not. + match (block.stmts, block.expr) { + ([], None) => { + self.allow_insert_closure &= !self.in_tail_pos; + }, + ([], Some(expr)) => self.visit_expr(expr), + (stmts, Some(expr)) => { + let in_tail_pos = self.in_tail_pos; + self.in_tail_pos = false; + for stmt in stmts { + self.visit_stmt(stmt); + } + self.in_tail_pos = in_tail_pos; + self.visit_expr(expr); + }, + ([stmts @ .., stmt], None) => { + let in_tail_pos = self.in_tail_pos; + self.in_tail_pos = false; + for stmt in stmts { + self.visit_stmt(stmt); + } + self.in_tail_pos = in_tail_pos; + self.visit_stmt(stmt); + }, + } + } + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { if !self.can_use_entry { return; @@ -357,15 +444,16 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, '_, 'tcx> { return; } - self.insertions.push(Insertion { + self.edits.push(Edit::Insertion(Insertion { call: expr, value: insert_expr.value, - }); + })); self.is_map_used = true; + self.allow_insert_closure &= self.in_tail_pos; // The value doesn't affect whether there is only a single insert expression. let is_single_insert = self.is_single_insert; - self.visit_expr(insert_expr.value); + self.visit_non_tail_expr(insert_expr.value); self.is_single_insert = is_single_insert; }, _ if SpanlessEq::new(self.cx).eq_expr(self.map, expr) => { @@ -374,7 +462,7 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, '_, 'tcx> { _ => match expr.kind { ExprKind::If(cond_expr, then_expr, Some(else_expr)) => { self.is_single_insert = false; - self.visit_expr(cond_expr); + self.visit_non_tail_expr(cond_expr); // Each branch may contain it's own insert expression. let mut is_map_used = self.visit_cond_arm(then_expr); is_map_used |= self.visit_cond_arm(else_expr); @@ -382,31 +470,38 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, '_, 'tcx> { }, ExprKind::Match(scrutinee_expr, arms, _) => { self.is_single_insert = false; - self.visit_expr(scrutinee_expr); + self.visit_non_tail_expr(scrutinee_expr); // Each branch may contain it's own insert expression. let mut is_map_used = self.is_map_used; for arm in arms { if let Some(Guard::If(guard) | Guard::IfLet(_, guard)) = arm.guard { - self.visit_expr(guard) + self.visit_non_tail_expr(guard) } is_map_used |= self.visit_cond_arm(arm.body); } self.is_map_used = is_map_used; }, ExprKind::Loop(block, ..) => { + self.loops.push(expr.hir_id); + self.allow_insert_closure &= !self.in_tail_pos; // Don't allow insertions inside of a loop. - let insertions_len = self.insertions.len(); + let edit_len = self.edits.len(); self.visit_block(block); - if self.insertions.len() != insertions_len { + if self.edits.len() != edit_len { self.can_use_entry = false; } + self.loops.pop(); }, ExprKind::Block(block, _) => self.visit_block(block), ExprKind::InlineAsm(_) | ExprKind::LlvmInlineAsm(_) => { self.can_use_entry = false; }, _ => { + self.allow_insert_closure &= !self.in_tail_pos; + self.allow_insert_closure &= can_move_expr_to_closure_no_visit(self.cx, expr, &self.loops); + // Sub expressions are no longer in the tail position. self.is_single_insert = false; + self.in_tail_pos = false; walk_expr(self, expr); }, }, @@ -415,18 +510,19 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, '_, 'tcx> { } struct InsertSearchResults<'tcx> { - insertions: Vec>, + edits: Vec>, + allow_insert_closure: bool, is_single_insert: bool, } impl InsertSearchResults<'tcx> { fn as_single_insertion(&self) -> Option> { - self.is_single_insert.then(|| self.insertions[0]) + self.is_single_insert.then(|| self.edits[0].as_insertion().unwrap()) } fn snippet_occupied(&self, cx: &LateContext<'_>, mut span: Span, app: &mut Applicability) -> String { let ctxt = span.ctxt(); let mut res = String::new(); - for insertion in self.insertions.iter() { + for insertion in self.edits.iter().filter_map(|e| e.as_insertion()) { res.push_str(&snippet_with_applicability( cx, span.until(insertion.call.span), @@ -451,7 +547,7 @@ impl InsertSearchResults<'tcx> { fn snippet_vacant(&self, cx: &LateContext<'_>, mut span: Span, app: &mut Applicability) -> String { let ctxt = span.ctxt(); let mut res = String::new(); - for insertion in self.insertions.iter() { + for insertion in self.edits.iter().filter_map(|e| e.as_insertion()) { res.push_str(&snippet_with_applicability( cx, span.until(insertion.call.span), @@ -485,27 +581,57 @@ impl InsertSearchResults<'tcx> { res.push_str(&snippet_with_applicability(cx, span, "..", app)); res } + + fn snippet_closure(&self, cx: &LateContext<'_>, mut span: Span, app: &mut Applicability) -> String { + let ctxt = span.ctxt(); + let mut res = String::new(); + for edit in &self.edits { + match *edit { + Edit::Insertion(insertion) => { + res.push_str(&snippet_with_applicability( + cx, + span.until(insertion.call.span), + "..", + app, + )); + res.push_str(&snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0); + span = span.trim_start(insertion.call.span).unwrap_or(DUMMY_SP); + }, + Edit::RemoveSemi(semi_span) => { + res.push_str(&snippet_with_applicability(cx, span.until(semi_span), "..", app)); + span = span.trim_start(semi_span).unwrap_or(DUMMY_SP); + }, + } + } + res.push_str(&snippet_with_applicability(cx, span, "..", app)); + res + } } fn find_insert_calls( cx: &LateContext<'tcx>, contains_expr: &ContainsExpr<'tcx>, expr: &'tcx Expr<'_>, ) -> Option> { - let mut insertions = Vec::new(); + let mut edits = Vec::new(); let mut s = InsertSearcher { cx, map: contains_expr.map, key: contains_expr.key, ctxt: expr.span.ctxt(), - insertions: &mut insertions, + edits: &mut edits, is_map_used: false, + allow_insert_closure: true, can_use_entry: true, + in_tail_pos: true, is_single_insert: true, + loops: Vec::new(), }; s.visit_expr(expr); + let allow_insert_closure = s.allow_insert_closure; let is_single_insert = s.is_single_insert; s.can_use_entry.then(|| InsertSearchResults { - insertions, + edits, + allow_insert_closure, is_single_insert, }) } diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs index 29be0739977..3f8b976fcac 100644 --- a/clippy_lints/src/manual_map.rs +++ b/clippy_lints/src/manual_map.rs @@ -1,15 +1,13 @@ use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; -use clippy_utils::ty::{can_partially_move_ty, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; -use clippy_utils::{in_constant, is_allowed, is_else_clause, is_lang_ctor, match_var, peel_hir_expr_refs}; +use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; +use clippy_utils::{can_move_expr_to_closure, in_constant, is_allowed, is_else_clause, is_lang_ctor, match_var, peel_hir_expr_refs}; use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{ - def::Res, - intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor}, - Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind, Path, QPath, + Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind, QPath, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -193,51 +191,6 @@ impl LateLintPass<'_> for ManualMap { } } -// Checks if the expression can be moved into a closure as is. -fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { - struct V<'cx, 'tcx> { - cx: &'cx LateContext<'tcx>, - make_closure: bool, - } - impl Visitor<'tcx> for V<'_, 'tcx> { - type Map = ErasedMap<'tcx>; - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - - fn visit_expr(&mut self, e: &'tcx Expr<'_>) { - match e.kind { - ExprKind::Break(..) - | ExprKind::Continue(_) - | ExprKind::Ret(_) - | ExprKind::Yield(..) - | ExprKind::InlineAsm(_) - | ExprKind::LlvmInlineAsm(_) => { - self.make_closure = false; - }, - // Accessing a field of a local value can only be done if the type isn't - // partially moved. - ExprKind::Field(base_expr, _) - if matches!( - base_expr.kind, - ExprKind::Path(QPath::Resolved(_, Path { res: Res::Local(_), .. })) - ) && can_partially_move_ty(self.cx, self.cx.typeck_results().expr_ty(base_expr)) => - { - // TODO: check if the local has been partially moved. Assume it has for now. - self.make_closure = false; - return; - } - _ => (), - }; - walk_expr(self, e); - } - } - - let mut v = V { cx, make_closure: true }; - v.visit_expr(expr); - v.make_closure -} - // Checks whether the expression could be passed as a function, or whether a closure is needed. // Returns the function to be passed to `map` if it exists. fn can_pass_as_func(cx: &LateContext<'tcx>, binding: Ident, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 0abee49f40c..5d6c1337be6 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -60,7 +60,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; -use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::intravisit::{self, walk_expr, ErasedMap, NestedVisitorMap, Visitor}; use rustc_hir::LangItem::{ResultErr, ResultOk}; use rustc_hir::{ def, Arm, BindingAnnotation, Block, Body, Constness, Destination, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl, @@ -82,7 +82,7 @@ use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::Integer; use crate::consts::{constant, Constant}; -use crate::ty::is_recursively_primitive_type; +use crate::ty::{can_partially_move_ty, is_recursively_primitive_type}; pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Option { if let Ok(version) = RustcVersion::parse(msrv) { @@ -539,6 +539,73 @@ pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio None } +/// Checks if the top level expression can be moved into a closure as is. +pub fn can_move_expr_to_closure_no_visit(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, jump_targets: &[HirId]) -> bool { + match expr.kind { + ExprKind::Break(Destination { target_id: Ok(id), .. }, _) + | ExprKind::Continue(Destination { target_id: Ok(id), .. }) + if jump_targets.contains(&id) => + { + true + }, + ExprKind::Break(..) + | ExprKind::Continue(_) + | ExprKind::Ret(_) + | ExprKind::Yield(..) + | ExprKind::InlineAsm(_) + | ExprKind::LlvmInlineAsm(_) => false, + // Accessing a field of a local value can only be done if the type isn't + // partially moved. + ExprKind::Field(base_expr, _) + if matches!( + base_expr.kind, + ExprKind::Path(QPath::Resolved(_, Path { res: Res::Local(_), .. })) + ) && can_partially_move_ty(cx, cx.typeck_results().expr_ty(base_expr)) => + { + // TODO: check if the local has been partially moved. Assume it has for now. + false + } + _ => true, + } +} + +/// Checks if the expression can be moved into a closure as is. +pub fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { + struct V<'cx, 'tcx> { + cx: &'cx LateContext<'tcx>, + loops: Vec, + allow_closure: bool, + } + impl Visitor<'tcx> for V<'_, 'tcx> { + type Map = ErasedMap<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + + fn visit_expr(&mut self, e: &'tcx Expr<'_>) { + if !self.allow_closure { + return; + } + if let ExprKind::Loop(b, ..) = e.kind { + self.loops.push(e.hir_id); + self.visit_block(b); + self.loops.pop(); + } else { + self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops); + walk_expr(self, e); + } + } + } + + let mut v = V { + cx, + allow_closure: true, + loops: Vec::new(), + }; + v.visit_expr(expr); + v.allow_closure +} + /// Returns the method names and argument list of nested method call expressions that make up /// `expr`. method/span lists are sorted with the most recent call first. pub fn method_calls<'tcx>( diff --git a/tests/ui/entry.fixed b/tests/ui/entry.fixed index 60371c9833c..524f2132797 100644 --- a/tests/ui/entry.fixed +++ b/tests/ui/entry.fixed @@ -15,13 +15,21 @@ fn foo() {} fn hash_map(m: &mut HashMap, k: K, v: V, v2: V) { m.entry(k).or_insert(v); - if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { + m.entry(k).or_insert_with(|| { if true { - e.insert(v); + v } else { - e.insert(v2); + v2 } - } + }); + + m.entry(k).or_insert_with(|| { + if true { + v + } else { + v2 + } + }); if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { if true { @@ -32,21 +40,21 @@ fn hash_map(m: &mut HashMap, k: K, v: V, v2: } } - if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { + m.entry(k).or_insert_with(|| { foo(); - e.insert(v); - } + v + }); - if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { + m.entry(k).or_insert_with(|| { match 0 { 1 if true => { - e.insert(v); + v }, _ => { - e.insert(v2); + v2 }, - }; - } + } + }); if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { match 0 { @@ -60,35 +68,33 @@ fn hash_map(m: &mut HashMap, k: K, v: V, v2: }; } - if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { + m.entry(k).or_insert_with(|| { foo(); match 0 { 0 if false => { - e.insert(v); + v }, 1 => { foo(); - e.insert(v); + v }, 2 | 3 => { for _ in 0..2 { foo(); } if true { - e.insert(v); + v } else { - e.insert(v2); - }; + v2 + } }, _ => { - e.insert(v2); + v2 }, } - } + }); - if let std::collections::hash_map::Entry::Vacant(e) = m.entry(m!(k)) { - e.insert(m!(v)); - } + m.entry(m!(k)).or_insert_with(|| m!(v)); } fn btree_map(m: &mut BTreeMap, k: K, v: V, v2: V) { diff --git a/tests/ui/entry.rs b/tests/ui/entry.rs index 4d3e241de78..ff4890eeeb6 100644 --- a/tests/ui/entry.rs +++ b/tests/ui/entry.rs @@ -25,6 +25,14 @@ fn hash_map(m: &mut HashMap, k: K, v: V, v2: } } + if !m.contains_key(&k) { + if true { + m.insert(k, v) + } else { + m.insert(k, v2) + }; + } + if !m.contains_key(&k) { if true { m.insert(k, v); diff --git a/tests/ui/entry.stderr b/tests/ui/entry.stderr index ab108ed6861..63b5f5a0b2c 100644 --- a/tests/ui/entry.stderr +++ b/tests/ui/entry.stderr @@ -22,11 +22,11 @@ LL | | } | help: try this | -LL | if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { +LL | m.entry(k).or_insert_with(|| { LL | if true { -LL | e.insert(v); +LL | v LL | } else { -LL | e.insert(v2); +LL | v2 LL | } ... @@ -35,6 +35,28 @@ error: usage of `contains_key` followed by `insert` on a `HashMap` | LL | / if !m.contains_key(&k) { LL | | if true { +LL | | m.insert(k, v) +LL | | } else { +LL | | m.insert(k, v2) +LL | | }; +LL | | } + | |_____^ + | +help: try this + | +LL | m.entry(k).or_insert_with(|| { +LL | if true { +LL | v +LL | } else { +LL | v2 +LL | } + ... + +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry.rs:36:5 + | +LL | / if !m.contains_key(&k) { +LL | | if true { LL | | m.insert(k, v); LL | | } else { ... | @@ -53,7 +75,7 @@ LL | return; ... error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:37:5 + --> $DIR/entry.rs:45:5 | LL | / if !m.contains_key(&k) { LL | | foo(); @@ -63,14 +85,14 @@ LL | | } | help: try this | -LL | if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { +LL | m.entry(k).or_insert_with(|| { LL | foo(); -LL | e.insert(v); -LL | } +LL | v +LL | }); | error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:42:5 + --> $DIR/entry.rs:50:5 | LL | / if !m.contains_key(&k) { LL | | match 0 { @@ -83,16 +105,16 @@ LL | | } | help: try this | -LL | if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { +LL | m.entry(k).or_insert_with(|| { LL | match 0 { LL | 1 if true => { -LL | e.insert(v); +LL | v LL | }, LL | _ => { ... error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:53:5 + --> $DIR/entry.rs:61:5 | LL | / if !m.contains_key(&k) { LL | | match 0 { @@ -114,7 +136,7 @@ LL | }, ... error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:65:5 + --> $DIR/entry.rs:73:5 | LL | / if !m.contains_key(&k) { LL | | foo(); @@ -127,31 +149,24 @@ LL | | } | help: try this | -LL | if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { +LL | m.entry(k).or_insert_with(|| { LL | foo(); LL | match 0 { LL | 0 if false => { -LL | e.insert(v); +LL | v LL | }, ... error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:91:5 + --> $DIR/entry.rs:99:5 | LL | / if !m.contains_key(&m!(k)) { LL | | m.insert(m!(k), m!(v)); LL | | } - | |_____^ - | -help: try this - | -LL | if let std::collections::hash_map::Entry::Vacant(e) = m.entry(m!(k)) { -LL | e.insert(m!(v)); -LL | } - | + | |_____^ help: try this: `m.entry(m!(k)).or_insert_with(|| m!(v));` error: usage of `contains_key` followed by `insert` on a `BTreeMap` - --> $DIR/entry.rs:97:5 + --> $DIR/entry.rs:105:5 | LL | / if !m.contains_key(&k) { LL | | m.insert(k, v); @@ -167,5 +182,5 @@ LL | foo(); LL | } | -error: aborting due to 9 previous errors +error: aborting due to 10 previous errors -- cgit 1.4.1-3-g733a5 From bcf348800751e8a03fdbaa49130e53a463e3a441 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 29 Mar 2021 20:17:03 -0400 Subject: Minor cleanup of `map_entry` and a few additional tests. --- clippy_lints/src/entry.rs | 187 ++++++++++++++++++------------------ clippy_lints/src/manual_map.rs | 8 +- clippy_utils/src/lib.rs | 59 +++--------- tests/ui/entry.fixed | 58 ++++++++++- tests/ui/entry.rs | 58 ++++++++++- tests/ui/entry.stderr | 30 +++--- tests/ui/entry_unfixable.rs | 50 ---------- tests/ui/string_lit_as_bytes.fixed | 2 +- tests/ui/string_lit_as_bytes.rs | 2 +- tests/ui/string_lit_as_bytes.stderr | 4 +- 10 files changed, 238 insertions(+), 220 deletions(-) delete mode 100644 tests/ui/entry_unfixable.rs (limited to 'tests') diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index 0decb22a491..8db5050a5ac 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -77,40 +77,33 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { let map_str = snippet_with_context(cx, contains_expr.map.span, contains_expr.call_ctxt, "..", &mut app).0; let key_str = snippet_with_context(cx, contains_expr.key.span, contains_expr.call_ctxt, "..", &mut app).0; let sugg = if let Some(else_expr) = else_expr { - // if .. { .. } else { .. } let else_search = match find_insert_calls(cx, &contains_expr, else_expr) { - Some(search) if !(then_search.edits.is_empty() && search.edits.is_empty()) => search, - _ => return, + Some(search) => search, + None => return, }; - if then_search.edits.is_empty() || else_search.edits.is_empty() { - // if .. { insert } else { .. } or if .. { .. } else { then } of - let (then_str, else_str, entry_kind) = if else_search.edits.is_empty() { - if contains_expr.negated { - ( - then_search.snippet_vacant(cx, then_expr.span, &mut app), - snippet_with_applicability(cx, else_expr.span, "{ .. }", &mut app), - "Vacant(e)", - ) - } else { - ( - then_search.snippet_occupied(cx, then_expr.span, &mut app), - snippet_with_applicability(cx, else_expr.span, "{ .. }", &mut app), - "Occupied(mut e)", - ) - } - } else if contains_expr.negated { - ( + if then_search.edits.is_empty() && else_search.edits.is_empty() { + // No insertions + return; + } else if then_search.edits.is_empty() || else_search.edits.is_empty() { + // if .. { insert } else { .. } or if .. { .. } else { insert } + let ((then_str, entry_kind), else_str) = match (else_search.edits.is_empty(), contains_expr.negated) { + (true, true) => ( + then_search.snippet_vacant(cx, then_expr.span, &mut app), + snippet_with_applicability(cx, else_expr.span, "{ .. }", &mut app), + ), + (true, false) => ( + then_search.snippet_occupied(cx, then_expr.span, &mut app), + snippet_with_applicability(cx, else_expr.span, "{ .. }", &mut app), + ), + (false, true) => ( else_search.snippet_occupied(cx, else_expr.span, &mut app), snippet_with_applicability(cx, then_expr.span, "{ .. }", &mut app), - "Occupied(mut e)", - ) - } else { - ( + ), + (false, false) => ( else_search.snippet_vacant(cx, else_expr.span, &mut app), snippet_with_applicability(cx, then_expr.span, "{ .. }", &mut app), - "Vacant(e)", - ) + ), }; format!( "if let {}::{} = {}.entry({}) {} else {}", @@ -123,19 +116,15 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { ) } else { // if .. { insert } else { insert } - let (then_str, else_str, then_entry, else_entry) = if contains_expr.negated { + let ((then_str, then_entry), (else_str, else_entry)) = if contains_expr.negated { ( then_search.snippet_vacant(cx, then_expr.span, &mut app), else_search.snippet_occupied(cx, else_expr.span, &mut app), - "Vacant(e)", - "Occupied(mut e)", ) } else { ( then_search.snippet_occupied(cx, then_expr.span, &mut app), else_search.snippet_vacant(cx, else_expr.span, &mut app), - "Occupied(mut e)", - "Vacant(e)", ) }; let indent_str = snippet_indent(cx, expr.span); @@ -153,19 +142,18 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { indent = indent_str, ) } - } else if then_search.edits.is_empty() { - // no insertions - return; } else { + if then_search.edits.is_empty() { + // no insertions + return; + } + // if .. { insert } if !then_search.allow_insert_closure { let (body_str, entry_kind) = if contains_expr.negated { - (then_search.snippet_vacant(cx, then_expr.span, &mut app), "Vacant(e)") + then_search.snippet_vacant(cx, then_expr.span, &mut app) } else { - ( - then_search.snippet_occupied(cx, then_expr.span, &mut app), - "Occupied(mut e)", - ) + then_search.snippet_occupied(cx, then_expr.span, &mut app) }; format!( "if let {}::{} = {}.entry({}) {}", @@ -184,7 +172,8 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { format!("{}.entry({}).or_insert({});", map_str, key_str, value_str) } } else { - // Todo: if let Some(v) = map.get_mut(k) + // TODO: suggest using `if let Some(v) = map.get_mut(k) { .. }` here. + // This would need to be a different lint. return; } } else { @@ -192,7 +181,8 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { if contains_expr.negated { format!("{}.entry({}).or_insert_with(|| {});", map_str, key_str, block_str) } else { - // Todo: if let Some(v) = map.get_mut(k) + // TODO: suggest using `if let Some(v) = map.get_mut(k) { .. }` here. + // This would need to be a different lint. return; } } @@ -222,7 +212,7 @@ impl MapType { Self::BTree => "BTreeMap", } } - fn entry_path(self) -> &'staic str { + fn entry_path(self) -> &'static str { match self { Self::Hash => "std::collections::hash_map::Entry", Self::BTree => "std::collections::btree_map::Entry", @@ -312,15 +302,16 @@ struct Insertion<'tcx> { value: &'tcx Expr<'tcx>, } -// This visitor needs to do a multiple things: -// * Find all usages of the map. Only insertions into the map which share the same key are -// permitted. All others will prevent the lint. -// * Determine if the final statement executed is an insertion. This is needed to use `insert_with`. -// * Determine if there's any sub-expression that can't be placed in a closure. -// * Determine if there's only a single insert statement. This is needed to give better suggestions. - +/// This visitor needs to do a multiple things: +/// * Find all usages of the map. An insertion can only be made before any other usages of the map. +/// * Determine if there's an insertion using the same key. There's no need for the entry api +/// otherwise. +/// * Determine if the final statement executed is an insertion. This is needed to use +/// `or_insert_with`. +/// * Determine if there's any sub-expression that can't be placed in a closure. +/// * Determine if there's only a single insert statement. `or_insert` can be used in this case. #[allow(clippy::struct_excessive_bools)] -struct InsertSearcher<'cx, 'i, 'tcx> { +struct InsertSearcher<'cx, 'tcx> { cx: &'cx LateContext<'tcx>, /// The map expression used in the contains call. map: &'tcx Expr<'tcx>, @@ -334,13 +325,16 @@ struct InsertSearcher<'cx, 'i, 'tcx> { can_use_entry: bool, /// Whether this expression is the final expression in this code path. This may be a statement. in_tail_pos: bool, - // A single insert expression has a slightly different suggestion. + // Is this expression a single insert. A slightly better suggestion can be made in this case. is_single_insert: bool, + /// If the visitor has seen the map being used. is_map_used: bool, - edits: &'i mut Vec>, + /// The locations where changes need to be made for the suggestion. + edits: Vec>, + /// A stack of loops the visitor is currently in. loops: Vec, } -impl<'tcx> InsertSearcher<'_, '_, 'tcx> { +impl<'tcx> InsertSearcher<'_, 'tcx> { /// Visit the expression as a branch in control flow. Multiple insert calls can be used, but /// only if they are on separate code paths. This will return whether the map was used in the /// given expression. @@ -363,7 +357,7 @@ impl<'tcx> InsertSearcher<'_, '_, 'tcx> { self.in_tail_pos = in_tail_pos; } } -impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, '_, 'tcx> { +impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> { type Map = ErasedMap<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None @@ -483,6 +477,7 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, '_, 'tcx> { }, ExprKind::Loop(block, ..) => { self.loops.push(expr.hir_id); + self.is_single_insert = false; self.allow_insert_closure &= !self.in_tail_pos; // Don't allow insertions inside of a loop. let edit_len = self.edits.len(); @@ -519,7 +514,13 @@ impl InsertSearchResults<'tcx> { self.is_single_insert.then(|| self.edits[0].as_insertion().unwrap()) } - fn snippet_occupied(&self, cx: &LateContext<'_>, mut span: Span, app: &mut Applicability) -> String { + fn snippet( + &self, + cx: &LateContext<'_>, + mut span: Span, + app: &mut Applicability, + write_wrapped: impl Fn(&mut String, Insertion<'_>, SyntaxContext, &mut Applicability), + ) -> String { let ctxt = span.ctxt(); let mut res = String::new(); for insertion in self.edits.iter().filter_map(|e| e.as_insertion()) { @@ -530,13 +531,13 @@ impl InsertSearchResults<'tcx> { app, )); if is_expr_used_or_unified(cx.tcx, insertion.call) { - res.push_str("Some(e.insert("); - res.push_str(&snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0); - res.push_str("))"); + write_wrapped(&mut res, insertion, ctxt, app); } else { - res.push_str("e.insert("); - res.push_str(&snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0); - res.push(')'); + let _ = write!( + res, + "e.insert({})", + snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0 + ); } span = span.trim_start(insertion.call.span).unwrap_or(DUMMY_SP); } @@ -544,42 +545,41 @@ impl InsertSearchResults<'tcx> { res } - fn snippet_vacant(&self, cx: &LateContext<'_>, mut span: Span, app: &mut Applicability) -> String { - let ctxt = span.ctxt(); - let mut res = String::new(); - for insertion in self.edits.iter().filter_map(|e| e.as_insertion()) { - res.push_str(&snippet_with_applicability( - cx, - span.until(insertion.call.span), - "..", - app, - )); - if is_expr_used_or_unified(cx.tcx, insertion.call) { - if is_expr_final_block_expr(cx.tcx, insertion.call) { - let _ = write!( + fn snippet_occupied(&self, cx: &LateContext<'_>, span: Span, app: &mut Applicability) -> (String, &'static str) { + ( + self.snippet(cx, span, app, |res, insertion, ctxt, app| { + // Insertion into a map would return `Some(&mut value)`, but the entry returns `&mut value` + let _ = write!( + res, + "Some(e.insert({}))", + snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0 + ); + }), + "Occupied(mut e)", + ) + } + + fn snippet_vacant(&self, cx: &LateContext<'_>, span: Span, app: &mut Applicability) -> (String, &'static str) { + ( + self.snippet(cx, span, app, |res, insertion, ctxt, app| { + // Insertion into a map would return `None`, but the entry returns a mutable reference. + let _ = if is_expr_final_block_expr(cx.tcx, insertion.call) { + write!( res, "e.insert({});\n{}None", snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0, snippet_indent(cx, insertion.call.span).as_deref().unwrap_or(""), - ); + ) } else { - let _ = write!( + write!( res, "{{ e.insert({}); None }}", snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0, - ); - } - } else { - let _ = write!( - res, - "e.insert({})", - snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0, - ); - } - span = span.trim_start(insertion.call.span).unwrap_or(DUMMY_SP); - } - res.push_str(&snippet_with_applicability(cx, span, "..", app)); - res + ) + }; + }), + "Vacant(e)", + ) } fn snippet_closure(&self, cx: &LateContext<'_>, mut span: Span, app: &mut Applicability) -> String { @@ -588,6 +588,7 @@ impl InsertSearchResults<'tcx> { for edit in &self.edits { match *edit { Edit::Insertion(insertion) => { + // Cut out the value from `map.insert(key, value)` res.push_str(&snippet_with_applicability( cx, span.until(insertion.call.span), @@ -598,6 +599,7 @@ impl InsertSearchResults<'tcx> { span = span.trim_start(insertion.call.span).unwrap_or(DUMMY_SP); }, Edit::RemoveSemi(semi_span) => { + // Cut out the semicolon. This allows the value to be returned from the closure. res.push_str(&snippet_with_applicability(cx, span.until(semi_span), "..", app)); span = span.trim_start(semi_span).unwrap_or(DUMMY_SP); }, @@ -607,18 +609,18 @@ impl InsertSearchResults<'tcx> { res } } + fn find_insert_calls( cx: &LateContext<'tcx>, contains_expr: &ContainsExpr<'tcx>, expr: &'tcx Expr<'_>, ) -> Option> { - let mut edits = Vec::new(); let mut s = InsertSearcher { cx, map: contains_expr.map, key: contains_expr.key, ctxt: expr.span.ctxt(), - edits: &mut edits, + edits: Vec::new(), is_map_used: false, allow_insert_closure: true, can_use_entry: true, @@ -629,6 +631,7 @@ fn find_insert_calls( s.visit_expr(expr); let allow_insert_closure = s.allow_insert_closure; let is_single_insert = s.is_single_insert; + let edits = s.edits; s.can_use_entry.then(|| InsertSearchResults { edits, allow_insert_closure, diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs index 3f8b976fcac..d9b75149b9f 100644 --- a/clippy_lints/src/manual_map.rs +++ b/clippy_lints/src/manual_map.rs @@ -2,13 +2,13 @@ use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; -use clippy_utils::{can_move_expr_to_closure, in_constant, is_allowed, is_else_clause, is_lang_ctor, match_var, peel_hir_expr_refs}; +use clippy_utils::{ + can_move_expr_to_closure, in_constant, is_allowed, is_else_clause, is_lang_ctor, match_var, peel_hir_expr_refs, +}; use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; -use rustc_hir::{ - Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind, QPath, -}; +use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 5d6c1337be6..90a6fd225ab 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -64,8 +64,8 @@ use rustc_hir::intravisit::{self, walk_expr, ErasedMap, NestedVisitorMap, Visito use rustc_hir::LangItem::{ResultErr, ResultOk}; use rustc_hir::{ def, Arm, BindingAnnotation, Block, Body, Constness, Destination, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl, - ImplItem, ImplItemKind, Item, ItemKind, LangItem, MatchSource, Node, Param, Pat, PatKind, Path, PathSegment, QPath, - Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, + ImplItem, ImplItemKind, Item, ItemKind, LangItem, Local, MatchSource, Node, Param, Pat, PatKind, Path, PathSegment, + QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, }; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::exports::Export; @@ -1312,48 +1312,7 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { did.map_or(false, |did| must_use_attr(&cx.tcx.get_attrs(did)).is_some()) } -pub fn get_expr_use_node(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option> { - let map = tcx.hir(); - let mut child_id = expr.hir_id; - let mut iter = map.parent_iter(child_id); - loop { - match iter.next() { - None => break None, - Some((id, Node::Block(_))) => child_id = id, - Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id, - Some((_, Node::Expr(expr))) => match expr.kind { - ExprKind::Break( - Destination { - target_id: Ok(dest), .. - }, - _, - ) => { - iter = map.parent_iter(dest); - child_id = dest; - }, - ExprKind::DropTemps(_) | ExprKind::Block(..) => child_id = expr.hir_id, - ExprKind::If(control_expr, ..) | ExprKind::Match(control_expr, ..) - if control_expr.hir_id != child_id => - { - child_id = expr.hir_id - }, - _ => break Some(Node::Expr(expr)), - }, - Some((_, node)) => break Some(node), - } - } -} - -pub fn is_expr_used(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { - !matches!( - get_expr_use_node(tcx, expr), - Some(Node::Stmt(Stmt { - kind: StmtKind::Expr(_) | StmtKind::Semi(_), - .. - })) - ) -} - +/// 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 map = tcx.hir(); let mut child_id = expr.hir_id; @@ -1374,16 +1333,26 @@ pub fn get_expr_use_or_unification_node(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> O } } +/// Checks if the result of an expression is used, or it's type is unified with another branch. pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { !matches!( get_expr_use_or_unification_node(tcx, expr), None | Some(Node::Stmt(Stmt { - kind: StmtKind::Expr(_) | StmtKind::Semi(_), + kind: StmtKind::Expr(_) + | StmtKind::Semi(_) + | StmtKind::Local(Local { + pat: Pat { + kind: PatKind::Wild, + .. + }, + .. + }), .. })) ) } +/// Checks if the expression is the final expression returned from a block. pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { matches!(get_parent_node(tcx, expr.hir_id), Some(Node::Block(..))) } diff --git a/tests/ui/entry.fixed b/tests/ui/entry.fixed index 524f2132797..cfad3090ba3 100644 --- a/tests/ui/entry.fixed +++ b/tests/ui/entry.fixed @@ -2,6 +2,7 @@ #![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)] #![warn(clippy::map_entry)] +#![feature(asm)] use std::collections::{BTreeMap, HashMap}; use std::hash::Hash; @@ -10,11 +11,19 @@ macro_rules! m { ($e:expr) => {{ $e }}; } +macro_rules! insert { + ($map:expr, $key:expr, $val:expr) => { + $map.insert($key, $val) + }; +} + fn foo() {} -fn hash_map(m: &mut HashMap, k: K, v: V, v2: V) { +fn hash_map(m: &mut HashMap, m2: &mut HashMap, k: K, k2: K, v: V, v2: V) { + // or_insert(v) m.entry(k).or_insert(v); + // semicolon on insert, use or_insert_with(..) m.entry(k).or_insert_with(|| { if true { v @@ -23,6 +32,7 @@ fn hash_map(m: &mut HashMap, k: K, v: V, v2: } }); + // semicolon on if, use or_insert_with(..) m.entry(k).or_insert_with(|| { if true { v @@ -31,6 +41,7 @@ fn hash_map(m: &mut HashMap, k: K, v: V, v2: } }); + // early return, use if let if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { if true { e.insert(v); @@ -40,11 +51,13 @@ fn hash_map(m: &mut HashMap, k: K, v: V, v2: } } + // use or_insert_with(..) m.entry(k).or_insert_with(|| { foo(); v }); + // semicolon on insert and match, use or_insert_with(..) m.entry(k).or_insert_with(|| { match 0 { 1 if true => { @@ -56,18 +69,17 @@ fn hash_map(m: &mut HashMap, k: K, v: V, v2: } }); + // one branch doesn't insert, use if let if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { match 0 { - 0 => {}, - 1 => { - e.insert(v); - }, + 0 => foo(), _ => { e.insert(v2); }, }; } + // use or_insert_with m.entry(k).or_insert_with(|| { foo(); match 0 { @@ -94,10 +106,46 @@ fn hash_map(m: &mut HashMap, k: K, v: V, v2: } }); + // ok, insert in loop + if !m.contains_key(&k) { + for _ in 0..2 { + m.insert(k, v); + } + } + + // macro_expansion test, use or_insert(..) m.entry(m!(k)).or_insert_with(|| m!(v)); + + // ok, map used before insertion + if !m.contains_key(&k) { + let _ = m.len(); + m.insert(k, v); + } + + // ok, inline asm + if !m.contains_key(&k) { + unsafe { asm!("nop") } + m.insert(k, v); + } + + // ok, different keys. + if !m.contains_key(&k) { + m.insert(k2, v); + } + + // ok, different maps + if !m.contains_key(&k) { + m2.insert(k, v); + } + + // ok, insert in macro + if !m.contains_key(&k) { + insert!(m, k, v); + } } fn btree_map(m: &mut BTreeMap, k: K, v: V, v2: V) { + // insert then do something, use if let if let std::collections::btree_map::Entry::Vacant(e) = m.entry(k) { e.insert(v); foo(); diff --git a/tests/ui/entry.rs b/tests/ui/entry.rs index ff4890eeeb6..fa9280b58de 100644 --- a/tests/ui/entry.rs +++ b/tests/ui/entry.rs @@ -2,6 +2,7 @@ #![allow(unused, clippy::needless_pass_by_value, clippy::collapsible_if)] #![warn(clippy::map_entry)] +#![feature(asm)] use std::collections::{BTreeMap, HashMap}; use std::hash::Hash; @@ -10,13 +11,21 @@ macro_rules! m { ($e:expr) => {{ $e }}; } +macro_rules! insert { + ($map:expr, $key:expr, $val:expr) => { + $map.insert($key, $val) + }; +} + fn foo() {} -fn hash_map(m: &mut HashMap, k: K, v: V, v2: V) { +fn hash_map(m: &mut HashMap, m2: &mut HashMap, k: K, k2: K, v: V, v2: V) { + // or_insert(v) if !m.contains_key(&k) { m.insert(k, v); } + // semicolon on insert, use or_insert_with(..) if !m.contains_key(&k) { if true { m.insert(k, v); @@ -25,6 +34,7 @@ fn hash_map(m: &mut HashMap, k: K, v: V, v2: } } + // semicolon on if, use or_insert_with(..) if !m.contains_key(&k) { if true { m.insert(k, v) @@ -33,6 +43,7 @@ fn hash_map(m: &mut HashMap, k: K, v: V, v2: }; } + // early return, use if let if !m.contains_key(&k) { if true { m.insert(k, v); @@ -42,11 +53,13 @@ fn hash_map(m: &mut HashMap, k: K, v: V, v2: } } + // use or_insert_with(..) if !m.contains_key(&k) { foo(); m.insert(k, v); } + // semicolon on insert and match, use or_insert_with(..) if !m.contains_key(&k) { match 0 { 1 if true => { @@ -58,18 +71,17 @@ fn hash_map(m: &mut HashMap, k: K, v: V, v2: }; } + // one branch doesn't insert, use if let if !m.contains_key(&k) { match 0 { - 0 => {}, - 1 => { - m.insert(k, v); - }, + 0 => foo(), _ => { m.insert(k, v2); }, }; } + // use or_insert_with if !m.contains_key(&k) { foo(); match 0 { @@ -96,12 +108,48 @@ fn hash_map(m: &mut HashMap, k: K, v: V, v2: } } + // ok, insert in loop + if !m.contains_key(&k) { + for _ in 0..2 { + m.insert(k, v); + } + } + + // macro_expansion test, use or_insert(..) if !m.contains_key(&m!(k)) { m.insert(m!(k), m!(v)); } + + // ok, map used before insertion + if !m.contains_key(&k) { + let _ = m.len(); + m.insert(k, v); + } + + // ok, inline asm + if !m.contains_key(&k) { + unsafe { asm!("nop") } + m.insert(k, v); + } + + // ok, different keys. + if !m.contains_key(&k) { + m.insert(k2, v); + } + + // ok, different maps + if !m.contains_key(&k) { + m2.insert(k, v); + } + + // ok, insert in macro + if !m.contains_key(&k) { + insert!(m, k, v); + } } fn btree_map(m: &mut BTreeMap, k: K, v: V, v2: V) { + // insert then do something, use if let if !m.contains_key(&k) { m.insert(k, v); foo(); diff --git a/tests/ui/entry.stderr b/tests/ui/entry.stderr index 63b5f5a0b2c..2f075a97010 100644 --- a/tests/ui/entry.stderr +++ b/tests/ui/entry.stderr @@ -1,5 +1,5 @@ error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:16:5 + --> $DIR/entry.rs:24:5 | LL | / if !m.contains_key(&k) { LL | | m.insert(k, v); @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::map-entry` implied by `-D warnings` error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:20:5 + --> $DIR/entry.rs:29:5 | LL | / if !m.contains_key(&k) { LL | | if true { @@ -31,7 +31,7 @@ LL | } ... error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:28:5 + --> $DIR/entry.rs:38:5 | LL | / if !m.contains_key(&k) { LL | | if true { @@ -53,7 +53,7 @@ LL | } ... error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:36:5 + --> $DIR/entry.rs:47:5 | LL | / if !m.contains_key(&k) { LL | | if true { @@ -75,7 +75,7 @@ LL | return; ... error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:45:5 + --> $DIR/entry.rs:57:5 | LL | / if !m.contains_key(&k) { LL | | foo(); @@ -92,7 +92,7 @@ LL | }); | error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:50:5 + --> $DIR/entry.rs:63:5 | LL | / if !m.contains_key(&k) { LL | | match 0 { @@ -114,12 +114,12 @@ LL | _ => { ... error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:61:5 + --> $DIR/entry.rs:75:5 | LL | / if !m.contains_key(&k) { LL | | match 0 { -LL | | 0 => {}, -LL | | 1 => { +LL | | 0 => foo(), +LL | | _ => { ... | LL | | }; LL | | } @@ -129,14 +129,14 @@ help: try this | LL | if let std::collections::hash_map::Entry::Vacant(e) = m.entry(k) { LL | match 0 { -LL | 0 => {}, -LL | 1 => { -LL | e.insert(v); +LL | 0 => foo(), +LL | _ => { +LL | e.insert(v2); LL | }, ... error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:73:5 + --> $DIR/entry.rs:85:5 | LL | / if !m.contains_key(&k) { LL | | foo(); @@ -158,7 +158,7 @@ LL | }, ... error: usage of `contains_key` followed by `insert` on a `HashMap` - --> $DIR/entry.rs:99:5 + --> $DIR/entry.rs:119:5 | LL | / if !m.contains_key(&m!(k)) { LL | | m.insert(m!(k), m!(v)); @@ -166,7 +166,7 @@ LL | | } | |_____^ help: try this: `m.entry(m!(k)).or_insert_with(|| m!(v));` error: usage of `contains_key` followed by `insert` on a `BTreeMap` - --> $DIR/entry.rs:105:5 + --> $DIR/entry.rs:153:5 | LL | / if !m.contains_key(&k) { LL | | m.insert(k, v); diff --git a/tests/ui/entry_unfixable.rs b/tests/ui/entry_unfixable.rs deleted file mode 100644 index beb2d5c97b1..00000000000 --- a/tests/ui/entry_unfixable.rs +++ /dev/null @@ -1,50 +0,0 @@ -#![allow(unused, clippy::needless_pass_by_value)] -#![warn(clippy::map_entry)] - -use std::collections::{BTreeMap, HashMap}; -use std::hash::Hash; - -macro_rules! m { - ($map:expr, $key:expr, $value:expr) => { - $map.insert($key, $value) - }; - ($e:expr) => {{ $e }}; -} - -fn foo() {} - -// should not trigger -fn insert_other_if_absent(m: &mut HashMap, k: K, o: K, v: V) { - if !m.contains_key(&k) { - m.insert(o, v); - } -} - -// should not trigger, because the one uses different HashMap from another one -fn insert_from_different_map(m: HashMap, n: &mut HashMap, k: K, v: V) { - if !m.contains_key(&k) { - n.insert(k, v); - } -} - -// should not trigger, because the one uses different HashMap from another one -fn insert_from_different_map2(m: &mut HashMap, n: &mut HashMap, k: K, v: V) { - if !m.contains_key(&k) { - n.insert(k, v); - } -} - -fn insert_in_macro(m: &mut HashMap, k: K, v: V) { - if !m.contains_key(&k) { - m!(m, k, v); - } -} - -fn use_map_then_insert(m: &mut HashMap, k: K, v: V) { - if !m.contains_key(&k) { - let _ = m.len(); - m.insert(k, v); - } -} - -fn main() {} diff --git a/tests/ui/string_lit_as_bytes.fixed b/tests/ui/string_lit_as_bytes.fixed index dd22bfa5c53..df2256e4f97 100644 --- a/tests/ui/string_lit_as_bytes.fixed +++ b/tests/ui/string_lit_as_bytes.fixed @@ -22,7 +22,7 @@ fn str_lit_as_bytes() { let current_version = env!("CARGO_PKG_VERSION").as_bytes(); - let includestr = include_bytes!("entry_unfixable.rs"); + let includestr = include_bytes!("string_lit_as_bytes.rs"); let _ = b"string with newline\t\n"; } diff --git a/tests/ui/string_lit_as_bytes.rs b/tests/ui/string_lit_as_bytes.rs index d2a710ed6b8..c6bf8f732ed 100644 --- a/tests/ui/string_lit_as_bytes.rs +++ b/tests/ui/string_lit_as_bytes.rs @@ -22,7 +22,7 @@ fn str_lit_as_bytes() { let current_version = env!("CARGO_PKG_VERSION").as_bytes(); - let includestr = include_str!("entry_unfixable.rs").as_bytes(); + let includestr = include_str!("string_lit_as_bytes.rs").as_bytes(); let _ = "string with newline\t\n".as_bytes(); } diff --git a/tests/ui/string_lit_as_bytes.stderr b/tests/ui/string_lit_as_bytes.stderr index e0ddb070b50..f47d6161c6c 100644 --- a/tests/ui/string_lit_as_bytes.stderr +++ b/tests/ui/string_lit_as_bytes.stderr @@ -27,8 +27,8 @@ LL | let bs = "lit to owned".to_owned().into_bytes(); error: calling `as_bytes()` on `include_str!(..)` --> $DIR/string_lit_as_bytes.rs:25:22 | -LL | let includestr = include_str!("entry_unfixable.rs").as_bytes(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `include_bytes!(..)` instead: `include_bytes!("entry_unfixable.rs")` +LL | let includestr = include_str!("string_lit_as_bytes.rs").as_bytes(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `include_bytes!(..)` instead: `include_bytes!("string_lit_as_bytes.rs")` error: calling `as_bytes()` on a string literal --> $DIR/string_lit_as_bytes.rs:27:13 -- cgit 1.4.1-3-g733a5 From 779d98f6ccbd76807e811a134bb00a4e0d92c6db Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 15 Apr 2021 10:37:42 -0400 Subject: Don't allow adjustments for `manual_map` --- clippy_lints/src/manual_map.rs | 6 ++++++ tests/ui/manual_map_option.fixed | 7 +++++++ tests/ui/manual_map_option.rs | 7 +++++++ 3 files changed, 20 insertions(+) (limited to 'tests') diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs index 29be0739977..f16ed4104fe 100644 --- a/clippy_lints/src/manual_map.rs +++ b/clippy_lints/src/manual_map.rs @@ -104,12 +104,18 @@ impl LateLintPass<'_> for ManualMap { None => return, }; + // These two lints will go back and forth with each other. if cx.typeck_results().expr_ty(some_expr) == cx.tcx.types.unit && !is_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id) { return; } + // `map` won't perform any adjustments. + if !cx.typeck_results().expr_adjustments(some_expr).is_empty() { + return; + } + if !can_move_expr_to_closure(cx, some_expr) { return; } diff --git a/tests/ui/manual_map_option.fixed b/tests/ui/manual_map_option.fixed index ee015845777..40d01df6379 100644 --- a/tests/ui/manual_map_option.fixed +++ b/tests/ui/manual_map_option.fixed @@ -146,4 +146,11 @@ fn main() { None => None, }; } + + // #7077 + let s = &String::new(); + let _: Option<&str> = match Some(s) { + Some(s) => Some(s), + None => None, + }; } diff --git a/tests/ui/manual_map_option.rs b/tests/ui/manual_map_option.rs index 29509bddfd9..cfef0c5cc4e 100644 --- a/tests/ui/manual_map_option.rs +++ b/tests/ui/manual_map_option.rs @@ -212,4 +212,11 @@ fn main() { None => None, }; } + + // #7077 + let s = &String::new(); + let _: Option<&str> = match Some(s) { + Some(s) => Some(s), + None => None, + }; } -- cgit 1.4.1-3-g733a5 From f6c5d8d599070bacad800ac7014fa3a6f140eadc Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Wed, 7 Apr 2021 16:19:25 -0400 Subject: Remove all usages of `match_path`, `match_qpath` and `match_path_ast` except the `author` lint. Add note to fix `MATCH_TYPE_ON_DIAG_ITEM` Add false negative test for `uninit_assumed_init` --- clippy_lints/src/assertions_on_constants.rs | 5 +- clippy_lints/src/excessive_bools.rs | 6 +- clippy_lints/src/implicit_hasher.rs | 7 +- clippy_lints/src/implicit_saturating_sub.rs | 43 ++++++++--- clippy_lints/src/infinite_iter.rs | 4 +- clippy_lints/src/map_identity.rs | 4 +- clippy_lints/src/matches.rs | 24 ++++-- clippy_lints/src/methods/filter_map_identity.rs | 12 +-- clippy_lints/src/methods/flat_map_identity.rs | 18 ++--- .../src/methods/from_iter_instead_of_collect.rs | 11 +-- .../src/methods/manual_saturating_arithmetic.rs | 6 +- clippy_lints/src/methods/mod.rs | 2 +- clippy_lints/src/methods/uninit_assumed_init.rs | 5 +- clippy_lints/src/methods/unnecessary_filter_map.rs | 2 - clippy_lints/src/misc.rs | 18 ++--- clippy_lints/src/ptr.rs | 17 ++--- clippy_lints/src/question_mark.rs | 11 ++- clippy_lints/src/returns.rs | 7 +- clippy_lints/src/slow_vector_initialization.rs | 69 ++++++++--------- clippy_lints/src/transmuting_null.rs | 23 +++--- clippy_lints/src/types/borrowed_box.rs | 9 ++- clippy_lints/src/unnecessary_wraps.rs | 10 +-- clippy_lints/src/utils/internal_lints.rs | 14 ++-- clippy_utils/src/lib.rs | 89 ++++++++++++++-------- clippy_utils/src/paths.rs | 12 +-- doc/adding_lints.md | 2 +- .../ui-internal/collapsible_span_lint_calls.fixed | 48 +----------- tests/ui-internal/collapsible_span_lint_calls.rs | 48 +----------- .../ui-internal/collapsible_span_lint_calls.stderr | 10 +-- tests/ui-internal/match_type_on_diag_item.rs | 21 ++--- tests/ui-internal/match_type_on_diag_item.stderr | 30 +++----- tests/ui/repl_uninit.rs | 4 +- tests/ui/uninit.rs | 5 +- tests/ui/uninit.stderr | 8 +- 34 files changed, 269 insertions(+), 335 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs index a0993bb6913..c565e29d078 100644 --- a/clippy_lints/src/assertions_on_constants.rs +++ b/clippy_lints/src/assertions_on_constants.rs @@ -127,10 +127,9 @@ fn match_assert_with_message<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) _ => &block.expr, }; // function call - if let Some(args) = match_panic_call(cx, begin_panic_call); - if args.len() == 1; + if let Some(arg) = match_panic_call(cx, begin_panic_call); // bind the second argument of the `assert!` macro if it exists - if let panic_message = snippet_opt(cx, args[0].span); + if let panic_message = snippet_opt(cx, arg.span); // second argument of begin_panic is irrelevant // as is the second match arm then { diff --git a/clippy_lints/src/excessive_bools.rs b/clippy_lints/src/excessive_bools.rs index 249ee27330b..4e2dbf005d5 100644 --- a/clippy_lints/src/excessive_bools.rs +++ b/clippy_lints/src/excessive_bools.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::{in_macro, match_path_ast}; +use clippy_utils::in_macro; use rustc_ast::ast::{AssocItemKind, Extern, FnKind, FnSig, ImplKind, Item, ItemKind, TraitKind, Ty, TyKind}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -126,7 +126,9 @@ impl_lint_pass!(ExcessiveBools => [STRUCT_EXCESSIVE_BOOLS, FN_PARAMS_EXCESSIVE_B fn is_bool_ty(ty: &Ty) -> bool { if let TyKind::Path(None, path) = &ty.kind { - return match_path_ast(path, &["bool"]); + if let [name] = path.segments.as_slice() { + return name.ident.name == sym::bool; + } } false } diff --git a/clippy_lints/src/implicit_hasher.rs b/clippy_lints/src/implicit_hasher.rs index 77a38544edc..03fe0d16d48 100644 --- a/clippy_lints/src/implicit_hasher.rs +++ b/clippy_lints/src/implicit_hasher.rs @@ -22,7 +22,7 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::paths; use clippy_utils::source::{snippet, snippet_opt}; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{differing_macro_contexts, match_path}; +use clippy_utils::{differing_macro_contexts, match_def_path}; declare_clippy_lint! { /// **What it does:** Checks for public `impl` or `fn` missing generalization @@ -333,12 +333,13 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 't if let ExprKind::Call(fun, args) = e.kind; if let ExprKind::Path(QPath::TypeRelative(ty, method)) = fun.kind; if let TyKind::Path(QPath::Resolved(None, ty_path)) = ty.kind; + if let Some(ty_did) = ty_path.res.opt_def_id(); then { if !TyS::same_type(self.target.ty(), self.maybe_typeck_results.unwrap().expr_ty(e)) { return; } - if match_path(ty_path, &paths::HASHMAP) { + if match_def_path(self.cx, ty_did, &paths::HASHMAP) { if method.ident.name == sym::new { self.suggestions .insert(e.span, "HashMap::default()".to_string()); @@ -351,7 +352,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 't ), ); } - } else if match_path(ty_path, &paths::HASHSET) { + } else if match_def_path(self.cx, ty_did, &paths::HASHSET) { if method.ident.name == sym::new { self.suggestions .insert(e.span, "HashSet::default()".to_string()); diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index cba3183e869..4069a685ea0 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{in_macro, match_qpath, SpanlessEq}; +use clippy_utils::{in_macro, SpanlessEq}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind, QPath, StmtKind}; +use rustc_hir::{lang_items::LangItem, BinOpKind, Expr, ExprKind, QPath, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -87,7 +87,13 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { // Get the variable name let var_name = ares_path.segments[0].ident.name.as_str(); - const INT_TYPES: [&str; 5] = ["i8", "i16", "i32", "i64", "i128"]; + const INT_TYPES: [LangItem; 5] = [ + LangItem::I8, + LangItem::I16, + LangItem::I32, + LangItem::I64, + LangItem::Isize + ]; match cond_num_val.kind { ExprKind::Lit(ref cond_lit) => { @@ -99,17 +105,30 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { }; } }, - ExprKind::Path(ref cond_num_path) => { - if INT_TYPES.iter().any(|int_type| match_qpath(cond_num_path, &[int_type, "MIN"])) { - print_lint_and_sugg(cx, &var_name, expr); - }; + ExprKind::Path(QPath::TypeRelative(_, name)) => { + if_chain! { + if name.ident.as_str() == "MIN"; + if let Some(const_id) = cx.typeck_results().type_dependent_def_id(cond_num_val.hir_id); + if let Some(impl_id) = cx.tcx.impl_of_method(const_id); + let mut int_ids = INT_TYPES.iter().filter_map(|&ty| cx.tcx.lang_items().require(ty).ok()); + if int_ids.any(|int_id| int_id == impl_id); + then { + print_lint_and_sugg(cx, &var_name, expr) + } + } }, - ExprKind::Call(func, _) => { - if let ExprKind::Path(ref cond_num_path) = func.kind { - if INT_TYPES.iter().any(|int_type| match_qpath(cond_num_path, &[int_type, "min_value"])) { - print_lint_and_sugg(cx, &var_name, expr); + ExprKind::Call(func, []) => { + if_chain! { + if let ExprKind::Path(QPath::TypeRelative(_, name)) = func.kind; + if name.ident.as_str() == "min_value"; + if let Some(func_id) = cx.typeck_results().type_dependent_def_id(func.hir_id); + if let Some(impl_id) = cx.tcx.impl_of_method(func_id); + let mut int_ids = INT_TYPES.iter().filter_map(|&ty| cx.tcx.lang_items().require(ty).ok()); + if int_ids.any(|int_id| int_id == impl_id); + then { + print_lint_and_sugg(cx, &var_name, expr) } - }; + } }, _ => (), } diff --git a/clippy_lints/src/infinite_iter.rs b/clippy_lints/src/infinite_iter.rs index bbb4ddc613a..afee20ce43e 100644 --- a/clippy_lints/src/infinite_iter.rs +++ b/clippy_lints/src/infinite_iter.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::{implements_trait, match_type}; -use clippy_utils::{get_trait_def_id, higher, match_qpath, paths}; +use clippy_utils::{get_trait_def_id, higher, is_qpath_def_path, paths}; use rustc_hir::{BorrowKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -163,7 +163,7 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { ExprKind::Box(e) | ExprKind::AddrOf(BorrowKind::Ref, _, e) => is_infinite(cx, e), ExprKind::Call(path, _) => { if let ExprKind::Path(ref qpath) = path.kind { - match_qpath(qpath, &paths::REPEAT).into() + is_qpath_def_path(cx, qpath, path.hir_id, &paths::ITER_REPEAT).into() } else { Finite } diff --git a/clippy_lints/src/map_identity.rs b/clippy_lints/src/map_identity.rs index e7719e7663d..41cda23510e 100644 --- a/clippy_lints/src/map_identity.rs +++ b/clippy_lints/src/map_identity.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_adjusted, is_trait_method, match_path, match_var, paths, remove_blocks}; +use clippy_utils::{is_adjusted, is_qpath_def_path, is_trait_method, match_var, paths, remove_blocks}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Body, Expr, ExprKind, Pat, PatKind, QPath, StmtKind}; @@ -80,7 +80,7 @@ fn get_map_argument<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { match expr.kind { ExprKind::Closure(_, _, body_id, _, _) => is_body_identity_function(cx, cx.tcx.hir().body(body_id)), - ExprKind::Path(QPath::Resolved(_, path)) => match_path(path, &paths::STD_CONVERT_IDENTITY), + ExprKind::Path(ref path) => is_qpath_def_path(cx, path, expr.hir_id, &paths::CONVERT_IDENTITY), _ => false, } } diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 30091e0e2d7..8f1112cff7b 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -1701,7 +1701,7 @@ mod redundant_pattern_match { use super::REDUNDANT_PATTERN_MATCHING; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; - use clippy_utils::{is_lang_ctor, is_trait_method, match_qpath, paths}; + use clippy_utils::{is_lang_ctor, is_qpath_def_path, is_trait_method, paths}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -1735,8 +1735,8 @@ mod redundant_pattern_match { kind = &inner.kind; } let good_method = match kind { - PatKind::TupleStruct(ref path, patterns, _) if patterns.len() == 1 => { - if let PatKind::Wild = patterns[0].kind { + PatKind::TupleStruct(ref path, [sub_pat], _) => { + if let PatKind::Wild = sub_pat.kind { if is_lang_ctor(cx, path, ResultOk) { "is_ok()" } else if is_lang_ctor(cx, path, ResultErr) { @@ -1745,9 +1745,9 @@ mod redundant_pattern_match { "is_some()" } else if is_lang_ctor(cx, path, PollReady) { "is_ready()" - } else if match_qpath(path, &paths::IPADDR_V4) { + } else if is_qpath_def_path(cx, path, sub_pat.hir_id, &paths::IPADDR_V4) { "is_ipv4()" - } else if match_qpath(path, &paths::IPADDR_V6) { + } else if is_qpath_def_path(cx, path, sub_pat.hir_id, &paths::IPADDR_V6) { "is_ipv6()" } else { return; @@ -1821,6 +1821,7 @@ mod redundant_pattern_match { ) if patterns_left.len() == 1 && patterns_right.len() == 1 => { if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].kind, &patterns_right[0].kind) { find_good_method_for_match( + cx, arms, path_left, path_right, @@ -1831,6 +1832,7 @@ mod redundant_pattern_match { ) .or_else(|| { find_good_method_for_match( + cx, arms, path_left, path_right, @@ -1850,6 +1852,7 @@ mod redundant_pattern_match { { if let PatKind::Wild = patterns[0].kind { find_good_method_for_match( + cx, arms, path_left, path_right, @@ -1860,6 +1863,7 @@ mod redundant_pattern_match { ) .or_else(|| { find_good_method_for_match( + cx, arms, path_left, path_right, @@ -1900,7 +1904,9 @@ mod redundant_pattern_match { } } + #[allow(clippy::too_many_arguments)] fn find_good_method_for_match<'a>( + cx: &LateContext<'_>, arms: &[Arm<'_>], path_left: &QPath<'_>, path_right: &QPath<'_>, @@ -1909,9 +1915,13 @@ mod redundant_pattern_match { should_be_left: &'a str, should_be_right: &'a str, ) -> Option<&'a str> { - let body_node_pair = if match_qpath(path_left, expected_left) && match_qpath(path_right, expected_right) { + let body_node_pair = if is_qpath_def_path(cx, path_left, arms[0].pat.hir_id, expected_left) + && is_qpath_def_path(cx, path_right, arms[1].pat.hir_id, expected_right) + { (&(*arms[0].body).kind, &(*arms[1].body).kind) - } else if match_qpath(path_right, expected_left) && match_qpath(path_left, expected_right) { + } else if is_qpath_def_path(cx, path_right, arms[1].pat.hir_id, expected_left) + && is_qpath_def_path(cx, path_left, arms[0].pat.hir_id, expected_right) + { (&(*arms[1].body).kind, &(*arms[0].body).kind) } else { return None; diff --git a/clippy_lints/src/methods/filter_map_identity.rs b/clippy_lints/src/methods/filter_map_identity.rs index 3a61f4ccad7..403fe8d3546 100644 --- a/clippy_lints/src/methods/filter_map_identity.rs +++ b/clippy_lints/src/methods/filter_map_identity.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{is_trait_method, match_qpath, path_to_local_id, paths}; +use clippy_utils::{is_expr_path_def_path, is_trait_method, path_to_local_id, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; @@ -33,14 +33,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_map_arg: } } - if_chain! { - if let hir::ExprKind::Path(ref qpath) = filter_map_arg.kind; - - if match_qpath(qpath, &paths::STD_CONVERT_IDENTITY); - - then { - apply_lint("called `filter_map(std::convert::identity)` on an `Iterator`"); - } + if is_expr_path_def_path(cx, filter_map_arg, &paths::CONVERT_IDENTITY) { + apply_lint("called `filter_map(std::convert::identity)` on an `Iterator`"); } } } diff --git a/clippy_lints/src/methods/flat_map_identity.rs b/clippy_lints/src/methods/flat_map_identity.rs index dd613d0cd63..25f8434cb94 100644 --- a/clippy_lints/src/methods/flat_map_identity.rs +++ b/clippy_lints/src/methods/flat_map_identity.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{is_trait_method, match_qpath, paths}; +use clippy_utils::{is_expr_path_def_path, is_trait_method, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; @@ -16,8 +16,6 @@ pub(super) fn check<'tcx>( flat_map_span: Span, ) { if is_trait_method(cx, expr, sym::Iterator) { - let arg_node = &flat_map_arg.kind; - let apply_lint = |message: &str| { span_lint_and_sugg( cx, @@ -31,8 +29,8 @@ pub(super) fn check<'tcx>( }; if_chain! { - if let hir::ExprKind::Closure(_, _, body_id, _, _) = arg_node; - let body = cx.tcx.hir().body(*body_id); + if let hir::ExprKind::Closure(_, _, body_id, _, _) = flat_map_arg.kind; + let body = cx.tcx.hir().body(body_id); if let hir::PatKind::Binding(_, _, binding_ident, _) = body.params[0].pat.kind; if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = body.value.kind; @@ -45,14 +43,8 @@ pub(super) fn check<'tcx>( } } - if_chain! { - if let hir::ExprKind::Path(ref qpath) = arg_node; - - if match_qpath(qpath, &paths::STD_CONVERT_IDENTITY); - - then { - apply_lint("called `flat_map(std::convert::identity)` on an `Iterator`"); - } + if is_expr_path_def_path(cx, flat_map_arg, &paths::CONVERT_IDENTITY) { + apply_lint("called `flat_map(std::convert::identity)` on an `Iterator`"); } } } diff --git a/clippy_lints/src/methods/from_iter_instead_of_collect.rs b/clippy_lints/src/methods/from_iter_instead_of_collect.rs index 707c54f7a3c..28d0e8cd4ae 100644 --- a/clippy_lints/src/methods/from_iter_instead_of_collect.rs +++ b/clippy_lints/src/methods/from_iter_instead_of_collect.rs @@ -1,26 +1,23 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::implements_trait; -use clippy_utils::{get_trait_def_id, match_qpath, paths, sugg}; +use clippy_utils::{is_expr_path_def_path, paths, sugg}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::ExprKind; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty::Ty; use rustc_span::sym; use super::FROM_ITER_INSTEAD_OF_COLLECT; -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>], func_kind: &ExprKind<'_>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>], func: &hir::Expr<'_>) { if_chain! { - if let hir::ExprKind::Path(path) = func_kind; - if match_qpath(path, &["from_iter"]); + if is_expr_path_def_path(cx, func, &paths::FROM_ITERATOR_METHOD); let ty = cx.typeck_results().expr_ty(expr); let arg_ty = cx.typeck_results().expr_ty(&args[0]); - if let Some(from_iter_id) = get_trait_def_id(cx, &paths::FROM_ITERATOR); if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator); - if implements_trait(cx, ty, from_iter_id, &[]) && implements_trait(cx, arg_ty, iter_id, &[]); + if implements_trait(cx, arg_ty, iter_id, &[]); then { // `expr` implements `FromIterator` trait let iter_expr = sugg::Sugg::hir(cx, &args[0], "..").maybe_par(); diff --git a/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/clippy_lints/src/methods/manual_saturating_arithmetic.rs index ecb8b72ef46..2fddea7068d 100644 --- a/clippy_lints/src/methods/manual_saturating_arithmetic.rs +++ b/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::match_qpath; +use clippy_utils::is_qpath_def_path; use clippy_utils::source::snippet_with_applicability; use if_chain::if_chain; use rustc_ast::ast; @@ -94,11 +94,11 @@ fn is_min_or_max<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) -> Option LateLintPass<'tcx> for Methods { match expr.kind { hir::ExprKind::Call(func, args) => { - from_iter_instead_of_collect::check(cx, expr, args, &func.kind); + from_iter_instead_of_collect::check(cx, expr, args, func); }, hir::ExprKind::MethodCall(method_call, ref method_span, args, _) => { or_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args); diff --git a/clippy_lints/src/methods/uninit_assumed_init.rs b/clippy_lints/src/methods/uninit_assumed_init.rs index 0ae65c0c01d..1a5894e48d1 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::{match_def_path, match_qpath, paths}; +use clippy_utils::{is_expr_path_def_path, match_def_path, paths}; use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::LateContext; @@ -12,8 +12,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr if_chain! { if let hir::ExprKind::Call(callee, args) = recv.kind; if args.is_empty(); - if let hir::ExprKind::Path(ref path) = callee.kind; - if match_qpath(path, &paths::MEM_MAYBEUNINIT_UNINIT); + 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)); then { span_lint( diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index 66255b77331..b61c4ffe9b3 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -61,8 +61,6 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc } return (true, false); } - // We don't know. It might do anything. - return (true, true); } (true, true) }, diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index d156c1d5bf4..0b0cd9be46c 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -20,8 +20,8 @@ use rustc_span::symbol::sym; use crate::consts::{constant, Constant}; use clippy_utils::sugg::Sugg; use clippy_utils::{ - get_item_name, get_parent_expr, higher, in_constant, is_diag_trait_item, is_integer_const, iter_input_pats, - last_path_segment, match_qpath, unsext, SpanlessEq, + expr_path_res, get_item_name, get_parent_expr, higher, in_constant, is_diag_trait_item, is_integer_const, + iter_input_pats, last_path_segment, match_any_def_paths, paths, unsext, SpanlessEq, }; declare_clippy_lint! { @@ -564,13 +564,13 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: } ) }, - ExprKind::Call(path, v) if v.len() == 1 => { - if let ExprKind::Path(ref path) = path.kind { - if match_qpath(path, &["String", "from_str"]) || match_qpath(path, &["String", "from"]) { - (cx.typeck_results().expr_ty(&v[0]), snippet(cx, v[0].span, "..")) - } else { - return; - } + ExprKind::Call(path, [arg]) => { + if expr_path_res(cx, path) + .opt_def_id() + .and_then(|id| match_any_def_paths(cx, id, &[&paths::FROM_STR_METHOD, &paths::FROM_FROM])) + .is_some() + { + (cx.typeck_results().expr_ty(arg), snippet(cx, arg.span, "..")) } else { return; } diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 2ab1e958ec8..b0674f90678 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -4,7 +4,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_the use clippy_utils::ptr::get_spans; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{is_type_diagnostic_item, match_type, walk_ptrs_hir_ty}; -use clippy_utils::{is_allowed, match_def_path, paths}; +use clippy_utils::{expr_path_res, is_allowed, match_any_def_paths, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{ @@ -417,14 +417,11 @@ fn get_rptr_lm<'tcx>(ty: &'tcx Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, } fn is_null_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - if_chain! { - if let ExprKind::Call(path, []) = expr.kind; - if let ExprKind::Path(ref qpath) = path.kind; - if let Some(fn_def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id(); - then { - match_def_path(cx, fn_def_id, &paths::PTR_NULL) || match_def_path(cx, fn_def_id, &paths::PTR_NULL_MUT) - } else { - false - } + if let ExprKind::Call(pathexp, []) = expr.kind { + expr_path_res(cx, pathexp).opt_def_id().map_or(false, |id| { + match_any_def_paths(cx, id, &[&paths::PTR_NULL, &paths::PTR_NULL_MUT]).is_some() + }) + } else { + false } } diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index ea9ff37e13f..30bee213900 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -3,10 +3,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, match_qpath}; +use clippy_utils::{eq_expr_value, path_to_local_id}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::LangItem::OptionNone; +use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, MatchSource, PatKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -101,15 +101,14 @@ impl QuestionMark { if Self::is_option(cx, subject); if let PatKind::TupleStruct(path1, fields, None) = &arms[0].pat.kind; - if match_qpath(path1, &["Some"]); - if let PatKind::Binding(annot, _, bind, _) = &fields[0].kind; + if is_lang_ctor(cx, path1, OptionSome); + 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) = &arms[0].body.kind; if block.stmts.is_empty(); if let Some(trailing_expr) = &block.expr; - if let ExprKind::Path(path) = &trailing_expr.kind; - if match_qpath(path, &[&bind.as_str()]); + if path_to_local_id(trailing_expr, bind_id); if let PatKind::Wild = arms[1].pat.kind; if Self::expression_returns_none(cx, arms[1].body); diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 3a6151dce71..b565c77aaec 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_opt; -use clippy_utils::{fn_def_id, in_macro, match_qpath}; +use clippy_utils::{fn_def_id, in_macro, path_to_local_id}; use if_chain::if_chain; use rustc_ast::ast::Attribute; use rustc_errors::Applicability; @@ -84,9 +84,8 @@ impl<'tcx> LateLintPass<'tcx> for Return { if local.ty.is_none(); if cx.tcx.hir().attrs(local.hir_id).is_empty(); if let Some(initexpr) = &local.init; - if let PatKind::Binding(.., ident, _) = local.pat.kind; - if let ExprKind::Path(qpath) = &retexpr.kind; - if match_qpath(qpath, &[&*ident.name.as_str()]); + if let PatKind::Binding(_, local_id, _, _) = local.pat.kind; + if path_to_local_id(retexpr, local_id); if !last_statement_borrows(cx, initexpr); if !in_external_macro(cx.sess(), initexpr.span); if !in_external_macro(cx.sess(), retexpr.span); diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index 8cf89ae456e..191781be000 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::sugg::Sugg; -use clippy_utils::{get_enclosing_block, match_qpath, SpanlessEq}; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{get_enclosing_block, is_expr_path_def_path, path_to_local, path_to_local_id, paths, SpanlessEq}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -9,7 +10,7 @@ use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, PatKind, QPath, use rustc_lint::{LateContext, LateLintPass, Lint}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::symbol::Symbol; +use rustc_span::symbol::sym; declare_clippy_lint! { /// **What it does:** Checks slow zero-filled vector initialization @@ -46,8 +47,8 @@ declare_lint_pass!(SlowVectorInit => [SLOW_VECTOR_INITIALIZATION]); /// assigned to a variable. For example, `let mut vec = Vec::with_capacity(0)` or /// `vec = Vec::with_capacity(0)` struct VecAllocation<'tcx> { - /// Symbol of the local variable name - variable_name: Symbol, + /// HirId of the variable + local_id: HirId, /// Reference to the expression which allocates the vector allocation_expr: &'tcx Expr<'tcx>, @@ -72,16 +73,15 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit { if_chain! { if let ExprKind::Assign(left, right, _) = expr.kind; - // Extract variable name - if let ExprKind::Path(QPath::Resolved(_, path)) = left.kind; - if let Some(variable_name) = path.segments.get(0); + // Extract variable + if let Some(local_id) = path_to_local(left); // Extract len argument - if let Some(len_arg) = Self::is_vec_with_capacity(right); + if let Some(len_arg) = Self::is_vec_with_capacity(cx, right); then { let vi = VecAllocation { - variable_name: variable_name.ident.name, + local_id, allocation_expr: right, len_expr: len_arg, }; @@ -95,13 +95,13 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit { // Matches statements which initializes vectors. For example: `let mut vec = Vec::with_capacity(10)` if_chain! { if let StmtKind::Local(local) = stmt.kind; - if let PatKind::Binding(BindingAnnotation::Mutable, .., variable_name, None) = local.pat.kind; + if let PatKind::Binding(BindingAnnotation::Mutable, local_id, _, None) = local.pat.kind; if let Some(init) = local.init; - if let Some(len_arg) = Self::is_vec_with_capacity(init); + if let Some(len_arg) = Self::is_vec_with_capacity(cx, init); then { let vi = VecAllocation { - variable_name: variable_name.name, + local_id, allocation_expr: init, len_expr: len_arg, }; @@ -115,19 +115,18 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit { impl SlowVectorInit { /// Checks if the given expression is `Vec::with_capacity(..)`. It will return the expression /// of the first argument of `with_capacity` call if it matches or `None` if it does not. - fn is_vec_with_capacity<'tcx>(expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { + fn is_vec_with_capacity<'tcx>(cx: &LateContext<'_>, expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { if_chain! { - if let ExprKind::Call(func, args) = expr.kind; - if let ExprKind::Path(ref path) = func.kind; - if match_qpath(path, &["Vec", "with_capacity"]); - if args.len() == 1; - + if let ExprKind::Call(func, [arg]) = expr.kind; + if let ExprKind::Path(QPath::TypeRelative(ty, name)) = func.kind; + if name.ident.as_str() == "with_capacity"; + if is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::vec_type); then { - return Some(&args[0]); + Some(arg) + } else { + None } } - - None } /// Search initialization for the given vector @@ -208,11 +207,9 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { fn search_slow_extend_filling(&mut self, expr: &'tcx Expr<'_>) { if_chain! { if self.initialization_found; - if let ExprKind::MethodCall(path, _, args, _) = expr.kind; - if let ExprKind::Path(ref qpath_subj) = args[0].kind; - if match_qpath(qpath_subj, &[&*self.vec_alloc.variable_name.as_str()]); + if let ExprKind::MethodCall(path, _, [self_arg, extend_arg], _) = expr.kind; + if path_to_local_id(self_arg, self.vec_alloc.local_id); if path.ident.name == sym!(extend); - if let Some(extend_arg) = args.get(1); if self.is_repeat_take(extend_arg); then { @@ -225,11 +222,9 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { fn search_slow_resize_filling(&mut self, expr: &'tcx Expr<'_>) { if_chain! { if self.initialization_found; - if let ExprKind::MethodCall(path, _, args, _) = expr.kind; - if let ExprKind::Path(ref qpath_subj) = args[0].kind; - if match_qpath(qpath_subj, &[&*self.vec_alloc.variable_name.as_str()]); + if let ExprKind::MethodCall(path, _, [self_arg, len_arg, fill_arg], _) = expr.kind; + if path_to_local_id(self_arg, self.vec_alloc.local_id); if path.ident.name == sym!(resize); - if let (Some(len_arg), Some(fill_arg)) = (args.get(1), args.get(2)); // Check that is filled with 0 if let ExprKind::Lit(ref lit) = fill_arg.kind; @@ -252,7 +247,7 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { // Check that take is applied to `repeat(0)` if let Some(repeat_expr) = take_args.get(0); - if Self::is_repeat_zero(repeat_expr); + if self.is_repeat_zero(repeat_expr); // Check that len expression is equals to `with_capacity` expression if let Some(len_arg) = take_args.get(1); @@ -267,21 +262,19 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> { } /// Returns `true` if given expression is `repeat(0)` - fn is_repeat_zero(expr: &Expr<'_>) -> bool { + fn is_repeat_zero(&self, expr: &Expr<'_>) -> bool { if_chain! { - if let ExprKind::Call(fn_expr, repeat_args) = expr.kind; - if let ExprKind::Path(ref qpath_repeat) = fn_expr.kind; - if match_qpath(qpath_repeat, &["repeat"]); - if let Some(repeat_arg) = repeat_args.get(0); + if let ExprKind::Call(fn_expr, [repeat_arg]) = expr.kind; + if is_expr_path_def_path(self.cx, fn_expr, &paths::ITER_REPEAT); if let ExprKind::Lit(ref lit) = repeat_arg.kind; if let LitKind::Int(0, _) = lit.node; then { - return true + true + } else { + false } } - - false } } diff --git a/clippy_lints/src/transmuting_null.rs b/clippy_lints/src/transmuting_null.rs index 755132da591..888ecab1046 100644 --- a/clippy_lints/src/transmuting_null.rs +++ b/clippy_lints/src/transmuting_null.rs @@ -1,6 +1,6 @@ use crate::consts::{constant_context, Constant}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::{match_def_path, paths}; +use clippy_utils::{is_expr_path_def_path, paths}; use if_chain::if_chain; use rustc_ast::LitKind; use rustc_hir::{Expr, ExprKind}; @@ -37,18 +37,15 @@ impl<'tcx> LateLintPass<'tcx> for TransmutingNull { } if_chain! { - if let ExprKind::Call(func, args) = expr.kind; - if args.len() == 1; - if let ExprKind::Path(ref path) = func.kind; - if let Some(func_def_id) = cx.qpath_res(path, func.hir_id).opt_def_id(); - if match_def_path(cx, func_def_id, &paths::TRANSMUTE); - then { + if let ExprKind::Call(func, [arg]) = expr.kind; + if is_expr_path_def_path(cx, func, &paths::TRANSMUTE); + then { // Catching transmute over constants that resolve to `null`. let mut const_eval_context = constant_context(cx, cx.typeck_results()); if_chain! { - if let ExprKind::Path(ref _qpath) = args[0].kind; - let x = const_eval_context.expr(&args[0]); + if let ExprKind::Path(ref _qpath) = arg.kind; + let x = const_eval_context.expr(arg); if let Some(Constant::RawPtr(0)) = x; then { span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG) @@ -58,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for TransmutingNull { // Catching: // `std::mem::transmute(0 as *const i32)` if_chain! { - if let ExprKind::Cast(inner_expr, _cast_ty) = args[0].kind; + if let ExprKind::Cast(inner_expr, _cast_ty) = arg.kind; if let ExprKind::Lit(ref lit) = inner_expr.kind; if let LitKind::Int(0, _) = lit.node; then { @@ -69,10 +66,8 @@ impl<'tcx> LateLintPass<'tcx> for TransmutingNull { // Catching: // `std::mem::transmute(std::ptr::null::())` if_chain! { - if let ExprKind::Call(func1, []) = args[0].kind; - if let ExprKind::Path(ref path1) = func1.kind; - if let Some(func1_def_id) = cx.qpath_res(path1, func1.hir_id).opt_def_id(); - if match_def_path(cx, func1_def_id, &paths::PTR_NULL); + if let ExprKind::Call(func1, []) = arg.kind; + if is_expr_path_def_path(cx, func1, &paths::PTR_NULL); then { span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG) } diff --git a/clippy_lints/src/types/borrowed_box.rs b/clippy_lints/src/types/borrowed_box.rs index 1425d8f3f37..bdeff035e5e 100644 --- a/clippy_lints/src/types/borrowed_box.rs +++ b/clippy_lints/src/types/borrowed_box.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; -use clippy_utils::{match_path, paths}; +use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{ @@ -28,7 +28,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m _ => None, }); then { - if is_any_trait(inner) { + if is_any_trait(cx, inner) { // Ignore `Box` types; see issue #1884 for details. return false; } @@ -84,13 +84,14 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m } // Returns true if given type is `Any` trait. -fn is_any_trait(t: &hir::Ty<'_>) -> bool { +fn is_any_trait(cx: &LateContext<'_>, t: &hir::Ty<'_>) -> bool { if_chain! { if let TyKind::TraitObject(traits, ..) = t.kind; if !traits.is_empty(); + if let Some(trait_did) = traits[0].trait_ref.trait_def_id(); // Only Send/Sync can be used as additional traits, so it is enough to // check only the first trait. - if match_path(traits[0].trait_ref.path, &paths::ANY_TRAIT); + if match_def_path(cx, trait_did, &paths::ANY_TRAIT); then { return true; } diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index d2c0f60d296..f2f1410aed7 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -104,14 +104,12 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { if_chain! { if !in_macro(ret_expr.span); // Check if a function call. - if let ExprKind::Call(func, args) = ret_expr.kind; - // Get the Path of the function call. - if let ExprKind::Path(ref qpath) = func.kind; + if let ExprKind::Call(func, [arg]) = ret_expr.kind; // Check if OPTION_SOME or RESULT_OK, depending on return type. + if let ExprKind::Path(qpath) = &func.kind; if is_lang_ctor(cx, qpath, lang_item); - if args.len() == 1; // Make sure the function argument does not contain a return expression. - if !contains_return(&args[0]); + if !contains_return(arg); then { suggs.push( ( @@ -119,7 +117,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { if inner_type.is_unit() { "".to_string() } else { - snippet(cx, args[0].span.source_callsite(), "..").to_string() + snippet(cx, arg.span.source_callsite(), "..").to_string() } ) ); diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index cf8039d6059..3d3d0e19d26 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -3,7 +3,8 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sug use clippy_utils::source::snippet; use clippy_utils::ty::match_type; use clippy_utils::{ - is_else_clause, is_expn_of, match_def_path, match_qpath, method_calls, path_to_res, paths, run_lints, SpanlessEq, + is_else_clause, is_expn_of, is_expr_path_def_path, match_def_path, method_calls, path_to_res, paths, run_lints, + SpanlessEq, }; use if_chain::if_chain; use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, ModKind, NodeId}; @@ -578,8 +579,7 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { if_chain! { if let ExprKind::Call(func, and_then_args) = expr.kind; - if let ExprKind::Path(ref path) = func.kind; - if match_qpath(path, &["span_lint_and_then"]); + if is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]); if and_then_args.len() == 5; if let ExprKind::Closure(_, _, body_id, _, _) = &and_then_args[4].kind; let body = cx.tcx.hir().body(*body_id); @@ -761,8 +761,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem { if_chain! { // Check if this is a call to utils::match_type() if let ExprKind::Call(fn_path, [context, ty, ty_path]) = expr.kind; - if let ExprKind::Path(fn_qpath) = &fn_path.kind; - if match_qpath(fn_qpath, &["utils", "match_type"]); + if is_expr_path_def_path(cx, fn_path, &["clippy_utils", "ty", "match_type"]); // Extract the path to the matched type if let Some(segments) = path_to_matched_type(cx, ty_path); let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect(); @@ -771,6 +770,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem { let diag_items = cx.tcx.diagnostic_items(ty_did.krate); if let Some(item_name) = diag_items.iter().find_map(|(k, v)| if *v == ty_did { Some(k) } else { None }); then { + // TODO: check paths constants from external crates. let cx_snippet = snippet(cx, context.span, "_"); let ty_snippet = snippet(cx, ty.span, "_"); @@ -778,9 +778,9 @@ impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem { cx, MATCH_TYPE_ON_DIAGNOSTIC_ITEM, expr.span, - "usage of `utils::match_type()` on a type diagnostic item", + "usage of `clippy_utils::ty::match_type()` on a type diagnostic item", "try", - format!("utils::is_type_diagnostic_item({}, {}, sym::{})", cx_snippet, ty_snippet, item_name), + format!("clippy_utils::ty::is_type_diagnostic_item({}, {}, sym::{})", cx_snippet, ty_snippet, item_name), Applicability::MaybeIncorrect, ); } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 8e1a2105b96..4a523520a43 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -397,6 +397,29 @@ pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool { } } +/// If the expression is a path, resolve it. Otherwise, return `Res::Err`. +pub fn expr_path_res(cx: &LateContext<'_>, expr: &Expr<'_>) -> Res { + if let ExprKind::Path(p) = &expr.kind { + cx.qpath_res(p, expr.hir_id) + } else { + Res::Err + } +} + +/// Resolves the path to a `DefId` and checks if it matches the given path. +pub fn is_qpath_def_path(cx: &LateContext<'_>, path: &QPath<'_>, hir_id: HirId, segments: &[&str]) -> bool { + cx.qpath_res(path, hir_id) + .opt_def_id() + .map_or(false, |id| match_def_path(cx, id, segments)) +} + +/// If the expression is a path, resolves it to a `DefId` and checks if it matches the given path. +pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[&str]) -> bool { + expr_path_res(cx, expr) + .opt_def_id() + .map_or(false, |id| match_def_path(cx, id, segments)) +} + /// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the /// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from /// `QPath::Resolved.1.res.opt_def_id()`. @@ -425,20 +448,6 @@ pub fn match_path(path: &Path<'_>, segments: &[&str]) -> bool { .all(|(a, b)| a.ident.name.as_str() == *b) } -/// Matches a `Path` against a slice of segment string literals, e.g. -/// -/// # Examples -/// ```rust,ignore -/// match_path_ast(path, &["std", "rt", "begin_unwind"]) -/// ``` -pub fn match_path_ast(path: &ast::Path, segments: &[&str]) -> bool { - path.segments - .iter() - .rev() - .zip(segments.iter().rev()) - .all(|(a, b)| a.ident.name.as_str() == *b) -} - /// If the expression is a path to a local, returns the canonical `HirId` of the local. pub fn path_to_local(expr: &Expr<'_>) -> Option { if let ExprKind::Path(QPath::Resolved(None, ref path)) = expr.kind { @@ -1148,29 +1157,47 @@ pub fn match_function_call<'tcx>( None } +/// Checks if the given `DefId` matches any of the paths. Returns the index of matching path, if +/// any. +pub fn match_any_def_paths(cx: &LateContext<'_>, did: DefId, paths: &[&[&str]]) -> Option { + let search_path = cx.get_def_path(did); + paths + .iter() + .position(|p| p.iter().map(|x| Symbol::intern(x)).eq(search_path.iter().cloned())) +} + +/// Checks if the given `DefId` matches the path. pub fn match_def_path<'tcx>(cx: &LateContext<'tcx>, did: DefId, syms: &[&str]) -> bool { - // We have to convert `syms` to `&[Symbol]` here because rustc's `match_def_path` - // accepts only that. We should probably move to Symbols in Clippy as well. - let syms = syms.iter().map(|p| Symbol::intern(p)).collect::>(); - cx.match_def_path(did, &syms) + // We should probably move to Symbols in Clippy as well rather than interning every time. + let path = cx.get_def_path(did); + syms.iter().map(|x| Symbol::intern(x)).eq(path.iter().cloned()) } -pub fn match_panic_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx [Expr<'tcx>]> { - match_function_call(cx, expr, &paths::BEGIN_PANIC) - .or_else(|| match_function_call(cx, expr, &paths::BEGIN_PANIC_FMT)) - .or_else(|| match_function_call(cx, expr, &paths::PANIC_ANY)) - .or_else(|| match_function_call(cx, expr, &paths::PANICKING_PANIC)) - .or_else(|| match_function_call(cx, expr, &paths::PANICKING_PANIC_FMT)) - .or_else(|| match_function_call(cx, expr, &paths::PANICKING_PANIC_STR)) +pub fn match_panic_call(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { + if let ExprKind::Call(func, [arg]) = expr.kind { + expr_path_res(cx, func) + .opt_def_id() + .map_or(false, |id| match_panic_def_id(cx, id)) + .then(|| arg) + } else { + None + } } pub fn match_panic_def_id(cx: &LateContext<'_>, did: DefId) -> bool { - match_def_path(cx, did, &paths::BEGIN_PANIC) - || match_def_path(cx, did, &paths::BEGIN_PANIC_FMT) - || match_def_path(cx, did, &paths::PANIC_ANY) - || match_def_path(cx, did, &paths::PANICKING_PANIC) - || match_def_path(cx, did, &paths::PANICKING_PANIC_FMT) - || match_def_path(cx, did, &paths::PANICKING_PANIC_STR) + match_any_def_paths( + cx, + did, + &[ + &paths::BEGIN_PANIC, + &paths::BEGIN_PANIC_FMT, + &paths::PANIC_ANY, + &paths::PANICKING_PANIC, + &paths::PANICKING_PANIC_FMT, + &paths::PANICKING_PANIC_STR, + ], + ) + .is_some() } /// Returns the list of condition expressions and the list of blocks in a diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index ed8915f59e1..21011dbded6 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -4,7 +4,7 @@ //! Whenever possible, please consider diagnostic items over hardcoded paths. //! See for more information. -pub const ANY_TRAIT: [&str; 3] = ["std", "any", "Any"]; +pub const ANY_TRAIT: [&str; 3] = ["core", "any", "Any"]; 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"]; @@ -42,6 +42,8 @@ pub const FMT_ARGUMENTS_NEW_V1_FORMATTED: [&str; 4] = ["core", "fmt", "Arguments pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"]; 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"]; +pub const FROM_STR_METHOD: [&str; 5] = ["core", "str", "traits", "FromStr", "from_str"]; pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; pub const HASH: [&str; 3] = ["core", "hash", "Hash"]; pub const HASHMAP: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"]; @@ -58,8 +60,9 @@ pub const INTO: [&str; 3] = ["core", "convert", "Into"]; pub const INTO_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "IntoIterator"]; pub const IO_READ: [&str; 3] = ["std", "io", "Read"]; pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"]; -pub const IPADDR_V4: [&str; 4] = ["std", "net", "IpAddr", "V4"]; -pub const IPADDR_V6: [&str; 4] = ["std", "net", "IpAddr", "V6"]; +pub const IPADDR_V4: [&str; 5] = ["std", "net", "ip", "IpAddr", "V4"]; +pub const IPADDR_V6: [&str; 5] = ["std", "net", "ip", "IpAddr", "V6"]; +pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"]; #[cfg(feature = "internal-lints")] pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"]; #[cfg(feature = "internal-lints")] @@ -126,7 +129,6 @@ pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"]; pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"]; pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"]; pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"]; -pub const REPEAT: [&str; 3] = ["core", "iter", "repeat"]; pub const RESULT: [&str; 3] = ["core", "result", "Result"]; pub const RESULT_ERR: [&str; 4] = ["core", "result", "Result", "Err"]; pub const RESULT_OK: [&str; 4] = ["core", "result", "Result", "Ok"]; @@ -140,7 +142,7 @@ pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "", "into_vec pub const SLICE_ITER: [&str; 4] = ["core", "slice", "iter", "Iter"]; pub const STDERR: [&str; 4] = ["std", "io", "stdio", "stderr"]; pub const STDOUT: [&str; 4] = ["std", "io", "stdio", "stdout"]; -pub const STD_CONVERT_IDENTITY: [&str; 3] = ["std", "convert", "identity"]; +pub const CONVERT_IDENTITY: [&str; 3] = ["core", "convert", "identity"]; pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"]; pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"]; pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"]; diff --git a/doc/adding_lints.md b/doc/adding_lints.md index 99b86953d51..50f0d724016 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -625,7 +625,7 @@ in the following steps: Here are some pointers to things you are likely going to need for every lint: * [Clippy utils][utils] - Various helper functions. Maybe the function you need - is already in here (`implements_trait`, `match_path`, `snippet`, etc) + is already in here (`implements_trait`, `match_def_path`, `snippet`, etc) * [Clippy diagnostics][diagnostics] * [The `if_chain` macro][if_chain] * [`from_expansion`][from_expansion] and [`in_external_macro`][in_external_macro] diff --git a/tests/ui-internal/collapsible_span_lint_calls.fixed b/tests/ui-internal/collapsible_span_lint_calls.fixed index e588c23345e..7764cc8da78 100644 --- a/tests/ui-internal/collapsible_span_lint_calls.fixed +++ b/tests/ui-internal/collapsible_span_lint_calls.fixed @@ -2,58 +2,18 @@ #![deny(clippy::internal)] #![feature(rustc_private)] +extern crate clippy_utils; extern crate rustc_ast; extern crate rustc_errors; extern crate rustc_lint; extern crate rustc_session; extern crate rustc_span; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then}; use rustc_ast::ast::Expr; -use rustc_errors::{Applicability, DiagnosticBuilder}; -use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Span; - -#[allow(unused_variables)] -pub fn span_lint_and_then<'a, T: LintContext, F>(cx: &'a T, lint: &'static Lint, sp: Span, msg: &str, f: F) -where - F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>), -{ -} - -#[allow(unused_variables)] -fn span_lint_and_help<'a, T: LintContext>( - cx: &'a T, - lint: &'static Lint, - span: Span, - msg: &str, - option_span: Option, - help: &str, -) { -} - -#[allow(unused_variables)] -fn span_lint_and_note<'a, T: LintContext>( - cx: &'a T, - lint: &'static Lint, - span: Span, - msg: &str, - note_span: Option, - note: &str, -) { -} - -#[allow(unused_variables)] -fn span_lint_and_sugg<'a, T: LintContext>( - cx: &'a T, - lint: &'static Lint, - sp: Span, - msg: &str, - help: &str, - sugg: String, - applicability: Applicability, -) { -} declare_tool_lint! { pub clippy::TEST_LINT, diff --git a/tests/ui-internal/collapsible_span_lint_calls.rs b/tests/ui-internal/collapsible_span_lint_calls.rs index d5dd3bb562b..bdd296db832 100644 --- a/tests/ui-internal/collapsible_span_lint_calls.rs +++ b/tests/ui-internal/collapsible_span_lint_calls.rs @@ -2,58 +2,18 @@ #![deny(clippy::internal)] #![feature(rustc_private)] +extern crate clippy_utils; extern crate rustc_ast; extern crate rustc_errors; extern crate rustc_lint; extern crate rustc_session; extern crate rustc_span; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then}; use rustc_ast::ast::Expr; -use rustc_errors::{Applicability, DiagnosticBuilder}; -use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Span; - -#[allow(unused_variables)] -pub fn span_lint_and_then<'a, T: LintContext, F>(cx: &'a T, lint: &'static Lint, sp: Span, msg: &str, f: F) -where - F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>), -{ -} - -#[allow(unused_variables)] -fn span_lint_and_help<'a, T: LintContext>( - cx: &'a T, - lint: &'static Lint, - span: Span, - msg: &str, - option_span: Option, - help: &str, -) { -} - -#[allow(unused_variables)] -fn span_lint_and_note<'a, T: LintContext>( - cx: &'a T, - lint: &'static Lint, - span: Span, - msg: &str, - note_span: Option, - note: &str, -) { -} - -#[allow(unused_variables)] -fn span_lint_and_sugg<'a, T: LintContext>( - cx: &'a T, - lint: &'static Lint, - sp: Span, - msg: &str, - help: &str, - sugg: String, - applicability: Applicability, -) { -} declare_tool_lint! { pub clippy::TEST_LINT, diff --git a/tests/ui-internal/collapsible_span_lint_calls.stderr b/tests/ui-internal/collapsible_span_lint_calls.stderr index 874d4a9f255..0632b038577 100644 --- a/tests/ui-internal/collapsible_span_lint_calls.stderr +++ b/tests/ui-internal/collapsible_span_lint_calls.stderr @@ -1,5 +1,5 @@ error: this call is collapsible - --> $DIR/collapsible_span_lint_calls.rs:75:9 + --> $DIR/collapsible_span_lint_calls.rs:35:9 | LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { LL | | db.span_suggestion(expr.span, help_msg, sugg.to_string(), Applicability::MachineApplicable); @@ -14,7 +14,7 @@ LL | #![deny(clippy::internal)] = note: `#[deny(clippy::collapsible_span_lint_calls)]` implied by `#[deny(clippy::internal)]` error: this call is collapsible - --> $DIR/collapsible_span_lint_calls.rs:78:9 + --> $DIR/collapsible_span_lint_calls.rs:38:9 | LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { LL | | db.span_help(expr.span, help_msg); @@ -22,7 +22,7 @@ LL | | }); | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg)` error: this call is collapsible - --> $DIR/collapsible_span_lint_calls.rs:81:9 + --> $DIR/collapsible_span_lint_calls.rs:41:9 | LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { LL | | db.help(help_msg); @@ -30,7 +30,7 @@ LL | | }); | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg)` error: this call is collspible - --> $DIR/collapsible_span_lint_calls.rs:84:9 + --> $DIR/collapsible_span_lint_calls.rs:44:9 | LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { LL | | db.span_note(expr.span, note_msg); @@ -38,7 +38,7 @@ LL | | }); | |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg)` error: this call is collspible - --> $DIR/collapsible_span_lint_calls.rs:87:9 + --> $DIR/collapsible_span_lint_calls.rs:47:9 | LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { LL | | db.note(note_msg); diff --git a/tests/ui-internal/match_type_on_diag_item.rs b/tests/ui-internal/match_type_on_diag_item.rs index fe950b0aa7c..063f0c6460c 100644 --- a/tests/ui-internal/match_type_on_diag_item.rs +++ b/tests/ui-internal/match_type_on_diag_item.rs @@ -1,29 +1,18 @@ #![deny(clippy::internal)] #![feature(rustc_private)] +extern crate clippy_utils; extern crate rustc_hir; extern crate rustc_lint; extern crate rustc_middle; + #[macro_use] extern crate rustc_session; +use clippy_utils::{paths, ty::match_type}; use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty; -mod paths { - pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"]; -} - -mod utils { - use super::*; - - pub fn match_type(_cx: &LateContext<'_>, _ty: Ty<'_>, _path: &[&str]) -> bool { - false - } -} - -use utils::match_type; - declare_lint! { pub TEST_LINT, Warn, @@ -38,12 +27,12 @@ impl<'tcx> LateLintPass<'tcx> for Pass { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr) { let ty = cx.typeck_results().expr_ty(expr); - let _ = match_type(cx, ty, &paths::VEC); + let _ = match_type(cx, ty, &paths::VEC); // FIXME: Doesn't lint external paths let _ = match_type(cx, ty, &OPTION); let _ = match_type(cx, ty, &["core", "result", "Result"]); let rc_path = &["alloc", "rc", "Rc"]; - let _ = utils::match_type(cx, ty, rc_path); + let _ = clippy_utils::ty::match_type(cx, ty, rc_path); } } diff --git a/tests/ui-internal/match_type_on_diag_item.stderr b/tests/ui-internal/match_type_on_diag_item.stderr index 82465dbaf6e..71472960565 100644 --- a/tests/ui-internal/match_type_on_diag_item.stderr +++ b/tests/ui-internal/match_type_on_diag_item.stderr @@ -1,8 +1,8 @@ -error: usage of `utils::match_type()` on a type diagnostic item - --> $DIR/match_type_on_diag_item.rs:41:17 +error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item + --> $DIR/match_type_on_diag_item.rs:31:17 | -LL | let _ = match_type(cx, ty, &paths::VEC); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::vec_type)` +LL | let _ = match_type(cx, ty, &OPTION); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::option_type)` | note: the lint level is defined here --> $DIR/match_type_on_diag_item.rs:1:9 @@ -11,23 +11,17 @@ LL | #![deny(clippy::internal)] | ^^^^^^^^^^^^^^^^ = note: `#[deny(clippy::match_type_on_diagnostic_item)]` implied by `#[deny(clippy::internal)]` -error: usage of `utils::match_type()` on a type diagnostic item - --> $DIR/match_type_on_diag_item.rs:42:17 - | -LL | let _ = match_type(cx, ty, &OPTION); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::option_type)` - -error: usage of `utils::match_type()` on a type diagnostic item - --> $DIR/match_type_on_diag_item.rs:43:17 +error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item + --> $DIR/match_type_on_diag_item.rs:32:17 | LL | let _ = match_type(cx, ty, &["core", "result", "Result"]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::result_type)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::result_type)` -error: usage of `utils::match_type()` on a type diagnostic item - --> $DIR/match_type_on_diag_item.rs:46:17 +error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item + --> $DIR/match_type_on_diag_item.rs:35:17 | -LL | let _ = utils::match_type(cx, ty, rc_path); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `utils::is_type_diagnostic_item(cx, ty, sym::Rc)` +LL | let _ = clippy_utils::ty::match_type(cx, ty, rc_path); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::Rc)` -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/repl_uninit.rs b/tests/ui/repl_uninit.rs index ad5b8e4857d..6c7e2b854dc 100644 --- a/tests/ui/repl_uninit.rs +++ b/tests/ui/repl_uninit.rs @@ -1,5 +1,5 @@ -#![allow(deprecated, invalid_value)] -#![warn(clippy::all)] +#![allow(deprecated, invalid_value, clippy::uninit_assumed_init)] +#![warn(clippy::mem_replace_with_uninit)] use std::mem; diff --git a/tests/ui/uninit.rs b/tests/ui/uninit.rs index f42b884e0f0..1ed3883c1f0 100644 --- a/tests/ui/uninit.rs +++ b/tests/ui/uninit.rs @@ -1,6 +1,6 @@ #![feature(stmt_expr_attributes)] -use std::mem::MaybeUninit; +use std::mem::{self, MaybeUninit}; fn main() { let _: usize = unsafe { MaybeUninit::uninit().assume_init() }; @@ -19,4 +19,7 @@ fn main() { // This is OK, because all constitutent types are uninit-compatible. let _: (MaybeUninit, [MaybeUninit; 2]) = unsafe { MaybeUninit::uninit().assume_init() }; + + // Was a false negative. + let _: usize = unsafe { mem::MaybeUninit::uninit().assume_init() }; } diff --git a/tests/ui/uninit.stderr b/tests/ui/uninit.stderr index a37233ecdda..85b64a8419a 100644 --- a/tests/ui/uninit.stderr +++ b/tests/ui/uninit.stderr @@ -12,5 +12,11 @@ error: this call for this type may be undefined behavior LL | let _: [u8; 0] = unsafe { MaybeUninit::uninit().assume_init() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: this call for this type may be undefined behavior + --> $DIR/uninit.rs:24:29 + | +LL | let _: usize = unsafe { mem::MaybeUninit::uninit().assume_init() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors -- cgit 1.4.1-3-g733a5 From b6581636bd9cfb16337a9ffcecd1b38e68a6abce Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 3 Jan 2021 14:01:01 -0500 Subject: Improve `redundant_pattern_matching` Add a note when the drop order change may result in different behaviour. --- clippy_lints/src/matches.rs | 229 ++++++++++++++++++--- .../ui/redundant_pattern_matching_drop_order.fixed | 58 ++++++ tests/ui/redundant_pattern_matching_drop_order.rs | 58 ++++++ .../redundant_pattern_matching_drop_order.stderr | 171 +++++++++++++++ tests/ui/redundant_pattern_matching_option.fixed | 7 +- tests/ui/redundant_pattern_matching_option.rs | 7 +- tests/ui/redundant_pattern_matching_option.stderr | 38 ++-- tests/ui/redundant_pattern_matching_poll.fixed | 7 +- tests/ui/redundant_pattern_matching_poll.rs | 7 +- tests/ui/redundant_pattern_matching_poll.stderr | 36 ++-- tests/ui/redundant_pattern_matching_result.fixed | 3 +- tests/ui/redundant_pattern_matching_result.rs | 3 +- tests/ui/redundant_pattern_matching_result.stderr | 44 ++-- 13 files changed, 579 insertions(+), 89 deletions(-) create mode 100644 tests/ui/redundant_pattern_matching_drop_order.fixed create mode 100644 tests/ui/redundant_pattern_matching_drop_order.rs create mode 100644 tests/ui/redundant_pattern_matching_drop_order.stderr (limited to 'tests') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 8f1112cff7b..dc5232085ed 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -423,7 +423,12 @@ declare_clippy_lint! { /// **Why is this bad?** It's more concise and clear to just use the proper /// utility function /// - /// **Known problems:** None. + /// **Known problems:** This will change the drop order for the matched type. Both `if let` and + /// `while let` will drop the value at the end of the block, both `if` and `while` will drop the + /// value before entering the block. For most types this change will not matter, but for a few + /// types this will not be an acceptable change (e.g. locks). See the + /// [reference](https://doc.rust-lang.org/reference/destructors.html#drop-scopes) for more about + /// drop order. /// /// **Example:** /// @@ -1700,55 +1705,204 @@ where mod redundant_pattern_match { use super::REDUNDANT_PATTERN_MATCHING; use clippy_utils::diagnostics::span_lint_and_then; - use clippy_utils::source::snippet; + use clippy_utils::source::{snippet, snippet_with_applicability}; + use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, is_type_lang_item, match_type}; use clippy_utils::{is_lang_ctor, is_qpath_def_path, is_trait_method, paths}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk}; - use rustc_hir::{Arm, Expr, ExprKind, MatchSource, PatKind, QPath}; + use rustc_hir::{ + intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor}, + Arm, Block, Expr, ExprKind, LangItem, MatchSource, Node, PatKind, QPath, + }; use rustc_lint::LateContext; + use rustc_middle::ty::{self, subst::GenericArgKind, Ty}; use rustc_span::sym; pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Match(op, arms, ref match_source) = &expr.kind { match match_source { MatchSource::Normal => find_sugg_for_match(cx, expr, op, arms), - MatchSource::IfLetDesugar { .. } => find_sugg_for_if_let(cx, expr, op, arms, "if"), - MatchSource::WhileLetDesugar => find_sugg_for_if_let(cx, expr, op, arms, "while"), + MatchSource::IfLetDesugar { contains_else_clause } => { + find_sugg_for_if_let(cx, expr, op, &arms[0], "if", *contains_else_clause) + }, + MatchSource::WhileLetDesugar => find_sugg_for_if_let(cx, expr, op, &arms[0], "while", false), _ => {}, } } } + // Check if the drop order for a type matters + fn type_needs_ordered_drop(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + if !ty.needs_drop(cx.tcx, cx.param_env) { + false + } else if cx + .tcx + .lang_items() + .drop_trait() + .map_or(false, |id| !implements_trait(cx, ty, id, &[])) + { + // This type doesn't implement drop, so no side effects here. + // Check if any component type has any. + match ty.kind() { + ty::Tuple(_) => ty.tuple_fields().any(|ty| type_needs_ordered_drop(cx, ty)), + ty::Array(ty, _) => type_needs_ordered_drop(cx, ty), + ty::Adt(adt, subs) => adt + .all_fields() + .map(|f| f.ty(cx.tcx, subs)) + .any(|ty| type_needs_ordered_drop(cx, ty)), + _ => true, + } + } + // Check for std types which implement drop, but only for memory allocation. + else if is_type_diagnostic_item(cx, ty, sym::vec_type) + || is_type_lang_item(cx, ty, LangItem::OwnedBox) + || is_type_diagnostic_item(cx, ty, sym::Rc) + || is_type_diagnostic_item(cx, ty, sym::Arc) + || is_type_diagnostic_item(cx, ty, sym::cstring_type) + || match_type(cx, ty, &paths::BTREEMAP) + || match_type(cx, ty, &paths::LINKED_LIST) + || match_type(cx, ty, &paths::WEAK_RC) + || match_type(cx, ty, &paths::WEAK_ARC) + { + // Check all of the generic arguments. + if let ty::Adt(_, subs) = ty.kind() { + subs.types().any(|ty| type_needs_ordered_drop(cx, ty)) + } else { + true + } + } else { + true + } + } + + // Extract the generic arguments out of a type + fn try_get_generic_ty(ty: Ty<'_>, index: usize) -> Option> { + if_chain! { + if let ty::Adt(_, subs) = ty.kind(); + if let Some(sub) = subs.get(index); + if let GenericArgKind::Type(sub_ty) = sub.unpack(); + then { + Some(sub_ty) + } else { + None + } + } + } + + // Checks if there are any temporaries created in the given expression for which drop order + // matters. + fn temporaries_need_ordered_drop(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + struct V<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + res: bool, + } + impl<'a, 'tcx> Visitor<'tcx> for V<'a, 'tcx> { + type Map = ErasedMap<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { + match expr.kind { + // Taking the reference of a value leaves a temporary + // e.g. In `&String::new()` the string is a temporary value. + // Remaining fields are temporary values + // e.g. In `(String::new(), 0).1` the string is a temporary value. + ExprKind::AddrOf(_, _, expr) | ExprKind::Field(expr, _) => { + if !matches!(expr.kind, ExprKind::Path(_)) { + if type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(expr)) { + self.res = true; + } else { + self.visit_expr(expr); + } + } + }, + // the base type is alway taken by reference. + // e.g. In `(vec![0])[0]` the vector is a temporary value. + ExprKind::Index(base, index) => { + if !matches!(base.kind, ExprKind::Path(_)) { + if type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(base)) { + self.res = true; + } else { + self.visit_expr(base); + } + } + self.visit_expr(index); + }, + // Method calls can take self by reference. + // e.g. In `String::new().len()` the string is a temporary value. + ExprKind::MethodCall(_, _, [self_arg, args @ ..], _) => { + if !matches!(self_arg.kind, ExprKind::Path(_)) { + let self_by_ref = self + .cx + .typeck_results() + .type_dependent_def_id(expr.hir_id) + .map_or(false, |id| self.cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref()); + if self_by_ref + && type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(self_arg)) + { + self.res = true; + } else { + self.visit_expr(self_arg) + } + } + args.iter().for_each(|arg| self.visit_expr(arg)); + }, + // Either explicitly drops values, or changes control flow. + ExprKind::DropTemps(_) + | ExprKind::Ret(_) + | ExprKind::Break(..) + | ExprKind::Yield(..) + | ExprKind::Block(Block { expr: None, .. }, _) + | ExprKind::Loop(..) => (), + + // Only consider the final expression. + ExprKind::Block(Block { expr: Some(expr), .. }, _) => self.visit_expr(expr), + + _ => walk_expr(self, expr), + } + } + } + + let mut v = V { cx, res: false }; + v.visit_expr(expr); + v.res + } + fn find_sugg_for_if_let<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, - op: &Expr<'_>, - arms: &[Arm<'_>], + op: &'tcx Expr<'tcx>, + arm: &Arm<'_>, keyword: &'static str, + has_else: bool, ) { // also look inside refs - let mut kind = &arms[0].pat.kind; + let mut kind = &arm.pat.kind; // if we have &None for example, peel it so we can detect "if let None = x" if let PatKind::Ref(inner, _mutability) = kind { kind = &inner.kind; } - let good_method = match kind { + let op_ty = cx.typeck_results().expr_ty(op); + // Determine which function should be used, and the type contained by the corresponding + // variant. + let (good_method, inner_ty) = match kind { PatKind::TupleStruct(ref path, [sub_pat], _) => { if let PatKind::Wild = sub_pat.kind { if is_lang_ctor(cx, path, ResultOk) { - "is_ok()" + ("is_ok()", try_get_generic_ty(op_ty, 0).unwrap_or(op_ty)) } else if is_lang_ctor(cx, path, ResultErr) { - "is_err()" + ("is_err()", try_get_generic_ty(op_ty, 1).unwrap_or(op_ty)) } else if is_lang_ctor(cx, path, OptionSome) { - "is_some()" + ("is_some()", op_ty) } else if is_lang_ctor(cx, path, PollReady) { - "is_ready()" + ("is_ready()", op_ty) } else if is_qpath_def_path(cx, path, sub_pat.hir_id, &paths::IPADDR_V4) { - "is_ipv4()" + ("is_ipv4()", op_ty) } else if is_qpath_def_path(cx, path, sub_pat.hir_id, &paths::IPADDR_V6) { - "is_ipv6()" + ("is_ipv6()", op_ty) } else { return; } @@ -1757,17 +1911,36 @@ mod redundant_pattern_match { } }, PatKind::Path(ref path) => { - if is_lang_ctor(cx, path, OptionNone) { + let method = if is_lang_ctor(cx, path, OptionNone) { "is_none()" } else if is_lang_ctor(cx, path, PollPending) { "is_pending()" } else { return; - } + }; + // `None` and `Pending` don't have an inner type. + (method, cx.tcx.types.unit) }, _ => return, }; + // If this is the last expression in a block or there is an else clause then the whole + // type needs to be considered, not just the inner type of the branch being matched on. + // Note the last expression in a block is dropped after all local bindings. + let check_ty = if has_else + || (keyword == "if" && matches!(cx.tcx.hir().parent_iter(expr.hir_id).next(), Some((_, Node::Block(..))))) + { + op_ty + } else { + inner_ty + }; + + // All temporaries created in the scrutinee expression are dropped at the same time as the + // scrutinee would be, so they have to be considered as well. + // e.g. in `if let Some(x) = foo.lock().unwrap().baz.as_ref() { .. }` the lock will be held + // for the duration if body. + let needs_drop = type_needs_ordered_drop(cx, check_ty) || temporaries_need_ordered_drop(cx, op); + // check that `while_let_on_iterator` lint does not trigger if_chain! { if keyword == "while"; @@ -1786,7 +1959,7 @@ mod redundant_pattern_match { span_lint_and_then( cx, REDUNDANT_PATTERN_MATCHING, - arms[0].pat.span, + arm.pat.span, &format!("redundant pattern matching, consider using `{}`", good_method), |diag| { // while let ... = ... { ... } @@ -1800,12 +1973,20 @@ mod redundant_pattern_match { // while let ... = ... { ... } // ^^^^^^^^^^^^^^^^^^^ let span = expr_span.until(op_span.shrink_to_hi()); - diag.span_suggestion( - span, - "try this", - format!("{} {}.{}", keyword, snippet(cx, op_span, "_"), good_method), - Applicability::MachineApplicable, // snippet - ); + + let mut app = if needs_drop { + Applicability::MaybeIncorrect + } else { + Applicability::MachineApplicable + }; + let sugg = snippet_with_applicability(cx, op_span, "_", &mut app); + + diag.span_suggestion(span, "try this", format!("{} {}.{}", keyword, sugg, good_method), app); + + if needs_drop { + diag.note("this will change drop order of the result, as well as all temporaries"); + diag.note("add `#[allow(clippy::redundant_pattern_matching)]` if this is important"); + } }, ); } diff --git a/tests/ui/redundant_pattern_matching_drop_order.fixed b/tests/ui/redundant_pattern_matching_drop_order.fixed new file mode 100644 index 00000000000..794ed542435 --- /dev/null +++ b/tests/ui/redundant_pattern_matching_drop_order.fixed @@ -0,0 +1,58 @@ +// run-rustfix + +// Issue #5746 +#![warn(clippy::redundant_pattern_matching)] +#![allow(clippy::if_same_then_else)] +use std::task::Poll::{Pending, Ready}; + +fn main() { + let m = std::sync::Mutex::new((0, 0)); + + // Result + if m.lock().is_ok() {} + if Err::<(), _>(m.lock().unwrap().0).is_err() {} + + { + if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok() {} + } + if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok() { + } else { + } + if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok() {} + if Err::, _>(()).is_err() {} + + if Ok::<_, ()>(String::new()).is_ok() {} + if Err::<(), _>((String::new(), ())).is_err() {} + + // Option + if Some(m.lock()).is_some() {} + if Some(m.lock().unwrap().0).is_some() {} + + { + if None::>.is_none() {} + } + if None::>.is_none() { + } else { + } + + if None::>.is_none() {} + + if Some(String::new()).is_some() {} + if Some((String::new(), ())).is_some() {} + + // Poll + if Ready(m.lock()).is_ready() {} + if Ready(m.lock().unwrap().0).is_ready() {} + + { + if Pending::>.is_pending() {} + } + if Pending::>.is_pending() { + } else { + } + + if Pending::>.is_pending() {} + + if Ready(String::new()).is_ready() {} + if Ready((String::new(), ())).is_ready() {} +} diff --git a/tests/ui/redundant_pattern_matching_drop_order.rs b/tests/ui/redundant_pattern_matching_drop_order.rs new file mode 100644 index 00000000000..b9c82d86f61 --- /dev/null +++ b/tests/ui/redundant_pattern_matching_drop_order.rs @@ -0,0 +1,58 @@ +// run-rustfix + +// Issue #5746 +#![warn(clippy::redundant_pattern_matching)] +#![allow(clippy::if_same_then_else)] +use std::task::Poll::{Pending, Ready}; + +fn main() { + let m = std::sync::Mutex::new((0, 0)); + + // Result + if let Ok(_) = m.lock() {} + if let Err(_) = Err::<(), _>(m.lock().unwrap().0) {} + + { + if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {} + } + if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) { + } else { + } + if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {} + if let Err(_) = Err::, _>(()) {} + + if let Ok(_) = Ok::<_, ()>(String::new()) {} + if let Err(_) = Err::<(), _>((String::new(), ())) {} + + // Option + if let Some(_) = Some(m.lock()) {} + if let Some(_) = Some(m.lock().unwrap().0) {} + + { + if let None = None::> {} + } + if let None = None::> { + } else { + } + + if let None = None::> {} + + if let Some(_) = Some(String::new()) {} + if let Some(_) = Some((String::new(), ())) {} + + // Poll + if let Ready(_) = Ready(m.lock()) {} + if let Ready(_) = Ready(m.lock().unwrap().0) {} + + { + if let Pending = Pending::> {} + } + if let Pending = Pending::> { + } else { + } + + if let Pending = Pending::> {} + + if let Ready(_) = Ready(String::new()) {} + if let Ready(_) = Ready((String::new(), ())) {} +} diff --git a/tests/ui/redundant_pattern_matching_drop_order.stderr b/tests/ui/redundant_pattern_matching_drop_order.stderr new file mode 100644 index 00000000000..eb7aa70ee27 --- /dev/null +++ b/tests/ui/redundant_pattern_matching_drop_order.stderr @@ -0,0 +1,171 @@ +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_drop_order.rs:12:12 + | +LL | if let Ok(_) = m.lock() {} + | -------^^^^^----------- help: try this: `if m.lock().is_ok()` + | + = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` + = note: this will change drop order of the result, as well as all temporaries + = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching_drop_order.rs:13:12 + | +LL | if let Err(_) = Err::<(), _>(m.lock().unwrap().0) {} + | -------^^^^^^------------------------------------ help: try this: `if Err::<(), _>(m.lock().unwrap().0).is_err()` + | + = note: this will change drop order of the result, as well as all temporaries + = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_drop_order.rs:16:16 + | +LL | if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {} + | -------^^^^^----------------------------------------- help: try this: `if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok()` + | + = note: this will change drop order of the result, as well as all temporaries + = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_drop_order.rs:18:12 + | +LL | if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) { + | -------^^^^^----------------------------------------- help: try this: `if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok()` + | + = note: this will change drop order of the result, as well as all temporaries + = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_drop_order.rs:21:12 + | +LL | if let Ok(_) = Ok::<_, std::sync::MutexGuard<()>>(()) {} + | -------^^^^^----------------------------------------- help: try this: `if Ok::<_, std::sync::MutexGuard<()>>(()).is_ok()` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching_drop_order.rs:22:12 + | +LL | if let Err(_) = Err::, _>(()) {} + | -------^^^^^^------------------------------------------ help: try this: `if Err::, _>(()).is_err()` + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching_drop_order.rs:24:12 + | +LL | if let Ok(_) = Ok::<_, ()>(String::new()) {} + | -------^^^^^----------------------------- help: try this: `if Ok::<_, ()>(String::new()).is_ok()` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching_drop_order.rs:25:12 + | +LL | if let Err(_) = Err::<(), _>((String::new(), ())) {} + | -------^^^^^^------------------------------------ help: try this: `if Err::<(), _>((String::new(), ())).is_err()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching_drop_order.rs:28:12 + | +LL | if let Some(_) = Some(m.lock()) {} + | -------^^^^^^^----------------- help: try this: `if Some(m.lock()).is_some()` + | + = note: this will change drop order of the result, as well as all temporaries + = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching_drop_order.rs:29:12 + | +LL | if let Some(_) = Some(m.lock().unwrap().0) {} + | -------^^^^^^^---------------------------- help: try this: `if Some(m.lock().unwrap().0).is_some()` + | + = note: this will change drop order of the result, as well as all temporaries + = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching_drop_order.rs:32:16 + | +LL | if let None = None::> {} + | -------^^^^------------------------------------ help: try this: `if None::>.is_none()` + | + = note: this will change drop order of the result, as well as all temporaries + = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching_drop_order.rs:34:12 + | +LL | if let None = None::> { + | -------^^^^------------------------------------ help: try this: `if None::>.is_none()` + | + = note: this will change drop order of the result, as well as all temporaries + = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching_drop_order.rs:38: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_drop_order.rs:40:12 + | +LL | if let Some(_) = Some(String::new()) {} + | -------^^^^^^^---------------------- help: try this: `if Some(String::new()).is_some()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching_drop_order.rs:41:12 + | +LL | if let Some(_) = Some((String::new(), ())) {} + | -------^^^^^^^---------------------------- help: try this: `if Some((String::new(), ())).is_some()` + +error: redundant pattern matching, consider using `is_ready()` + --> $DIR/redundant_pattern_matching_drop_order.rs:44:12 + | +LL | if let Ready(_) = Ready(m.lock()) {} + | -------^^^^^^^^------------------ help: try this: `if Ready(m.lock()).is_ready()` + | + = note: this will change drop order of the result, as well as all temporaries + = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important + +error: redundant pattern matching, consider using `is_ready()` + --> $DIR/redundant_pattern_matching_drop_order.rs:45:12 + | +LL | if let Ready(_) = Ready(m.lock().unwrap().0) {} + | -------^^^^^^^^----------------------------- help: try this: `if Ready(m.lock().unwrap().0).is_ready()` + | + = note: this will change drop order of the result, as well as all temporaries + = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important + +error: redundant pattern matching, consider using `is_pending()` + --> $DIR/redundant_pattern_matching_drop_order.rs:48:16 + | +LL | if let Pending = Pending::> {} + | -------^^^^^^^--------------------------------------- help: try this: `if Pending::>.is_pending()` + | + = note: this will change drop order of the result, as well as all temporaries + = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important + +error: redundant pattern matching, consider using `is_pending()` + --> $DIR/redundant_pattern_matching_drop_order.rs:50:12 + | +LL | if let Pending = Pending::> { + | -------^^^^^^^--------------------------------------- help: try this: `if Pending::>.is_pending()` + | + = note: this will change drop order of the result, as well as all temporaries + = note: add `#[allow(clippy::redundant_pattern_matching)]` if this is important + +error: redundant pattern matching, consider using `is_pending()` + --> $DIR/redundant_pattern_matching_drop_order.rs:54: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_drop_order.rs:56:12 + | +LL | if let Ready(_) = Ready(String::new()) {} + | -------^^^^^^^^----------------------- help: try this: `if Ready(String::new()).is_ready()` + +error: redundant pattern matching, consider using `is_ready()` + --> $DIR/redundant_pattern_matching_drop_order.rs:57:12 + | +LL | if let Ready(_) = Ready((String::new(), ())) {} + | -------^^^^^^^^----------------------------- help: try this: `if Ready((String::new(), ())).is_ready()` + +error: aborting due to 22 previous errors + diff --git a/tests/ui/redundant_pattern_matching_option.fixed b/tests/ui/redundant_pattern_matching_option.fixed index 66f580a0a68..99714477266 100644 --- a/tests/ui/redundant_pattern_matching_option.fixed +++ b/tests/ui/redundant_pattern_matching_option.fixed @@ -2,7 +2,12 @@ #![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] -#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)] +#![allow( + unused_must_use, + clippy::needless_bool, + clippy::match_like_matches_macro, + clippy::if_same_then_else +)] fn main() { if None::<()>.is_none() {} diff --git a/tests/ui/redundant_pattern_matching_option.rs b/tests/ui/redundant_pattern_matching_option.rs index f18b27b8b95..8309847e181 100644 --- a/tests/ui/redundant_pattern_matching_option.rs +++ b/tests/ui/redundant_pattern_matching_option.rs @@ -2,7 +2,12 @@ #![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] -#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)] +#![allow( + unused_must_use, + clippy::needless_bool, + clippy::match_like_matches_macro, + clippy::if_same_then_else +)] fn main() { if let None = None::<()> {} diff --git a/tests/ui/redundant_pattern_matching_option.stderr b/tests/ui/redundant_pattern_matching_option.stderr index 58482a0ab70..613a30d4a48 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:8:12 + --> $DIR/redundant_pattern_matching_option.rs:13: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:10:12 + --> $DIR/redundant_pattern_matching_option.rs:15: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:12:12 + --> $DIR/redundant_pattern_matching_option.rs:17: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:18:15 + --> $DIR/redundant_pattern_matching_option.rs:23: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:20:15 + --> $DIR/redundant_pattern_matching_option.rs:25: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:22:15 + --> $DIR/redundant_pattern_matching_option.rs:27: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:25:15 + --> $DIR/redundant_pattern_matching_option.rs:30: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:33:5 + --> $DIR/redundant_pattern_matching_option.rs:38: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:38:5 + --> $DIR/redundant_pattern_matching_option.rs:43: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:43:13 + --> $DIR/redundant_pattern_matching_option.rs:48: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:49:20 + --> $DIR/redundant_pattern_matching_option.rs:54: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:53:20 + --> $DIR/redundant_pattern_matching_option.rs:58: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:55:19 + --> $DIR/redundant_pattern_matching_option.rs:60: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:74:12 + --> $DIR/redundant_pattern_matching_option.rs:79: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:76:12 + --> $DIR/redundant_pattern_matching_option.rs:81: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:78:15 + --> $DIR/redundant_pattern_matching_option.rs:83: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:80:15 + --> $DIR/redundant_pattern_matching_option.rs:85: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:82:5 + --> $DIR/redundant_pattern_matching_option.rs:87: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:87:5 + --> $DIR/redundant_pattern_matching_option.rs:92: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 465aa80dac2..c2977453804 100644 --- a/tests/ui/redundant_pattern_matching_poll.fixed +++ b/tests/ui/redundant_pattern_matching_poll.fixed @@ -2,7 +2,12 @@ #![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] -#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)] +#![allow( + unused_must_use, + clippy::needless_bool, + clippy::match_like_matches_macro, + clippy::if_same_then_else +)] use std::task::Poll::{self, Pending, Ready}; diff --git a/tests/ui/redundant_pattern_matching_poll.rs b/tests/ui/redundant_pattern_matching_poll.rs index 7891ff353b1..665c8c41750 100644 --- a/tests/ui/redundant_pattern_matching_poll.rs +++ b/tests/ui/redundant_pattern_matching_poll.rs @@ -2,7 +2,12 @@ #![warn(clippy::all)] #![warn(clippy::redundant_pattern_matching)] -#![allow(unused_must_use, clippy::needless_bool, clippy::match_like_matches_macro)] +#![allow( + unused_must_use, + clippy::needless_bool, + clippy::match_like_matches_macro, + clippy::if_same_then_else +)] use std::task::Poll::{self, Pending, Ready}; diff --git a/tests/ui/redundant_pattern_matching_poll.stderr b/tests/ui/redundant_pattern_matching_poll.stderr index 5ffc6c47c90..5ecf024a733 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:10:12 + --> $DIR/redundant_pattern_matching_poll.rs:15: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:12:12 + --> $DIR/redundant_pattern_matching_poll.rs:17: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:14:12 + --> $DIR/redundant_pattern_matching_poll.rs:19: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:20:15 + --> $DIR/redundant_pattern_matching_poll.rs:25: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:22:15 + --> $DIR/redundant_pattern_matching_poll.rs:27: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:24:15 + --> $DIR/redundant_pattern_matching_poll.rs:29: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:30:5 + --> $DIR/redundant_pattern_matching_poll.rs:35: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:35:5 + --> $DIR/redundant_pattern_matching_poll.rs:40: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:40:13 + --> $DIR/redundant_pattern_matching_poll.rs:45: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:46:20 + --> $DIR/redundant_pattern_matching_poll.rs:51: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:50:20 + --> $DIR/redundant_pattern_matching_poll.rs:55: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:52:19 + --> $DIR/redundant_pattern_matching_poll.rs:57: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:68:12 + --> $DIR/redundant_pattern_matching_poll.rs:73: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:70:12 + --> $DIR/redundant_pattern_matching_poll.rs:75: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:72:15 + --> $DIR/redundant_pattern_matching_poll.rs:77: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:74:15 + --> $DIR/redundant_pattern_matching_poll.rs:79: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:76:5 + --> $DIR/redundant_pattern_matching_poll.rs:81: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:81:5 + --> $DIR/redundant_pattern_matching_poll.rs:86:5 | LL | / match Pending::<()> { LL | | Ready(_) => false, diff --git a/tests/ui/redundant_pattern_matching_result.fixed b/tests/ui/redundant_pattern_matching_result.fixed index e94c5704b48..d7af5d762ae 100644 --- a/tests/ui/redundant_pattern_matching_result.fixed +++ b/tests/ui/redundant_pattern_matching_result.fixed @@ -7,7 +7,8 @@ clippy::needless_bool, clippy::match_like_matches_macro, clippy::unnecessary_wraps, - deprecated + deprecated, + clippy::if_same_then_else )] fn main() { diff --git a/tests/ui/redundant_pattern_matching_result.rs b/tests/ui/redundant_pattern_matching_result.rs index 5d175294232..e06d4485ae4 100644 --- a/tests/ui/redundant_pattern_matching_result.rs +++ b/tests/ui/redundant_pattern_matching_result.rs @@ -7,7 +7,8 @@ clippy::needless_bool, clippy::match_like_matches_macro, clippy::unnecessary_wraps, - deprecated + deprecated, + clippy::if_same_then_else )] fn main() { diff --git a/tests/ui/redundant_pattern_matching_result.stderr b/tests/ui/redundant_pattern_matching_result.stderr index d6a46babb77..e06f095da20 100644 --- a/tests/ui/redundant_pattern_matching_result.stderr +++ b/tests/ui/redundant_pattern_matching_result.stderr @@ -1,5 +1,5 @@ error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:15:12 + --> $DIR/redundant_pattern_matching_result.rs:16:12 | LL | if let Ok(_) = &result {} | -------^^^^^---------- help: try this: `if result.is_ok()` @@ -7,31 +7,31 @@ LL | if let Ok(_) = &result {} = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:17:12 + --> $DIR/redundant_pattern_matching_result.rs:18:12 | LL | if let Ok(_) = Ok::(42) {} | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:19:12 + --> $DIR/redundant_pattern_matching_result.rs:20:12 | LL | if let Err(_) = Err::(42) {} | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:21:15 + --> $DIR/redundant_pattern_matching_result.rs:22:15 | LL | while let Ok(_) = Ok::(10) {} | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:23:15 + --> $DIR/redundant_pattern_matching_result.rs:24:15 | LL | while let Err(_) = Ok::(10) {} | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:33:5 + --> $DIR/redundant_pattern_matching_result.rs:34:5 | LL | / match Ok::(42) { LL | | Ok(_) => true, @@ -40,7 +40,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:38:5 + --> $DIR/redundant_pattern_matching_result.rs:39:5 | LL | / match Ok::(42) { LL | | Ok(_) => false, @@ -49,7 +49,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_err()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:43:5 + --> $DIR/redundant_pattern_matching_result.rs:44:5 | LL | / match Err::(42) { LL | | Ok(_) => false, @@ -58,7 +58,7 @@ LL | | }; | |_____^ help: try this: `Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:48:5 + --> $DIR/redundant_pattern_matching_result.rs:49:5 | LL | / match Err::(42) { LL | | Ok(_) => true, @@ -67,73 +67,73 @@ LL | | }; | |_____^ help: try this: `Err::(42).is_ok()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:53:20 + --> $DIR/redundant_pattern_matching_result.rs:54:20 | LL | let _ = if let Ok(_) = Ok::(4) { true } else { false }; | -------^^^^^--------------------- help: try this: `if Ok::(4).is_ok()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:59:20 + --> $DIR/redundant_pattern_matching_result.rs:60:20 | LL | let _ = if let Ok(_) = gen_res() { | -------^^^^^------------ help: try this: `if gen_res().is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:61:19 + --> $DIR/redundant_pattern_matching_result.rs:62:19 | LL | } else if let Err(_) = gen_res() { | -------^^^^^^------------ help: try this: `if gen_res().is_err()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_result.rs:84:19 + --> $DIR/redundant_pattern_matching_result.rs:85:19 | LL | while let Some(_) = r#try!(result_opt()) {} | ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_result.rs:85:16 + --> $DIR/redundant_pattern_matching_result.rs:86:16 | LL | if let Some(_) = r#try!(result_opt()) {} | -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_result.rs:91:12 + --> $DIR/redundant_pattern_matching_result.rs:92:12 | LL | if let Some(_) = m!() {} | -------^^^^^^^------- help: try this: `if m!().is_some()` error: redundant pattern matching, consider using `is_some()` - --> $DIR/redundant_pattern_matching_result.rs:92:15 + --> $DIR/redundant_pattern_matching_result.rs:93:15 | LL | while let Some(_) = m!() {} | ----------^^^^^^^------- help: try this: `while m!().is_some()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:110:12 + --> $DIR/redundant_pattern_matching_result.rs:111:12 | LL | if let Ok(_) = Ok::(42) {} | -------^^^^^--------------------- help: try this: `if Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:112:12 + --> $DIR/redundant_pattern_matching_result.rs:113:12 | LL | if let Err(_) = Err::(42) {} | -------^^^^^^---------------------- help: try this: `if Err::(42).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:114:15 + --> $DIR/redundant_pattern_matching_result.rs:115:15 | LL | while let Ok(_) = Ok::(10) {} | ----------^^^^^--------------------- help: try this: `while Ok::(10).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:116:15 + --> $DIR/redundant_pattern_matching_result.rs:117:15 | LL | while let Err(_) = Ok::(10) {} | ----------^^^^^^--------------------- help: try this: `while Ok::(10).is_err()` error: redundant pattern matching, consider using `is_ok()` - --> $DIR/redundant_pattern_matching_result.rs:118:5 + --> $DIR/redundant_pattern_matching_result.rs:119:5 | LL | / match Ok::(42) { LL | | Ok(_) => true, @@ -142,7 +142,7 @@ LL | | }; | |_____^ help: try this: `Ok::(42).is_ok()` error: redundant pattern matching, consider using `is_err()` - --> $DIR/redundant_pattern_matching_result.rs:123:5 + --> $DIR/redundant_pattern_matching_result.rs:124:5 | LL | / match Err::(42) { LL | | Ok(_) => false, -- cgit 1.4.1-3-g733a5 From 9a55c0c1763cc5894ce1dbf1c40d03119e3de5f3 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 15 Apr 2021 22:05:37 -0400 Subject: Fix `single_match` Check for `PartialEq` in addition to `StructuralPartialEq` before suggesting `==` --- clippy_lints/src/matches.rs | 7 +++++-- tests/ui/single_match.rs | 8 ++++++++ tests/ui/single_match.stderr | 11 ++++++++++- 3 files changed, 23 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 8f1112cff7b..e4d1451b369 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -738,8 +738,11 @@ fn report_single_match_single_pattern( let (msg, sugg) = if_chain! { if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind; let (ty, ty_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(ex)); - if let Some(trait_id) = cx.tcx.lang_items().structural_peq_trait(); - if ty.is_integral() || ty.is_char() || ty.is_str() || implements_trait(cx, ty, trait_id, &[]); + if let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait(); + if let Some(pe_trait_id) = cx.tcx.lang_items().eq_trait(); + if ty.is_integral() || ty.is_char() || ty.is_str() + || (implements_trait(cx, ty, spe_trait_id, &[]) + && implements_trait(cx, ty, pe_trait_id, &[ty.into()])); then { // scrutinee derives PartialEq and the pattern is a constant. let pat_ref_count = match pat.kind { diff --git a/tests/ui/single_match.rs b/tests/ui/single_match.rs index ca884b41c45..b1819e08d53 100644 --- a/tests/ui/single_match.rs +++ b/tests/ui/single_match.rs @@ -135,6 +135,14 @@ fn if_suggestion() { Bar::A => println!(), _ => (), } + + // issue #7038 + struct X; + let x = Some(X); + match x { + None => println!(), + _ => (), + }; } macro_rules! single_match { diff --git a/tests/ui/single_match.stderr b/tests/ui/single_match.stderr index 7ea6955b740..9ef2a8668a6 100644 --- a/tests/ui/single_match.stderr +++ b/tests/ui/single_match.stderr @@ -119,5 +119,14 @@ LL | | _ => (), LL | | } | |_____^ help: try this: `if let Bar::A = x { println!() }` -error: aborting due to 12 previous errors +error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` + --> $DIR/single_match.rs:142:5 + | +LL | / match x { +LL | | None => println!(), +LL | | _ => (), +LL | | }; + | |_____^ help: try this: `if let None = x { println!() }` + +error: aborting due to 13 previous errors -- cgit 1.4.1-3-g733a5 From 0462666c7067d8992501aa8997d16e18da021ee5 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 16 Apr 2021 11:00:08 -0500 Subject: Add cloned_instead_of_copied lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 2 ++ .../src/methods/cloned_instead_of_copied.rs | 38 ++++++++++++++++++++++ clippy_lints/src/methods/mod.rs | 26 +++++++++++++++ clippy_utils/src/ty.rs | 21 +++++++++++- tests/ui/cloned_instead_of_copied.fixed | 15 +++++++++ tests/ui/cloned_instead_of_copied.rs | 15 +++++++++ tests/ui/cloned_instead_of_copied.stderr | 34 +++++++++++++++++++ 8 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/methods/cloned_instead_of_copied.rs create mode 100644 tests/ui/cloned_instead_of_copied.fixed create mode 100644 tests/ui/cloned_instead_of_copied.rs create mode 100644 tests/ui/cloned_instead_of_copied.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 7885bdfb12c..483365b5e91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2148,6 +2148,7 @@ Released 2018-09-13 [`clone_double_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_double_ref [`clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy [`clone_on_ref_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_ref_ptr +[`cloned_instead_of_copied`]: https://rust-lang.github.io/rust-clippy/master/index.html#cloned_instead_of_copied [`cmp_nan`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_nan [`cmp_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_null [`cmp_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_owned diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 93631c5669b..0f48799d339 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -759,6 +759,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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, @@ -1380,6 +1381,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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::IMPLICIT_CLONE), LintId::of(methods::INEFFICIENT_TO_STRING), diff --git a/clippy_lints/src/methods/cloned_instead_of_copied.rs b/clippy_lints/src/methods/cloned_instead_of_copied.rs new file mode 100644 index 00000000000..ba97ab3900c --- /dev/null +++ b/clippy_lints/src/methods/cloned_instead_of_copied.rs @@ -0,0 +1,38 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_trait_method; +use clippy_utils::ty::{get_iterator_item_ty, is_copy}; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::{sym, Span}; + +use super::CLONED_INSTEAD_OF_COPIED; + +pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span) { + let recv_ty = cx.typeck_results().expr_ty_adjusted(recv); + let inner_ty = match recv_ty.kind() { + // `Option` -> `T` + ty::Adt(adt, subst) if cx.tcx.is_diagnostic_item(sym::option_type, adt.did) => subst.type_at(0), + _ if is_trait_method(cx, expr, sym::Iterator) => match get_iterator_item_ty(cx, recv_ty) { + // ::Item + Some(ty) => ty, + _ => return, + }, + _ => return, + }; + match inner_ty.kind() { + // &T where T: Copy + ty::Ref(_, ty, _) if is_copy(cx, ty) => {}, + _ => return, + }; + span_lint_and_sugg( + cx, + CLONED_INSTEAD_OF_COPIED, + span, + "used `cloned` where `copied` could be used instead", + "try", + "copied".into(), + Applicability::MachineApplicable, + ) +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 6c21ff0f6c2..49a9af2a465 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -8,6 +8,7 @@ mod chars_next_cmp; mod chars_next_cmp_with_unwrap; mod clone_on_copy; mod clone_on_ref_ptr; +mod cloned_instead_of_copied; mod expect_fun_call; mod expect_used; mod filetype_is_file; @@ -73,6 +74,29 @@ use rustc_span::symbol::SymbolStr; use rustc_span::{sym, Span}; use rustc_typeck::hir_ty_to_ty; +declare_clippy_lint! { + /// **What it does:** Checks for usages of `cloned()` on an `Iterator` or `Option` where + /// `copied()` could be used instead. + /// + /// **Why is this bad?** `copied()` is better because it guarantees that the type being cloned + /// implements `Copy`. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// [1, 2, 3].iter().cloned(); + /// ``` + /// Use instead: + /// ```rust + /// [1, 2, 3].iter().copied(); + /// ``` + pub CLONED_INSTEAD_OF_COPIED, + pedantic, + "used `cloned` where `copied` could be used instead" +} + declare_clippy_lint! { /// **What it does:** Checks for `.unwrap()` calls on `Option`s and on `Result`s. /// @@ -1638,6 +1662,7 @@ impl_lint_pass!(Methods => [ CLONE_ON_COPY, CLONE_ON_REF_PTR, CLONE_DOUBLE_REF, + CLONED_INSTEAD_OF_COPIED, INEFFICIENT_TO_STRING, NEW_RET_NO_SELF, SINGLE_CHAR_PATTERN, @@ -1909,6 +1934,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv), ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv), ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv), + ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span), ("collect", []) => match method_call!(recv) { Some(("cloned", [recv2], _)) => iter_cloned_collect::check(cx, expr, recv2), Some(("map", [m_recv, m_arg], _)) => { diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 807cfbc4c7f..64a80f2554f 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -13,7 +13,7 @@ use rustc_lint::LateContext; use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; use rustc_middle::ty::{self, AdtDef, IntTy, Ty, TypeFoldable, UintTy}; use rustc_span::sym; -use rustc_span::symbol::Symbol; +use rustc_span::symbol::{Ident, Symbol}; use rustc_span::DUMMY_SP; use rustc_trait_selection::traits::query::normalize::AtExt; @@ -52,6 +52,25 @@ pub fn contains_adt_constructor(ty: Ty<'_>, adt: &AdtDef) -> bool { }) } +/// Resolves `::Item` for `T` +/// Do not invoke without first verifying that the type implements `Iterator` +pub fn get_iterator_item_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { + cx.tcx + .get_diagnostic_item(sym::Iterator) + .and_then(|iter_did| { + cx.tcx.associated_items(iter_did).find_by_name_and_kind( + cx.tcx, + Ident::from_str("Item"), + ty::AssocKind::Type, + iter_did, + ) + }) + .map(|assoc| { + let proj = cx.tcx.mk_projection(assoc.def_id, cx.tcx.mk_substs_trait(ty, &[])); + cx.tcx.normalize_erasing_regions(cx.param_env, proj) + }) +} + /// Returns true if ty has `iter` or `iter_mut` methods pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option { // FIXME: instead of this hard-coded list, we should check if `::iter` diff --git a/tests/ui/cloned_instead_of_copied.fixed b/tests/ui/cloned_instead_of_copied.fixed new file mode 100644 index 00000000000..4eb999e18e6 --- /dev/null +++ b/tests/ui/cloned_instead_of_copied.fixed @@ -0,0 +1,15 @@ +// run-rustfix +#![warn(clippy::cloned_instead_of_copied)] + +fn main() { + // yay + let _ = [1].iter().copied(); + let _ = vec!["hi"].iter().copied(); + let _ = Some(&1).copied(); + let _ = Box::new([1].iter()).copied(); + let _ = Box::new(Some(&1)).copied(); + + // nay + let _ = [String::new()].iter().cloned(); + let _ = Some(&String::new()).cloned(); +} diff --git a/tests/ui/cloned_instead_of_copied.rs b/tests/ui/cloned_instead_of_copied.rs new file mode 100644 index 00000000000..894496c0ebb --- /dev/null +++ b/tests/ui/cloned_instead_of_copied.rs @@ -0,0 +1,15 @@ +// run-rustfix +#![warn(clippy::cloned_instead_of_copied)] + +fn main() { + // yay + let _ = [1].iter().cloned(); + let _ = vec!["hi"].iter().cloned(); + let _ = Some(&1).cloned(); + let _ = Box::new([1].iter()).cloned(); + let _ = Box::new(Some(&1)).cloned(); + + // nay + let _ = [String::new()].iter().cloned(); + let _ = Some(&String::new()).cloned(); +} diff --git a/tests/ui/cloned_instead_of_copied.stderr b/tests/ui/cloned_instead_of_copied.stderr new file mode 100644 index 00000000000..e0707d32146 --- /dev/null +++ b/tests/ui/cloned_instead_of_copied.stderr @@ -0,0 +1,34 @@ +error: used `cloned` where `copied` could be used instead + --> $DIR/cloned_instead_of_copied.rs:6:24 + | +LL | let _ = [1].iter().cloned(); + | ^^^^^^ help: try: `copied` + | + = note: `-D clippy::cloned-instead-of-copied` implied by `-D warnings` + +error: used `cloned` where `copied` could be used instead + --> $DIR/cloned_instead_of_copied.rs:7:31 + | +LL | let _ = vec!["hi"].iter().cloned(); + | ^^^^^^ help: try: `copied` + +error: used `cloned` where `copied` could be used instead + --> $DIR/cloned_instead_of_copied.rs:8:22 + | +LL | let _ = Some(&1).cloned(); + | ^^^^^^ help: try: `copied` + +error: used `cloned` where `copied` could be used instead + --> $DIR/cloned_instead_of_copied.rs:9:34 + | +LL | let _ = Box::new([1].iter()).cloned(); + | ^^^^^^ help: try: `copied` + +error: used `cloned` where `copied` could be used instead + --> $DIR/cloned_instead_of_copied.rs:10:32 + | +LL | let _ = Box::new(Some(&1)).cloned(); + | ^^^^^^ help: try: `copied` + +error: aborting due to 5 previous errors + -- cgit 1.4.1-3-g733a5 From 2d050f4b2f6057ea0ac63a3d8d37c05992e4ee3e Mon Sep 17 00:00:00 2001 From: Aliénore Bouttefeux Date: Fri, 16 Apr 2021 19:38:01 +0200 Subject: add type in help of from over Into --- clippy_lints/src/from_over_into.rs | 2 +- tests/ui/from_over_into.stderr | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index e5ec245e502..5e2baba8943 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -73,7 +73,7 @@ impl LateLintPass<'_> for FromOverInto { cx.tcx.sess.source_map().guess_head_span(item.span), "an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true", None, - "consider to implement `From` instead", + &format!("consider to implement `From<{}>` instead", impl_trait_ref.self_ty()), ); } } diff --git a/tests/ui/from_over_into.stderr b/tests/ui/from_over_into.stderr index b101d2704fb..fb391759dce 100644 --- a/tests/ui/from_over_into.stderr +++ b/tests/ui/from_over_into.stderr @@ -5,7 +5,6 @@ LL | impl Into for String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::from-over-into` implied by `-D warnings` - = help: consider to implement `From` instead + = help: consider to implement `From` instead error: aborting due to previous error - -- cgit 1.4.1-3-g733a5 From ccd0f0b4af77e7bb1aaf214852f4c4d7e6d6b60d Mon Sep 17 00:00:00 2001 From: Aliénore Bouttefeux Date: Fri, 16 Apr 2021 20:13:25 +0200 Subject: added missing line in test ouput --- tests/ui/from_over_into.stderr | 1 + 1 file changed, 1 insertion(+) (limited to 'tests') diff --git a/tests/ui/from_over_into.stderr b/tests/ui/from_over_into.stderr index fb391759dce..2951e6bdac4 100644 --- a/tests/ui/from_over_into.stderr +++ b/tests/ui/from_over_into.stderr @@ -8,3 +8,4 @@ LL | impl Into for String { = help: consider to implement `From` instead error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From 559deddb3b5e376b79a019491bbdbf75eba2826d Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 15 Apr 2021 11:49:45 -0400 Subject: Allow allman style braces in `suspicious_else_formatting` --- clippy_lints/src/formatting.rs | 17 +++++++++++-- tests/ui/suspicious_else_formatting.rs | 26 ++++++++++++++++++++ tests/ui/suspicious_else_formatting.stderr | 39 ++++++++++++++++++++---------- 3 files changed, 67 insertions(+), 15 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/formatting.rs b/clippy_lints/src/formatting.rs index 48612befc68..3bd6a09d365 100644 --- a/clippy_lints/src/formatting.rs +++ b/clippy_lints/src/formatting.rs @@ -215,9 +215,22 @@ fn check_else(cx: &EarlyContext<'_>, expr: &Expr) { // the snippet should look like " else \n " with maybe comments anywhere // it’s bad when there is a ‘\n’ after the “else” if let Some(else_snippet) = snippet_opt(cx, else_span); - if let Some(else_pos) = else_snippet.find("else"); - if else_snippet[else_pos..].contains('\n'); + if let Some((pre_else, post_else)) = else_snippet.split_once("else"); + if let Some((_, post_else_post_eol)) = post_else.split_once('\n'); + then { + // Allow allman style braces `} \n else \n {` + if_chain! { + if is_block(else_); + if let Some((_, pre_else_post_eol)) = pre_else.split_once('\n'); + // Exactly one eol before and after the else + if !pre_else_post_eol.contains('\n'); + if !post_else_post_eol.contains('\n'); + then { + return; + } + } + let else_desc = if is_if(else_) { "if" } else { "{..}" }; span_lint_and_note( cx, diff --git a/tests/ui/suspicious_else_formatting.rs b/tests/ui/suspicious_else_formatting.rs index 226010ec6df..547615b10d9 100644 --- a/tests/ui/suspicious_else_formatting.rs +++ b/tests/ui/suspicious_else_formatting.rs @@ -40,6 +40,7 @@ fn main() { { } + // This is fine, though weird. Allman style braces on the else. if foo() { } else @@ -76,4 +77,29 @@ fn main() { } if foo() { } + + // Almost Allman style braces. Lint these. + if foo() { + } + + else + { + + } + + if foo() { + } + else + + { + + } + + // #3864 - Allman style braces + if foo() + { + } + else + { + } } diff --git a/tests/ui/suspicious_else_formatting.stderr b/tests/ui/suspicious_else_formatting.stderr index bbc036d376f..d8d67b4138a 100644 --- a/tests/ui/suspicious_else_formatting.stderr +++ b/tests/ui/suspicious_else_formatting.stderr @@ -41,37 +41,50 @@ LL | | { | = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}` -error: this is an `else {..}` but the formatting might hide it - --> $DIR/suspicious_else_formatting.rs:44:6 +error: this is an `else if` but the formatting might hide it + --> $DIR/suspicious_else_formatting.rs:51:6 | -LL | } +LL | } else | ______^ -LL | | else -LL | | { +LL | | if foo() { // the span of the above error should continue here | |____^ | - = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}` + = note: to remove this lint, remove the `else` or remove the new line between `else` and `if` error: this is an `else if` but the formatting might hide it - --> $DIR/suspicious_else_formatting.rs:50:6 + --> $DIR/suspicious_else_formatting.rs:56:6 | -LL | } else +LL | } | ______^ +LL | | else LL | | if foo() { // the span of the above error should continue here | |____^ | = note: to remove this lint, remove the `else` or remove the new line between `else` and `if` -error: this is an `else if` but the formatting might hide it - --> $DIR/suspicious_else_formatting.rs:55:6 +error: this is an `else {..}` but the formatting might hide it + --> $DIR/suspicious_else_formatting.rs:83:6 | LL | } | ______^ +LL | | LL | | else -LL | | if foo() { // the span of the above error should continue here +LL | | { | |____^ | - = note: to remove this lint, remove the `else` or remove the new line between `else` and `if` + = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}` + +error: this is an `else {..}` but the formatting might hide it + --> $DIR/suspicious_else_formatting.rs:91:6 + | +LL | } + | ______^ +LL | | else +LL | | +LL | | { + | |____^ + | + = note: to remove this lint, remove the `else` or remove the new line between `else` and `{..}` -error: aborting due to 8 previous errors +error: aborting due to 9 previous errors -- cgit 1.4.1-3-g733a5 From 5af078ac1b705abde41cc2e7c71be38701943cdb Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 16 Apr 2021 15:06:21 -0500 Subject: Add flat_map_option lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 2 ++ clippy_lints/src/matches.rs | 2 +- clippy_lints/src/methods/flat_map_option.rs | 34 +++++++++++++++++++++++++++++ clippy_lints/src/methods/mod.rs | 30 ++++++++++++++++++++++++- clippy_utils/src/attrs.rs | 2 +- tests/ui/flat_map_option.fixed | 13 +++++++++++ tests/ui/flat_map_option.rs | 13 +++++++++++ tests/ui/flat_map_option.stderr | 16 ++++++++++++++ 9 files changed, 110 insertions(+), 3 deletions(-) create mode 100644 clippy_lints/src/methods/flat_map_option.rs create mode 100644 tests/ui/flat_map_option.fixed create mode 100644 tests/ui/flat_map_option.rs create mode 100644 tests/ui/flat_map_option.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 483365b5e91..56cdd356469 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2216,6 +2216,7 @@ Released 2018-09-13 [`filter_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_next [`find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#find_map [`flat_map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#flat_map_identity +[`flat_map_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#flat_map_option [`float_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_arithmetic [`float_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp [`float_cmp_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp_const diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 0f48799d339..a7871b7aba7 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -770,6 +770,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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, @@ -1383,6 +1384,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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::IMPLICIT_CLONE), LintId::of(methods::INEFFICIENT_TO_STRING), LintId::of(methods::MAP_FLATTEN), diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 2c8d8a0f132..44b4eb29035 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -1509,7 +1509,7 @@ fn opt_parent_let<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option<&'a Local<' /// Gets all arms that are unbounded `PatRange`s. fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) -> Vec> { arms.iter() - .flat_map(|arm| { + .filter_map(|arm| { if let Arm { pat, guard: None, .. } = *arm { if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind { let lhs = match lhs { diff --git a/clippy_lints/src/methods/flat_map_option.rs b/clippy_lints/src/methods/flat_map_option.rs new file mode 100644 index 00000000000..12d560653ed --- /dev/null +++ b/clippy_lints/src/methods/flat_map_option.rs @@ -0,0 +1,34 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_trait_method; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::{source_map::Span, sym}; + +use super::FLAT_MAP_OPTION; +use clippy_utils::ty::is_type_diagnostic_item; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, span: Span) { + if !is_trait_method(cx, expr, sym::Iterator) { + return; + } + let arg_ty = cx.typeck_results().expr_ty_adjusted(arg); + let sig = match arg_ty.kind() { + ty::Closure(_, substs) => substs.as_closure().sig(), + _ if arg_ty.is_fn() => arg_ty.fn_sig(cx.tcx), + _ => return, + }; + if !is_type_diagnostic_item(cx, sig.output().skip_binder(), sym::option_type) { + return; + } + span_lint_and_sugg( + cx, + FLAT_MAP_OPTION, + span, + "used `flat_map` where `filter_map` could be used instead", + "try", + "filter_map".into(), + Applicability::MachineApplicable, + ) +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 49a9af2a465..c2cd3011d14 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -17,6 +17,7 @@ mod filter_map_identity; mod filter_map_next; mod filter_next; mod flat_map_identity; +mod flat_map_option; mod from_iter_instead_of_collect; mod get_unwrap; mod implicit_clone; @@ -97,6 +98,29 @@ declare_clippy_lint! { "used `cloned` where `copied` could be used instead" } +declare_clippy_lint! { + /// **What it does:** Checks for usages of `Iterator::flat_map()` where `filter_map()` could be + /// used instead. + /// + /// **Why is this bad?** When applicable, `filter_map()` is more clear since it shows that + /// `Option` is used to produce 0 or 1 items. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let nums: Vec = ["1", "2", "whee!"].iter().flat_map(|x| x.parse().ok()).collect(); + /// ``` + /// Use instead: + /// ```rust + /// let nums: Vec = ["1", "2", "whee!"].iter().filter_map(|x| x.parse().ok()).collect(); + /// ``` + pub FLAT_MAP_OPTION, + pedantic, + "used `flat_map` where `filter_map` could be used instead" +} + declare_clippy_lint! { /// **What it does:** Checks for `.unwrap()` calls on `Option`s and on `Result`s. /// @@ -1663,6 +1687,7 @@ impl_lint_pass!(Methods => [ CLONE_ON_REF_PTR, CLONE_DOUBLE_REF, CLONED_INSTEAD_OF_COPIED, + FLAT_MAP_OPTION, INEFFICIENT_TO_STRING, NEW_RET_NO_SELF, SINGLE_CHAR_PATTERN, @@ -1958,7 +1983,10 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio unnecessary_filter_map::check(cx, expr, arg); filter_map_identity::check(cx, expr, arg, span); }, - ("flat_map", [flm_arg]) => flat_map_identity::check(cx, expr, flm_arg, span), + ("flat_map", [arg]) => { + flat_map_identity::check(cx, expr, arg, span); + flat_map_option::check(cx, expr, arg, span); + }, ("flatten", []) => { if let Some(("map", [recv, map_arg], _)) = method_call!(recv) { map_flatten::check(cx, expr, recv, map_arg); diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index 5e44c7b3ee0..c0584e1e226 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -154,6 +154,6 @@ pub fn is_doc_hidden(attrs: &[ast::Attribute]) -> bool { attrs .iter() .filter(|attr| attr.has_name(sym::doc)) - .flat_map(ast::Attribute::meta_item_list) + .filter_map(ast::Attribute::meta_item_list) .any(|l| attr::list_contains_name(&l, sym::hidden)) } diff --git a/tests/ui/flat_map_option.fixed b/tests/ui/flat_map_option.fixed new file mode 100644 index 00000000000..6a34f008995 --- /dev/null +++ b/tests/ui/flat_map_option.fixed @@ -0,0 +1,13 @@ +// run-rustfix +#![warn(clippy::flat_map_option)] +#![allow(clippy::redundant_closure, clippy::unnecessary_filter_map)] + +fn main() { + // yay + let c = |x| Some(x); + let _ = [1].iter().filter_map(c); + let _ = [1].iter().filter_map(Some); + + // nay + let _ = [1].iter().flat_map(|_| &Some(1)); +} diff --git a/tests/ui/flat_map_option.rs b/tests/ui/flat_map_option.rs new file mode 100644 index 00000000000..2479abddbf0 --- /dev/null +++ b/tests/ui/flat_map_option.rs @@ -0,0 +1,13 @@ +// run-rustfix +#![warn(clippy::flat_map_option)] +#![allow(clippy::redundant_closure, clippy::unnecessary_filter_map)] + +fn main() { + // yay + let c = |x| Some(x); + let _ = [1].iter().flat_map(c); + let _ = [1].iter().flat_map(Some); + + // nay + let _ = [1].iter().flat_map(|_| &Some(1)); +} diff --git a/tests/ui/flat_map_option.stderr b/tests/ui/flat_map_option.stderr new file mode 100644 index 00000000000..a9d8056dee9 --- /dev/null +++ b/tests/ui/flat_map_option.stderr @@ -0,0 +1,16 @@ +error: used `flat_map` where `filter_map` could be used instead + --> $DIR/flat_map_option.rs:8:24 + | +LL | let _ = [1].iter().flat_map(c); + | ^^^^^^^^ help: try: `filter_map` + | + = note: `-D clippy::flat-map-option` implied by `-D warnings` + +error: used `flat_map` where `filter_map` could be used instead + --> $DIR/flat_map_option.rs:9:24 + | +LL | let _ = [1].iter().flat_map(Some); + | ^^^^^^^^ help: try: `filter_map` + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From 243dc4625050d791eb8f91b6b4da2f8dc8449a8d Mon Sep 17 00:00:00 2001 From: Andre Bogus Date: Sun, 18 Apr 2021 23:49:54 +0200 Subject: un-double `return` on try_err --- clippy_lints/src/try_err.rs | 11 ++++++++--- tests/ui/try_err.fixed | 8 ++++++++ tests/ui/try_err.rs | 8 ++++++++ tests/ui/try_err.stderr | 8 +++++++- 4 files changed, 31 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/try_err.rs b/clippy_lints/src/try_err.rs index 0b8c03c6865..ebb39ea4877 100644 --- a/clippy_lints/src/try_err.rs +++ b/clippy_lints/src/try_err.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{snippet, snippet_with_macro_callsite}; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{differing_macro_contexts, in_macro, is_lang_ctor, match_def_path, paths}; +use clippy_utils::{differing_macro_contexts, get_parent_expr, in_macro, is_lang_ctor, match_def_path, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::ResultErr; @@ -102,10 +102,15 @@ impl<'tcx> LateLintPass<'tcx> for TryErr { } else { snippet(cx, err_arg.span, "_") }; + let ret_prefix = if get_parent_expr(cx, expr).map_or(false, |e| matches!(e.kind, ExprKind::Ret(_))) { + "" // already returns + } else { + "return " + }; let suggestion = if err_ty == expr_err_ty { - format!("return {}{}{}", prefix, origin_snippet, suffix) + format!("{}{}{}{}", ret_prefix, prefix, origin_snippet, suffix) } else { - format!("return {}{}.into(){}", prefix, origin_snippet, suffix) + format!("{}{}{}.into(){}", ret_prefix, prefix, origin_snippet, suffix) }; span_lint_and_sugg( diff --git a/tests/ui/try_err.fixed b/tests/ui/try_err.fixed index 5b96bb59c5f..264194419c7 100644 --- a/tests/ui/try_err.fixed +++ b/tests/ui/try_err.fixed @@ -160,3 +160,11 @@ pub fn poll_next(ready: bool) -> Poll>> { Poll::Ready(None) } + +// Tests that `return` is not duplicated +pub fn try_return(x: bool) -> Result { + if x { + return Err(42); + } + Ok(0) +} diff --git a/tests/ui/try_err.rs b/tests/ui/try_err.rs index f220d697d2c..bc6979bf457 100644 --- a/tests/ui/try_err.rs +++ b/tests/ui/try_err.rs @@ -160,3 +160,11 @@ pub fn poll_next(ready: bool) -> Poll>> { Poll::Ready(None) } + +// Tests that `return` is not duplicated +pub fn try_return(x: bool) -> Result { + if x { + return Err(42)?; + } + Ok(0) +} diff --git a/tests/ui/try_err.stderr b/tests/ui/try_err.stderr index 2c01d37192e..8f332a9b649 100644 --- a/tests/ui/try_err.stderr +++ b/tests/ui/try_err.stderr @@ -74,5 +74,11 @@ error: returning an `Err(_)` with the `?` operator LL | Err(io::ErrorKind::NotFound)? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `return Poll::Ready(Some(Err(io::ErrorKind::NotFound.into())))` -error: aborting due to 10 previous errors +error: returning an `Err(_)` with the `?` operator + --> $DIR/try_err.rs:167:16 + | +LL | return Err(42)?; + | ^^^^^^^^ help: try this: `Err(42)` + +error: aborting due to 11 previous errors -- cgit 1.4.1-3-g733a5 From c6b381c59bffe8ce62fcf9569fb1a39189234dcc Mon Sep 17 00:00:00 2001 From: Basavesh Shivakumar Date: Mon, 19 Apr 2021 17:20:21 +0200 Subject: useless use of format! should return function directly --- clippy_lints/src/format.rs | 11 ++++++----- tests/ui/format.fixed | 4 ++++ tests/ui/format.rs | 4 ++++ tests/ui/format.stderr | 8 +++++++- 4 files changed, 21 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index 4729abbd8e3..c2b055ed648 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::paths; use clippy_utils::source::{snippet, snippet_opt}; +use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{is_expn_of, last_path_segment, match_def_path, match_function_call}; use if_chain::if_chain; @@ -100,15 +101,15 @@ fn on_argumentv1_new<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arms: & return Some(format!("{:?}.to_string()", s.as_str())); } } else { - let snip = snippet(cx, format_args.span, ""); + let sugg = Sugg::hir(cx, format_args, ""); if let ExprKind::MethodCall(path, _, _, _) = format_args.kind { if path.ident.name == sym!(to_string) { - return Some(format!("{}", snip)); + return Some(format!("{}", sugg)); } } else if let ExprKind::Binary(..) = format_args.kind { - return Some(format!("{}", snip)); + return Some(format!("{}", sugg)); } - return Some(format!("{}.to_string()", snip)); + return Some(format!("{}.to_string()", sugg.maybe_par())); } } } @@ -136,7 +137,7 @@ fn on_new_v1<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option = vec!["foo".to_string(), "bar".to_string()]; + let _s: String = (&*v.join("\n")).to_string(); } diff --git a/tests/ui/format.rs b/tests/ui/format.rs index b604d79cca3..683957f0ff0 100644 --- a/tests/ui/format.rs +++ b/tests/ui/format.rs @@ -67,4 +67,8 @@ fn main() { // False positive let a = "foo".to_string(); let _ = Some(format!("{}", a + "bar")); + + // Wrap it with braces + let v: Vec = vec!["foo".to_string(), "bar".to_string()]; + let _s: String = format!("{}", &*v.join("\n")); } diff --git a/tests/ui/format.stderr b/tests/ui/format.stderr index 96df7f37f77..2017eb2b383 100644 --- a/tests/ui/format.stderr +++ b/tests/ui/format.stderr @@ -87,5 +87,11 @@ error: useless use of `format!` LL | let _ = Some(format!("{}", a + "bar")); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `a + "bar"` -error: aborting due to 13 previous errors +error: useless use of `format!` + --> $DIR/format.rs:73:22 + | +LL | let _s: String = format!("{}", &*v.join("/n")); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `(&*v.join("/n")).to_string()` + +error: aborting due to 14 previous errors -- cgit 1.4.1-3-g733a5 From 42d070286e993fe671dc4655a545c5c9e3a1feb4 Mon Sep 17 00:00:00 2001 From: Caleb Tennis Date: Sun, 18 Apr 2021 19:13:46 -0400 Subject: Ignore aarch64 for this test as it's x86 assembly only. Fixes #7091 --- tests/ui/asm_syntax.rs | 5 ++++- tests/ui/asm_syntax.stderr | 10 +++++----- 2 files changed, 9 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/tests/ui/asm_syntax.rs b/tests/ui/asm_syntax.rs index 658cae397e1..4a62f6f2909 100644 --- a/tests/ui/asm_syntax.rs +++ b/tests/ui/asm_syntax.rs @@ -1,5 +1,7 @@ -#![feature(asm)] // only-x86_64 +// ignore-aarch64 + +#![feature(asm)] #[warn(clippy::inline_asm_x86_intel_syntax)] mod warn_intel { @@ -23,6 +25,7 @@ mod warn_att { } } +#[cfg(target_arch = "x86_64")] fn main() { unsafe { warn_att::use_asm(); diff --git a/tests/ui/asm_syntax.stderr b/tests/ui/asm_syntax.stderr index 27b51166eac..e3abbe08658 100644 --- a/tests/ui/asm_syntax.stderr +++ b/tests/ui/asm_syntax.stderr @@ -1,5 +1,5 @@ error: Intel x86 assembly syntax used - --> $DIR/asm_syntax.rs:7:9 + --> $DIR/asm_syntax.rs:9:9 | LL | asm!(""); | ^^^^^^^^^ @@ -8,7 +8,7 @@ LL | asm!(""); = help: use AT&T x86 assembly syntax error: Intel x86 assembly syntax used - --> $DIR/asm_syntax.rs:8:9 + --> $DIR/asm_syntax.rs:10:9 | LL | asm!("", options()); | ^^^^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | asm!("", options()); = help: use AT&T x86 assembly syntax error: Intel x86 assembly syntax used - --> $DIR/asm_syntax.rs:9:9 + --> $DIR/asm_syntax.rs:11:9 | LL | asm!("", options(nostack)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL | asm!("", options(nostack)); = help: use AT&T x86 assembly syntax error: AT&T x86 assembly syntax used - --> $DIR/asm_syntax.rs:21:9 + --> $DIR/asm_syntax.rs:23:9 | LL | asm!("", options(att_syntax)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | asm!("", options(att_syntax)); = help: use Intel x86 assembly syntax error: AT&T x86 assembly syntax used - --> $DIR/asm_syntax.rs:22:9 + --> $DIR/asm_syntax.rs:24:9 | LL | asm!("", options(nostack, att_syntax)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From e2e104b993277a8096299a2d2eb99d4641f1e318 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 15 Apr 2021 15:26:57 +0200 Subject: Add lint to check for boolean comparison in assert macro calls --- CHANGELOG.md | 1 + clippy_lints/src/bool_assert_comparison.rs | 75 +++++++++++++++++++ clippy_lints/src/lib.rs | 5 ++ clippy_utils/src/ast_utils.rs | 32 +++++++++ tests/ui/bool_assert_comparison.rs | 59 +++++++++++++++ tests/ui/bool_assert_comparison.stderr | 112 +++++++++++++++++++++++++++++ 6 files changed, 284 insertions(+) create mode 100644 clippy_lints/src/bool_assert_comparison.rs create mode 100644 tests/ui/bool_assert_comparison.rs create mode 100644 tests/ui/bool_assert_comparison.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 7885bdfb12c..830a3efb7df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2124,6 +2124,7 @@ Released 2018-09-13 [`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name [`blanket_clippy_restriction_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#blanket_clippy_restriction_lints [`blocks_in_if_conditions`]: https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_if_conditions +[`bool_assert_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison [`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison [`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const [`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box diff --git a/clippy_lints/src/bool_assert_comparison.rs b/clippy_lints/src/bool_assert_comparison.rs new file mode 100644 index 00000000000..bee706ed402 --- /dev/null +++ b/clippy_lints/src/bool_assert_comparison.rs @@ -0,0 +1,75 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::{ast_utils, is_direct_expn_of}; +use rustc_ast::ast::{Expr, ExprKind, Lit, LitKind}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** This lint warns about boolean comparisons in assert-like macros. + /// + /// **Why is this bad?** It is shorter to use the equivalent. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // Bad + /// assert_eq!("a".is_empty(), false); + /// assert_ne!("a".is_empty(), true); + /// + /// // Good + /// assert!(!"a".is_empty()); + /// ``` + pub BOOL_ASSERT_COMPARISON, + style, + "Using a boolean as comparison value in an assert_* macro when there is no need" +} + +declare_lint_pass!(BoolAssertComparison => [BOOL_ASSERT_COMPARISON]); + +fn is_bool_lit(e: &Expr) -> bool { + matches!( + e.kind, + ExprKind::Lit(Lit { + kind: LitKind::Bool(_), + .. + }) + ) && !e.span.from_expansion() +} + +impl EarlyLintPass for BoolAssertComparison { + fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) { + let macros = ["assert_eq", "debug_assert_eq"]; + let inverted_macros = ["assert_ne", "debug_assert_ne"]; + + for mac in macros.iter().chain(inverted_macros.iter()) { + if let Some(span) = is_direct_expn_of(e.span, mac) { + if let Some([a, b]) = ast_utils::extract_assert_macro_args(e) { + let nb_bool_args = is_bool_lit(a) as usize + is_bool_lit(b) as usize; + + if nb_bool_args != 1 { + // If there are two boolean arguments, we definitely don't understand + // what's going on, so better leave things as is... + // + // Or there is simply no boolean and then we can leave things as is! + return; + } + + let non_eq_mac = &mac[..mac.len() - 3]; + span_lint_and_sugg( + cx, + BOOL_ASSERT_COMPARISON, + span, + &format!("used `{}!` with a literal bool", mac), + "replace it with", + format!("{}!(..)", non_eq_mac), + Applicability::MaybeIncorrect, + ); + return; + } + } + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 78a95b00403..f08388f9037 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -178,6 +178,7 @@ mod await_holding_invalid; mod bit_mask; mod blacklisted_name; mod blocks_in_if_conditions; +mod bool_assert_comparison; mod booleans; mod bytecount; mod cargo_common_metadata; @@ -567,6 +568,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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, @@ -1270,6 +1272,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box from_str_radix_10::FromStrRadix10); store.register_late_pass(|| box manual_map::ManualMap); store.register_late_pass(move || box if_then_some_else_none::IfThenSomeElseNone::new(msrv)); + store.register_early_pass(|| box bool_assert_comparison::BoolAssertComparison); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(arithmetic::FLOAT_ARITHMETIC), @@ -1448,6 +1451,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), @@ -1734,6 +1738,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), 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), diff --git a/clippy_utils/src/ast_utils.rs b/clippy_utils/src/ast_utils.rs index eaea3e636f9..93e10c836cc 100644 --- a/clippy_utils/src/ast_utils.rs +++ b/clippy_utils/src/ast_utils.rs @@ -5,6 +5,7 @@ #![allow(clippy::similar_names, clippy::wildcard_imports, clippy::enum_glob_use)] use crate::{both, over}; +use if_chain::if_chain; use rustc_ast::ptr::P; use rustc_ast::{self as ast, *}; use rustc_span::symbol::Ident; @@ -571,3 +572,34 @@ pub fn eq_mac_args(l: &MacArgs, r: &MacArgs) -> bool { _ => false, } } + +/// Extract args from an assert-like macro. +/// +/// Currently working with: +/// - `assert_eq!` and `assert_ne!` +/// - `debug_assert_eq!` and `debug_assert_ne!` +/// +/// For example: +/// +/// `debug_assert_eq!(a, b)` will return Some([a, b]) +pub fn extract_assert_macro_args(mut expr: &Expr) -> Option<[&Expr; 2]> { + if_chain! { + if let ExprKind::If(_, ref block, _) = expr.kind; + if let StmtKind::Semi(ref e) = block.stmts.get(0)?.kind; + then { + expr = e; + } + } + if_chain! { + if let ExprKind::Block(ref block, _) = expr.kind; + if let StmtKind::Expr(ref expr) = block.stmts.get(0)?.kind; + if let ExprKind::Match(ref match_expr, _) = expr.kind; + if let ExprKind::Tup(ref tup) = match_expr.kind; + if let [a, b, ..] = tup.as_slice(); + if let (&ExprKind::AddrOf(_, _, ref a), &ExprKind::AddrOf(_, _, ref b)) = (&a.kind, &b.kind); + then { + return Some([&*a, &*b]); + } + } + None +} diff --git a/tests/ui/bool_assert_comparison.rs b/tests/ui/bool_assert_comparison.rs new file mode 100644 index 00000000000..2de402fae8c --- /dev/null +++ b/tests/ui/bool_assert_comparison.rs @@ -0,0 +1,59 @@ +#![warn(clippy::bool_assert_comparison)] + +macro_rules! a { + () => { + true + }; +} +macro_rules! b { + () => { + true + }; +} + +fn main() { + assert_eq!("a".len(), 1); + assert_eq!("a".is_empty(), false); + assert_eq!("".is_empty(), true); + assert_eq!(true, "".is_empty()); + assert_eq!(a!(), b!()); + assert_eq!(a!(), "".is_empty()); + assert_eq!("".is_empty(), b!()); + + assert_ne!("a".len(), 1); + assert_ne!("a".is_empty(), false); + assert_ne!("".is_empty(), true); + assert_ne!(true, "".is_empty()); + assert_ne!(a!(), b!()); + assert_ne!(a!(), "".is_empty()); + assert_ne!("".is_empty(), b!()); + + debug_assert_eq!("a".len(), 1); + debug_assert_eq!("a".is_empty(), false); + debug_assert_eq!("".is_empty(), true); + debug_assert_eq!(true, "".is_empty()); + debug_assert_eq!(a!(), b!()); + debug_assert_eq!(a!(), "".is_empty()); + debug_assert_eq!("".is_empty(), b!()); + + debug_assert_ne!("a".len(), 1); + debug_assert_ne!("a".is_empty(), false); + debug_assert_ne!("".is_empty(), true); + debug_assert_ne!(true, "".is_empty()); + debug_assert_ne!(a!(), b!()); + debug_assert_ne!(a!(), "".is_empty()); + debug_assert_ne!("".is_empty(), b!()); + + // assert with error messages + assert_eq!("a".len(), 1, "tadam {}", 1); + assert_eq!("a".len(), 1, "tadam {}", true); + assert_eq!("a".is_empty(), false, "tadam {}", 1); + assert_eq!("a".is_empty(), false, "tadam {}", true); + assert_eq!(false, "a".is_empty(), "tadam {}", true); + + debug_assert_eq!("a".len(), 1, "tadam {}", 1); + debug_assert_eq!("a".len(), 1, "tadam {}", true); + debug_assert_eq!("a".is_empty(), false, "tadam {}", 1); + debug_assert_eq!("a".is_empty(), false, "tadam {}", true); + debug_assert_eq!(false, "a".is_empty(), "tadam {}", true); +} diff --git a/tests/ui/bool_assert_comparison.stderr b/tests/ui/bool_assert_comparison.stderr new file mode 100644 index 00000000000..f57acf520d5 --- /dev/null +++ b/tests/ui/bool_assert_comparison.stderr @@ -0,0 +1,112 @@ +error: used `assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:16:5 + | +LL | assert_eq!("a".is_empty(), false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + | + = note: `-D clippy::bool-assert-comparison` implied by `-D warnings` + +error: used `assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:17:5 + | +LL | assert_eq!("".is_empty(), true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + +error: used `assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:18:5 + | +LL | assert_eq!(true, "".is_empty()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + +error: used `assert_ne!` with a literal bool + --> $DIR/bool_assert_comparison.rs:24:5 + | +LL | assert_ne!("a".is_empty(), false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + +error: used `assert_ne!` with a literal bool + --> $DIR/bool_assert_comparison.rs:25:5 + | +LL | assert_ne!("".is_empty(), true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + +error: used `assert_ne!` with a literal bool + --> $DIR/bool_assert_comparison.rs:26:5 + | +LL | assert_ne!(true, "".is_empty()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + +error: used `debug_assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:32:5 + | +LL | debug_assert_eq!("a".is_empty(), false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + +error: used `debug_assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:33:5 + | +LL | debug_assert_eq!("".is_empty(), true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + +error: used `debug_assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:34:5 + | +LL | debug_assert_eq!(true, "".is_empty()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + +error: used `debug_assert_ne!` with a literal bool + --> $DIR/bool_assert_comparison.rs:40:5 + | +LL | debug_assert_ne!("a".is_empty(), false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + +error: used `debug_assert_ne!` with a literal bool + --> $DIR/bool_assert_comparison.rs:41:5 + | +LL | debug_assert_ne!("".is_empty(), true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + +error: used `debug_assert_ne!` with a literal bool + --> $DIR/bool_assert_comparison.rs:42:5 + | +LL | debug_assert_ne!(true, "".is_empty()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + +error: used `assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:50:5 + | +LL | assert_eq!("a".is_empty(), false, "tadam {}", 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + +error: used `assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:51:5 + | +LL | assert_eq!("a".is_empty(), false, "tadam {}", true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + +error: used `assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:52:5 + | +LL | assert_eq!(false, "a".is_empty(), "tadam {}", true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + +error: used `debug_assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:56:5 + | +LL | debug_assert_eq!("a".is_empty(), false, "tadam {}", 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + +error: used `debug_assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:57:5 + | +LL | debug_assert_eq!("a".is_empty(), false, "tadam {}", true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + +error: used `debug_assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:58:5 + | +LL | debug_assert_eq!(false, "a".is_empty(), "tadam {}", true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + +error: aborting due to 18 previous errors + -- cgit 1.4.1-3-g733a5 From 6eae905808fe830a731ed37e3f4a6264ae98e129 Mon Sep 17 00:00:00 2001 From: Yawara ISHIDA Date: Tue, 20 Apr 2021 13:33:39 +0900 Subject: Add a test for FP in macro expansion --- clippy_lints/src/inconsistent_struct_constructor.rs | 3 ++- tests/ui/inconsistent_struct_constructor.fixed | 13 +++++++++++++ tests/ui/inconsistent_struct_constructor.rs | 13 +++++++++++++ tests/ui/inconsistent_struct_constructor.stderr | 4 ++-- 4 files changed, 30 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/inconsistent_struct_constructor.rs b/clippy_lints/src/inconsistent_struct_constructor.rs index 7532f182360..d138c3a8acf 100644 --- a/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/clippy_lints/src/inconsistent_struct_constructor.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::in_macro; use clippy_utils::source::snippet; use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; @@ -66,7 +67,7 @@ declare_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRU impl LateLintPass<'_> for InconsistentStructConstructor { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if_chain! { - if !expr.span.from_expansion(); + if !in_macro(expr.span); if let ExprKind::Struct(qpath, fields, base) = expr.kind; let ty = cx.typeck_results().expr_ty(expr); if let Some(adt_def) = ty.ty_adt_def(); diff --git a/tests/ui/inconsistent_struct_constructor.fixed b/tests/ui/inconsistent_struct_constructor.fixed index 8d9c3110035..d1025743790 100644 --- a/tests/ui/inconsistent_struct_constructor.fixed +++ b/tests/ui/inconsistent_struct_constructor.fixed @@ -13,6 +13,15 @@ struct Foo { z: i32, } +macro_rules! new_foo { + () => { + let x = 1; + let y = 1; + let z = 1; + Foo { y, x, z } + }; +} + mod without_base { use super::Foo; @@ -24,6 +33,10 @@ mod without_base { // Should lint. Foo { x, y, z }; + // Should NOT lint. + // issue #7069. + new_foo!(); + // Shoule NOT lint because the order is the same as in the definition. Foo { x, y, z }; diff --git a/tests/ui/inconsistent_struct_constructor.rs b/tests/ui/inconsistent_struct_constructor.rs index 63fac910501..b095aa64a21 100644 --- a/tests/ui/inconsistent_struct_constructor.rs +++ b/tests/ui/inconsistent_struct_constructor.rs @@ -13,6 +13,15 @@ struct Foo { z: i32, } +macro_rules! new_foo { + () => { + let x = 1; + let y = 1; + let z = 1; + Foo { y, x, z } + }; +} + mod without_base { use super::Foo; @@ -24,6 +33,10 @@ mod without_base { // Should lint. Foo { y, x, z }; + // Should NOT lint. + // issue #7069. + new_foo!(); + // Shoule NOT lint because the order is the same as in the definition. Foo { x, y, z }; diff --git a/tests/ui/inconsistent_struct_constructor.stderr b/tests/ui/inconsistent_struct_constructor.stderr index d021bb19579..ef308dedb16 100644 --- a/tests/ui/inconsistent_struct_constructor.stderr +++ b/tests/ui/inconsistent_struct_constructor.stderr @@ -1,5 +1,5 @@ error: struct constructor field order is inconsistent with struct definition field order - --> $DIR/inconsistent_struct_constructor.rs:25:9 + --> $DIR/inconsistent_struct_constructor.rs:34:9 | LL | Foo { y, x, z }; | ^^^^^^^^^^^^^^^ help: try: `Foo { x, y, z }` @@ -7,7 +7,7 @@ LL | Foo { y, x, z }; = note: `-D clippy::inconsistent-struct-constructor` implied by `-D warnings` error: struct constructor field order is inconsistent with struct definition field order - --> $DIR/inconsistent_struct_constructor.rs:43:9 + --> $DIR/inconsistent_struct_constructor.rs:56:9 | LL | / Foo { LL | | z, -- cgit 1.4.1-3-g733a5 From 224881b94df5215952c7a955acaefacb388c50c5 Mon Sep 17 00:00:00 2001 From: Elliot Bobrow Date: Mon, 12 Apr 2021 18:24:47 -0700 Subject: add unnecessary_self_imports lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 4 ++ clippy_lints/src/modulo_arithmetic.rs | 2 +- clippy_lints/src/unnecessary_self_imports.rs | 67 ++++++++++++++++++++++++++++ tests/ui/unnecessary_self_imports.fixed | 10 +++++ tests/ui/unnecessary_self_imports.rs | 10 +++++ tests/ui/unnecessary_self_imports.stderr | 23 ++++++++++ 7 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/unnecessary_self_imports.rs create mode 100644 tests/ui/unnecessary_self_imports.fixed create mode 100644 tests/ui/unnecessary_self_imports.rs create mode 100644 tests/ui/unnecessary_self_imports.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 7885bdfb12c..d9a198f8243 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2519,6 +2519,7 @@ Released 2018-09-13 [`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations [`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation +[`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports [`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by [`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap [`unnecessary_wraps`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 78a95b00403..7e12e9be4aa 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -356,6 +356,7 @@ mod unicode; mod unit_return_expecting_ord; mod unit_types; mod unnamed_address; +mod unnecessary_self_imports; mod unnecessary_sort_by; mod unnecessary_wraps; mod unnested_or_patterns; @@ -963,6 +964,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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, @@ -1048,6 +1050,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box default_numeric_fallback::DefaultNumericFallback); store.register_late_pass(|| box inconsistent_struct_constructor::InconsistentStructConstructor); store.register_late_pass(|| box non_octal_unix_permissions::NonOctalUnixPermissions); + store.register_early_pass(|| box unnecessary_self_imports::UnnecessarySelfImports); let msrv = conf.msrv.as_ref().and_then(|s| { parse_msrv(s, None, None).or_else(|| { @@ -1320,6 +1323,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(strings::STRING_TO_STRING), LintId::of(strings::STR_TO_STRING), LintId::of(types::RC_BUFFER), + 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), diff --git a/clippy_lints/src/modulo_arithmetic.rs b/clippy_lints/src/modulo_arithmetic.rs index 6a52de4f713..64e9dc85466 100644 --- a/clippy_lints/src/modulo_arithmetic.rs +++ b/clippy_lints/src/modulo_arithmetic.rs @@ -4,7 +4,7 @@ use clippy_utils::sext; use if_chain::if_chain; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self}; +use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use std::fmt::Display; diff --git a/clippy_lints/src/unnecessary_self_imports.rs b/clippy_lints/src/unnecessary_self_imports.rs new file mode 100644 index 00000000000..48c54d79cf1 --- /dev/null +++ b/clippy_lints/src/unnecessary_self_imports.rs @@ -0,0 +1,67 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use if_chain::if_chain; +use rustc_ast::{Item, ItemKind, UseTreeKind}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::kw; + +declare_clippy_lint! { + /// **What it does:** Checks for imports ending in `::{self}`. + /// + /// **Why is this bad?** In most cases, this can be written much more cleanly by omitting `::{self}`. + /// + /// **Known problems:** Removing `::{self}` will cause any non-module items at the same path to also be imported. + /// This might cause a naming conflict (https://github.com/rust-lang/rustfmt/issues/3568). This lint makes no attempt + /// to detect this scenario and that is why it is a restriction lint. + /// + /// **Example:** + /// + /// ```rust + /// use std::io::{self}; + /// ``` + /// Use instead: + /// ```rust + /// use std::io; + /// ``` + pub UNNECESSARY_SELF_IMPORTS, + restriction, + "imports ending in `::{self}`, which can be omitted" +} + +declare_lint_pass!(UnnecessarySelfImports => [UNNECESSARY_SELF_IMPORTS]); + +impl EarlyLintPass for UnnecessarySelfImports { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + if_chain! { + if let ItemKind::Use(use_tree) = &item.kind; + if let UseTreeKind::Nested(nodes) = &use_tree.kind; + if let [(self_tree, _)] = &**nodes; + if let [self_seg] = &*self_tree.prefix.segments; + if self_seg.ident.name == kw::SelfLower; + if let Some(last_segment) = use_tree.prefix.segments.last(); + + then { + span_lint_and_then( + cx, + UNNECESSARY_SELF_IMPORTS, + item.span, + "import ending with `::{self}`", + |diag| { + diag.span_suggestion( + last_segment.span().with_hi(item.span.hi()), + "consider omitting `::{self}`", + format!( + "{}{};", + last_segment.ident, + if let UseTreeKind::Simple(Some(alias), ..) = self_tree.kind { format!(" as {}", alias) } else { String::new() }, + ), + Applicability::MaybeIncorrect, + ); + diag.note("this will slightly change semantics; any non-module items at the same path will also be imported"); + }, + ); + } + } + } +} diff --git a/tests/ui/unnecessary_self_imports.fixed b/tests/ui/unnecessary_self_imports.fixed new file mode 100644 index 00000000000..1185eaa1d55 --- /dev/null +++ b/tests/ui/unnecessary_self_imports.fixed @@ -0,0 +1,10 @@ +// run-rustfix +#![warn(clippy::unnecessary_self_imports)] +#![allow(unused_imports, dead_code)] + +use std::collections::hash_map::{self, *}; +use std::fs as alias; +use std::io::{self, Read}; +use std::rc; + +fn main() {} diff --git a/tests/ui/unnecessary_self_imports.rs b/tests/ui/unnecessary_self_imports.rs new file mode 100644 index 00000000000..56bfbc09402 --- /dev/null +++ b/tests/ui/unnecessary_self_imports.rs @@ -0,0 +1,10 @@ +// run-rustfix +#![warn(clippy::unnecessary_self_imports)] +#![allow(unused_imports, dead_code)] + +use std::collections::hash_map::{self, *}; +use std::fs::{self as alias}; +use std::io::{self, Read}; +use std::rc::{self}; + +fn main() {} diff --git a/tests/ui/unnecessary_self_imports.stderr b/tests/ui/unnecessary_self_imports.stderr new file mode 100644 index 00000000000..83a5618c983 --- /dev/null +++ b/tests/ui/unnecessary_self_imports.stderr @@ -0,0 +1,23 @@ +error: import ending with `::{self}` + --> $DIR/unnecessary_self_imports.rs:6:1 + | +LL | use std::fs::{self as alias}; + | ^^^^^^^^^-------------------- + | | + | help: consider omitting `::{self}`: `fs as alias;` + | + = note: `-D clippy::unnecessary-self-imports` implied by `-D warnings` + = note: this will slightly change semantics; any non-module items at the same path will also be imported + +error: import ending with `::{self}` + --> $DIR/unnecessary_self_imports.rs:8:1 + | +LL | use std::rc::{self}; + | ^^^^^^^^^----------- + | | + | help: consider omitting `::{self}`: `rc;` + | + = note: this will slightly change semantics; any non-module items at the same path will also be imported + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From 65778fa5e886c641422501e68ec7ffbe690abafc Mon Sep 17 00:00:00 2001 From: Takayuki Date: Thu, 22 Apr 2021 16:52:34 +0900 Subject: fix a false-positive inside const fn in comparison_chain --- clippy_lints/src/comparison_chain.rs | 14 +++++++++++++- tests/ui/comparison_chain.rs | 28 ++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs index 42e153909ce..150d8a3d673 100644 --- a/clippy_lints/src/comparison_chain.rs +++ b/clippy_lints/src/comparison_chain.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::implements_trait; use clippy_utils::{get_trait_def_id, if_sequence, is_else_clause, paths, SpanlessEq}; -use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_hir::{BinOpKind, Expr, ExprKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -64,6 +64,10 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain { return; } + if parent_node_is_if_const_fn(cx, expr) { + return; + } + // Check that there exists at least one explicit else condition let (conds, _) = if_sequence(expr); if conds.len() < 2 { @@ -123,3 +127,11 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain { fn kind_is_cmp(kind: BinOpKind) -> bool { matches!(kind, BinOpKind::Lt | BinOpKind::Gt | BinOpKind::Eq) } + +fn parent_node_is_if_const_fn(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + match cx.tcx.hir().find(cx.tcx.hir().get_parent_item(expr.hir_id)) { + Some(Node::Item(item)) => rustc_mir::const_eval::is_const_fn(cx.tcx, item.def_id.to_def_id()), + Some(Node::ImplItem(impl_item)) => rustc_mir::const_eval::is_const_fn(cx.tcx, impl_item.def_id.to_def_id()), + _ => false, + } +} diff --git a/tests/ui/comparison_chain.rs b/tests/ui/comparison_chain.rs index 3b03f8c7dfe..c12c6a31027 100644 --- a/tests/ui/comparison_chain.rs +++ b/tests/ui/comparison_chain.rs @@ -203,4 +203,32 @@ mod issue_5212 { } } +enum Sign { + Negative, + Positive, + Zero, +} + +impl Sign { + const fn sign_i8(n: i8) -> Self { + if n == 0 { + Sign::Zero + } else if n > 0 { + Sign::Positive + } else { + Sign::Negative + } + } +} + +const fn sign_i8(n: i8) -> Sign { + if n == 0 { + Sign::Zero + } else if n > 0 { + Sign::Positive + } else { + Sign::Negative + } +} + fn main() {} -- cgit 1.4.1-3-g733a5 From b48699e4cfd8af15669227049f3c6056d4084aba Mon Sep 17 00:00:00 2001 From: cherryblossom <31467609+cherryblossom000@users.noreply.github.com> Date: Thu, 22 Apr 2021 19:34:36 +1000 Subject: `single_component_path_imports`: ignore `pub(crate) use some_macro;` (fixes #7106) --- clippy_lints/src/single_component_path_imports.rs | 36 ++++++++++++++++++---- tests/ui/single_component_path_imports_macro.fixed | 21 +++++++++++++ tests/ui/single_component_path_imports_macro.rs | 21 +++++++++++++ .../ui/single_component_path_imports_macro.stderr | 10 ++++++ 4 files changed, 82 insertions(+), 6 deletions(-) create mode 100644 tests/ui/single_component_path_imports_macro.fixed create mode 100644 tests/ui/single_component_path_imports_macro.rs create mode 100644 tests/ui/single_component_path_imports_macro.stderr (limited to 'tests') diff --git a/clippy_lints/src/single_component_path_imports.rs b/clippy_lints/src/single_component_path_imports.rs index 6104103580e..a45bb102389 100644 --- a/clippy_lints/src/single_component_path_imports.rs +++ b/clippy_lints/src/single_component_path_imports.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::in_macro; -use rustc_ast::{ptr::P, Crate, Item, ItemKind, ModKind, UseTreeKind}; +use rustc_ast::{ptr::P, Crate, Item, ItemKind, MacroDef, ModKind, UseTreeKind, VisibilityKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -60,8 +60,21 @@ fn check_mod(cx: &EarlyContext<'_>, items: &[P]) { // ``` let mut single_use_usages = Vec::new(); + // keep track of macros defined in the module as we don't want it to trigger on this (#7106) + // ```rust,ignore + // macro_rules! foo { () => {} }; + // pub(crate) use foo; + // ``` + let mut macros = Vec::new(); + for item in items { - track_uses(cx, &item, &mut imports_reused_with_self, &mut single_use_usages); + track_uses( + cx, + &item, + &mut imports_reused_with_self, + &mut single_use_usages, + &mut macros, + ); } for single_use in &single_use_usages { @@ -96,6 +109,7 @@ fn track_uses( item: &Item, imports_reused_with_self: &mut Vec, single_use_usages: &mut Vec<(Symbol, Span, bool)>, + macros: &mut Vec, ) { if in_macro(item.span) || item.vis.kind.is_pub() { return; @@ -105,14 +119,22 @@ fn track_uses( ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) => { check_mod(cx, &items); }, + ItemKind::MacroDef(MacroDef { macro_rules: true, .. }) => { + macros.push(item.ident.name); + }, ItemKind::Use(use_tree) => { let segments = &use_tree.prefix.segments; + let should_report = + |name: &Symbol| !macros.contains(name) || matches!(item.vis.kind, VisibilityKind::Inherited); + // keep track of `use some_module;` usages if segments.len() == 1 { if let UseTreeKind::Simple(None, _, _) = use_tree.kind { - let ident = &segments[0].ident; - single_use_usages.push((ident.name, item.span, true)); + let name = segments[0].ident.name; + if should_report(&name) { + single_use_usages.push((name, item.span, true)); + } } return; } @@ -124,8 +146,10 @@ fn track_uses( let segments = &tree.0.prefix.segments; if segments.len() == 1 { if let UseTreeKind::Simple(None, _, _) = tree.0.kind { - let ident = &segments[0].ident; - single_use_usages.push((ident.name, tree.0.span, false)); + let name = segments[0].ident.name; + if should_report(&name) { + single_use_usages.push((name, tree.0.span, false)); + } } } } diff --git a/tests/ui/single_component_path_imports_macro.fixed b/tests/ui/single_component_path_imports_macro.fixed new file mode 100644 index 00000000000..05863f9a2bf --- /dev/null +++ b/tests/ui/single_component_path_imports_macro.fixed @@ -0,0 +1,21 @@ +// run-rustfix +// edition:2018 +#![warn(clippy::single_component_path_imports)] +#![allow(unused_imports)] + +// #7106: use statements exporting a macro within a crate should not trigger lint + +macro_rules! m1 { + () => {}; +} +pub(crate) use m1; // ok + +macro_rules! m2 { + () => {}; +} + // fail + +fn main() { + m1!(); + m2!(); +} diff --git a/tests/ui/single_component_path_imports_macro.rs b/tests/ui/single_component_path_imports_macro.rs new file mode 100644 index 00000000000..633deea348b --- /dev/null +++ b/tests/ui/single_component_path_imports_macro.rs @@ -0,0 +1,21 @@ +// run-rustfix +// edition:2018 +#![warn(clippy::single_component_path_imports)] +#![allow(unused_imports)] + +// #7106: use statements exporting a macro within a crate should not trigger lint + +macro_rules! m1 { + () => {}; +} +pub(crate) use m1; // ok + +macro_rules! m2 { + () => {}; +} +use m2; // fail + +fn main() { + m1!(); + m2!(); +} diff --git a/tests/ui/single_component_path_imports_macro.stderr b/tests/ui/single_component_path_imports_macro.stderr new file mode 100644 index 00000000000..239efb393b1 --- /dev/null +++ b/tests/ui/single_component_path_imports_macro.stderr @@ -0,0 +1,10 @@ +error: this import is redundant + --> $DIR/single_component_path_imports_macro.rs:16:1 + | +LL | use m2; // fail + | ^^^^^^^ help: remove it entirely + | + = note: `-D clippy::single-component-path-imports` implied by `-D warnings` + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From 22f8c13cf5650d6c9d6ee7b4f0e88bffba9076ca Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 1 Apr 2021 22:46:10 -0400 Subject: Improve `implicit_return` Better suggestions when returning macro calls. Suggest changeing all the break expressions in a loop, not just the final statement. Don't lint divergent functions. Don't suggest returning the result of any divergent fuction. --- clippy_lints/src/implicit_return.rs | 241 ++++++++++++++++++++++++------------ clippy_utils/src/source.rs | 31 +++-- clippy_utils/src/visitors.rs | 55 +++++++- tests/ui/implicit_return.fixed | 62 +++++++--- tests/ui/implicit_return.rs | 62 +++++++--- tests/ui/implicit_return.stderr | 55 ++++++-- 6 files changed, 366 insertions(+), 140 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs index 6b379b0d59b..251a7361871 100644 --- a/clippy_lints/src/implicit_return.rs +++ b/clippy_lints/src/implicit_return.rs @@ -1,13 +1,15 @@ -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::match_panic_def_id; -use clippy_utils::source::snippet_opt; -use if_chain::if_chain; +use clippy_utils::{ + diagnostics::span_lint_and_sugg, + source::{snippet_with_applicability, snippet_with_context, walk_span_to_context}, + visitors::visit_break_exprs, +}; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; -use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId, MatchSource, StmtKind}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, FnRetTy, HirId}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Span; +use rustc_span::{Span, SyntaxContext}; declare_clippy_lint! { /// **What it does:** Checks for missing return statements at the end of a block. @@ -39,89 +41,159 @@ declare_clippy_lint! { declare_lint_pass!(ImplicitReturn => [IMPLICIT_RETURN]); -static LINT_BREAK: &str = "change `break` to `return` as shown"; -static LINT_RETURN: &str = "add `return` as shown"; - -fn lint(cx: &LateContext<'_>, outer_span: Span, inner_span: Span, msg: &str) { - let outer_span = outer_span.source_callsite(); - let inner_span = inner_span.source_callsite(); - - span_lint_and_then(cx, IMPLICIT_RETURN, outer_span, "missing `return` statement", |diag| { - if let Some(snippet) = snippet_opt(cx, inner_span) { - diag.span_suggestion( - outer_span, - msg, - format!("return {}", snippet), - Applicability::MachineApplicable, - ); - } - }); +fn lint_return(cx: &LateContext<'_>, span: Span) { + let mut app = Applicability::MachineApplicable; + let snip = snippet_with_applicability(cx, span, "..", &mut app); + span_lint_and_sugg( + cx, + IMPLICIT_RETURN, + span, + "missing `return` statement", + "add `return` as shown", + format!("return {}", snip), + app, + ); +} + +fn lint_break(cx: &LateContext<'_>, break_span: Span, expr_span: Span) { + let mut app = Applicability::MachineApplicable; + let snip = snippet_with_context(cx, expr_span, break_span.ctxt(), "..", &mut app).0; + span_lint_and_sugg( + cx, + IMPLICIT_RETURN, + break_span, + "missing `return` statement", + "change `break` to `return` as shown", + format!("return {}", snip), + app, + ) +} + +enum LintLocation { + /// The lint was applied to a parent expression. + Parent, + /// The lint was applied to this expression, a child, or not applied. + Inner, } +impl LintLocation { + fn still_parent(self, b: bool) -> Self { + if b { self } else { Self::Inner } + } -fn expr_match(cx: &LateContext<'_>, expr: &Expr<'_>) { + fn is_parent(&self) -> bool { + matches!(*self, Self::Parent) + } +} + +// Gets the call site if the span is in a child context. Otherwise returns `None`. +fn get_call_site(span: Span, ctxt: SyntaxContext) -> Option { + (span.ctxt() != ctxt).then(|| walk_span_to_context(span, ctxt).unwrap_or(span)) +} + +fn lint_implicit_returns( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + // The context of the function body. + ctxt: SyntaxContext, + // Whether the expression is from a macro expansion. + call_site_span: Option, +) -> LintLocation { match expr.kind { - // loops could be using `break` instead of `return` - ExprKind::Block(block, ..) | ExprKind::Loop(block, ..) => { - if let Some(expr) = &block.expr { - expr_match(cx, expr); - } - // only needed in the case of `break` with `;` at the end - else if let Some(stmt) = block.stmts.last() { - if_chain! { - if let StmtKind::Semi(expr, ..) = &stmt.kind; - // make sure it's a break, otherwise we want to skip - if let ExprKind::Break(.., Some(break_expr)) = &expr.kind; - then { - lint(cx, expr.span, break_expr.span, LINT_BREAK); - } - } - } - }, - // use `return` instead of `break` - ExprKind::Break(.., break_expr) => { - if let Some(break_expr) = break_expr { - lint(cx, expr.span, break_expr.span, LINT_BREAK); + ExprKind::Block( + Block { + expr: Some(block_expr), .. + }, + _, + ) => lint_implicit_returns( + cx, + block_expr, + ctxt, + call_site_span.or_else(|| get_call_site(block_expr.span, ctxt)), + ) + .still_parent(call_site_span.is_some()), + + ExprKind::If(_, then_expr, Some(else_expr)) => { + // Both `then_expr` or `else_expr` are required to be blocks in the same context as the `if`. Don't + // bother checking. + let res = lint_implicit_returns(cx, then_expr, ctxt, call_site_span).still_parent(call_site_span.is_some()); + if res.is_parent() { + // The return was added as a parent of this if expression. + return res; } + lint_implicit_returns(cx, else_expr, ctxt, call_site_span).still_parent(call_site_span.is_some()) }, - ExprKind::If(.., if_expr, else_expr) => { - expr_match(cx, if_expr); - if let Some(else_expr) = else_expr { - expr_match(cx, else_expr); + ExprKind::Match(_, arms, _) => { + for arm in arms { + let res = lint_implicit_returns( + cx, + arm.body, + ctxt, + call_site_span.or_else(|| get_call_site(arm.body.span, ctxt)), + ) + .still_parent(call_site_span.is_some()); + if res.is_parent() { + // The return was added as a parent of this match expression. + return res; + } } + LintLocation::Inner }, - ExprKind::Match(.., arms, source) => { - let check_all_arms = match source { - MatchSource::IfLetDesugar { - contains_else_clause: has_else, - } => has_else, - _ => true, - }; - - if check_all_arms { - for arm in arms { - expr_match(cx, arm.body); + + ExprKind::Loop(block, ..) => { + let mut add_return = false; + visit_break_exprs(block, |break_expr, dest, sub_expr| { + if dest.target_id.ok() == Some(expr.hir_id) { + if call_site_span.is_none() && break_expr.span.ctxt() == ctxt { + lint_break(cx, break_expr.span, sub_expr.unwrap().span); + } else { + // the break expression is from a macro call, add a return to the loop + add_return = true; + } + } + }); + if add_return { + #[allow(clippy::option_if_let_else)] + if let Some(span) = call_site_span { + lint_return(cx, span); + LintLocation::Parent + } else { + lint_return(cx, expr.span); + LintLocation::Inner } } else { - expr_match(cx, arms.first().expect("`if let` doesn't have a single arm").body); + LintLocation::Inner } }, - // skip if it already has a return statement - ExprKind::Ret(..) => (), - // make sure it's not a call that panics - ExprKind::Call(expr, ..) => { - if_chain! { - if let ExprKind::Path(qpath) = &expr.kind; - if let Some(path_def_id) = cx.qpath_res(qpath, expr.hir_id).opt_def_id(); - if match_panic_def_id(cx, path_def_id); - then { } - else { - lint(cx, expr.span, expr.span, LINT_RETURN) - } + + // If expressions without an else clause, and blocks without a final expression can only be the final expression + // if they are divergent, or return the unit type. + ExprKind::If(_, _, None) | ExprKind::Block(Block { expr: None, .. }, _) | ExprKind::Ret(_) => { + LintLocation::Inner + }, + + // Any divergent expression doesn't need a return statement. + ExprKind::MethodCall(..) + | ExprKind::Call(..) + | ExprKind::Binary(..) + | ExprKind::Unary(..) + | ExprKind::Index(..) + if cx.typeck_results().expr_ty(expr).is_never() => + { + LintLocation::Inner + }, + + _ => + { + #[allow(clippy::option_if_let_else)] + if let Some(span) = call_site_span { + lint_return(cx, span); + LintLocation::Parent + } else { + lint_return(cx, expr.span); + LintLocation::Inner } }, - // everything else is missing `return` - _ => lint(cx, expr.span, expr.span, LINT_RETURN), } } @@ -129,19 +201,24 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitReturn { fn check_fn( &mut self, cx: &LateContext<'tcx>, - _: FnKind<'tcx>, - _: &'tcx FnDecl<'_>, + kind: FnKind<'tcx>, + decl: &'tcx FnDecl<'_>, body: &'tcx Body<'_>, span: Span, _: HirId, ) { - if span.from_expansion() { + if (!matches!(kind, FnKind::Closure) && matches!(decl.output, FnRetTy::DefaultReturn(_))) + || span.ctxt() != body.value.span.ctxt() + || in_external_macro(cx.sess(), span) + { return; } - let body = cx.tcx.hir().body(body.id()); - if cx.typeck_results().expr_ty(&body.value).is_unit() { + + let res_ty = cx.typeck_results().expr_ty(&body.value); + if res_ty.is_unit() || res_ty.is_never() { return; } - expr_match(cx, &body.value); + + lint_implicit_returns(cx, &body.value, body.value.span.ctxt(), None); } } diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index 53180d1f9f5..2e731c182ec 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -280,17 +280,17 @@ pub fn snippet_with_context( default: &'a str, applicability: &mut Applicability, ) -> (Cow<'a, str>, bool) { - let outer_span = hygiene::walk_chain(span, outer); - let (span, is_macro_call) = if outer_span.ctxt() == outer { - (outer_span, span.ctxt() != outer) - } else { - // The span is from a macro argument, and the outer context is the macro using the argument - if *applicability != Applicability::Unspecified { - *applicability = Applicability::MaybeIncorrect; - } - // TODO: get the argument span. - (span, false) - }; + let (span, is_macro_call) = walk_span_to_context(span, outer).map_or_else( + || { + // The span is from a macro argument, and the outer context is the macro using the argument + if *applicability != Applicability::Unspecified { + *applicability = Applicability::MaybeIncorrect; + } + // TODO: get the argument span. + (span, false) + }, + |outer_span| (outer_span, span.ctxt() != outer), + ); ( snippet_with_applicability(cx, span, default, applicability), @@ -298,6 +298,15 @@ pub fn snippet_with_context( ) } +/// Walks the span up to the target context, thereby returning the macro call site if the span is +/// inside a macro expansion, or the original span if it is not. Note this will return `None` in the +/// case of the span being in a macro expansion, but the target context is from expanding a macro +/// argument. +pub fn walk_span_to_context(span: Span, outer: SyntaxContext) -> Option { + let outer_span = hygiene::walk_chain(span, outer); + (outer_span.ctxt() == outer).then(|| outer_span) +} + /// Removes block comments from the given `Vec` of lines. /// /// # Examples diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index 5a8c629e333..d431bdf34ee 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -1,7 +1,7 @@ use crate::path_to_local_id; use rustc_hir as hir; -use rustc_hir::intravisit::{self, walk_expr, NestedVisitorMap, Visitor}; -use rustc_hir::{Arm, Body, Expr, HirId, Stmt}; +use rustc_hir::intravisit::{self, walk_expr, ErasedMap, NestedVisitorMap, Visitor}; +use rustc_hir::{Arm, Block, Body, Destination, Expr, ExprKind, HirId, Stmt}; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; @@ -188,3 +188,54 @@ impl<'v> Visitor<'v> for LocalUsedVisitor<'v> { NestedVisitorMap::OnlyBodies(self.hir) } } + +pub trait Visitable<'tcx> { + fn visit>(self, v: &mut V); +} +impl Visitable<'tcx> for &'tcx Expr<'tcx> { + fn visit>(self, v: &mut V) { + v.visit_expr(self) + } +} +impl Visitable<'tcx> for &'tcx Block<'tcx> { + fn visit>(self, v: &mut V) { + v.visit_block(self) + } +} +impl<'tcx> Visitable<'tcx> for &'tcx Stmt<'tcx> { + fn visit>(self, v: &mut V) { + v.visit_stmt(self) + } +} +impl<'tcx> Visitable<'tcx> for &'tcx Body<'tcx> { + fn visit>(self, v: &mut V) { + v.visit_body(self) + } +} +impl<'tcx> Visitable<'tcx> for &'tcx Arm<'tcx> { + fn visit>(self, v: &mut V) { + v.visit_arm(self) + } +} + +pub fn visit_break_exprs<'tcx>( + node: impl Visitable<'tcx>, + f: impl FnMut(&'tcx Expr<'tcx>, Destination, Option<&'tcx Expr<'tcx>>), +) { + struct V(F); + impl<'tcx, F: FnMut(&'tcx Expr<'tcx>, Destination, Option<&'tcx Expr<'tcx>>)> Visitor<'tcx> for V { + type Map = ErasedMap<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + + fn visit_expr(&mut self, e: &'tcx Expr<'_>) { + if let ExprKind::Break(dest, sub_expr) = e.kind { + self.0(e, dest, sub_expr) + } + walk_expr(self, e); + } + } + + node.visit(&mut V(f)); +} diff --git a/tests/ui/implicit_return.fixed b/tests/ui/implicit_return.fixed index 59f7ad9c106..c0fc4b926a0 100644 --- a/tests/ui/implicit_return.fixed +++ b/tests/ui/implicit_return.fixed @@ -1,7 +1,7 @@ // run-rustfix #![warn(clippy::implicit_return)] -#![allow(clippy::needless_return, unused)] +#![allow(clippy::needless_return, clippy::needless_bool, unused, clippy::never_loop)] fn test_end_of_fn() -> bool { if true { @@ -12,7 +12,6 @@ fn test_end_of_fn() -> bool { return true } -#[allow(clippy::needless_bool)] fn test_if_block() -> bool { if true { return true } else { return false } } @@ -25,7 +24,6 @@ fn test_match(x: bool) -> bool { } } -#[allow(clippy::needless_return)] fn test_match_with_unreachable(x: bool) -> bool { match x { true => return false, @@ -33,14 +31,12 @@ fn test_match_with_unreachable(x: bool) -> bool { } } -#[allow(clippy::never_loop)] fn test_loop() -> bool { loop { return true; } } -#[allow(clippy::never_loop)] fn test_loop_with_block() -> bool { loop { { @@ -49,7 +45,6 @@ fn test_loop_with_block() -> bool { } } -#[allow(clippy::never_loop)] fn test_loop_with_nests() -> bool { loop { if true { @@ -83,15 +78,48 @@ fn test_return_macro() -> String { return format!("test {}", "test") } -fn main() { - let _ = test_end_of_fn(); - let _ = test_if_block(); - let _ = test_match(true); - let _ = test_match_with_unreachable(true); - let _ = test_loop(); - let _ = test_loop_with_block(); - let _ = test_loop_with_nests(); - let _ = test_loop_with_if_let(); - test_closure(); - let _ = test_return_macro(); +fn macro_branch_test() -> bool { + macro_rules! m { + ($t:expr, $f:expr) => { + if true { $t } else { $f } + }; + } + return m!(true, false) +} + +fn loop_test() -> bool { + 'outer: loop { + if true { + return true; + } + + let _ = loop { + if false { + return false; + } + if true { + break true; + } + }; + } } + +fn loop_macro_test() -> bool { + macro_rules! m { + ($e:expr) => { + break $e + }; + } + return loop { + m!(true); + } +} + +fn divergent_test() -> bool { + fn diverge() -> ! { + panic!() + } + diverge() +} + +fn main() {} diff --git a/tests/ui/implicit_return.rs b/tests/ui/implicit_return.rs index 2c1bc046515..737ffd5ddce 100644 --- a/tests/ui/implicit_return.rs +++ b/tests/ui/implicit_return.rs @@ -1,7 +1,7 @@ // run-rustfix #![warn(clippy::implicit_return)] -#![allow(clippy::needless_return, unused)] +#![allow(clippy::needless_return, clippy::needless_bool, unused, clippy::never_loop)] fn test_end_of_fn() -> bool { if true { @@ -12,7 +12,6 @@ fn test_end_of_fn() -> bool { true } -#[allow(clippy::needless_bool)] fn test_if_block() -> bool { if true { true } else { false } } @@ -25,7 +24,6 @@ fn test_match(x: bool) -> bool { } } -#[allow(clippy::needless_return)] fn test_match_with_unreachable(x: bool) -> bool { match x { true => return false, @@ -33,14 +31,12 @@ fn test_match_with_unreachable(x: bool) -> bool { } } -#[allow(clippy::never_loop)] fn test_loop() -> bool { loop { break true; } } -#[allow(clippy::never_loop)] fn test_loop_with_block() -> bool { loop { { @@ -49,7 +45,6 @@ fn test_loop_with_block() -> bool { } } -#[allow(clippy::never_loop)] fn test_loop_with_nests() -> bool { loop { if true { @@ -83,15 +78,48 @@ fn test_return_macro() -> String { format!("test {}", "test") } -fn main() { - let _ = test_end_of_fn(); - let _ = test_if_block(); - let _ = test_match(true); - let _ = test_match_with_unreachable(true); - let _ = test_loop(); - let _ = test_loop_with_block(); - let _ = test_loop_with_nests(); - let _ = test_loop_with_if_let(); - test_closure(); - let _ = test_return_macro(); +fn macro_branch_test() -> bool { + macro_rules! m { + ($t:expr, $f:expr) => { + if true { $t } else { $f } + }; + } + m!(true, false) } + +fn loop_test() -> bool { + 'outer: loop { + if true { + break true; + } + + let _ = loop { + if false { + break 'outer false; + } + if true { + break true; + } + }; + } +} + +fn loop_macro_test() -> bool { + macro_rules! m { + ($e:expr) => { + break $e + }; + } + loop { + m!(true); + } +} + +fn divergent_test() -> bool { + fn diverge() -> ! { + panic!() + } + diverge() +} + +fn main() {} diff --git a/tests/ui/implicit_return.stderr b/tests/ui/implicit_return.stderr index 3608319e5bd..632e30cbdc6 100644 --- a/tests/ui/implicit_return.stderr +++ b/tests/ui/implicit_return.stderr @@ -7,64 +7,97 @@ LL | true = note: `-D clippy::implicit-return` implied by `-D warnings` error: missing `return` statement - --> $DIR/implicit_return.rs:17:15 + --> $DIR/implicit_return.rs:16:15 | LL | if true { true } else { false } | ^^^^ help: add `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:17:29 + --> $DIR/implicit_return.rs:16:29 | LL | if true { true } else { false } | ^^^^^ help: add `return` as shown: `return false` error: missing `return` statement - --> $DIR/implicit_return.rs:23:17 + --> $DIR/implicit_return.rs:22:17 | LL | true => false, | ^^^^^ help: add `return` as shown: `return false` error: missing `return` statement - --> $DIR/implicit_return.rs:24:20 + --> $DIR/implicit_return.rs:23:20 | LL | false => { true }, | ^^^^ help: add `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:39:9 + --> $DIR/implicit_return.rs:36:9 | LL | break true; | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:47:13 + --> $DIR/implicit_return.rs:43:13 | LL | break true; | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:56:13 + --> $DIR/implicit_return.rs:51:13 | LL | break true; | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:74:18 + --> $DIR/implicit_return.rs:69:18 | LL | let _ = || { true }; | ^^^^ help: add `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:75:16 + --> $DIR/implicit_return.rs:70:16 | LL | let _ = || true; | ^^^^ help: add `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:83:5 + --> $DIR/implicit_return.rs:78:5 | LL | format!("test {}", "test") | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `return` as shown: `return format!("test {}", "test")` -error: aborting due to 11 previous errors +error: missing `return` statement + --> $DIR/implicit_return.rs:87:5 + | +LL | m!(true, false) + | ^^^^^^^^^^^^^^^ help: add `return` as shown: `return m!(true, false)` + +error: missing `return` statement + --> $DIR/implicit_return.rs:93:13 + | +LL | break true; + | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true` + +error: missing `return` statement + --> $DIR/implicit_return.rs:98:17 + | +LL | break 'outer false; + | ^^^^^^^^^^^^^^^^^^ help: change `break` to `return` as shown: `return false` + +error: missing `return` statement + --> $DIR/implicit_return.rs:113:5 + | +LL | / loop { +LL | | m!(true); +LL | | } + | |_____^ + | +help: add `return` as shown + | +LL | return loop { +LL | m!(true); +LL | } + | + +error: aborting due to 15 previous errors -- cgit 1.4.1-3-g733a5 From 74cf5f2fc6c121ae0eaaa709c9a303cd82fe9b30 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 2 Apr 2021 17:53:14 -0400 Subject: Fix `implicit_return` suggestion for async functions --- clippy_lints/src/implicit_return.rs | 11 +++++++++- clippy_utils/src/lib.rs | 40 ++++++++++++++++++++++++++++++++++--- tests/ui/implicit_return.fixed | 6 ++++++ tests/ui/implicit_return.rs | 6 ++++++ tests/ui/implicit_return.stderr | 38 ++++++++++++++++++++--------------- 5 files changed, 81 insertions(+), 20 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs index 251a7361871..39612dbf050 100644 --- a/clippy_lints/src/implicit_return.rs +++ b/clippy_lints/src/implicit_return.rs @@ -1,5 +1,6 @@ use clippy_utils::{ diagnostics::span_lint_and_sugg, + get_async_fn_body, is_async_fn, source::{snippet_with_applicability, snippet_with_context, walk_span_to_context}, visitors::visit_break_exprs, }; @@ -219,6 +220,14 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitReturn { return; } - lint_implicit_returns(cx, &body.value, body.value.span.ctxt(), None); + let expr = if is_async_fn(kind) { + match get_async_fn_body(cx.tcx, body) { + Some(e) => e, + None => return, + } + } else { + &body.value + }; + lint_implicit_returns(cx, expr, expr.span.ctxt(), None); } } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index b7017411927..6dc96e51a6b 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -60,12 +60,12 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; -use rustc_hir::intravisit::{self, walk_expr, ErasedMap, NestedVisitorMap, Visitor}; +use rustc_hir::intravisit::{self, walk_expr, ErasedMap, FnKind, NestedVisitorMap, Visitor}; use rustc_hir::LangItem::{ResultErr, ResultOk}; use rustc_hir::{ def, Arm, BindingAnnotation, Block, Body, Constness, Destination, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl, - ImplItem, ImplItemKind, Item, ItemKind, LangItem, Local, MatchSource, Node, Param, Pat, PatKind, Path, PathSegment, - QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, + ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, Node, Param, Pat, PatKind, Path, + PathSegment, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, }; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::exports::Export; @@ -1300,6 +1300,40 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, (conds, blocks) } +/// Checks if the given function kind is an async function. +pub fn is_async_fn(kind: FnKind) -> bool { + matches!(kind, FnKind::ItemFn(_, _, header, _) if header.asyncness == IsAsync::Async) +} + +/// Peels away all the compiler generated code surrounding the body of an async function, +pub fn get_async_fn_body(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> { + if let ExprKind::Call( + _, + &[Expr { + kind: ExprKind::Closure(_, _, body, _, _), + .. + }], + ) = body.value.kind + { + if let ExprKind::Block( + Block { + stmts: [], + expr: + Some(Expr { + kind: ExprKind::DropTemps(expr), + .. + }), + .. + }, + _, + ) = tcx.hir().body(body).value.kind + { + return Some(expr); + } + }; + None +} + // Finds the `#[must_use]` attribute, if any pub fn must_use_attr(attrs: &[Attribute]) -> Option<&Attribute> { attrs.iter().find(|a| a.has_name(sym::must_use)) diff --git a/tests/ui/implicit_return.fixed b/tests/ui/implicit_return.fixed index c0fc4b926a0..7698b88a88c 100644 --- a/tests/ui/implicit_return.fixed +++ b/tests/ui/implicit_return.fixed @@ -1,3 +1,4 @@ +// edition:2018 // run-rustfix #![warn(clippy::implicit_return)] @@ -122,4 +123,9 @@ fn divergent_test() -> bool { diverge() } +// issue #6940 +async fn foo() -> bool { + return true +} + fn main() {} diff --git a/tests/ui/implicit_return.rs b/tests/ui/implicit_return.rs index 737ffd5ddce..45bbc2ec670 100644 --- a/tests/ui/implicit_return.rs +++ b/tests/ui/implicit_return.rs @@ -1,3 +1,4 @@ +// edition:2018 // run-rustfix #![warn(clippy::implicit_return)] @@ -122,4 +123,9 @@ fn divergent_test() -> bool { diverge() } +// issue #6940 +async fn foo() -> bool { + true +} + fn main() {} diff --git a/tests/ui/implicit_return.stderr b/tests/ui/implicit_return.stderr index 632e30cbdc6..16fe9ed444f 100644 --- a/tests/ui/implicit_return.stderr +++ b/tests/ui/implicit_return.stderr @@ -1,5 +1,5 @@ error: missing `return` statement - --> $DIR/implicit_return.rs:12:5 + --> $DIR/implicit_return.rs:13:5 | LL | true | ^^^^ help: add `return` as shown: `return true` @@ -7,85 +7,85 @@ LL | true = note: `-D clippy::implicit-return` implied by `-D warnings` error: missing `return` statement - --> $DIR/implicit_return.rs:16:15 + --> $DIR/implicit_return.rs:17:15 | LL | if true { true } else { false } | ^^^^ help: add `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:16:29 + --> $DIR/implicit_return.rs:17:29 | LL | if true { true } else { false } | ^^^^^ help: add `return` as shown: `return false` error: missing `return` statement - --> $DIR/implicit_return.rs:22:17 + --> $DIR/implicit_return.rs:23:17 | LL | true => false, | ^^^^^ help: add `return` as shown: `return false` error: missing `return` statement - --> $DIR/implicit_return.rs:23:20 + --> $DIR/implicit_return.rs:24:20 | LL | false => { true }, | ^^^^ help: add `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:36:9 + --> $DIR/implicit_return.rs:37:9 | LL | break true; | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:43:13 + --> $DIR/implicit_return.rs:44:13 | LL | break true; | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:51:13 + --> $DIR/implicit_return.rs:52:13 | LL | break true; | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:69:18 + --> $DIR/implicit_return.rs:70:18 | LL | let _ = || { true }; | ^^^^ help: add `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:70:16 + --> $DIR/implicit_return.rs:71:16 | LL | let _ = || true; | ^^^^ help: add `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:78:5 + --> $DIR/implicit_return.rs:79:5 | LL | format!("test {}", "test") | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `return` as shown: `return format!("test {}", "test")` error: missing `return` statement - --> $DIR/implicit_return.rs:87:5 + --> $DIR/implicit_return.rs:88:5 | LL | m!(true, false) | ^^^^^^^^^^^^^^^ help: add `return` as shown: `return m!(true, false)` error: missing `return` statement - --> $DIR/implicit_return.rs:93:13 + --> $DIR/implicit_return.rs:94:13 | LL | break true; | ^^^^^^^^^^ help: change `break` to `return` as shown: `return true` error: missing `return` statement - --> $DIR/implicit_return.rs:98:17 + --> $DIR/implicit_return.rs:99:17 | LL | break 'outer false; | ^^^^^^^^^^^^^^^^^^ help: change `break` to `return` as shown: `return false` error: missing `return` statement - --> $DIR/implicit_return.rs:113:5 + --> $DIR/implicit_return.rs:114:5 | LL | / loop { LL | | m!(true); @@ -99,5 +99,11 @@ LL | m!(true); LL | } | -error: aborting due to 15 previous errors +error: missing `return` statement + --> $DIR/implicit_return.rs:128:5 + | +LL | true + | ^^^^ help: add `return` as shown: `return true` + +error: aborting due to 16 previous errors -- cgit 1.4.1-3-g733a5 From 5625d58f9f3fbff3d81cbab76fbbcfc5ff06c833 Mon Sep 17 00:00:00 2001 From: Aliénore Bouttefeux Date: Fri, 16 Apr 2021 20:12:16 +0200 Subject: add detection unused_io_amount of "or", "or_else" and "ok" --- clippy_lints/src/unused_io_amount.rs | 20 +++++++++++++----- tests/ui/unused_io_amount.rs | 41 +++++++++++++++++++++++++++++++++++- tests/ui/unused_io_amount.stderr | 30 +++++++++++++++++++++++++- 3 files changed, 84 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index 5e8e530f480..3387f35bac3 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -47,25 +47,35 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { func.kind, hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::TryIntoResult, _)) ) { - check_method_call(cx, &args[0], expr); + check_map_error(cx, &args[0], expr); } } else { - check_method_call(cx, res, expr); + check_map_error(cx, res, expr); } }, - hir::ExprKind::MethodCall(path, _, args, _) => match &*path.ident.as_str() { "expect" | "unwrap" | "unwrap_or" | "unwrap_or_else" => { - check_method_call(cx, &args[0], expr); + check_map_error(cx, &args[0], expr); }, _ => (), }, - _ => (), } } } +fn check_map_error(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) { + let mut call = call; + while let hir::ExprKind::MethodCall(ref path, _, ref args, _) = call.kind { + if matches!(&*path.ident.as_str(), "or" | "or_else" | "ok") { + call = &args[0]; + } else { + break; + } + } + check_method_call(cx, call, expr); +} + fn check_method_call(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) { if let hir::ExprKind::MethodCall(path, _, _, _) = call.kind { let symbol = &*path.ident.as_str(); diff --git a/tests/ui/unused_io_amount.rs b/tests/ui/unused_io_amount.rs index ebaba9629db..8b141e25942 100644 --- a/tests/ui/unused_io_amount.rs +++ b/tests/ui/unused_io_amount.rs @@ -1,7 +1,7 @@ #![allow(dead_code)] #![warn(clippy::unused_io_amount)] -use std::io; +use std::io::{self, Read}; fn question_mark(s: &mut T) -> io::Result<()> { s.write(b"test")?; @@ -22,4 +22,43 @@ fn vectored(s: &mut T) -> io::Result<()> { Ok(()) } +fn ok(file: &str) -> Option<()> { + let mut reader = std::fs::File::open(file).ok()?; + let mut result = [0u8; 0]; + reader.read(&mut result).ok()?; + Some(()) +} + +#[allow(clippy::redundant_closure)] +#[allow(clippy::bind_instead_of_map)] +fn or_else(file: &str) -> io::Result<()> { + let mut reader = std::fs::File::open(file)?; + let mut result = [0u8; 0]; + reader.read(&mut result).or_else(|err| Err(err))?; + Ok(()) +} + +#[derive(Debug)] +enum Error { + Kind, +} + +fn or(file: &str) -> Result<(), Error> { + let mut reader = std::fs::File::open(file).unwrap(); + let mut result = [0u8; 0]; + reader.read(&mut result).or(Err(Error::Kind))?; + Ok(()) +} + +fn combine_or(file: &str) -> Result<(), Error> { + let mut reader = std::fs::File::open(file).unwrap(); + let mut result = [0u8; 0]; + reader + .read(&mut result) + .or(Err(Error::Kind)) + .or(Err(Error::Kind)) + .expect("error"); + Ok(()) +} + fn main() {} diff --git a/tests/ui/unused_io_amount.stderr b/tests/ui/unused_io_amount.stderr index 5219d63980b..d8dfc0e5a79 100644 --- a/tests/ui/unused_io_amount.stderr +++ b/tests/ui/unused_io_amount.stderr @@ -36,5 +36,33 @@ error: written amount is not handled LL | s.write_vectored(&[io::IoSlice::new(&[])])?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 6 previous errors +error: read amount is not handled. Use `Read::read_exact` instead + --> $DIR/unused_io_amount.rs:28:5 + | +LL | reader.read(&mut result).ok()?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: read amount is not handled. Use `Read::read_exact` instead + --> $DIR/unused_io_amount.rs:37:5 + | +LL | reader.read(&mut result).or_else(|err| Err(err))?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: read amount is not handled. Use `Read::read_exact` instead + --> $DIR/unused_io_amount.rs:49:5 + | +LL | reader.read(&mut result).or(Err(Error::Kind))?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: read amount is not handled. Use `Read::read_exact` instead + --> $DIR/unused_io_amount.rs:56:5 + | +LL | / reader +LL | | .read(&mut result) +LL | | .or(Err(Error::Kind)) +LL | | .or(Err(Error::Kind)) +LL | | .expect("error"); + | |________________________^ + +error: aborting due to 10 previous errors -- cgit 1.4.1-3-g733a5 From db7ad648e7e17eff0f66de2c25c4d581982f981f Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 25 Apr 2021 10:07:01 -0400 Subject: Fix ICE checking for feature gated const fn --- clippy_utils/src/qualify_min_const_fn.rs | 10 ++++------ tests/ui/crashes/ice-7126.rs | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 6 deletions(-) create mode 100644 tests/ui/crashes/ice-7126.rs (limited to 'tests') diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index b2ce58b597b..a08dcf19e5b 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -364,7 +364,7 @@ fn check_terminator( fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<&RustcVersion>) -> bool { rustc_mir::const_eval::is_const_fn(tcx, def_id) - && if let Some(const_stab) = tcx.lookup_const_stability(def_id) { + && tcx.lookup_const_stability(def_id).map_or(true, |const_stab| { if let rustc_attr::StabilityLevel::Stable { since } = const_stab.level { // Checking MSRV is manually necessary because `rustc` has no such concept. This entire // function could be removed if `rustc` provided a MSRV-aware version of `is_const_fn`. @@ -375,10 +375,8 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<&RustcVersion>) -> b .expect("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted"), ) } else { - // `rustc_mir::const_eval::is_const_fn` should return false for unstably const functions. - unreachable!(); + // Unstable const fn with the feature enabled. + msrv.is_none() } - } else { - true - } + }) } diff --git a/tests/ui/crashes/ice-7126.rs b/tests/ui/crashes/ice-7126.rs new file mode 100644 index 00000000000..ca563ba0978 --- /dev/null +++ b/tests/ui/crashes/ice-7126.rs @@ -0,0 +1,14 @@ +// This test requires a feature gated const fn and will stop working in the future. + +#![feature(const_btree_new)] + +use std::collections::BTreeMap; + +struct Foo(BTreeMap); +impl Foo { + fn new() -> Self { + Self(BTreeMap::new()) + } +} + +fn main() {} -- cgit 1.4.1-3-g733a5 From efc4c6c957ea2b0d870f4728fa934042213da5e8 Mon Sep 17 00:00:00 2001 From: Andre Bogus Date: Sun, 25 Apr 2021 18:10:38 +0200 Subject: extend `single_element_loop` to match `.iter()` --- clippy_lints/src/loops/single_element_loop.rs | 8 +++++++- tests/ui/single_element_loop.fixed | 5 +++++ tests/ui/single_element_loop.rs | 4 ++++ tests/ui/single_element_loop.stderr | 18 +++++++++++++++++- 4 files changed, 33 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/loops/single_element_loop.rs b/clippy_lints/src/loops/single_element_loop.rs index fc067e81bca..0fd09ff7197 100644 --- a/clippy_lints/src/loops/single_element_loop.rs +++ b/clippy_lints/src/loops/single_element_loop.rs @@ -14,8 +14,14 @@ pub(super) fn check<'tcx>( body: &'tcx Expr<'_>, expr: &'tcx Expr<'_>, ) { + let arg_expr = match arg.kind { + ExprKind::AddrOf(BorrowKind::Ref, _, ref_arg) => ref_arg, + ExprKind::MethodCall(method, _, args, _) if args.len() == 1 && method.ident.name == rustc_span::sym::iter => { + &args[0] + }, + _ => return, + }; if_chain! { - if let ExprKind::AddrOf(BorrowKind::Ref, _, arg_expr) = arg.kind; if let PatKind::Binding(.., target, _) = pat.kind; if let ExprKind::Array([arg_expression]) = arg_expr.kind; if let ExprKind::Path(ref list_item) = arg_expression.kind; diff --git a/tests/ui/single_element_loop.fixed b/tests/ui/single_element_loop.fixed index 8ca068293a6..c307afffcb8 100644 --- a/tests/ui/single_element_loop.fixed +++ b/tests/ui/single_element_loop.fixed @@ -8,4 +8,9 @@ fn main() { let item = &item1; println!("{}", item); } + + { + let item = &item1; + println!("{:?}", item); + } } diff --git a/tests/ui/single_element_loop.rs b/tests/ui/single_element_loop.rs index 57e9336a31f..2c0c03b7211 100644 --- a/tests/ui/single_element_loop.rs +++ b/tests/ui/single_element_loop.rs @@ -7,4 +7,8 @@ fn main() { for item in &[item1] { println!("{}", item); } + + for item in [item1].iter() { + println!("{:?}", item); + } } diff --git a/tests/ui/single_element_loop.stderr b/tests/ui/single_element_loop.stderr index 90be1dc3283..0e35a33ded5 100644 --- a/tests/ui/single_element_loop.stderr +++ b/tests/ui/single_element_loop.stderr @@ -15,5 +15,21 @@ LL | println!("{}", item); LL | } | -error: aborting due to previous error +error: for loop over a single element + --> $DIR/single_element_loop.rs:11:5 + | +LL | / for item in [item1].iter() { +LL | | println!("{:?}", item); +LL | | } + | |_____^ + | +help: try + | +LL | { +LL | let item = &item1; +LL | println!("{:?}", item); +LL | } + | + +error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From dcf4e07458c4136f2107d5ff26590c47f9590b1a Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 26 Apr 2021 11:09:24 -0500 Subject: Finish MSRV for cloned_instead_of_copied --- clippy_lints/src/utils/conf.rs | 2 +- tests/ui/min_rust_version_attr.rs | 4 ++++ tests/ui/min_rust_version_attr.stderr | 8 ++++---- 3 files changed, 9 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 147f823491d..2f8714b1799 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -106,7 +106,7 @@ macro_rules! define_Conf { pub use self::helpers::Conf; define_Conf! { - /// Lint: REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, NEEDLESS_QUESTION_MARK, PTR_AS_PTR. The minimum rust version that the project supports + /// Lint: CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, NEEDLESS_QUESTION_MARK, PTR_AS_PTR. The minimum rust version that the project supports (msrv, "msrv": Option, None), /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses (blacklisted_names, "blacklisted_names": Vec, ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()), diff --git a/tests/ui/min_rust_version_attr.rs b/tests/ui/min_rust_version_attr.rs index 49ace1ca128..7f9f7ddc535 100644 --- a/tests/ui/min_rust_version_attr.rs +++ b/tests/ui/min_rust_version_attr.rs @@ -4,6 +4,10 @@ use std::ops::{Deref, RangeFrom}; +fn cloned_instead_of_copied() { + let _ = [1].iter().cloned(); +} + fn option_as_ref_deref() { let mut opt = Some(String::from("123")); diff --git a/tests/ui/min_rust_version_attr.stderr b/tests/ui/min_rust_version_attr.stderr index 8d3575c2da8..ddb1e1f3724 100644 --- a/tests/ui/min_rust_version_attr.stderr +++ b/tests/ui/min_rust_version_attr.stderr @@ -1,12 +1,12 @@ error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:156:24 + --> $DIR/min_rust_version_attr.rs:160:24 | LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); | ^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::manual-strip` implied by `-D warnings` note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:155:9 + --> $DIR/min_rust_version_attr.rs:159:9 | LL | if s.starts_with("hello, ") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,13 +17,13 @@ LL | assert_eq!(.to_uppercase(), "WORLD!"); | error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:168:24 + --> $DIR/min_rust_version_attr.rs:172:24 | LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); | ^^^^^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:167:9 + --> $DIR/min_rust_version_attr.rs:171:9 | LL | if s.starts_with("hello, ") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From 84003aa7a18f2673b20340dd20344dda8265ba7a Mon Sep 17 00:00:00 2001 From: Mateusz Gacek <96mateusz.gacek@gmail.com> Date: Mon, 26 Apr 2021 12:08:24 -0700 Subject: fix invalid code suggestion in `manual_unwrap_or`, due to macro expansion --- clippy_lints/src/manual_unwrap_or.rs | 11 ++++++++++- tests/ui/manual_unwrap_or.fixed | 12 ++++++++++++ tests/ui/manual_unwrap_or.rs | 15 +++++++++++++++ tests/ui/manual_unwrap_or.stderr | 12 +++++++++++- 4 files changed, 48 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index 65baa2552cc..520162559e5 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -112,6 +112,15 @@ fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { then { let reindented_or_body = 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)` + sugg::Sugg::hir_with_macro_callsite(cx, scrutinee, "..") + } + else { + sugg::Sugg::hir(cx, scrutinee, "..").maybe_par() + }; + span_lint_and_sugg( cx, MANUAL_UNWRAP_OR, expr.span, @@ -119,7 +128,7 @@ fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { "replace with", format!( "{}.unwrap_or({})", - sugg::Sugg::hir(cx, scrutinee, "..").maybe_par(), + suggestion, reindented_or_body, ), Applicability::MachineApplicable, diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index f1d3252230b..e7a29596b73 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -151,4 +151,16 @@ const fn const_fn_result_unwrap_or() { }; } +mod issue6965 { + macro_rules! some_macro { + () => { + if 1 > 2 { Some(1) } else { None } + }; + } + + fn test() { + let _ = some_macro!().unwrap_or(0); + } +} + fn main() {} diff --git a/tests/ui/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs index c9eee25a5b1..66006b6c616 100644 --- a/tests/ui/manual_unwrap_or.rs +++ b/tests/ui/manual_unwrap_or.rs @@ -190,4 +190,19 @@ const fn const_fn_result_unwrap_or() { }; } +mod issue6965 { + macro_rules! some_macro { + () => { + if 1 > 2 { Some(1) } else { None } + }; + } + + fn test() { + let _ = match some_macro!() { + Some(val) => val, + None => 0, + }; + } +} + fn main() {} diff --git a/tests/ui/manual_unwrap_or.stderr b/tests/ui/manual_unwrap_or.stderr index fc174c4c270..99625b789b6 100644 --- a/tests/ui/manual_unwrap_or.stderr +++ b/tests/ui/manual_unwrap_or.stderr @@ -141,5 +141,15 @@ LL | | Err(_) => "Alice", LL | | }; | |_____^ help: replace with: `Ok::<&str, &str>("Bob").unwrap_or("Alice")` -error: aborting due to 13 previous errors +error: this pattern reimplements `Option::unwrap_or` + --> $DIR/manual_unwrap_or.rs:201:17 + | +LL | let _ = match some_macro!() { + | _________________^ +LL | | Some(val) => val, +LL | | None => 0, +LL | | }; + | |_________^ help: replace with: `some_macro!().unwrap_or(0)` + +error: aborting due to 14 previous errors -- cgit 1.4.1-3-g733a5 From d7627dcfc8a60aaedccf002738dc44a2576fa8fd Mon Sep 17 00:00:00 2001 From: Mateusz Gacek <96mateusz.gacek@gmail.com> Date: Mon, 26 Apr 2021 13:03:51 -0700 Subject: Fix FN in `iter_cloned_collect` with a large array --- clippy_lints/src/methods/utils.rs | 4 +--- tests/ui/iter_cloned_collect.fixed | 4 ++++ tests/ui/iter_cloned_collect.rs | 4 ++++ tests/ui/iter_cloned_collect.stderr | 8 +++++++- 4 files changed, 16 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/utils.rs b/clippy_lints/src/methods/utils.rs index f6bf37e08b9..0daea47816a 100644 --- a/clippy_lints/src/methods/utils.rs +++ b/clippy_lints/src/methods/utils.rs @@ -18,9 +18,7 @@ pub(super) fn derefs_to_slice<'tcx>( ty::Slice(_) => true, ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()), ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym::vec_type), - ty::Array(_, size) => size - .try_eval_usize(cx.tcx, cx.param_env) - .map_or(false, |size| size < 32), + ty::Array(_, size) => size.try_eval_usize(cx.tcx, cx.param_env).is_some(), ty::Ref(_, inner, _) => may_slice(cx, inner), _ => false, } diff --git a/tests/ui/iter_cloned_collect.fixed b/tests/ui/iter_cloned_collect.fixed index 2773227e26b..39cc58cd298 100644 --- a/tests/ui/iter_cloned_collect.fixed +++ b/tests/ui/iter_cloned_collect.fixed @@ -19,4 +19,8 @@ fn main() { let _: Vec = std::ffi::CStr::from_ptr(std::ptr::null()) .to_bytes().to_vec(); } + + // Issue #6808 + let arr: [u8; 64] = [0; 64]; + let _: Vec<_> = arr.to_vec(); } diff --git a/tests/ui/iter_cloned_collect.rs b/tests/ui/iter_cloned_collect.rs index 60a4eac23c7..c2a036ec09f 100644 --- a/tests/ui/iter_cloned_collect.rs +++ b/tests/ui/iter_cloned_collect.rs @@ -22,4 +22,8 @@ fn main() { .cloned() .collect(); } + + // Issue #6808 + let arr: [u8; 64] = [0; 64]; + let _: Vec<_> = arr.iter().cloned().collect(); } diff --git a/tests/ui/iter_cloned_collect.stderr b/tests/ui/iter_cloned_collect.stderr index b90a1e6c919..e1df61794ce 100644 --- a/tests/ui/iter_cloned_collect.stderr +++ b/tests/ui/iter_cloned_collect.stderr @@ -22,5 +22,11 @@ LL | | .cloned() LL | | .collect(); | |______________________^ help: try: `.to_vec()` -error: aborting due to 3 previous errors +error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable + --> $DIR/iter_cloned_collect.rs:28:24 + | +LL | let _: Vec<_> = arr.iter().cloned().collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()` + +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From 33ed8b5b24caf1e3d1d4d8020f61ca65102c87f8 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 26 Apr 2021 15:49:25 -0500 Subject: Remove needless_question_mark MSRV --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/needless_question_mark.rs | 42 ++++------------- clippy_lints/src/utils/conf.rs | 2 +- tests/ui/needless_question_mark.fixed | 72 ------------------------------ tests/ui/needless_question_mark.rs | 72 ------------------------------ tests/ui/needless_question_mark.stderr | 22 +-------- 6 files changed, 13 insertions(+), 199 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 2ff633bcda5..c84890299df 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1076,7 +1076,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box from_over_into::FromOverInto::new(msrv)); store.register_late_pass(move || box use_self::UseSelf::new(msrv)); store.register_late_pass(move || box missing_const_for_fn::MissingConstForFn::new(msrv)); - store.register_late_pass(move || box needless_question_mark::NeedlessQuestionMark::new(msrv)); + store.register_late_pass(move || box needless_question_mark::NeedlessQuestionMark); store.register_late_pass(move || box casts::Casts::new(msrv)); store.register_early_pass(move || box unnested_or_patterns::UnnestedOrPatterns::new(msrv)); diff --git a/clippy_lints/src/needless_question_mark.rs b/clippy_lints/src/needless_question_mark.rs index cfe7ae6630e..7b156a8c49d 100644 --- a/clippy_lints/src/needless_question_mark.rs +++ b/clippy_lints/src/needless_question_mark.rs @@ -1,15 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_lang_ctor; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{differing_macro_contexts, meets_msrv}; +use clippy_utils::{differing_macro_contexts, is_lang_ctor}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionSome, ResultOk}; use rustc_hir::{Body, Expr, ExprKind, LangItem, MatchSource, QPath}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_semver::RustcVersion; -use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; declare_clippy_lint! { @@ -63,21 +61,7 @@ declare_clippy_lint! { "Suggest `value.inner_option` instead of `Some(value.inner_option?)`. The same goes for `Result`." } -const NEEDLESS_QUESTION_MARK_RESULT_MSRV: RustcVersion = RustcVersion::new(1, 13, 0); -const NEEDLESS_QUESTION_MARK_OPTION_MSRV: RustcVersion = RustcVersion::new(1, 22, 0); - -pub struct NeedlessQuestionMark { - msrv: Option, -} - -impl NeedlessQuestionMark { - #[must_use] - pub fn new(msrv: Option) -> Self { - Self { msrv } - } -} - -impl_lint_pass!(NeedlessQuestionMark => [NEEDLESS_QUESTION_MARK]); +declare_lint_pass!(NeedlessQuestionMark => [NEEDLESS_QUESTION_MARK]); #[derive(Debug)] enum SomeOkCall<'a> { @@ -111,7 +95,7 @@ impl LateLintPass<'_> for NeedlessQuestionMark { _ => return, }; - if let Some(ok_some_call) = is_some_or_ok_call(self, cx, e) { + if let Some(ok_some_call) = is_some_or_ok_call(cx, e) { emit_lint(cx, &ok_some_call); } } @@ -127,14 +111,12 @@ impl LateLintPass<'_> for NeedlessQuestionMark { if_chain! { if let Some(expr) = expr_opt; - if let Some(ok_some_call) = is_some_or_ok_call(self, cx, expr); + if let Some(ok_some_call) = is_some_or_ok_call(cx, expr); then { emit_lint(cx, &ok_some_call); } }; } - - extract_msrv_attr!(LateContext); } fn emit_lint(cx: &LateContext<'_>, expr: &SomeOkCall<'_>) { @@ -153,11 +135,7 @@ fn emit_lint(cx: &LateContext<'_>, expr: &SomeOkCall<'_>) { ); } -fn is_some_or_ok_call<'a>( - nqml: &NeedlessQuestionMark, - cx: &'a LateContext<'_>, - expr: &'a Expr<'_>, -) -> Option> { +fn is_some_or_ok_call<'a>(cx: &'a LateContext<'_>, expr: &'a Expr<'_>) -> Option> { if_chain! { // Check outer expression matches CALL_IDENT(ARGUMENT) format if let ExprKind::Call(path, args) = &expr.kind; @@ -188,8 +166,7 @@ fn is_some_or_ok_call<'a>( let inner_is_some = is_type_diagnostic_item(cx, inner_ty, sym::option_type); // Check for Option MSRV - let meets_option_msrv = meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_OPTION_MSRV); - if outer_is_some && inner_is_some && meets_option_msrv { + if outer_is_some && inner_is_some { return Some(SomeOkCall::SomeCall(expr, inner_expr)); } @@ -202,8 +179,7 @@ fn is_some_or_ok_call<'a>( let does_not_call_from = !has_implicit_error_from(cx, expr, inner_expr); // Must meet Result MSRV - let meets_result_msrv = meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_RESULT_MSRV); - if outer_is_result && inner_is_result && does_not_call_from && meets_result_msrv { + if outer_is_result && inner_is_result && does_not_call_from { return Some(SomeOkCall::OkCall(expr, inner_expr)); } } diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 2f8714b1799..d56855a71c1 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -106,7 +106,7 @@ macro_rules! define_Conf { pub use self::helpers::Conf; define_Conf! { - /// Lint: CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, NEEDLESS_QUESTION_MARK, PTR_AS_PTR. The minimum rust version that the project supports + /// Lint: CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR. The minimum rust version that the project supports (msrv, "msrv": Option, None), /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses (blacklisted_names, "blacklisted_names": Vec, ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()), diff --git a/tests/ui/needless_question_mark.fixed b/tests/ui/needless_question_mark.fixed index fd8433870bb..52ddd9d2dc8 100644 --- a/tests/ui/needless_question_mark.fixed +++ b/tests/ui/needless_question_mark.fixed @@ -96,78 +96,6 @@ where fn main() {} -mod question_mark_none { - #![clippy::msrv = "1.12.0"] - fn needless_question_mark_option() -> Option { - struct TO { - magic: Option, - } - let to = TO { magic: None }; - Some(to.magic?) // should not be triggered - } - - fn needless_question_mark_result() -> Result { - struct TO { - magic: Result, - } - let to = TO { magic: Ok(1_usize) }; - Ok(to.magic?) // should not be triggered - } - - fn main() { - needless_question_mark_option(); - needless_question_mark_result(); - } -} - -mod question_mark_result { - #![clippy::msrv = "1.21.0"] - fn needless_question_mark_option() -> Option { - struct TO { - magic: Option, - } - let to = TO { magic: None }; - Some(to.magic?) // should not be triggered - } - - fn needless_question_mark_result() -> Result { - struct TO { - magic: Result, - } - let to = TO { magic: Ok(1_usize) }; - to.magic // should be triggered - } - - fn main() { - needless_question_mark_option(); - needless_question_mark_result(); - } -} - -mod question_mark_both { - #![clippy::msrv = "1.22.0"] - fn needless_question_mark_option() -> Option { - struct TO { - magic: Option, - } - let to = TO { magic: None }; - to.magic // should be triggered - } - - fn needless_question_mark_result() -> Result { - struct TO { - magic: Result, - } - let to = TO { magic: Ok(1_usize) }; - to.magic // should be triggered - } - - fn main() { - needless_question_mark_option(); - needless_question_mark_result(); - } -} - // #6921 if a macro wraps an expr in Some( ) and the ? is in the macro use, // the suggestion fails to apply; do not lint macro_rules! some_in_macro { diff --git a/tests/ui/needless_question_mark.rs b/tests/ui/needless_question_mark.rs index 36d45ac7e03..1ea4ba0d83f 100644 --- a/tests/ui/needless_question_mark.rs +++ b/tests/ui/needless_question_mark.rs @@ -96,78 +96,6 @@ where fn main() {} -mod question_mark_none { - #![clippy::msrv = "1.12.0"] - fn needless_question_mark_option() -> Option { - struct TO { - magic: Option, - } - let to = TO { magic: None }; - Some(to.magic?) // should not be triggered - } - - fn needless_question_mark_result() -> Result { - struct TO { - magic: Result, - } - let to = TO { magic: Ok(1_usize) }; - Ok(to.magic?) // should not be triggered - } - - fn main() { - needless_question_mark_option(); - needless_question_mark_result(); - } -} - -mod question_mark_result { - #![clippy::msrv = "1.21.0"] - fn needless_question_mark_option() -> Option { - struct TO { - magic: Option, - } - let to = TO { magic: None }; - Some(to.magic?) // should not be triggered - } - - fn needless_question_mark_result() -> Result { - struct TO { - magic: Result, - } - let to = TO { magic: Ok(1_usize) }; - Ok(to.magic?) // should be triggered - } - - fn main() { - needless_question_mark_option(); - needless_question_mark_result(); - } -} - -mod question_mark_both { - #![clippy::msrv = "1.22.0"] - fn needless_question_mark_option() -> Option { - struct TO { - magic: Option, - } - let to = TO { magic: None }; - Some(to.magic?) // should be triggered - } - - fn needless_question_mark_result() -> Result { - struct TO { - magic: Result, - } - let to = TO { magic: Ok(1_usize) }; - Ok(to.magic?) // should be triggered - } - - fn main() { - needless_question_mark_option(); - needless_question_mark_result(); - } -} - // #6921 if a macro wraps an expr in Some( ) and the ? is in the macro use, // the suggestion fails to apply; do not lint macro_rules! some_in_macro { diff --git a/tests/ui/needless_question_mark.stderr b/tests/ui/needless_question_mark.stderr index 7cbf1e505ad..afd68d91e51 100644 --- a/tests/ui/needless_question_mark.stderr +++ b/tests/ui/needless_question_mark.stderr @@ -67,25 +67,7 @@ LL | return Ok(t.magic?); | ^^^^^^^^^^^^ help: try: `t.magic` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:138:9 - | -LL | Ok(to.magic?) // should be triggered - | ^^^^^^^^^^^^^ help: try: `to.magic` - -error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:154:9 - | -LL | Some(to.magic?) // should be triggered - | ^^^^^^^^^^^^^^^ help: try: `to.magic` - -error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:162:9 - | -LL | Ok(to.magic?) // should be triggered - | ^^^^^^^^^^^^^ help: try: `to.magic` - -error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:187:27 + --> $DIR/needless_question_mark.rs:115:27 | LL | || -> Option<_> { Some(Some($expr)?) }() | ^^^^^^^^^^^^^^^^^^ help: try: `Some($expr)` @@ -95,5 +77,5 @@ LL | let _x = some_and_qmark_in_macro!(x?); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 15 previous errors +error: aborting due to 12 previous errors -- cgit 1.4.1-3-g733a5 From 63425de77db7f4dee55cfceeed7e174bfe986b0d Mon Sep 17 00:00:00 2001 From: Andre Bogus Date: Thu, 29 Apr 2021 10:10:58 +0200 Subject: while_immutable_cond: check condition for mutation --- clippy_lints/src/loops/while_immutable_condition.rs | 13 ++++++++----- tests/ui/infinite_loop.rs | 12 ++++++++++++ 2 files changed, 20 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/loops/while_immutable_condition.rs b/clippy_lints/src/loops/while_immutable_condition.rs index de267cc77d2..55404b87ec9 100644 --- a/clippy_lints/src/loops/while_immutable_condition.rs +++ b/clippy_lints/src/loops/while_immutable_condition.rs @@ -28,11 +28,14 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, expr: &' return; } let used_in_condition = &var_visitor.ids; - let no_cond_variable_mutated = if let Some(used_mutably) = mutated_variables(expr, cx) { - used_in_condition.is_disjoint(&used_mutably) - } else { - return; - }; + let mutated_in_body = mutated_variables(expr, cx); + let mutated_in_condition = mutated_variables(cond, cx); + let no_cond_variable_mutated = + if let (Some(used_mutably_body), Some(used_mutably_cond)) = (mutated_in_body, mutated_in_condition) { + used_in_condition.is_disjoint(&used_mutably_body) && used_in_condition.is_disjoint(&used_mutably_cond) + } else { + return; + }; let mutable_static_in_cond = var_visitor.def_ids.iter().any(|(_, v)| *v); let mut has_break_or_return_visitor = HasBreakOrReturnVisitor { diff --git a/tests/ui/infinite_loop.rs b/tests/ui/infinite_loop.rs index 72591f12baf..3d8fb8507e5 100644 --- a/tests/ui/infinite_loop.rs +++ b/tests/ui/infinite_loop.rs @@ -192,11 +192,23 @@ fn while_loop_with_break_and_return() { } } +fn immutable_condition_false_positive(mut n: u64) -> u32 { + let mut count = 0; + while { + n >>= 1; + n != 0 + } { + count += 1; + } + count +} + fn main() { immutable_condition(); unused_var(); used_immutable(); internally_mutable(); + immutable_condition_false_positive(5); let mut c = Counter { count: 0 }; c.inc_n(5); -- cgit 1.4.1-3-g733a5 From 1e22e564e4d272c296a6acb8596831fbaa05bc7b Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 27 Apr 2021 21:04:06 -0500 Subject: Refactor config deserialization --- clippy_lints/src/lib.rs | 4 +- clippy_lints/src/utils/conf.rs | 232 +++++++++------------ tests/ui-toml/bad_toml_type/conf_bad_type.stderr | 2 +- .../conf_deprecated_key/conf_deprecated_key.stderr | 2 +- 4 files changed, 108 insertions(+), 132 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 57843afaddb..40a793e48cf 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -382,6 +382,7 @@ mod zero_sized_map_values; // end lints modules, do not remove this comment, it’s used in `update_lints` pub use crate::utils::conf::Conf; +use crate::utils::conf::TryConf; /// Register all pre expansion lints /// @@ -421,8 +422,7 @@ pub fn read_conf(sess: &Session) -> Conf { file_name }; - let (conf, errors) = utils::conf::read(&file_name); - + let TryConf { conf, errors } = utils::conf::read(&file_name); // all conf errors are non-fatal, we just use the default conf in case of error for error in errors { sess.struct_err(&format!( diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index e221be63538..747d92dd742 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -1,98 +1,111 @@ //! Read configurations files. -#![deny(clippy::missing_docs_in_private_items)] +#![allow(clippy::module_name_repetitions)] -use std::lazy::SyncLazy; +use serde::de::{Deserializer, IgnoredAny, IntoDeserializer, MapAccess, Visitor}; +use serde::Deserialize; +use std::error::Error; use std::path::{Path, PathBuf}; -use std::sync::Mutex; use std::{env, fmt, fs, io}; -/// Error from reading a configuration file. -#[derive(Debug)] -pub enum Error { - /// An I/O error. - Io(io::Error), - /// Not valid toml or doesn't fit the expected config format - Toml(String), +/// Conf with parse errors +#[derive(Default)] +pub struct TryConf { + pub conf: Conf, + pub errors: Vec, } -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Io(err) => err.fmt(f), - Self::Toml(err) => err.fmt(f), +impl TryConf { + fn from_error(error: impl Error) -> Self { + Self { + conf: Conf::default(), + errors: vec![error.to_string()], } } } -impl From for Error { - fn from(e: io::Error) -> Self { - Self::Io(e) - } -} +macro_rules! define_Conf { + ($( + #[$doc:meta] + $(#[conf_deprecated($dep:literal)])? + ($name:ident: $ty:ty $(= $default:expr)?), + )*) => { + /// Clippy lint configuration + pub struct Conf { + $(#[$doc] pub $name: $ty,)* + } -/// Vec of errors that might be collected during config toml parsing -static ERRORS: SyncLazy>> = SyncLazy::new(|| Mutex::new(Vec::new())); + mod defaults { + $(pub fn $name() -> $ty { define_Conf!(@default $($default)?) })* + } -macro_rules! define_Conf { - ($(#[$doc:meta] ($config:ident, $config_str:literal: $Ty:ty, $default:expr),)+) => { - mod helpers { - use serde::Deserialize; - /// Type used to store lint configuration. - #[derive(Deserialize)] - #[serde(rename_all = "kebab-case", deny_unknown_fields)] - pub struct Conf { - $( - #[$doc] - #[serde(default = $config_str)] - #[serde(with = $config_str)] - pub $config: $Ty, - )+ - #[allow(dead_code)] - #[serde(default)] - third_party: Option<::toml::Value>, + impl Default for Conf { + fn default() -> Self { + Self { $($name: defaults::$name(),)* } } + } - $( - mod $config { - use serde::Deserialize; - pub fn deserialize<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<$Ty, D::Error> { - use super::super::{ERRORS, Error}; - - Ok( - <$Ty>::deserialize(deserializer).unwrap_or_else(|e| { - ERRORS - .lock() - .expect("no threading here") - .push(Error::Toml(e.to_string())); - super::$config() - }) - ) - } - } + impl<'de> Deserialize<'de> for TryConf { + fn deserialize(deserializer: D) -> Result where D: Deserializer<'de> { + deserializer.deserialize_map(ConfVisitor) + } + } + + #[derive(Deserialize)] + #[serde(field_identifier, rename_all = "kebab-case")] + #[allow(non_camel_case_types)] + enum Field { $($name,)* third_party, } + + struct ConfVisitor; + + impl<'de> Visitor<'de> for ConfVisitor { + type Value = TryConf; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("Conf") + } - #[must_use] - fn $config() -> $Ty { - let x = $default; - x + fn visit_map(self, mut map: V) -> Result where V: MapAccess<'de> { + let mut errors = Vec::new(); + $(let mut $name = None;)* + // could get `Field` here directly, but get `str` first for diagnostics + while let Some(name) = map.next_key::<&str>()? { + match Field::deserialize(name.into_deserializer())? { + $(Field::$name => { + $(errors.push(format!("deprecated field `{}`. {}", name, $dep));)? + match map.next_value() { + Err(e) => errors.push(e.to_string()), + Ok(value) => match $name { + Some(_) => errors.push(format!("duplicate field `{}`", name)), + None => $name = Some(value), + } + } + })* + // white-listed; ignore + Field::third_party => drop(map.next_value::()) + } } - )+ + let conf = Conf { $($name: $name.unwrap_or_else(defaults::$name),)* }; + Ok(TryConf { conf, errors }) + } } }; + (@default) => (Default::default()); + (@default $default:expr) => ($default); } -pub use self::helpers::Conf; define_Conf! { /// Lint: CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR. The minimum rust version that the project supports - (msrv, "msrv": Option, None), + (msrv: Option), /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses - (blacklisted_names, "blacklisted_names": Vec, ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()), + (blacklisted_names: Vec = ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()), /// Lint: COGNITIVE_COMPLEXITY. The maximum cognitive complexity a function can have - (cognitive_complexity_threshold, "cognitive_complexity_threshold": u64, 25), + (cognitive_complexity_threshold: u64 = 25), /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY. Use the Cognitive Complexity lint instead. - (cyclomatic_complexity_threshold, "cyclomatic_complexity_threshold": Option, None), + #[conf_deprecated("Please use `cognitive-complexity-threshold` instead.")] + (cyclomatic_complexity_threshold: Option), /// Lint: DOC_MARKDOWN. The list of words this lint should not consider as identifiers needing ticks - (doc_valid_idents, "doc_valid_idents": Vec, [ + (doc_valid_idents: Vec = [ "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "DirectX", "ECMAScript", @@ -113,54 +126,47 @@ define_Conf! { "CamelCase", ].iter().map(ToString::to_string).collect()), /// Lint: TOO_MANY_ARGUMENTS. The maximum number of argument a function or method can have - (too_many_arguments_threshold, "too_many_arguments_threshold": u64, 7), + (too_many_arguments_threshold: u64 = 7), /// Lint: TYPE_COMPLEXITY. The maximum complexity a type can have - (type_complexity_threshold, "type_complexity_threshold": u64, 250), + (type_complexity_threshold: u64 = 250), /// Lint: MANY_SINGLE_CHAR_NAMES. The maximum number of single char bindings a scope may have - (single_char_binding_names_threshold, "single_char_binding_names_threshold": u64, 4), + (single_char_binding_names_threshold: u64 = 4), /// Lint: BOXED_LOCAL, USELESS_VEC. The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap - (too_large_for_stack, "too_large_for_stack": u64, 200), + (too_large_for_stack: u64 = 200), /// Lint: ENUM_VARIANT_NAMES. The minimum number of enum variants for the lints about variant names to trigger - (enum_variant_name_threshold, "enum_variant_name_threshold": u64, 3), + (enum_variant_name_threshold: u64 = 3), /// Lint: LARGE_ENUM_VARIANT. The maximum size of a enum's variant to avoid box suggestion - (enum_variant_size_threshold, "enum_variant_size_threshold": u64, 200), + (enum_variant_size_threshold: u64 = 200), /// Lint: VERBOSE_BIT_MASK. The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros' - (verbose_bit_mask_threshold, "verbose_bit_mask_threshold": u64, 1), + (verbose_bit_mask_threshold: u64 = 1), /// Lint: DECIMAL_LITERAL_REPRESENTATION. The lower bound for linting decimal literals - (literal_representation_threshold, "literal_representation_threshold": u64, 16384), + (literal_representation_threshold: u64 = 16384), /// Lint: TRIVIALLY_COPY_PASS_BY_REF. The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference. - (trivial_copy_size_limit, "trivial_copy_size_limit": Option, None), + (trivial_copy_size_limit: Option), /// Lint: LARGE_TYPE_PASS_BY_MOVE. The minimum size (in bytes) to consider a type for passing by reference instead of by value. - (pass_by_value_size_limit, "pass_by_value_size_limit": u64, 256), + (pass_by_value_size_limit: u64 = 256), /// Lint: TOO_MANY_LINES. The maximum number of lines a function or method can have - (too_many_lines_threshold, "too_many_lines_threshold": u64, 100), + (too_many_lines_threshold: u64 = 100), /// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS. The maximum allowed size for arrays on the stack - (array_size_threshold, "array_size_threshold": u64, 512_000), + (array_size_threshold: u64 = 512_000), /// Lint: VEC_BOX. The size of the boxed type in bytes, where boxing in a `Vec` is allowed - (vec_box_size_threshold, "vec_box_size_threshold": u64, 4096), + (vec_box_size_threshold: u64 = 4096), /// Lint: TYPE_REPETITION_IN_BOUNDS. The maximum number of bounds a trait can have to be linted - (max_trait_bounds, "max_trait_bounds": u64, 3), + (max_trait_bounds: u64 = 3), /// Lint: STRUCT_EXCESSIVE_BOOLS. The maximum number of bools a struct can have - (max_struct_bools, "max_struct_bools": u64, 3), + (max_struct_bools: u64 = 3), /// Lint: FN_PARAMS_EXCESSIVE_BOOLS. The maximum number of bools function parameters can have - (max_fn_params_bools, "max_fn_params_bools": u64, 3), + (max_fn_params_bools: u64 = 3), /// Lint: WILDCARD_IMPORTS. Whether to allow certain wildcard imports (prelude, super in tests). - (warn_on_all_wildcard_imports, "warn_on_all_wildcard_imports": bool, false), + (warn_on_all_wildcard_imports: bool), /// Lint: DISALLOWED_METHOD. The list of disallowed methods, written as fully qualified paths. - (disallowed_methods, "disallowed_methods": Vec, Vec::::new()), + (disallowed_methods: Vec), /// Lint: UNREADABLE_LITERAL. Should the fraction of a decimal be linted to include separators. - (unreadable_literal_lint_fractions, "unreadable_literal_lint_fractions": bool, true), + (unreadable_literal_lint_fractions: bool = true), /// Lint: UPPER_CASE_ACRONYMS. Enables verbose mode. Triggers if there is more than one uppercase char next to each other - (upper_case_acronyms_aggressive, "upper_case_acronyms_aggressive": bool, false), + (upper_case_acronyms_aggressive: bool), /// Lint: _CARGO_COMMON_METADATA. For internal testing only, ignores the current `publish` settings in the Cargo manifest. - (cargo_ignore_publish, "cargo_ignore_publish": bool, false), -} - -impl Default for Conf { - #[must_use] - fn default() -> Self { - toml::from_str("").expect("we never error on empty config files") - } + (cargo_ignore_publish: bool), } /// Search for the configuration file. @@ -194,43 +200,13 @@ pub fn lookup_conf_file() -> io::Result> { } } -/// Produces a `Conf` filled with the default values and forwards the errors -/// -/// Used internally for convenience -fn default(errors: Vec) -> (Conf, Vec) { - (Conf::default(), errors) -} - /// Read the `toml` configuration file. /// /// In case of error, the function tries to continue as much as possible. -pub fn read(path: &Path) -> (Conf, Vec) { +pub fn read(path: &Path) -> TryConf { let content = match fs::read_to_string(path) { + Err(e) => return TryConf::from_error(e), Ok(content) => content, - Err(err) => return default(vec![err.into()]), }; - - assert!(ERRORS.lock().expect("no threading -> mutex always safe").is_empty()); - match toml::from_str(&content) { - Ok(toml) => { - let mut errors = ERRORS.lock().expect("no threading -> mutex always safe").split_off(0); - - let toml_ref: &Conf = &toml; - - let cyc_field: Option = toml_ref.cyclomatic_complexity_threshold; - - if cyc_field.is_some() { - let cyc_err = "found deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead.".to_string(); - errors.push(Error::Toml(cyc_err)); - } - - (toml, errors) - }, - Err(e) => { - let mut errors = ERRORS.lock().expect("no threading -> mutex always safe").split_off(0); - errors.push(Error::Toml(e.to_string())); - - default(errors) - }, - } + toml::from_str(&content).unwrap_or_else(TryConf::from_error) } diff --git a/tests/ui-toml/bad_toml_type/conf_bad_type.stderr b/tests/ui-toml/bad_toml_type/conf_bad_type.stderr index efd02bcbb6e..c7bc261de6c 100644 --- a/tests/ui-toml/bad_toml_type/conf_bad_type.stderr +++ b/tests/ui-toml/bad_toml_type/conf_bad_type.stderr @@ -1,4 +1,4 @@ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: invalid type: integer `42`, expected a sequence +error: error reading Clippy's configuration file `$DIR/clippy.toml`: invalid type: integer `42`, expected a sequence for key `blacklisted-names` error: aborting due to previous error diff --git a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr index 34267c0daf7..8bf9fe64c6d 100644 --- a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr +++ b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr @@ -1,4 +1,4 @@ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: found deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead. +error: error reading Clippy's configuration file `$DIR/clippy.toml`: deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead. error: aborting due to previous error -- cgit 1.4.1-3-g733a5 From 857d9f15da507adbc3e59560f3c15568cd0ae0e5 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Sun, 2 May 2021 16:56:46 -0500 Subject: Fix error punctuation --- clippy_lints/src/utils/conf.rs | 2 +- tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 747d92dd742..aeca5b9ccce 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -102,7 +102,7 @@ define_Conf! { /// Lint: COGNITIVE_COMPLEXITY. The maximum cognitive complexity a function can have (cognitive_complexity_threshold: u64 = 25), /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY. Use the Cognitive Complexity lint instead. - #[conf_deprecated("Please use `cognitive-complexity-threshold` instead.")] + #[conf_deprecated("Please use `cognitive-complexity-threshold` instead")] (cyclomatic_complexity_threshold: Option), /// Lint: DOC_MARKDOWN. The list of words this lint should not consider as identifiers needing ticks (doc_valid_idents: Vec = [ diff --git a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr index 8bf9fe64c6d..90021a034a3 100644 --- a/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr +++ b/tests/ui-toml/conf_deprecated_key/conf_deprecated_key.stderr @@ -1,4 +1,4 @@ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead. +error: error reading Clippy's configuration file `$DIR/clippy.toml`: deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead error: aborting due to previous error -- cgit 1.4.1-3-g733a5 From c0808998481402cb1cd502ad6aa54e253840efc6 Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Mon, 3 May 2021 16:18:41 +0200 Subject: add strip_{prefix,suffix} to PATTERN_METHODS this will warn, if a single_char_pattern is used in one of the above methods --- clippy_lints/src/methods/mod.rs | 4 +++- tests/ui/single_char_pattern.fixed | 2 ++ tests/ui/single_char_pattern.rs | 2 ++ tests/ui/single_char_pattern.stderr | 34 +++++++++++++++++++++++----------- 4 files changed, 30 insertions(+), 12 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index e15dbb899b3..5ea989aa684 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2190,7 +2190,7 @@ const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [ ]; #[rustfmt::skip] -const PATTERN_METHODS: [(&str, usize); 17] = [ +const PATTERN_METHODS: [(&str, usize); 19] = [ ("contains", 1), ("starts_with", 1), ("ends_with", 1), @@ -2206,6 +2206,8 @@ const PATTERN_METHODS: [(&str, usize); 17] = [ ("rmatches", 1), ("match_indices", 1), ("rmatch_indices", 1), + ("strip_prefix", 1), + ("strip_suffix", 1), ("trim_start_matches", 1), ("trim_end_matches", 1), ]; diff --git a/tests/ui/single_char_pattern.fixed b/tests/ui/single_char_pattern.fixed index d8b5f19e144..fcbe9af9f56 100644 --- a/tests/ui/single_char_pattern.fixed +++ b/tests/ui/single_char_pattern.fixed @@ -33,6 +33,8 @@ fn main() { x.rmatch_indices('x'); x.trim_start_matches('x'); x.trim_end_matches('x'); + x.strip_prefix('x'); + x.strip_suffix('x'); // Make sure we escape characters correctly. x.split('\n'); x.split('\''); diff --git a/tests/ui/single_char_pattern.rs b/tests/ui/single_char_pattern.rs index a7bc73e3756..b8bc20f4070 100644 --- a/tests/ui/single_char_pattern.rs +++ b/tests/ui/single_char_pattern.rs @@ -33,6 +33,8 @@ fn main() { x.rmatch_indices("x"); x.trim_start_matches("x"); x.trim_end_matches("x"); + x.strip_prefix("x"); + x.strip_suffix("x"); // Make sure we escape characters correctly. x.split("\n"); x.split("'"); diff --git a/tests/ui/single_char_pattern.stderr b/tests/ui/single_char_pattern.stderr index ee4e7e50efd..6d94d8a34e3 100644 --- a/tests/ui/single_char_pattern.stderr +++ b/tests/ui/single_char_pattern.stderr @@ -121,64 +121,76 @@ LL | x.trim_end_matches("x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:37:13 + --> $DIR/single_char_pattern.rs:36:20 + | +LL | x.strip_prefix("x"); + | ^^^ help: try using a `char` instead: `'x'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:37:20 + | +LL | x.strip_suffix("x"); + | ^^^ help: try using a `char` instead: `'x'` + +error: single-character string constant used as pattern + --> $DIR/single_char_pattern.rs:39:13 | LL | x.split("/n"); | ^^^^ help: try using a `char` instead: `'/n'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:38:13 + --> $DIR/single_char_pattern.rs:40:13 | LL | x.split("'"); | ^^^ help: try using a `char` instead: `'/''` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:39:13 + --> $DIR/single_char_pattern.rs:41:13 | LL | x.split("/'"); | ^^^^ help: try using a `char` instead: `'/''` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:44:31 + --> $DIR/single_char_pattern.rs:46:31 | LL | x.replace(";", ",").split(","); // issue #2978 | ^^^ help: try using a `char` instead: `','` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:45:19 + --> $DIR/single_char_pattern.rs:47:19 | LL | x.starts_with("/x03"); // issue #2996 | ^^^^^^ help: try using a `char` instead: `'/x03'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:52:13 + --> $DIR/single_char_pattern.rs:54:13 | LL | x.split(r"a"); | ^^^^ help: try using a `char` instead: `'a'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:53:13 + --> $DIR/single_char_pattern.rs:55:13 | LL | x.split(r#"a"#); | ^^^^^^ help: try using a `char` instead: `'a'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:54:13 + --> $DIR/single_char_pattern.rs:56:13 | LL | x.split(r###"a"###); | ^^^^^^^^^^ help: try using a `char` instead: `'a'` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:55:13 + --> $DIR/single_char_pattern.rs:57:13 | LL | x.split(r###"'"###); | ^^^^^^^^^^ help: try using a `char` instead: `'/''` error: single-character string constant used as pattern - --> $DIR/single_char_pattern.rs:56:13 + --> $DIR/single_char_pattern.rs:58:13 | LL | x.split(r###"#"###); | ^^^^^^^^^^ help: try using a `char` instead: `'#'` -error: aborting due to 30 previous errors +error: aborting due to 32 previous errors -- cgit 1.4.1-3-g733a5 From 64eb18e6759f4375d01d7e3352447ff6b4305184 Mon Sep 17 00:00:00 2001 From: Takayuki Date: Tue, 4 May 2021 17:04:10 +0900 Subject: move builtin_type_shadow to its own module --- clippy_lints/src/misc_early/builtin_type_shadow.rs | 19 +++++++++++++++++ clippy_lints/src/misc_early/mod.rs | 18 +++++----------- tests/ui/builtin-type-shadow.rs | 9 -------- tests/ui/builtin-type-shadow.stderr | 24 ---------------------- tests/ui/builtin_type_shadow.rs | 9 ++++++++ tests/ui/builtin_type_shadow.stderr | 24 ++++++++++++++++++++++ 6 files changed, 57 insertions(+), 46 deletions(-) create mode 100644 clippy_lints/src/misc_early/builtin_type_shadow.rs delete mode 100644 tests/ui/builtin-type-shadow.rs delete mode 100644 tests/ui/builtin-type-shadow.stderr create mode 100644 tests/ui/builtin_type_shadow.rs create mode 100644 tests/ui/builtin_type_shadow.stderr (limited to 'tests') diff --git a/clippy_lints/src/misc_early/builtin_type_shadow.rs b/clippy_lints/src/misc_early/builtin_type_shadow.rs new file mode 100644 index 00000000000..9f6b0bdc7a4 --- /dev/null +++ b/clippy_lints/src/misc_early/builtin_type_shadow.rs @@ -0,0 +1,19 @@ +use clippy_utils::diagnostics::span_lint; +use rustc_ast::ast::{GenericParam, GenericParamKind}; +use rustc_hir::PrimTy; +use rustc_lint::EarlyContext; + +use super::BUILTIN_TYPE_SHADOW; + +pub(super) fn check(cx: &EarlyContext<'_>, param: &GenericParam) { + if let GenericParamKind::Type { .. } = param.kind { + if let Some(prim_ty) = PrimTy::from_name(param.ident.name) { + span_lint( + cx, + BUILTIN_TYPE_SHADOW, + param.ident.span, + &format!("this generic shadows the built-in type `{}`", prim_ty.name()), + ); + } + } +} diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs index 3c6a7071c24..94740093d3b 100644 --- a/clippy_lints/src/misc_early/mod.rs +++ b/clippy_lints/src/misc_early/mod.rs @@ -1,13 +1,14 @@ +mod builtin_type_shadow; + use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_opt; use rustc_ast::ast::{ - BindingMode, Expr, ExprKind, GenericParamKind, Generics, Lit, LitFloatType, LitIntType, LitKind, Mutability, - NodeId, Pat, PatKind, UnOp, + BindingMode, Expr, ExprKind, Generics, Lit, LitFloatType, LitIntType, LitKind, Mutability, NodeId, Pat, PatKind, + UnOp, }; use rustc_ast::visit::FnKind; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; -use rustc_hir::PrimTy; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -265,16 +266,7 @@ declare_lint_pass!(MiscEarlyLints => [ impl EarlyLintPass for MiscEarlyLints { fn check_generics(&mut self, cx: &EarlyContext<'_>, gen: &Generics) { for param in &gen.params { - if let GenericParamKind::Type { .. } = param.kind { - if let Some(prim_ty) = PrimTy::from_name(param.ident.name) { - span_lint( - cx, - BUILTIN_TYPE_SHADOW, - param.ident.span, - &format!("this generic shadows the built-in type `{}`", prim_ty.name()), - ); - } - } + builtin_type_shadow::check(cx, param); } } diff --git a/tests/ui/builtin-type-shadow.rs b/tests/ui/builtin-type-shadow.rs deleted file mode 100644 index 69b8b6a0e68..00000000000 --- a/tests/ui/builtin-type-shadow.rs +++ /dev/null @@ -1,9 +0,0 @@ -#![warn(clippy::builtin_type_shadow)] -#![allow(non_camel_case_types)] - -fn foo(a: u32) -> u32 { - 42 - // ^ rustc's type error -} - -fn main() {} diff --git a/tests/ui/builtin-type-shadow.stderr b/tests/ui/builtin-type-shadow.stderr deleted file mode 100644 index f42b246afd2..00000000000 --- a/tests/ui/builtin-type-shadow.stderr +++ /dev/null @@ -1,24 +0,0 @@ -error: this generic shadows the built-in type `u32` - --> $DIR/builtin-type-shadow.rs:4:8 - | -LL | fn foo(a: u32) -> u32 { - | ^^^ - | - = note: `-D clippy::builtin-type-shadow` implied by `-D warnings` - -error[E0308]: mismatched types - --> $DIR/builtin-type-shadow.rs:5:5 - | -LL | fn foo(a: u32) -> u32 { - | --- --- expected `u32` because of return type - | | - | this type parameter -LL | 42 - | ^^ expected type parameter `u32`, found integer - | - = note: expected type parameter `u32` - found type `{integer}` - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/builtin_type_shadow.rs b/tests/ui/builtin_type_shadow.rs new file mode 100644 index 00000000000..69b8b6a0e68 --- /dev/null +++ b/tests/ui/builtin_type_shadow.rs @@ -0,0 +1,9 @@ +#![warn(clippy::builtin_type_shadow)] +#![allow(non_camel_case_types)] + +fn foo(a: u32) -> u32 { + 42 + // ^ rustc's type error +} + +fn main() {} diff --git a/tests/ui/builtin_type_shadow.stderr b/tests/ui/builtin_type_shadow.stderr new file mode 100644 index 00000000000..47a8a1e623e --- /dev/null +++ b/tests/ui/builtin_type_shadow.stderr @@ -0,0 +1,24 @@ +error: this generic shadows the built-in type `u32` + --> $DIR/builtin_type_shadow.rs:4:8 + | +LL | fn foo(a: u32) -> u32 { + | ^^^ + | + = note: `-D clippy::builtin-type-shadow` implied by `-D warnings` + +error[E0308]: mismatched types + --> $DIR/builtin_type_shadow.rs:5:5 + | +LL | fn foo(a: u32) -> u32 { + | --- --- expected `u32` because of return type + | | + | this type parameter +LL | 42 + | ^^ expected type parameter `u32`, found integer + | + = note: expected type parameter `u32` + found type `{integer}` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. -- cgit 1.4.1-3-g733a5 From 0854f0caee1d6d16e57eb1d3f5abf539b3bee3f0 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Tue, 4 May 2021 16:48:25 +0200 Subject: Don't trigger `field_reassign_with_default` in macros Producing a good suggestion for this lint is already hard when no macros are involved. With macros the lint message and the suggestion are just confusing. Since both, producing a good suggestion and figuring out if this pattern can be re-written inside a macro is nearly impossible, just bail out. --- clippy_lints/src/default.rs | 3 +-- tests/ui/field_reassign_with_default.rs | 20 ++++++++++++++++ tests/ui/field_reassign_with_default.stderr | 36 ++++++++++++++--------------- 3 files changed, 39 insertions(+), 20 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index 7a53d390bb4..947479db8f5 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -7,7 +7,6 @@ use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::{Ident, Symbol}; @@ -122,7 +121,7 @@ impl LateLintPass<'_> for Default { if let StmtKind::Local(local) = stmt.kind; if let Some(expr) = local.init; if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id); - if !in_external_macro(cx.tcx.sess, expr.span); + if !in_macro(expr.span); // only take bindings to identifiers if let PatKind::Binding(_, binding_id, ident, _) = local.pat.kind; // only when assigning `... = Default::default()` diff --git a/tests/ui/field_reassign_with_default.rs b/tests/ui/field_reassign_with_default.rs index 1368c5d7984..787053fb000 100644 --- a/tests/ui/field_reassign_with_default.rs +++ b/tests/ui/field_reassign_with_default.rs @@ -29,6 +29,21 @@ struct C { i: Vec, j: i64, } + +#[derive(Default)] +struct D { + a: Option, + b: Option, +} + +macro_rules! m { + ($key:ident: $value:tt) => {{ + let mut data = $crate::D::default(); + data.$key = Some($value); + data + }}; +} + /// Implements .next() that returns a different number each time. struct SideEffect(i32); @@ -143,6 +158,11 @@ fn main() { let mut a: WrapperMulti = Default::default(); a.i = 42; + + // Don't lint in macros + m! { + a: 42 + }; } mod m { diff --git a/tests/ui/field_reassign_with_default.stderr b/tests/ui/field_reassign_with_default.stderr index dd7c0360bb1..b56db08ec8a 100644 --- a/tests/ui/field_reassign_with_default.stderr +++ b/tests/ui/field_reassign_with_default.stderr @@ -1,108 +1,108 @@ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:48:5 + --> $DIR/field_reassign_with_default.rs:63:5 | LL | a.i = 42; | ^^^^^^^^^ | = note: `-D clippy::field-reassign-with-default` implied by `-D warnings` note: consider initializing the variable with `main::A { i: 42, ..Default::default() }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:47:5 + --> $DIR/field_reassign_with_default.rs:62:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:88:5 + --> $DIR/field_reassign_with_default.rs:103:5 | LL | a.j = 43; | ^^^^^^^^^ | note: consider initializing the variable with `main::A { j: 43, i: 42 }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:87:5 + --> $DIR/field_reassign_with_default.rs:102:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:93:5 + --> $DIR/field_reassign_with_default.rs:108:5 | LL | a.i = 42; | ^^^^^^^^^ | note: consider initializing the variable with `main::A { i: 42, j: 44 }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:92:5 + --> $DIR/field_reassign_with_default.rs:107:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:99:5 + --> $DIR/field_reassign_with_default.rs:114:5 | LL | a.i = 42; | ^^^^^^^^^ | note: consider initializing the variable with `main::A { i: 42, ..Default::default() }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:98:5 + --> $DIR/field_reassign_with_default.rs:113:5 | LL | let mut a = A::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:109:5 + --> $DIR/field_reassign_with_default.rs:124:5 | LL | a.i = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: consider initializing the variable with `main::A { i: Default::default(), ..Default::default() }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:108:5 + --> $DIR/field_reassign_with_default.rs:123:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:113:5 + --> $DIR/field_reassign_with_default.rs:128:5 | LL | a.i = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: consider initializing the variable with `main::A { i: Default::default(), j: 45 }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:112:5 + --> $DIR/field_reassign_with_default.rs:127:5 | LL | let mut a: A = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:135:5 + --> $DIR/field_reassign_with_default.rs:150:5 | LL | a.i = vec![1]; | ^^^^^^^^^^^^^^ | note: consider initializing the variable with `C { i: vec![1], ..Default::default() }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:134:5 + --> $DIR/field_reassign_with_default.rs:149:5 | LL | let mut a: C = C::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:142:5 + --> $DIR/field_reassign_with_default.rs:157:5 | LL | a.i = true; | ^^^^^^^^^^^ | note: consider initializing the variable with `Wrapper:: { i: true }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:141:5 + --> $DIR/field_reassign_with_default.rs:156:5 | LL | let mut a: Wrapper = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: field assignment outside of initializer for an instance created with Default::default() - --> $DIR/field_reassign_with_default.rs:145:5 + --> $DIR/field_reassign_with_default.rs:160:5 | LL | a.i = 42; | ^^^^^^^^^ | note: consider initializing the variable with `WrapperMulti:: { i: 42, ..Default::default() }` and removing relevant reassignments - --> $DIR/field_reassign_with_default.rs:144:5 + --> $DIR/field_reassign_with_default.rs:159:5 | LL | let mut a: WrapperMulti = Default::default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From b1faaaeb0cfb3310c2679f22a33d5ec855f8aaf8 Mon Sep 17 00:00:00 2001 From: Mateusz Gacek <96mateusz.gacek@gmail.com> Date: Mon, 3 May 2021 13:36:08 -0700 Subject: needless_collect: Lint cases with type annotations --- clippy_lints/src/loops/needless_collect.rs | 23 ++++++++++++---- tests/ui/needless_collect_indirect.rs | 29 +++++++++++++++++++- tests/ui/needless_collect_indirect.stderr | 44 +++++++++++++++++++++++++++++- 3 files changed, 89 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/loops/needless_collect.rs index 4d73aef76e8..293dd8b6b79 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/loops/needless_collect.rs @@ -7,9 +7,10 @@ use clippy_utils::{is_trait_method, path_to_local_id, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_block, walk_expr, NestedVisitorMap, Visitor}; -use rustc_hir::{Block, Expr, ExprKind, GenericArg, HirId, Local, Pat, PatKind, QPath, StmtKind}; +use rustc_hir::{Block, Expr, ExprKind, GenericArg, GenericArgs, HirId, Local, Pat, PatKind, QPath, StmtKind, Ty}; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; + use rustc_span::symbol::{sym, Ident}; use rustc_span::{MultiSpan, Span}; @@ -58,18 +59,30 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont } fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { + fn get_hir_id<'tcx>(ty: Option<&Ty<'tcx>>, method_args: Option<&GenericArgs<'tcx>>) -> Option { + if let Some(ty) = ty { + return Some(ty.hir_id); + } + + if let Some(generic_args) = method_args { + if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0) { + return Some(ty.hir_id); + } + } + + None + } if let ExprKind::Block(block, _) = expr.kind { for stmt in block.stmts { if_chain! { if let StmtKind::Local( Local { pat: Pat { hir_id: pat_id, kind: PatKind::Binding(_, _, ident, .. ), .. }, - init: Some(init_expr), .. } + init: Some(init_expr), ty, .. } ) = stmt.kind; if let ExprKind::MethodCall(method_name, collect_span, &[ref iter_source], ..) = init_expr.kind; if method_name.ident.name == sym!(collect) && is_trait_method(cx, init_expr, sym::Iterator); - if let Some(generic_args) = method_name.args; - if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0); - if let ty = cx.typeck_results().node_type(ty.hir_id); + if let Some(hir_id) = get_hir_id(*ty, method_name.args); + if let ty = cx.typeck_results().node_type(hir_id); if is_type_diagnostic_item(cx, ty, sym::vec_type) || is_type_diagnostic_item(cx, ty, sym::vecdeque_type) || match_type(cx, ty, &paths::LINKED_LIST); diff --git a/tests/ui/needless_collect_indirect.rs b/tests/ui/needless_collect_indirect.rs index 0918a6868ab..2647c6401a4 100644 --- a/tests/ui/needless_collect_indirect.rs +++ b/tests/ui/needless_collect_indirect.rs @@ -1,4 +1,4 @@ -use std::collections::{HashMap, VecDeque}; +use std::collections::{HashMap, LinkedList, VecDeque}; fn main() { let sample = [1; 5]; @@ -43,3 +43,30 @@ fn main() { .collect::>(); } } + +mod issue7110 { + // #7110 - lint for type annotation cases + use super::*; + + fn lint_vec(string: &str) -> usize { + let buffer: Vec<&str> = string.split('/').collect(); + buffer.len() + } + fn lint_vec_deque() -> usize { + let sample = [1; 5]; + let indirect_len: VecDeque<_> = sample.iter().collect(); + indirect_len.len() + } + fn lint_linked_list() -> usize { + let sample = [1; 5]; + let indirect_len: LinkedList<_> = sample.iter().collect(); + indirect_len.len() + } + fn dont_lint(string: &str) -> usize { + let buffer: Vec<&str> = string.split('/').collect(); + for buff in &buffer { + println!("{}", buff); + } + buffer.len() + } +} diff --git a/tests/ui/needless_collect_indirect.stderr b/tests/ui/needless_collect_indirect.stderr index c773b841f3b..642beb2865a 100644 --- a/tests/ui/needless_collect_indirect.stderr +++ b/tests/ui/needless_collect_indirect.stderr @@ -69,5 +69,47 @@ LL | LL | sample.into_iter().any(|x| x == a); | -error: aborting due to 5 previous errors +error: avoid using `collect()` when not needed + --> $DIR/needless_collect_indirect.rs:52:51 + | +LL | let buffer: Vec<&str> = string.split('/').collect(); + | ^^^^^^^ +LL | buffer.len() + | ------------ the iterator could be used here instead + | +help: take the original Iterator's count instead of collecting it and finding the length + | +LL | +LL | string.split('/').count() + | + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect_indirect.rs:57:55 + | +LL | let indirect_len: VecDeque<_> = sample.iter().collect(); + | ^^^^^^^ +LL | indirect_len.len() + | ------------------ the iterator could be used here instead + | +help: take the original Iterator's count instead of collecting it and finding the length + | +LL | +LL | sample.iter().count() + | + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect_indirect.rs:62:57 + | +LL | let indirect_len: LinkedList<_> = sample.iter().collect(); + | ^^^^^^^ +LL | indirect_len.len() + | ------------------ the iterator could be used here instead + | +help: take the original Iterator's count instead of collecting it and finding the length + | +LL | +LL | sample.iter().count() + | + +error: aborting due to 8 previous errors -- cgit 1.4.1-3-g733a5 From 0dc38c047e742d7d3c9f42866305a8c849eb4894 Mon Sep 17 00:00:00 2001 From: Mateusz Gacek <96mateusz.gacek@gmail.com> Date: Mon, 3 May 2021 23:58:41 -0700 Subject: Fix copy_iterator uitest --- tests/ui/copy_iterator.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/tests/ui/copy_iterator.rs b/tests/ui/copy_iterator.rs index e3d5928be23..ae67ebded43 100644 --- a/tests/ui/copy_iterator.rs +++ b/tests/ui/copy_iterator.rs @@ -16,8 +16,6 @@ impl Iterator for Countdown { fn main() { let my_iterator = Countdown(5); - let a: Vec<_> = my_iterator.take(1).collect(); - assert_eq!(a.len(), 1); - let b: Vec<_> = my_iterator.collect(); - assert_eq!(b.len(), 5); + assert_eq!(my_iterator.take(1).count(), 1); + assert_eq!(my_iterator.count(), 5); } -- cgit 1.4.1-3-g733a5 From 1835d8a2383c8ae3d26cdcba594fdb933a41f3fb Mon Sep 17 00:00:00 2001 From: Mateusz Gacek <96mateusz.gacek@gmail.com> Date: Tue, 4 May 2021 12:36:20 -0700 Subject: needless_collect: Add `BinaryHeap` for indirect usage lint --- clippy_lints/src/loops/needless_collect.rs | 1 + tests/ui/needless_collect_indirect.rs | 7 ++++++- tests/ui/needless_collect_indirect.stderr | 16 +++++++++++++++- 3 files changed, 22 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/loops/needless_collect.rs index 293dd8b6b79..88d586c9346 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/loops/needless_collect.rs @@ -85,6 +85,7 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo if let ty = cx.typeck_results().node_type(hir_id); if is_type_diagnostic_item(cx, ty, sym::vec_type) || is_type_diagnostic_item(cx, ty, sym::vecdeque_type) || + is_type_diagnostic_item(cx, ty, sym::BinaryHeap) || match_type(cx, ty, &paths::LINKED_LIST); if let Some(iter_calls) = detect_iter_and_into_iters(block, *ident); if let [iter_call] = &*iter_calls; diff --git a/tests/ui/needless_collect_indirect.rs b/tests/ui/needless_collect_indirect.rs index 2647c6401a4..2458bf1e490 100644 --- a/tests/ui/needless_collect_indirect.rs +++ b/tests/ui/needless_collect_indirect.rs @@ -1,4 +1,4 @@ -use std::collections::{HashMap, LinkedList, VecDeque}; +use std::collections::{BinaryHeap, HashMap, LinkedList, VecDeque}; fn main() { let sample = [1; 5]; @@ -62,6 +62,11 @@ mod issue7110 { let indirect_len: LinkedList<_> = sample.iter().collect(); indirect_len.len() } + fn lint_binary_heap() -> usize { + let sample = [1; 5]; + let indirect_len: BinaryHeap<_> = sample.iter().collect(); + indirect_len.len() + } fn dont_lint(string: &str) -> usize { let buffer: Vec<&str> = string.split('/').collect(); for buff in &buffer { diff --git a/tests/ui/needless_collect_indirect.stderr b/tests/ui/needless_collect_indirect.stderr index 642beb2865a..f094e182a48 100644 --- a/tests/ui/needless_collect_indirect.stderr +++ b/tests/ui/needless_collect_indirect.stderr @@ -111,5 +111,19 @@ LL | LL | sample.iter().count() | -error: aborting due to 8 previous errors +error: avoid using `collect()` when not needed + --> $DIR/needless_collect_indirect.rs:67:57 + | +LL | let indirect_len: BinaryHeap<_> = sample.iter().collect(); + | ^^^^^^^ +LL | indirect_len.len() + | ------------------ the iterator could be used here instead + | +help: take the original Iterator's count instead of collecting it and finding the length + | +LL | +LL | sample.iter().count() + | + +error: aborting due to 9 previous errors -- cgit 1.4.1-3-g733a5 From 919ed2ba03f6bf7b02df6eda53d1cbfc28161448 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 4 May 2021 16:22:43 -0500 Subject: Fix needless_quesiton_mark false positive --- clippy_lints/src/needless_question_mark.rs | 112 +++++------------------------ tests/ui/needless_question_mark.fixed | 5 ++ tests/ui/needless_question_mark.rs | 5 ++ tests/ui/needless_question_mark.stderr | 2 +- 4 files changed, 30 insertions(+), 94 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/needless_question_mark.rs b/clippy_lints/src/needless_question_mark.rs index 7b156a8c49d..5c9cce6aad4 100644 --- a/clippy_lints/src/needless_question_mark.rs +++ b/clippy_lints/src/needless_question_mark.rs @@ -1,14 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_lang_ctor; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{differing_macro_contexts, is_lang_ctor}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionSome, ResultOk}; use rustc_hir::{Body, Expr, ExprKind, LangItem, MatchSource, QPath}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TyS; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::sym; declare_clippy_lint! { /// **What it does:** @@ -63,12 +62,6 @@ declare_clippy_lint! { declare_lint_pass!(NeedlessQuestionMark => [NEEDLESS_QUESTION_MARK]); -#[derive(Debug)] -enum SomeOkCall<'a> { - SomeCall(&'a Expr<'a>, &'a Expr<'a>), - OkCall(&'a Expr<'a>, &'a Expr<'a>), -} - impl LateLintPass<'_> for NeedlessQuestionMark { /* * The question mark operator is compatible with both Result and Option, @@ -90,104 +83,37 @@ impl LateLintPass<'_> for NeedlessQuestionMark { */ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { - let e = match &expr.kind { - ExprKind::Ret(Some(e)) => e, - _ => return, - }; - - if let Some(ok_some_call) = is_some_or_ok_call(cx, e) { - emit_lint(cx, &ok_some_call); + if let ExprKind::Ret(Some(e)) = expr.kind { + check(cx, e); } } fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) { - // Function / Closure block - let expr_opt = if let ExprKind::Block(block, _) = &body.value.kind { - block.expr - } else { - // Single line closure - Some(&body.value) - }; - - if_chain! { - if let Some(expr) = expr_opt; - if let Some(ok_some_call) = is_some_or_ok_call(cx, expr); - then { - emit_lint(cx, &ok_some_call); - } - }; + check(cx, body.value.peel_blocks()); } } -fn emit_lint(cx: &LateContext<'_>, expr: &SomeOkCall<'_>) { - let (entire_expr, inner_expr) = match expr { - SomeOkCall::OkCall(outer, inner) | SomeOkCall::SomeCall(outer, inner) => (outer, inner), +fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { + let inner_expr = if_chain! { + if let ExprKind::Call(path, [arg]) = &expr.kind; + if let ExprKind::Path(ref qpath) = &path.kind; + if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk); + if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar) = &arg.kind; + if let ExprKind::Call(called, [inner_expr]) = &inner_expr_with_q.kind; + if let ExprKind::Path(QPath::LangItem(LangItem::TryIntoResult, _)) = &called.kind; + if expr.span.ctxt() == inner_expr.span.ctxt(); + let expr_ty = cx.typeck_results().expr_ty(expr); + let inner_ty = cx.typeck_results().expr_ty(inner_expr); + if TyS::same_type(expr_ty, inner_ty); + then { inner_expr } else { return; } }; - span_lint_and_sugg( cx, NEEDLESS_QUESTION_MARK, - entire_expr.span, + expr.span, "question mark operator is useless here", "try", format!("{}", snippet(cx, inner_expr.span, r#""...""#)), Applicability::MachineApplicable, ); } - -fn is_some_or_ok_call<'a>(cx: &'a LateContext<'_>, expr: &'a Expr<'_>) -> Option> { - if_chain! { - // Check outer expression matches CALL_IDENT(ARGUMENT) format - if let ExprKind::Call(path, args) = &expr.kind; - if let ExprKind::Path(ref qpath) = &path.kind; - if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk); - - // Extract inner expression from ARGUMENT - if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar) = &args[0].kind; - if let ExprKind::Call(called, args) = &inner_expr_with_q.kind; - if args.len() == 1; - - if let ExprKind::Path(QPath::LangItem(LangItem::TryIntoResult, _)) = &called.kind; - then { - // Extract inner expr type from match argument generated by - // question mark operator - let inner_expr = &args[0]; - - // if the inner expr is inside macro but the outer one is not, do not lint (#6921) - if differing_macro_contexts(expr.span, inner_expr.span) { - return None; - } - - let inner_ty = cx.typeck_results().expr_ty(inner_expr); - let outer_ty = cx.typeck_results().expr_ty(expr); - - // Check if outer and inner type are Option - let outer_is_some = is_type_diagnostic_item(cx, outer_ty, sym::option_type); - let inner_is_some = is_type_diagnostic_item(cx, inner_ty, sym::option_type); - - // Check for Option MSRV - if outer_is_some && inner_is_some { - return Some(SomeOkCall::SomeCall(expr, inner_expr)); - } - - // Check if outer and inner type are Result - let outer_is_result = is_type_diagnostic_item(cx, outer_ty, sym::result_type); - let inner_is_result = is_type_diagnostic_item(cx, inner_ty, sym::result_type); - - // Additional check: if the error type of the Result can be converted - // via the From trait, then don't match - let does_not_call_from = !has_implicit_error_from(cx, expr, inner_expr); - - // Must meet Result MSRV - if outer_is_result && inner_is_result && does_not_call_from { - return Some(SomeOkCall::OkCall(expr, inner_expr)); - } - } - } - - None -} - -fn has_implicit_error_from(cx: &LateContext<'_>, entire_expr: &Expr<'_>, inner_result_expr: &Expr<'_>) -> bool { - return cx.typeck_results().expr_ty(entire_expr) != cx.typeck_results().expr_ty(inner_result_expr); -} diff --git a/tests/ui/needless_question_mark.fixed b/tests/ui/needless_question_mark.fixed index 52ddd9d2dc8..f1fc81aa12b 100644 --- a/tests/ui/needless_question_mark.fixed +++ b/tests/ui/needless_question_mark.fixed @@ -94,6 +94,11 @@ where Ok(x?) } +// not quite needless +fn deref_ref(s: Option<&String>) -> Option<&str> { + Some(s?) +} + fn main() {} // #6921 if a macro wraps an expr in Some( ) and the ? is in the macro use, diff --git a/tests/ui/needless_question_mark.rs b/tests/ui/needless_question_mark.rs index 1ea4ba0d83f..44a0c5f61b5 100644 --- a/tests/ui/needless_question_mark.rs +++ b/tests/ui/needless_question_mark.rs @@ -94,6 +94,11 @@ where Ok(x?) } +// not quite needless +fn deref_ref(s: Option<&String>) -> Option<&str> { + Some(s?) +} + fn main() {} // #6921 if a macro wraps an expr in Some( ) and the ? is in the macro use, diff --git a/tests/ui/needless_question_mark.stderr b/tests/ui/needless_question_mark.stderr index afd68d91e51..fa698224530 100644 --- a/tests/ui/needless_question_mark.stderr +++ b/tests/ui/needless_question_mark.stderr @@ -67,7 +67,7 @@ LL | return Ok(t.magic?); | ^^^^^^^^^^^^ help: try: `t.magic` error: question mark operator is useless here - --> $DIR/needless_question_mark.rs:115:27 + --> $DIR/needless_question_mark.rs:120:27 | LL | || -> Option<_> { Some(Some($expr)?) }() | ^^^^^^^^^^^^^^^^^^ help: try: `Some($expr)` -- cgit 1.4.1-3-g733a5 From 83329ec7050da5093b0310ba3140c8c5ec093321 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 4 May 2021 17:20:15 -0500 Subject: Fix unused_unit macro false positive --- clippy_lints/src/unused_unit.rs | 4 +++- tests/ui/unused_unit.fixed | 7 +++++++ tests/ui/unused_unit.rs | 7 +++++++ 3 files changed, 17 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/clippy_lints/src/unused_unit.rs b/clippy_lints/src/unused_unit.rs index ce2d0b3ab2f..e14945651f5 100644 --- a/clippy_lints/src/unused_unit.rs +++ b/clippy_lints/src/unused_unit.rs @@ -47,7 +47,9 @@ impl EarlyLintPass for UnusedUnit { if_chain! { if let Some(stmt) = block.stmts.last(); if let ast::StmtKind::Expr(ref expr) = stmt.kind; - if is_unit_expr(expr) && !stmt.span.from_expansion(); + if is_unit_expr(expr); + let ctxt = block.span.ctxt(); + if stmt.span.ctxt() == ctxt && expr.span.ctxt() == ctxt; then { let sp = expr.span; span_lint_and_sugg( diff --git a/tests/ui/unused_unit.fixed b/tests/ui/unused_unit.fixed index a192ebde3eb..7bb43cf7ae8 100644 --- a/tests/ui/unused_unit.fixed +++ b/tests/ui/unused_unit.fixed @@ -80,3 +80,10 @@ fn test2(){} #[rustfmt::skip] fn test3(){} + +fn macro_expr() { + macro_rules! e { + () => (()); + } + e!() +} diff --git a/tests/ui/unused_unit.rs b/tests/ui/unused_unit.rs index 96041a7dd85..21073fb802a 100644 --- a/tests/ui/unused_unit.rs +++ b/tests/ui/unused_unit.rs @@ -80,3 +80,10 @@ fn test2() ->(){} #[rustfmt::skip] fn test3()-> (){} + +fn macro_expr() { + macro_rules! e { + () => (()); + } + e!() +} -- cgit 1.4.1-3-g733a5 From 59874f3bea8fc27ec0659768b661c7961e73d4ab Mon Sep 17 00:00:00 2001 From: flip1995 Date: Wed, 5 May 2021 15:35:14 +0200 Subject: Add regression test for stack overflow in redundant_pattern_matching --- tests/ui/crashes/ice-7169.rs | 9 +++++++++ tests/ui/crashes/ice-7169.stderr | 10 ++++++++++ 2 files changed, 19 insertions(+) create mode 100644 tests/ui/crashes/ice-7169.rs create mode 100644 tests/ui/crashes/ice-7169.stderr (limited to 'tests') diff --git a/tests/ui/crashes/ice-7169.rs b/tests/ui/crashes/ice-7169.rs new file mode 100644 index 00000000000..82095febc19 --- /dev/null +++ b/tests/ui/crashes/ice-7169.rs @@ -0,0 +1,9 @@ +#[derive(Default)] +struct A { + a: Vec>, + b: T, +} + +fn main() { + if let Ok(_) = Ok::<_, ()>(A::::default()) {} +} diff --git a/tests/ui/crashes/ice-7169.stderr b/tests/ui/crashes/ice-7169.stderr new file mode 100644 index 00000000000..5a9cd32380a --- /dev/null +++ b/tests/ui/crashes/ice-7169.stderr @@ -0,0 +1,10 @@ +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/ice-7169.rs:8:12 + | +LL | if let Ok(_) = Ok::<_, ()>(A::::default()) {} + | -------^^^^^-------------------------------------- help: try this: `if Ok::<_, ()>(A::::default()).is_ok()` + | + = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From 637751ff62662247480ae1641042a019d8e6609a Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sun, 31 Jan 2021 14:46:09 +0100 Subject: Metadata collection lint: Basic lint collection WIP-2021-02-01 WIP-2021-02-01 WIP-2021-02-13 --- .gitignore | 1 + Cargo.toml | 1 + clippy_lints/Cargo.toml | 2 + clippy_lints/src/lib.rs | 3 + clippy_lints/src/utils/internal_lints.rs | 3 + .../src/utils/internal_lints/metadata_collector.rs | 206 +++++++++++++++++++++ clippy_lints/src/utils/mod.rs | 2 +- tests/dogfood.rs | 9 + 8 files changed, 226 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/utils/internal_lints/metadata_collector.rs (limited to 'tests') diff --git a/.gitignore b/.gitignore index 376528e3085..523bab18828 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ out # gh pages docs util/gh-pages/lints.json +**/metadata_collection.json # rustfmt backups *.rs.bk diff --git a/Cargo.toml b/Cargo.toml index cade44a0a9a..bdee8e40821 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,6 +52,7 @@ rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util" } deny-warnings = [] integration = ["tempfile"] internal-lints = ["clippy_lints/internal-lints"] +metadata-collector-lint = ["clippy_lints/metadata-collector-lint"] [package.metadata.rust-analyzer] # This package uses #[feature(rustc_private)] diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 05cdd9d064a..d1d56fc84f9 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -20,6 +20,7 @@ pulldown-cmark = { version = "0.8", default-features = false } quine-mc_cluskey = "0.2.2" regex-syntax = "0.6" serde = { version = "1.0", features = ["derive"] } +serde_json = { version = "1.0", optional = true } toml = "0.5.3" unicode-normalization = "0.1" semver = "0.11" @@ -32,6 +33,7 @@ url = { version = "2.1.0", features = ["serde"] } deny-warnings = [] # build clippy with internal lints enabled, off by default internal-lints = ["clippy_utils/internal-lints"] +metadata-collector-lint = ["serde_json"] [package.metadata.rust-analyzer] # This crate uses #[feature(rustc_private)] diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 40a793e48cf..7668d4dd0b1 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1004,6 +1004,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem); store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass); } + #[cfg(feature = "metadata-collector-lint")] + store.register_late_pass(|| box utils::internal_lints::metadata_collector::MetadataCollector::default()); + store.register_late_pass(|| box utils::author::Author); store.register_late_pass(|| box await_holding_invalid::AwaitHolding); store.register_late_pass(|| box serde_api::SerdeApi); diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 3d3d0e19d26..ee7be24eae8 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -32,6 +32,9 @@ use rustc_typeck::hir_ty_to_ty; use std::borrow::{Borrow, Cow}; +#[cfg(feature = "metadata-collector-lint")] +pub mod metadata_collector; + declare_clippy_lint! { /// **What it does:** Checks for various things we like to keep tidy in clippy. /// diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs new file mode 100644 index 00000000000..dc4267697cb --- /dev/null +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -0,0 +1,206 @@ +//! This lint is used to collect metadata about clippy lints. This metadata is exported as a json +//! file and then used to generate the [clippy lint list](https://rust-lang.github.io/rust-clippy/master/index.html) +//! +//! This module and therefor the entire lint is guarded by a feature flag called +//! `internal_metadata_lint` +//! +//! The metadata currently contains: +//! - [ ] TODO The lint declaration line for [#1303](https://github.com/rust-lang/rust-clippy/issues/1303) +//! and [#6492](https://github.com/rust-lang/rust-clippy/issues/6492) +//! - [ ] TODO The Applicability for each lint for [#4310](https://github.com/rust-lang/rust-clippy/issues/4310) + +// # Applicability +// - TODO xFrednet 2021-01-17: Find all methods that take and modify applicability or predefine +// them? +// - TODO xFrednet 2021-01-17: Find lint emit and collect applicability +// # NITs +// - TODO xFrednet 2021-02-13: Collect depreciations and maybe renames + +use if_chain::if_chain; +use rustc_hir::{ExprKind, Item, ItemKind, Mutability}; +use rustc_lint::{CheckLintNameResult, LateContext, LateLintPass, LintContext, LintId}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{sym, Loc, Span}; +use serde::Serialize; +use std::fs::OpenOptions; +use std::io::prelude::*; + +use crate::utils::internal_lints::is_lint_ref_type; +use crate::utils::span_lint; + +const OUTPUT_FILE: &str = "metadata_collection.json"; +const BLACK_LISTED_LINTS: [&str; 2] = ["lint_author", "deep_code_inspection"]; + +declare_clippy_lint! { + /// **What it does:** Collects metadata about clippy lints for the website. + /// + /// This lint will be used to report problems of syntax parsing. You should hopefully never + /// see this but never say never I guess ^^ + /// + /// **Why is this bad?** This is not a bad thing but definitely a hacky way to do it. See + /// issue [#4310](https://github.com/rust-lang/rust-clippy/issues/4310) for a discussion + /// about the implementation. + /// + /// **Known problems:** Hopefully none. It would be pretty uncool to have a problem here :) + /// + /// **Example output:** + /// ```json,ignore + /// { + /// "id": "internal_metadata_collector", + /// "id_span": { + /// "path": "clippy_lints/src/utils/internal_lints/metadata_collector.rs", + /// "line": 1 + /// }, + /// "group": "clippy::internal", + /// "docs": " **What it does:** Collects metadata about clippy lints for the website. [...] " + /// } + /// ``` + pub INTERNAL_METADATA_COLLECTOR, + internal, + "A busy bee collection metadata about lints" +} + +impl_lint_pass!(MetadataCollector => [INTERNAL_METADATA_COLLECTOR]); + +#[allow(clippy::module_name_repetitions)] +#[derive(Debug, Clone, Default)] +pub struct MetadataCollector { + lints: Vec, +} + +impl Drop for MetadataCollector { + fn drop(&mut self) { + // You might ask: How hacky is this? + // My answer: YES + let mut file = OpenOptions::new().write(true).create(true).open(OUTPUT_FILE).unwrap(); + writeln!(file, "{}", serde_json::to_string_pretty(&self.lints).unwrap()).unwrap(); + } +} + +#[derive(Debug, Clone, Serialize)] +struct LintMetadata { + id: String, + id_span: SerializableSpan, + group: String, + docs: String, +} + +#[derive(Debug, Clone, Serialize)] +struct SerializableSpan { + path: String, + line: usize, +} + +impl SerializableSpan { + fn from_item(cx: &LateContext<'_>, item: &Item<'_>) -> Self { + Self::from_span(cx, item.ident.span) + } + + fn from_span(cx: &LateContext<'_>, span: Span) -> Self { + let loc: Loc = cx.sess().source_map().lookup_char_pos(span.lo()); + + Self { + path: format!("{}", loc.file.name), + line: 1, + } + } +} + +impl<'tcx> LateLintPass<'tcx> for MetadataCollector { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + if_chain! { + if let ItemKind::Static(ref ty, Mutability::Not, body_id) = item.kind; + if is_lint_ref_type(cx, ty); + let expr = &cx.tcx.hir().body(body_id).value; + if let ExprKind::AddrOf(_, _, ref inner_exp) = expr.kind; + if let ExprKind::Struct(_, _, _) = inner_exp.kind; + then { + let lint_name = item.ident.name.as_str().to_string().to_ascii_lowercase(); + if BLACK_LISTED_LINTS.contains(&lint_name.as_str()) { + return; + } + + let group: String; + let result = cx.lint_store.check_lint_name(lint_name.as_str(), Some(sym::clippy)); + if let CheckLintNameResult::Tool(Ok(lint_lst)) = result { + if let Some(group_some) = get_lint_group(cx, lint_lst[0]) { + group = group_some; + } else { + lint_collection_error(cx, item, "Unable to determine lint group"); + return; + } + } else { + lint_collection_error(cx, item, "Unable to find lint in lint_store"); + return; + } + + let docs: String; + if let Some(docs_some) = extract_attr_docs(item) { + docs = docs_some; + } else { + lint_collection_error(cx, item, "could not collect the lint documentation"); + return; + }; + + self.lints.push(LintMetadata { + id: lint_name, + id_span: SerializableSpan::from_item(cx, item), + group, + docs, + }); + } + } + } +} + +/// This function collects all documentation that has been added to an item using +/// `#[doc = r""]` attributes. Several attributes are aggravated using line breaks +/// +/// ```ignore +/// #[doc = r"Hello world!"] +/// #[doc = r"=^.^="] +/// struct SomeItem {} +/// ``` +/// +/// Would result in `Hello world!\n=^.^=\n` +fn extract_attr_docs(item: &Item<'_>) -> Option { + item.attrs + .iter() + .filter_map(|ref x| x.doc_str()) + .fold(None, |acc, sym| { + let mut doc_str = sym.as_str().to_string(); + doc_str.push('\n'); + + #[allow(clippy::option_if_let_else)] // See clippy#6737 + if let Some(mut x) = acc { + x.push_str(&doc_str); + Some(x) + } else { + Some(doc_str) + } + + // acc.map_or(Some(doc_str), |mut x| { + // x.push_str(&doc_str); + // Some(x) + // }) + }) +} + +fn get_lint_group(cx: &LateContext<'_>, lint_id: LintId) -> Option { + for (group_name, lints, _) in &cx.lint_store.get_lint_groups() { + if lints.iter().any(|x| *x == lint_id) { + return Some((*group_name).to_string()); + } + } + + None +} + +fn lint_collection_error(cx: &LateContext<'_>, item: &Item<'_>, message: &str) { + span_lint( + cx, + INTERNAL_METADATA_COLLECTOR, + item.ident.span, + &format!("Metadata collection error for `{}`: {}", item.ident.name, message), + ); +} diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index d8b31344e6d..b67448e3a57 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -1,5 +1,5 @@ pub mod author; pub mod conf; pub mod inspector; -#[cfg(feature = "internal-lints")] +#[cfg(any(feature = "internal-lints", feature = "metadata-collector-lint"))] pub mod internal_lints; diff --git a/tests/dogfood.rs b/tests/dogfood.rs index d92530f073f..b0da0bfcc35 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -1,3 +1,8 @@ +//! This test is a part of quality control and makes clippy eat what it produces. Awesome lints and +//! long error messages +//! +//! See [Eating your own dog food](https://en.wikipedia.org/wiki/Eating_your_own_dog_food) for context + // Dogfood cannot run on Windows #![cfg(not(windows))] #![feature(once_cell)] @@ -36,6 +41,10 @@ fn dogfood_clippy() { command.args(&["-D", "clippy::internal"]); } + if cfg!(feature = "metadata-collector-lint") { + command.args(&["-D", "clippy::internal"]); + } + let output = command.output().unwrap(); println!("status: {}", output.status); -- cgit 1.4.1-3-g733a5 From 8dca1b8f618fdec3f1b354d9bafa01b32ecc8ab6 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Thu, 18 Feb 2021 23:40:33 +0100 Subject: Metadata collection: Collecting Applicability assign values --- .../src/utils/internal_lints/metadata_collector.rs | 249 ++++++++++++++++----- clippy_utils/src/paths.rs | 7 + .../track_applicability_value.rs | 46 ++++ 3 files changed, 248 insertions(+), 54 deletions(-) create mode 100644 tests/ui-internal/metadata-collector/track_applicability_value.rs (limited to 'tests') diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 2bdb92376ca..e22aa285f17 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -24,7 +24,7 @@ use if_chain::if_chain; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::{self as hir, ExprKind, Item, ItemKind, Mutability}; use rustc_lint::{CheckLintNameResult, LateContext, LateLintPass, LintContext, LintId}; -use rustc_middle::ty::BorrowKind; +use rustc_middle::ty::{BorrowKind, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Loc, Span, Symbol}; use rustc_trait_selection::infer::TyCtxtInferExt; @@ -37,7 +37,8 @@ use std::path::Path; use crate::utils::internal_lints::is_lint_ref_type; use crate::utils::{ - last_path_segment, match_function_call, match_type, path_to_local_id, paths, span_lint, walk_ptrs_ty_depth, + get_enclosing_body, get_parent_expr_for_hir, last_path_segment, match_function_call, match_qpath, match_type, + path_to_local_id, paths, span_lint, walk_ptrs_ty_depth, }; /// This is the output file of the lint collector. @@ -147,6 +148,12 @@ struct SerializableSpan { line: usize, } +impl std::fmt::Display for SerializableSpan { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}:{}", self.path.rsplit('/').next().unwrap_or_default(), self.line) + } +} + impl SerializableSpan { fn from_item(cx: &LateContext<'_>, item: &Item<'_>) -> Self { Self::from_span(cx, item.ident.span) @@ -285,52 +292,54 @@ impl<'tcx> LateLintPass<'tcx> for MetadataCollector { /// ); /// ``` fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx hir::Local<'tcx>) { - if let Some(tc) = cx.maybe_typeck_results() { - // TODO xFrednet 2021-02-14: support nested applicability (only in tuples) - let local_ty = if let Some(ty) = local.ty { - hir_ty_to_ty(cx.tcx, ty) - } else if let Some(init) = local.init { - tc.expr_ty(init) - } else { - return; - }; - - if_chain! { - if match_type(cx, local_ty, &paths::APPLICABILITY); - if let Some(body) = get_parent_body(cx, local.hir_id); - then { - let span = SerializableSpan::from_span(cx, local.span); - let local_str = crate::utils::snippet(cx, local.span, "_"); - let value_life = format!("{} -- {}:{}\n", local_str, span.path.rsplit('/').next().unwrap_or_default(), span.line); - let value_hir_id = local.pat.hir_id; - let mut tracker = ValueTracker {cx, value_hir_id, value_life}; - - cx.tcx.infer_ctxt().enter(|infcx| { - let body_owner_id = cx.tcx.hir().body_owner_def_id(body.id()); - ExprUseVisitor::new( - &mut tracker, - &infcx, - body_owner_id, - cx.param_env, - cx.typeck_results() - ) - .consume_body(body); - }); - - log_to_file(&tracker.value_life); - lint_collection_error_span(cx, local.span, "Applicability value found"); + if_chain! { + if let Some(local_ty) = get_local_type(cx, local); + if match_type(cx, local_ty, &paths::APPLICABILITY); + if let Some(body) = get_enclosing_body(cx, local.hir_id); + then { + // TODO xFrednet: 2021-02-19: Remove debug code + let span = SerializableSpan::from_span(cx, local.span); + let local_str = crate::utils::snippet(cx, local.span, "_"); + log_to_file(&format!("{} -- {}\n", local_str, span)); + + let value_hir_id = local.pat.hir_id; + let mut tracker = ValueTracker::new(cx, value_hir_id); + if let Some(init_expr) = local.init { + tracker.process_assign_expr(init_expr) } + + // TODO xFrednet 2021-02-18: Support nested bodies + // Note: The `ExprUseVisitor` only searches though one body, this means that values + // references in nested bodies like closures are not found by this simple visitor. + cx.tcx.infer_ctxt().enter(|infcx| { + let body_owner_id = cx.tcx.hir().body_owner_def_id(body.id()); + ExprUseVisitor::new( + &mut tracker, + &infcx, + body_owner_id, + cx.param_env, + cx.typeck_results() + ) + .consume_body(body); + }); + + log_to_file(&format!("{:?}\n", tracker.value_mutations)); } } } } -fn get_parent_body<'a, 'tcx>(cx: &'a LateContext<'tcx>, id: hir::HirId) -> Option<&'tcx hir::Body<'tcx>> { - let map = cx.tcx.hir(); +fn get_local_type<'a>(cx: &'a LateContext<'_>, local: &'a hir::Local<'_>) -> Option> { + // TODO xFrednet 2021-02-14: support nested applicability (only in tuples) + if let Some(tc) = cx.maybe_typeck_results() { + if let Some(ty) = local.ty { + return Some(hir_ty_to_ty(cx.tcx, ty)); + } else if let Some(init) = local.init { + return Some(tc.expr_ty(init)); + } + } - map.parent_iter(id) - .find_map(|(parent, _)| map.maybe_body_owned_by(parent)) - .map(|body| map.body(body)) + None } fn sym_to_string(sym: Symbol) -> String { @@ -429,42 +438,174 @@ fn extract_emission_info<'tcx>(cx: &LateContext<'tcx>, args: &[hir::Expr<'_>]) - }) } -struct ValueTracker<'a, 'tcx> { - cx: &'a LateContext<'tcx>, +#[allow(dead_code)] +struct ValueTracker<'a, 'hir> { + cx: &'a LateContext<'hir>, value_hir_id: hir::HirId, - value_life: String, + value_mutations: Vec>, } -impl<'a, 'tcx> ValueTracker<'a, 'tcx> { +impl<'a, 'hir> ValueTracker<'a, 'hir> { + fn new(cx: &'a LateContext<'hir>, value_hir_id: hir::HirId) -> Self { + Self { + cx, + value_hir_id, + value_mutations: Vec::new(), + } + } + fn is_value_expr(&self, expr_id: hir::HirId) -> bool { match self.cx.tcx.hir().find(expr_id) { Some(hir::Node::Expr(expr)) => path_to_local_id(expr, self.value_hir_id), _ => false, } } + + /// This function extracts possible `ApplicabilityModifier` from an assign statement like this: + /// + /// ```rust, ignore + /// // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv The expression to process + /// let value = Applicability::MachineApplicable; + /// ``` + fn process_assign_expr(&mut self, expr: &'hir hir::Expr<'hir>) { + // This is a bit more complicated. I'll therefor settle on the simple solution of + // simplifying the cases we support. + match &expr.kind { + hir::ExprKind::Call(func_expr, ..) => { + // We only deal with resolved paths as this is the usual case. Other expression kinds like closures + // etc. are hard to track but might be a worthy improvement in the future + if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = func_expr.kind { + self.value_mutations.push(ApplicabilityModifier::Producer(path)); + } else { + let msg = format!( + "Unsupported Call expression at: {}", + SerializableSpan::from_span(self.cx, func_expr.span) + ); + self.value_mutations.push(ApplicabilityModifier::Unknown(msg)); + } + }, + hir::ExprKind::MethodCall(..) => { + let msg = format!( + "Unsupported MethodCall expression at: {}", + SerializableSpan::from_span(self.cx, expr.span) + ); + self.value_mutations.push(ApplicabilityModifier::Unknown(msg)); + }, + // We can ignore ifs without an else block because those can't be used as an assignment + hir::ExprKind::If(_con, if_block, Some(else_block)) => { + self.process_assign_expr(if_block); + self.process_assign_expr(else_block); + }, + hir::ExprKind::Match(_expr, arms, _) => { + for arm in *arms { + self.process_assign_expr(arm.body); + } + }, + hir::ExprKind::Loop(block, ..) | hir::ExprKind::Block(block, ..) => { + if let Some(block_expr) = block.expr { + self.process_assign_expr(block_expr); + } + }, + hir::ExprKind::Path(path) => { + for enum_value in &paths::APPLICABILITY_VALUES { + if match_qpath(path, enum_value) { + self.value_mutations + .push(ApplicabilityModifier::ConstValue(enum_value[2].to_string())); + } + } + }, + // hir::ExprKind::Field(expr, ident) => not supported + // hir::ExprKind::Index(expr, expr) => not supported + _ => { + let msg = format!( + "Unexpected expression at: {}", + SerializableSpan::from_span(self.cx, expr.span) + ); + self.value_mutations.push(ApplicabilityModifier::Unknown(msg)); + }, + } + } } -impl<'a, 'tcx> Delegate<'tcx> for ValueTracker<'a, 'tcx> { - fn consume(&mut self, _place_with_id: &PlaceWithHirId<'tcx>, expr_id: hir::HirId, _: ConsumeMode) { +impl<'a, 'hir> Delegate<'hir> for ValueTracker<'a, 'hir> { + fn consume(&mut self, _place_with_id: &PlaceWithHirId<'hir>, expr_id: hir::HirId, _: ConsumeMode) { if self.is_value_expr(expr_id) { // TODO xFrednet 2021-02-17: Check if lint emission and extract lint ID - todo!(); + if let Some(hir::Node::Expr(expr)) = self.cx.tcx.hir().find(expr_id) { + let span = SerializableSpan::from_span(self.cx, expr.span); + log_to_file(&format!("- consume {}\n", span)); + } } } - fn borrow(&mut self, _place_with_id: &PlaceWithHirId<'tcx>, expr_id: hir::HirId, bk: BorrowKind) { + fn borrow(&mut self, _place_with_id: &PlaceWithHirId<'hir>, expr_id: hir::HirId, bk: BorrowKind) { if self.is_value_expr(expr_id) { if let BorrowKind::MutBorrow = bk { // TODO xFrednet 2021-02-17: Save the function - todo!(); + if let Some(hir::Node::Expr(expr)) = self.cx.tcx.hir().find(expr_id) { + let span = SerializableSpan::from_span(self.cx, expr.span); + log_to_file(&format!("- &mut {}\n", span)); + } } } } - fn mutate(&mut self, _assignee_place: &PlaceWithHirId<'tcx>, expr_id: hir::HirId) { - if self.is_value_expr(expr_id) { - // TODO xFrednet 2021-02-17: Save the new value as a mutation - todo!(); + fn mutate(&mut self, _assignee_place: &PlaceWithHirId<'hir>, expr_id: hir::HirId) { + if_chain! { + if self.is_value_expr(expr_id); + if let Some(expr) = get_parent_expr_for_hir(self.cx, expr_id); + if let hir::ExprKind::Assign(_value_expr, assign_expr, ..) = expr.kind; + then { + self.process_assign_expr(assign_expr); + } } } } + +/// The life of a value in Rust is a true adventure. These are the corner stones of such a +/// fairy tale. Let me introduce you to the possible stepping stones a value might have in +/// in our crazy word: +#[derive(Debug)] +#[allow(dead_code)] +enum ApplicabilityModifier<'hir> { + Unknown(String), + /// A simple constant value. + /// + /// This is the actual character of a value. It's baseline. This only defines where the value + /// started. As in real life it can still change and fully decide who it wants to be. + ConstValue(String), + /// A producer is a function that returns an applicability value. + /// + /// This is the heritage of this value. This value comes from a long family tree and is not + /// just a black piece of paper. The evaluation of this stepping stone needs additional + /// context. We therefore only add a reference. This reference will later be used to ask + /// the librarian about the possible initial character that this value might have. + Producer(&'hir hir::Path<'hir>), + /// A modifier that takes the given applicability and might modify it + /// + /// What would an RPG be without it's NPCs. The special thing about modifiers is that they can + /// be actively interested in the story of the value and might make decisions based on the + /// character of this hero. This means that a modifier doesn't just force its way into the life + /// of our hero but it actually asks him how he's been. The possible modification is a result + /// of the situation. + /// + /// Take this part of our heroes life very seriously! + Modifier(&'hir hir::Path<'hir>), + /// The actual emission of a lint + /// + /// All good things must come to an end. Even the life of your awesome applicability hero. He + /// was the bravest soul that has ever wondered this earth. Songs will be written about his + /// heroic deeds. Castles will be named after him and the world how we know it will never be + /// the same! + /// + /// Is this a happy ending? Did he archive what he wanted in his life? Yes, YES, he has lived a + /// life and he will continue to live in all the lint suggestions that can be applied or just + /// displayed by Clippy. He might be no more, but his legacy will serve generations to come. + LintEmit(LintEmission), +} + +#[derive(Debug)] +struct LintEmission { + lint: String, + is_multi_line_sugg: bool, +} diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 91e5cd6c046..46f58b788e6 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -7,6 +7,13 @@ pub const ANY_TRAIT: [&str; 3] = ["core", "any", "Any"]; #[cfg(feature = "metadata-collector-lint")] pub const APPLICABILITY: [&str; 2] = ["rustc_lint_defs", "Applicability"]; +#[cfg(feature = "metadata-collector-lint")] +pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [ + ["rustc_lint_defs", "Applicability", "MachineApplicable"], + ["rustc_lint_defs", "Applicability", "MaybeIncorrect"], + ["rustc_lint_defs", "Applicability", "HasPlaceholders"], + ["rustc_lint_defs", "Applicability", "Unspecified"], +]; 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"]; diff --git a/tests/ui-internal/metadata-collector/track_applicability_value.rs b/tests/ui-internal/metadata-collector/track_applicability_value.rs new file mode 100644 index 00000000000..75fae623e61 --- /dev/null +++ b/tests/ui-internal/metadata-collector/track_applicability_value.rs @@ -0,0 +1,46 @@ +#![deny(clippy::internal)] +#![feature(rustc_private)] + +extern crate rustc_ast; +extern crate rustc_errors; +extern crate rustc_lint; +extern crate rustc_session; +extern crate rustc_span; + +use rustc_ast::ast::Expr; +use rustc_errors::{Applicability, DiagnosticBuilder}; +use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Span; + +fn producer_fn() -> Applicability { + Applicability::MachineApplicable +} + +fn modifier_fn(applicability: &mut Applicability) { + if let Applicability::MaybeIncorrect = applicability { + *applicability = Applicability::HasPlaceholders; + } +} + +struct Muh; + +impl Muh { + fn producer_method() -> Applicability { + Applicability::MachineApplicable + } +} + +fn main() { + let mut applicability = producer_fn(); + applicability = Applicability::MachineApplicable; + applicability = Muh::producer_method(); + + applicability = if true { + Applicability::HasPlaceholders + } else { + Applicability::MaybeIncorrect + }; + + modifier_fn(&mut applicability); +} -- cgit 1.4.1-3-g733a5 From a39912cfbbd682270b5f3d534bc69eedfaa73a7b Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sat, 20 Feb 2021 13:12:30 +0100 Subject: Metadata collection: Some refactoring for readability --- .../src/utils/internal_lints/metadata_collector.rs | 104 +++++++++++---------- .../track_applicability_value.rs | 4 + 2 files changed, 57 insertions(+), 51 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index c8637531d94..de94e47047b 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -37,8 +37,8 @@ use std::path::Path; use crate::utils::internal_lints::is_lint_ref_type; use crate::utils::{ - get_enclosing_body, get_parent_expr_for_hir, last_path_segment, match_function_call, match_qpath, match_type, - path_to_local_id, paths, span_lint, walk_ptrs_ty_depth, get_parent_expr + get_enclosing_body, get_parent_expr, get_parent_expr_for_hir, last_path_segment, match_function_call, match_qpath, + match_type, path_to_local_id, paths, span_lint, walk_ptrs_ty_depth, }; /// This is the output file of the lint collector. @@ -202,39 +202,19 @@ impl<'tcx> LateLintPass<'tcx> for MetadataCollector { /// ``` fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if_chain! { + // item validation if let ItemKind::Static(ref ty, Mutability::Not, body_id) = item.kind; if is_lint_ref_type(cx, ty); let expr = &cx.tcx.hir().body(body_id).value; if let ExprKind::AddrOf(_, _, ref inner_exp) = expr.kind; if let ExprKind::Struct(_, _, _) = inner_exp.kind; + // blacklist check + let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase(); + if !BLACK_LISTED_LINTS.contains(&lint_name.as_str()); + // metadata extraction + if let Some(group) = get_lint_group_or_lint(cx, &lint_name, item); + if let Some(docs) = extract_attr_docs_or_lint(cx, item); then { - let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase(); - if BLACK_LISTED_LINTS.contains(&lint_name.as_str()) { - return; - } - - let group: String; - let result = cx.lint_store.check_lint_name(lint_name.as_str(), Some(sym::clippy)); - if let CheckLintNameResult::Tool(Ok(lint_lst)) = result { - if let Some(group_some) = get_lint_group(cx, lint_lst[0]) { - group = group_some; - } else { - lint_collection_error_item(cx, item, "Unable to determine lint group"); - return; - } - } else { - lint_collection_error_item(cx, item, "Unable to find lint in lint_store"); - return; - } - - let docs: String; - if let Some(docs_some) = extract_attr_docs(item) { - docs = docs_some; - } else { - lint_collection_error_item(cx, item, "could not collect the lint documentation"); - return; - }; - self.lints.push(LintMetadata::new( lint_name, SerializableSpan::from_item(cx, item), @@ -301,7 +281,7 @@ impl<'tcx> LateLintPass<'tcx> for MetadataCollector { let span = SerializableSpan::from_span(cx, local.span); let local_str = crate::utils::snippet(cx, local.span, "_"); log_to_file(&format!("{} -- {}\n", local_str, span)); - + let value_hir_id = local.pat.hir_id; let mut tracker = ValueTracker::new(cx, value_hir_id); if let Some(init_expr) = local.init { @@ -342,10 +322,21 @@ fn get_local_type<'a>(cx: &'a LateContext<'_>, local: &'a hir::Local<'_>) -> Opt None } +// ================================================================== +// Lint definition extraction +// ================================================================== + fn sym_to_string(sym: Symbol) -> String { sym.as_str().to_string() } +fn extract_attr_docs_or_lint(cx: &LateContext<'_>, item: &Item<'_>) -> Option { + extract_attr_docs(item).or_else(|| { + lint_collection_error_item(cx, item, "could not collect the lint documentation"); + None + }) +} + /// This function collects all documentation that has been added to an item using /// `#[doc = r""]` attributes. Several attributes are aggravated using line breaks /// @@ -367,6 +358,19 @@ fn extract_attr_docs(item: &Item<'_>) -> Option { }) } +fn get_lint_group_or_lint(cx: &LateContext<'_>, lint_name: &str, item: &'tcx Item<'_>) -> Option { + let result = cx.lint_store.check_lint_name(lint_name, Some(sym::clippy)); + if let CheckLintNameResult::Tool(Ok(lint_lst)) = result { + get_lint_group(cx, lint_lst[0]).or_else(|| { + lint_collection_error_item(cx, item, "Unable to determine lint group"); + None + }) + } else { + lint_collection_error_item(cx, item, "Unable to find lint in lint_store"); + None + } +} + fn get_lint_group(cx: &LateContext<'_>, lint_id: LintId) -> Option { for (group_name, lints, _) in &cx.lint_store.get_lint_groups() { if lints.iter().any(|x| *x == lint_id) { @@ -526,19 +530,8 @@ impl<'a, 'hir> ValueTracker<'a, 'hir> { } } - fn process_borrow_expr(&mut self, access_hir_id: hir::HirId) { - let borrower: &rustc_hir::Expr<'_>; - if let Some(addr_of_expr) = get_parent_expr_for_hir(self.cx, access_hir_id) { - if let Some(borrower_expr) = get_parent_expr(self.cx, addr_of_expr) { - borrower = borrower_expr - } else { - return; - } - } else { - return; - } - - match &borrower.kind { + fn process_borrow_expr(&mut self, borrower_expr: &'hir rustc_hir::Expr<'hir>) { + match &borrower_expr.kind { hir::ExprKind::Call(func_expr, ..) => { // We only deal with resolved paths as this is the usual case. Other expression kinds like closures // etc. are hard to track but might be a worthy improvement in the future @@ -555,36 +548,45 @@ impl<'a, 'hir> ValueTracker<'a, 'hir> { hir::ExprKind::MethodCall(..) => { let msg = format!( "Unsupported borrow in MethodCall at: {}", - SerializableSpan::from_span(self.cx, borrower.span) + SerializableSpan::from_span(self.cx, borrower_expr.span) ); self.value_mutations.push(ApplicabilityModifier::Unknown(msg)); }, _ => { let msg = format!( "Unexpected borrow at: {}", - SerializableSpan::from_span(self.cx, borrower.span) + SerializableSpan::from_span(self.cx, borrower_expr.span) ); self.value_mutations.push(ApplicabilityModifier::Unknown(msg)); }, } } + + fn process_consume_expr(&mut self, consume_expr: &'hir rustc_hir::Expr<'hir>) { + // We are only interested in lint emissions. Other types like assignments might be + // interesting for further use or improvement but are to complex for now. + if let hir::ExprKind::Call(func_expr, ..) = &consume_expr.kind {} + } } impl<'a, 'hir> Delegate<'hir> for ValueTracker<'a, 'hir> { fn consume(&mut self, _place_with_id: &PlaceWithHirId<'hir>, expr_id: hir::HirId, _: ConsumeMode) { if self.is_value_expr(expr_id) { // TODO xFrednet 2021-02-17: Check if lint emission and extract lint ID - if let Some(hir::Node::Expr(expr)) = self.cx.tcx.hir().find(expr_id) { - let span = SerializableSpan::from_span(self.cx, expr.span); - log_to_file(&format!("- consume {}\n", span)); + if let Some(expr) = get_parent_expr_for_hir(self.cx, expr_id) { + log_to_file(&format!("- consume {:?}\n", expr)); } } } fn borrow(&mut self, _place_with_id: &PlaceWithHirId<'hir>, expr_id: hir::HirId, bk: BorrowKind) { - if self.is_value_expr(expr_id) { - if let BorrowKind::MutBorrow = bk { - self.process_borrow_expr(expr_id); + if_chain! { + if self.is_value_expr(expr_id); + if let BorrowKind::MutBorrow = bk; + if let Some(addr_of_expr) = get_parent_expr_for_hir(self.cx, expr_id); + if let Some(borrower_expr) = get_parent_expr(self.cx, addr_of_expr); + then { + self.process_borrow_expr(borrower_expr); } } } diff --git a/tests/ui-internal/metadata-collector/track_applicability_value.rs b/tests/ui-internal/metadata-collector/track_applicability_value.rs index 75fae623e61..b0f59e5cf8a 100644 --- a/tests/ui-internal/metadata-collector/track_applicability_value.rs +++ b/tests/ui-internal/metadata-collector/track_applicability_value.rs @@ -23,6 +23,8 @@ fn modifier_fn(applicability: &mut Applicability) { } } +fn consumer_fn(_applicability: Applicability) {} + struct Muh; impl Muh { @@ -43,4 +45,6 @@ fn main() { }; modifier_fn(&mut applicability); + + consumer_fn(applicability); } -- cgit 1.4.1-3-g733a5 From 4fc960301bcd4370bfc2238415c7ba3d2d8f9312 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Wed, 10 Mar 2021 23:04:12 +0100 Subject: Metadata collection: Rounding up the implementation --- .gitignore | 2 +- .../src/utils/internal_lints/metadata_collector.rs | 152 +- clippy_utils/src/paths.rs | 6 +- metadata_collection.json | 5778 -------------------- tests/dogfood.rs | 4 - .../track_applicability_value.rs | 50 - 6 files changed, 88 insertions(+), 5904 deletions(-) delete mode 100644 metadata_collection.json delete mode 100644 tests/ui-internal/metadata-collector/track_applicability_value.rs (limited to 'tests') diff --git a/.gitignore b/.gitignore index 565327a7f83..523bab18828 100644 --- a/.gitignore +++ b/.gitignore @@ -29,7 +29,7 @@ out # gh pages docs util/gh-pages/lints.json -# **/metadata_collection.json +**/metadata_collection.json # rustfmt backups *.rs.bk diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 0425689b0dd..37e90bf8686 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -7,34 +7,21 @@ //! The module transforms all lint names to ascii lowercase to ensure that we don't have mismatches //! during any comparison or mapping. (Please take care of this, it's not fun to spend time on such //! a simple mistake) -//! -//! The metadata currently contains: -//! - [x] TODO The lint declaration line for [#1303](https://github.com/rust-lang/rust-clippy/issues/1303) -//! and [#6492](https://github.com/rust-lang/rust-clippy/issues/6492) -//! - [ ] TODO The Applicability for each lint for [#4310](https://github.com/rust-lang/rust-clippy/issues/4310) - -// # Applicability -// - TODO xFrednet 2021-01-17: Find lint emit and collect applicability -// - TODO xFrednet 2021-02-28: 4x weird emission forwarding -// - See clippy_lints/src/enum_variants.rs@EnumVariantNames::check_name -// - TODO xFrednet 2021-02-28: 6x emission forwarding with local that is initializes from -// function. -// - See clippy_lints/src/methods/mod.rs@lint_binary_expr_with_method_call -// - TODO xFrednet 2021-02-28: 2x lint from local from function call -// - See clippy_lints/src/misc.rs@check_binary -// - TODO xFrednet 2021-02-28: 2x lint from local from method call -// - See clippy_lints/src/non_copy_const.rs@lint + // # NITs // - TODO xFrednet 2021-02-13: Collect depreciations and maybe renames use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::{self as hir, intravisit, intravisit::Visitor, ExprKind, Item, ItemKind, Mutability, QPath, def::DefKind}; +use rustc_hir::{ + self as hir, def::DefKind, intravisit, intravisit::Visitor, ExprKind, Item, ItemKind, Mutability, QPath, +}; use rustc_lint::{CheckLintNameResult, LateContext, LateLintPass, LintContext, LintId}; use rustc_middle::hir::map::Map; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Loc, Span, Symbol}; -use serde::Serialize; +use serde::{ser::SerializeStruct, Serialize, Serializer}; +use std::collections::BinaryHeap; use std::fs::{self, OpenOptions}; use std::io::prelude::*; use std::path::Path; @@ -47,7 +34,7 @@ use crate::utils::{ /// This is the output file of the lint collector. const OUTPUT_FILE: &str = "../metadata_collection.json"; /// These lints are excluded from the export. -const BLACK_LISTED_LINTS: [&str; 2] = ["lint_author", "deep_code_inspection"]; +const BLACK_LISTED_LINTS: [&str; 3] = ["lint_author", "deep_code_inspection", "internal_metadata_collector"]; /// These groups will be ignored by the lint group matcher. This is useful for collections like /// `clippy::all` const IGNORED_LINT_GROUPS: [&str; 1] = ["clippy::all"]; @@ -107,7 +94,7 @@ declare_clippy_lint! { /// } /// ``` pub INTERNAL_METADATA_COLLECTOR, - internal, + internal_warn, "A busy bee collection metadata about lints" } @@ -116,7 +103,10 @@ impl_lint_pass!(MetadataCollector => [INTERNAL_METADATA_COLLECTOR]); #[allow(clippy::module_name_repetitions)] #[derive(Debug, Clone, Default)] pub struct MetadataCollector { - lints: Vec, + /// All collected lints + /// + /// We use a Heap here to have the lints added in alphabetic order in the export + lints: BinaryHeap, applicability_into: FxHashMap, } @@ -124,6 +114,8 @@ impl Drop for MetadataCollector { /// You might ask: How hacky is this? /// My answer: YES fn drop(&mut self) { + // The metadata collector gets dropped twice, this makes sure that we only write + // when the list is full if self.lints.is_empty() { return; } @@ -131,7 +123,8 @@ impl Drop for MetadataCollector { let mut applicability_info = std::mem::take(&mut self.applicability_into); // Mapping the final data - self.lints + let mut lints = std::mem::take(&mut self.lints).into_sorted_vec(); + lints .iter_mut() .for_each(|x| x.applicability = applicability_info.remove(&x.id)); @@ -140,11 +133,11 @@ impl Drop for MetadataCollector { fs::remove_file(OUTPUT_FILE).unwrap(); } let mut file = OpenOptions::new().write(true).create(true).open(OUTPUT_FILE).unwrap(); - writeln!(file, "{}", serde_json::to_string_pretty(&self.lints).unwrap()).unwrap(); + writeln!(file, "{}", serde_json::to_string_pretty(&lints).unwrap()).unwrap(); } } -#[derive(Debug, Clone, Serialize)] +#[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)] struct LintMetadata { id: String, id_span: SerializableSpan, @@ -155,6 +148,24 @@ struct LintMetadata { applicability: Option, } +// impl Ord for LintMetadata { +// fn cmp(&self, other: &Self) -> Ordering { +// self.id.cmp(&other.id) +// } +// } +// +// impl PartialOrd for LintMetadata { +// fn partial_cmp(&self, other: &Self) -> Option { +// Some(self.cmp(other)) +// } +// } +// +// impl PartialEq for LintMetadata { +// fn eq(&self, other: &Self) -> bool { +// self.id == other.id +// } +// } + impl LintMetadata { fn new(id: String, id_span: SerializableSpan, group: String, docs: String) -> Self { Self { @@ -167,7 +178,7 @@ impl LintMetadata { } } -#[derive(Debug, Clone, Serialize)] +#[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)] struct SerializableSpan { path: String, line: usize, @@ -194,25 +205,30 @@ impl SerializableSpan { } } -#[derive(Debug, Clone, Default, Serialize)] +#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)] struct ApplicabilityInfo { /// Indicates if any of the lint emissions uses multiple spans. This is related to /// [rustfix#141](https://github.com/rust-lang/rustfix/issues/141) as such suggestions can /// currently not be applied automatically. - is_multi_suggestion: bool, - applicability: Option, -} - -#[allow(dead_code)] -fn log_to_file(msg: &str) { - let mut file = OpenOptions::new() - .write(true) - .append(true) - .create(true) - .open("metadata-lint.log") - .unwrap(); - - write!(file, "{}", msg).unwrap(); + is_multi_part_suggestion: bool, + applicability: Option, +} + +impl Serialize for ApplicabilityInfo { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let index = self.applicability.unwrap_or_default(); + + let mut s = serializer.serialize_struct("ApplicabilityInfo", 2)?; + s.serialize_field("is_multi_part_suggestion", &self.is_multi_part_suggestion)?; + s.serialize_field( + "applicability", + &paths::APPLICABILITY_VALUES[index][APPLICABILITY_NAME_INDEX], + )?; + s.end() + } } impl<'hir> LateLintPass<'hir> for MetadataCollector { @@ -266,16 +282,20 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector { /// ``` fn check_expr(&mut self, cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) { if let Some(args) = match_lint_emission(cx, expr) { - let mut emission_info = extract_complex_emission_info(cx, args); + let mut emission_info = extract_emission_info(cx, args); if emission_info.is_empty() { - lint_collection_error_span(cx, expr.span, "Look, here ... I have no clue what todo with it"); + // See: + // - src/misc.rs:734:9 + // - src/methods/mod.rs:3545:13 + // - src/methods/mod.rs:3496:13 + // We are basically unable to resolve the lint name it self. return; } for (lint_name, applicability, is_multi_part) in emission_info.drain(..) { let app_info = self.applicability_into.entry(lint_name).or_default(); app_info.applicability = applicability; - app_info.is_multi_suggestion = is_multi_part; + app_info.is_multi_part_suggestion = is_multi_part; } } } @@ -289,7 +309,7 @@ fn sym_to_string(sym: Symbol) -> String { } fn extract_attr_docs_or_lint(cx: &LateContext<'_>, item: &Item<'_>) -> Option { - extract_attr_docs(item).or_else(|| { + extract_attr_docs(cx, item).or_else(|| { lint_collection_error_item(cx, item, "could not collect the lint documentation"); None }) @@ -305,8 +325,10 @@ fn extract_attr_docs_or_lint(cx: &LateContext<'_>, item: &Item<'_>) -> Option) -> Option { - item.attrs +fn extract_attr_docs(cx: &LateContext<'_>, item: &Item<'_>) -> Option { + cx.tcx + .hir() + .attrs(item.hir_id()) .iter() .filter_map(|ref x| x.doc_str().map(|sym| sym.as_str().to_string())) .reduce(|mut acc, sym| { @@ -357,15 +379,6 @@ fn lint_collection_error_item(cx: &LateContext<'_>, item: &Item<'_>, message: &s ); } -fn lint_collection_error_span(cx: &LateContext<'_>, span: Span, message: &str) { - span_lint( - cx, - INTERNAL_METADATA_COLLECTOR, - span, - &format!("Metadata collection error: {}", message), - ); -} - // ================================================================== // Applicability // ================================================================== @@ -377,11 +390,15 @@ fn match_lint_emission<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) .find_map(|emission_fn| match_function_call(cx, expr, emission_fn)) } -fn extract_complex_emission_info<'hir>( +fn take_higher_applicability(a: Option, b: Option) -> Option { + a.map_or(b, |a| a.max(b.unwrap_or_default()).into()) +} + +fn extract_emission_info<'hir>( cx: &LateContext<'hir>, args: &'hir [hir::Expr<'hir>], -) -> Vec<(String, Option, bool)> { - let mut lints= Vec::new(); +) -> Vec<(String, Option, bool)> { + let mut lints = Vec::new(); let mut applicability = None; let mut multi_part = false; @@ -401,7 +418,10 @@ fn extract_complex_emission_info<'hir>( } } - lints.drain(..).map(|lint_name| (lint_name, applicability.clone(), multi_part)).collect() + lints + .drain(..) + .map(|lint_name| (lint_name, applicability, multi_part)) + .collect() } /// Resolves the possible lints that this expression could reference @@ -412,7 +432,7 @@ fn resolve_lints(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Vec, expr: &'hir hir::Expr<'hir>) -> Option { +fn resolve_applicability(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option { let mut resolver = ApplicabilityResolver::new(cx); resolver.visit_expr(expr); resolver.complete() @@ -457,7 +477,7 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for LintResolver<'a, 'hir> { if_chain! { if let ExprKind::Path(qpath) = &expr.kind; if let QPath::Resolved(_, path) = qpath; - + let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(&expr)); if match_type(self.cx, expr_ty, &paths::LINT); then { @@ -492,15 +512,11 @@ impl<'a, 'hir> ApplicabilityResolver<'a, 'hir> { } fn add_new_index(&mut self, new_index: usize) { - self.applicability_index = self - .applicability_index - .map_or(new_index, |old_index| old_index.min(new_index)) - .into(); + self.applicability_index = take_higher_applicability(self.applicability_index, Some(new_index)); } - fn complete(self) -> Option { + fn complete(self) -> Option { self.applicability_index - .map(|index| paths::APPLICABILITY_VALUES[index][APPLICABILITY_NAME_INDEX].to_string()) } } diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 1f19724ad25..7c7bb9b02b1 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -9,10 +9,10 @@ pub const ANY_TRAIT: [&str; 3] = ["core", "any", "Any"]; pub const APPLICABILITY: [&str; 2] = ["rustc_lint_defs", "Applicability"]; #[cfg(feature = "metadata-collector-lint")] pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [ - ["rustc_lint_defs", "Applicability", "MachineApplicable"], - ["rustc_lint_defs", "Applicability", "MaybeIncorrect"], - ["rustc_lint_defs", "Applicability", "HasPlaceholders"], ["rustc_lint_defs", "Applicability", "Unspecified"], + ["rustc_lint_defs", "Applicability", "HasPlaceholders"], + ["rustc_lint_defs", "Applicability", "MaybeIncorrect"], + ["rustc_lint_defs", "Applicability", "MachineApplicable"], ]; #[cfg(feature = "metadata-collector-lint")] pub const DIAGNOSTIC_BUILDER: [&str; 3] = ["rustc_errors", "diagnostic_builder", "DiagnosticBuilder"]; diff --git a/metadata_collection.json b/metadata_collection.json deleted file mode 100644 index f6f72ffa5fa..00000000000 --- a/metadata_collection.json +++ /dev/null @@ -1,5778 +0,0 @@ -[ - { - "id": "approx_constant", - "id_span": { - "path": "src/approx_const.rs", - "line": 34 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for floating point literals that approximate constants which are defined in\n [`std::f32::consts`](https://doc.rust-lang.org/stable/std/f32/consts/#constants)\n or\n [`std::f64::consts`](https://doc.rust-lang.org/stable/std/f64/consts/#constants),\n respectively, suggesting to use the predefined constant.\n\n **Why is this bad?** Usually, the definition in the standard library is more\n precise than what people come up with. If you find that your definition is\n actually more precise, please [file a Rust\n issue](https://github.com/rust-lang/rust/issues).\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = 3.14;\n let y = 1_f64 / x;\n ```\n Use predefined constants instead:\n ```rust\n let x = std::f32::consts::PI;\n let y = std::f64::consts::FRAC_1_PI;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "integer_arithmetic", - "id_span": { - "path": "src/arithmetic.rs", - "line": 28 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for integer arithmetic operations which could overflow or panic.\n Specifically, checks for any operators (`+`, `-`, `*`, `<<`, etc) which are capable\n of overflowing according to the [Rust\n Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow),\n or which can panic (`/`, `%`). No bounds analysis or sophisticated reasoning is\n attempted.\n\n **Why is this bad?** Integer overflow will trigger a panic in debug builds or will wrap in\n release mode. Division by zero will cause a panic in either mode. In some applications one\n wants explicitly checked, wrapping or saturating arithmetic.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let a = 0;\n a + 1;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "float_arithmetic", - "id_span": { - "path": "src/arithmetic.rs", - "line": 46 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for float arithmetic.\n **Why is this bad?** For some embedded systems or kernel development, it\n can be useful to rule out floating-point numbers.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let a = 0.0;\n a + 1.0;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "as_conversions", - "id_span": { - "path": "src/as_conversions.rs", - "line": 42 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of `as` conversions.\n Note that this lint is specialized in linting *every single* use of `as`\n regardless of whether good alternatives exist or not.\n If you want more precise lints for `as`, please consider using these separate lints:\n `unnecessary_cast`, `cast_lossless/possible_truncation/possible_wrap/precision_loss/sign_loss`,\n `fn_to_numeric_cast(_with_truncation)`, `char_lit_as_u8`, `ref_to_mut` and `ptr_as_ptr`.\n There is a good explanation the reason why this lint should work in this way and how it is useful\n [in this issue](https://github.com/rust-lang/rust-clippy/issues/5122).\n\n **Why is this bad?** `as` conversions will perform many kinds of\n conversions, including silently lossy conversions and dangerous coercions.\n There are cases when it makes sense to use `as`, so the lint is\n Allow by default.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n let a: u32;\n ...\n f(a as u16);\n ```\n\n Usually better represents the semantics you expect:\n ```rust,ignore\n f(a.try_into()?);\n ```\n or\n ```rust,ignore\n f(a.try_into().expect(\"Unexpected u16 overflow in f\"));\n ```\n\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "inline_asm_x86_intel_syntax", - "id_span": { - "path": "src/asm_syntax.rs", - "line": 78 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of Intel x86 assembly syntax.\n **Why is this bad?** The lint has been enabled to indicate a preference\n for AT&T x86 assembly syntax.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,no_run\n # #![feature(asm)]\n # unsafe { let ptr = \"\".as_ptr();\n asm!(\"lea {}, [{}]\", lateout(reg) _, in(reg) ptr);\n # }\n ```\n Use instead:\n ```rust,no_run\n # #![feature(asm)]\n # unsafe { let ptr = \"\".as_ptr();\n asm!(\"lea ({}), {}\", in(reg) ptr, lateout(reg) _, options(att_syntax));\n # }\n ```\n", - "applicability": null - }, - { - "id": "inline_asm_x86_att_syntax", - "id_span": { - "path": "src/asm_syntax.rs", - "line": 114 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of AT&T x86 assembly syntax.\n **Why is this bad?** The lint has been enabled to indicate a preference\n for Intel x86 assembly syntax.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,no_run\n # #![feature(asm)]\n # unsafe { let ptr = \"\".as_ptr();\n asm!(\"lea ({}), {}\", in(reg) ptr, lateout(reg) _, options(att_syntax));\n # }\n ```\n Use instead:\n ```rust,no_run\n # #![feature(asm)]\n # unsafe { let ptr = \"\".as_ptr();\n asm!(\"lea {}, [{}]\", lateout(reg) _, in(reg) ptr);\n # }\n ```\n", - "applicability": null - }, - { - "id": "assertions_on_constants", - "id_span": { - "path": "src/assertions_on_constants.rs", - "line": 23 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `assert!(true)` and `assert!(false)` calls.\n **Why is this bad?** Will be optimized out by the compiler or should probably be replaced by a\n `panic!()` or `unreachable!()`\n\n **Known problems:** None\n\n **Example:**\n ```rust,ignore\n assert!(false)\n assert!(true)\n const B: bool = false;\n assert!(B)\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "assign_op_pattern", - "id_span": { - "path": "src/assign_ops.rs", - "line": 33 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `a = a op b` or `a = b commutative_op a` patterns.\n\n **Why is this bad?** These can be written as the shorter `a op= b`.\n\n **Known problems:** While forbidden by the spec, `OpAssign` traits may have\n implementations that differ from the regular `Op` impl.\n\n **Example:**\n ```rust\n let mut a = 5;\n let b = 0;\n // ...\n // Bad\n a = a + b;\n\n // Good\n a += b;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "misrefactored_assign_op", - "id_span": { - "path": "src/assign_ops.rs", - "line": 56 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for `a op= a op b` or `a op= b op a` patterns.\n **Why is this bad?** Most likely these are bugs where one meant to write `a\n op= b`.\n\n **Known problems:** Clippy cannot know for sure if `a op= a op b` should have\n been `a = a op a op b` or `a = a op b`/`a op= b`. Therefore, it suggests both.\n If `a op= a op b` is really the correct behaviour it should be\n written as `a = a op a op b` as it's less confusing.\n\n **Example:**\n ```rust\n let mut a = 5;\n let b = 2;\n // ...\n a += a + b;\n ```\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "async_yields_async", - "id_span": { - "path": "src/async_yields_async.rs", - "line": 36 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for async blocks that yield values of types that can themselves be awaited.\n\n **Why is this bad?** An await is likely missing.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n async fn foo() {}\n\n fn bar() {\n let x = async {\n foo()\n };\n }\n ```\n Use instead:\n ```rust\n async fn foo() {}\n\n fn bar() {\n let x = async {\n foo().await\n };\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "invalid_atomic_ordering", - "id_span": { - "path": "src/atomic_ordering.rs", - "line": 47 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for usage of invalid atomic ordering in atomic loads/stores/exchanges/updates and\n memory fences.\n\n **Why is this bad?** Using an invalid atomic ordering\n will cause a panic at run-time.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,no_run\n # use std::sync::atomic::{self, AtomicU8, Ordering};\n\n let x = AtomicU8::new(0);\n\n // Bad: `Release` and `AcqRel` cannot be used for `load`.\n let _ = x.load(Ordering::Release);\n let _ = x.load(Ordering::AcqRel);\n\n // Bad: `Acquire` and `AcqRel` cannot be used for `store`.\n x.store(1, Ordering::Acquire);\n x.store(2, Ordering::AcqRel);\n\n // Bad: `Relaxed` cannot be used as a fence's ordering.\n atomic::fence(Ordering::Relaxed);\n atomic::compiler_fence(Ordering::Relaxed);\n\n // Bad: `Release` and `AcqRel` are both always invalid\n // for the failure ordering (the last arg).\n let _ = x.compare_exchange(1, 2, Ordering::SeqCst, Ordering::Release);\n let _ = x.compare_exchange_weak(2, 3, Ordering::AcqRel, Ordering::AcqRel);\n\n // Bad: The failure ordering is not allowed to be\n // stronger than the success order, and `SeqCst` is\n // stronger than `Relaxed`.\n let _ = x.fetch_update(Ordering::Relaxed, Ordering::SeqCst, |val| Some(val + val));\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "inline_always", - "id_span": { - "path": "src/attrs.rs", - "line": 65 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for items annotated with `#[inline(always)]`, unless the annotated function is empty or simply panics.\n\n **Why is this bad?** While there are valid uses of this annotation (and once\n you know when to use it, by all means `allow` this lint), it's a common\n newbie-mistake to pepper one's code with it.\n\n As a rule of thumb, before slapping `#[inline(always)]` on a function,\n measure if that additional function call really affects your runtime profile\n sufficiently to make up for the increase in compile time.\n\n **Known problems:** False positives, big time. This lint is meant to be\n deactivated by everyone doing serious performance work. This means having\n done the measurement.\n\n **Example:**\n ```ignore\n #[inline(always)]\n fn not_quite_hot_code(..) { ... }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "useless_attribute", - "id_span": { - "path": "src/attrs.rs", - "line": 99 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for `extern crate` and `use` items annotated with lint attributes.\n\n This lint permits `#[allow(unused_imports)]`, `#[allow(deprecated)]`,\n `#[allow(unreachable_pub)]`, `#[allow(clippy::wildcard_imports)]` and\n `#[allow(clippy::enum_glob_use)]` on `use` items and `#[allow(unused_imports)]` on\n `extern crate` items with a `#[macro_use]` attribute.\n\n **Why is this bad?** Lint attributes have no effect on crate imports. Most\n likely a `!` was forgotten.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n // Bad\n #[deny(dead_code)]\n extern crate foo;\n #[forbid(dead_code)]\n use foo::bar;\n\n // Ok\n #[allow(unused_imports)]\n use foo::baz;\n #[allow(unused_imports)]\n #[macro_use]\n extern crate baz;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "deprecated_semver", - "id_span": { - "path": "src/attrs.rs", - "line": 118 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for `#[deprecated]` annotations with a `since` field that is not a valid semantic version.\n\n **Why is this bad?** For checking the version of the deprecation, it must be\n a valid semver. Failing that, the contained information is useless.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n #[deprecated(since = \"forever\")]\n fn something_else() { /* ... */ }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "empty_line_after_outer_attr", - "id_span": { - "path": "src/attrs.rs", - "line": 153 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Checks for empty lines after outer attributes\n **Why is this bad?**\n Most likely the attribute was meant to be an inner attribute using a '!'.\n If it was meant to be an outer attribute, then the following item\n should not be separated by empty lines.\n\n **Known problems:** Can cause false positives.\n\n From the clippy side it's difficult to detect empty lines between an attributes and the\n following item because empty lines and comments are not part of the AST. The parsing\n currently works for basic cases but is not perfect.\n\n **Example:**\n ```rust\n // Good (as inner attribute)\n #![allow(dead_code)]\n\n fn this_is_fine() { }\n\n // Bad\n #[allow(dead_code)]\n\n fn not_quite_good_code() { }\n\n // Good (as outer attribute)\n #[allow(dead_code)]\n fn this_is_fine_too() { }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "blanket_clippy_restriction_lints", - "id_span": { - "path": "src/attrs.rs", - "line": 176 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `warn`/`deny`/`forbid` attributes targeting the whole clippy::restriction category.\n **Why is this bad?** Restriction lints sometimes are in contrast with other lints or even go against idiomatic rust.\n These lints should only be enabled on a lint-by-lint basis and with careful consideration.\n\n **Known problems:** None.\n\n **Example:**\n Bad:\n ```rust\n #![deny(clippy::restriction)]\n ```\n\n Good:\n ```rust\n #![deny(clippy::as_conversions)]\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "deprecated_cfg_attr", - "id_span": { - "path": "src/attrs.rs", - "line": 205 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for `#[cfg_attr(rustfmt, rustfmt_skip)]` and suggests to replace it with `#[rustfmt::skip]`.\n\n **Why is this bad?** Since tool_attributes ([rust-lang/rust#44690](https://github.com/rust-lang/rust/issues/44690))\n are stable now, they should be used instead of the old `cfg_attr(rustfmt)` attributes.\n\n **Known problems:** This lint doesn't detect crate level inner attributes, because they get\n processed before the PreExpansionPass lints get executed. See\n [#3123](https://github.com/rust-lang/rust-clippy/pull/3123#issuecomment-422321765)\n\n **Example:**\n\n Bad:\n ```rust\n #[cfg_attr(rustfmt, rustfmt_skip)]\n fn main() { }\n ```\n\n Good:\n ```rust\n #[rustfmt::skip]\n fn main() { }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "mismatched_target_os", - "id_span": { - "path": "src/attrs.rs", - "line": 238 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for cfg attributes having operating systems used in target family position.\n **Why is this bad?** The configuration option will not be recognised and the related item will not be included\n by the conditional compilation engine.\n\n **Known problems:** None.\n\n **Example:**\n\n Bad:\n ```rust\n #[cfg(linux)]\n fn conditional() { }\n ```\n\n Good:\n ```rust\n #[cfg(target_os = \"linux\")]\n fn conditional() { }\n ```\n\n Or:\n ```rust\n #[cfg(unix)]\n fn conditional() { }\n ```\n Check the [Rust Reference](https://doc.rust-lang.org/reference/conditional-compilation.html#target_os) for more details.\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "await_holding_lock", - "id_span": { - "path": "src/await_holding_invalid.rs", - "line": 47 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for calls to await while holding a non-async-aware MutexGuard.\n\n **Why is this bad?** The Mutex types found in std::sync and parking_lot\n are not designed to operate in an async context across await points.\n\n There are two potential solutions. One is to use an asynx-aware Mutex\n type. Many asynchronous foundation crates provide such a Mutex type. The\n other solution is to ensure the mutex is unlocked before calling await,\n either by introducing a scope or an explicit call to Drop::drop.\n\n **Known problems:** Will report false positive for explicitly dropped guards ([#6446](https://github.com/rust-lang/rust-clippy/issues/6446)).\n\n **Example:**\n\n ```rust,ignore\n use std::sync::Mutex;\n\n async fn foo(x: &Mutex) {\n let guard = x.lock().unwrap();\n *guard += 1;\n bar.await;\n }\n ```\n\n Use instead:\n ```rust,ignore\n use std::sync::Mutex;\n\n async fn foo(x: &Mutex) {\n {\n let guard = x.lock().unwrap();\n *guard += 1;\n }\n bar.await;\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "await_holding_refcell_ref", - "id_span": { - "path": "src/await_holding_invalid.rs", - "line": 86 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for calls to await while holding a `RefCell` `Ref` or `RefMut`.\n\n **Why is this bad?** `RefCell` refs only check for exclusive mutable access\n at runtime. Holding onto a `RefCell` ref across an `await` suspension point\n risks panics from a mutable ref shared while other refs are outstanding.\n\n **Known problems:** Will report false positive for explicitly dropped refs ([#6353](https://github.com/rust-lang/rust-clippy/issues/6353)).\n\n **Example:**\n\n ```rust,ignore\n use std::cell::RefCell;\n\n async fn foo(x: &RefCell) {\n let mut y = x.borrow_mut();\n *y += 1;\n bar.await;\n }\n ```\n\n Use instead:\n ```rust,ignore\n use std::cell::RefCell;\n\n async fn foo(x: &RefCell) {\n {\n let mut y = x.borrow_mut();\n *y += 1;\n }\n bar.await;\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "bad_bit_mask", - "id_span": { - "path": "src/bit_mask.rs", - "line": 44 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for incompatible bit masks in comparisons.\n The formula for detecting if an expression of the type `_ m\n c` (where `` is one of {`&`, `|`} and `` is one of\n {`!=`, `>=`, `>`, `!=`, `>=`, `>`}) can be determined from the following\n table:\n\n |Comparison |Bit Op|Example |is always|Formula |\n |------------|------|------------|---------|----------------------|\n |`==` or `!=`| `&` |`x & 2 == 3`|`false` |`c & m != c` |\n |`<` or `>=`| `&` |`x & 2 < 3` |`true` |`m < c` |\n |`>` or `<=`| `&` |`x & 1 > 1` |`false` |`m <= c` |\n |`==` or `!=`| `|` |`x | 1 == 0`|`false` |`c | m != c` |\n |`<` or `>=`| `|` |`x | 1 < 1` |`false` |`m >= c` |\n |`<=` or `>` | `|` |`x | 1 > 0` |`true` |`m > c` |\n\n **Why is this bad?** If the bits that the comparison cares about are always\n set to zero or one by the bit mask, the comparison is constant `true` or\n `false` (depending on mask, compared value, and operators).\n\n So the code is actively misleading, and the only reason someone would write\n this intentionally is to win an underhanded Rust contest or create a\n test-case for this lint.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let x = 1;\n if (x & 1 == 2) { }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "ineffective_bit_mask", - "id_span": { - "path": "src/bit_mask.rs", - "line": 73 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for bit masks in comparisons which can be removed without changing the outcome. The basic structure can be seen in the\n following table:\n\n |Comparison| Bit Op |Example |equals |\n |----------|---------|-----------|-------|\n |`>` / `<=`|`|` / `^`|`x | 2 > 3`|`x > 3`|\n |`<` / `>=`|`|` / `^`|`x ^ 1 < 4`|`x < 4`|\n\n **Why is this bad?** Not equally evil as [`bad_bit_mask`](#bad_bit_mask),\n but still a bit misleading, because the bit mask is ineffective.\n\n **Known problems:** False negatives: This lint will only match instances\n where we have figured out the math (which is for a power-of-two compared\n value). This means things like `x | 1 >= 7` (which would be better written\n as `x >= 6`) will not be reported (but bit masks like this are fairly\n uncommon).\n\n **Example:**\n ```rust\n # let x = 1;\n if (x | 1 > 3) { }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "verbose_bit_mask", - "id_span": { - "path": "src/bit_mask.rs", - "line": 92 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for bit masks that can be replaced by a call to `trailing_zeros`\n\n **Why is this bad?** `x.trailing_zeros() > 4` is much clearer than `x & 15\n == 0`\n\n **Known problems:** llvm generates better code for `x & 15 == 0` on x86\n\n **Example:**\n ```rust\n # let x = 1;\n if x & 0b1111 == 0 { }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "blacklisted_name", - "id_span": { - "path": "src/blacklisted_name.rs", - "line": 20 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for usage of blacklisted names for variables, such as `foo`.\n\n **Why is this bad?** These names are usually placeholder names and should be\n avoided.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let foo = 3.14;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "blocks_in_if_conditions", - "id_span": { - "path": "src/blocks_in_if_conditions.rs", - "line": 42 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `if` conditions that use blocks containing an expression, statements or conditions that use closures with blocks.\n\n **Why is this bad?** Style, using blocks in the condition makes it hard to read.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust\n // Bad\n if { true } { /* ... */ }\n\n // Good\n if true { /* ... */ }\n ```\n\n // or\n\n ```rust\n # fn somefunc() -> bool { true };\n // Bad\n if { let x = somefunc(); x } { /* ... */ }\n\n // Good\n let res = { let x = somefunc(); x };\n if res { /* ... */ }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "nonminimal_bool", - "id_span": { - "path": "src/booleans.rs", - "line": 31 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for boolean expressions that can be written more concisely.\n\n **Why is this bad?** Readability of boolean expressions suffers from\n unnecessary duplication.\n\n **Known problems:** Ignores short circuiting behavior of `||` and\n `&&`. Ignores `|`, `&` and `^`.\n\n **Example:**\n ```ignore\n if a && true // should be: if a\n if !(a == b) // should be: if a != b\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "logic_bug", - "id_span": { - "path": "src/booleans.rs", - "line": 49 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for boolean expressions that contain terminals that can be eliminated.\n\n **Why is this bad?** This is most likely a logic bug.\n\n **Known problems:** Ignores short circuiting behavior.\n\n **Example:**\n ```ignore\n if a && b || a { ... }\n ```\n The `b` is unnecessary, the expression is equivalent to `if a`.\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "naive_bytecount", - "id_span": { - "path": "src/bytecount.rs", - "line": 30 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for naive byte counts\n **Why is this bad?** The [`bytecount`](https://crates.io/crates/bytecount)\n crate has methods to count your bytes faster, especially for large slices.\n\n **Known problems:** If you have predominantly small slices, the\n `bytecount::count(..)` method may actually be slower. However, if you can\n ensure that less than 2³²-1 matches arise, the `naive_count_32(..)` can be\n faster in those cases.\n\n **Example:**\n\n ```rust\n # let vec = vec![1_u8];\n &vec.iter().filter(|x| **x == 0u8).count(); // use bytecount::count instead\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "cargo_common_metadata", - "id_span": { - "path": "src/cargo_common_metadata.rs", - "line": 49 - }, - "group": "clippy::cargo", - "docs": " **What it does:** Checks to see if all common metadata is defined in `Cargo.toml`. See: https://rust-lang-nursery.github.io/api-guidelines/documentation.html#cargotoml-includes-all-common-metadata-c-metadata\n\n **Why is this bad?** It will be more difficult for users to discover the\n purpose of the crate, and key information related to it.\n\n **Known problems:** None.\n\n **Example:**\n ```toml\n # This `Cargo.toml` is missing an authors field:\n [package]\n name = \"clippy\"\n version = \"0.0.212\"\n description = \"A bunch of helpful lints to avoid common pitfalls in Rust\"\n repository = \"https://github.com/rust-lang/rust-clippy\"\n readme = \"README.md\"\n license = \"MIT OR Apache-2.0\"\n keywords = [\"clippy\", \"lint\", \"plugin\"]\n categories = [\"development-tools\", \"development-tools::cargo-plugins\"]\n ```\n\n Should include an authors field like:\n\n ```toml\n # This `Cargo.toml` includes all common metadata\n [package]\n name = \"clippy\"\n version = \"0.0.212\"\n authors = [\"Someone \"]\n description = \"A bunch of helpful lints to avoid common pitfalls in Rust\"\n repository = \"https://github.com/rust-lang/rust-clippy\"\n readme = \"README.md\"\n license = \"MIT OR Apache-2.0\"\n keywords = [\"clippy\", \"lint\", \"plugin\"]\n categories = [\"development-tools\", \"development-tools::cargo-plugins\"]\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "case_sensitive_file_extension_comparisons", - "id_span": { - "path": "src/case_sensitive_file_extension_comparisons.rs", - "line": 34 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for calls to `ends_with` with possible file extensions\n and suggests to use a case-insensitive approach instead.\n\n **Why is this bad?**\n `ends_with` is case-sensitive and may not detect files with a valid extension.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n fn is_rust_file(filename: &str) -> bool {\n filename.ends_with(\".rs\")\n }\n ```\n Use instead:\n ```rust\n fn is_rust_file(filename: &str) -> bool {\n filename.rsplit('.').next().map(|ext| ext.eq_ignore_ascii_case(\"rs\")) == Some(true)\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "checked_conversions", - "id_span": { - "path": "src/checked_conversions.rs", - "line": 40 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for explicit bounds checking when casting.\n **Why is this bad?** Reduces the readability of statements & is error prone.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let foo: u32 = 5;\n # let _ =\n foo <= i32::MAX as u32\n # ;\n ```\n\n Could be written:\n\n ```rust\n # use std::convert::TryFrom;\n # let foo = 1;\n # let _ =\n i32::try_from(foo).is_ok()\n # ;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "cognitive_complexity", - "id_span": { - "path": "src/cognitive_complexity.rs", - "line": 24 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Checks for methods with high cognitive complexity.\n **Why is this bad?** Methods of high cognitive complexity tend to be hard to\n both read and maintain. Also LLVM will tend to optimize small methods better.\n\n **Known problems:** Sometimes it's hard to find a way to reduce the\n complexity.\n\n **Example:** No. You'll see it when you get the warning.\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "collapsible_if", - "id_span": { - "path": "src/collapsible_if.rs", - "line": 50 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for nested `if` statements which can be collapsed by `&&`-combining their conditions.\n\n **Why is this bad?** Each `if`-statement adds one level of nesting, which\n makes code look more complex than it really is.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n if x {\n if y {\n …\n }\n }\n\n ```\n\n Should be written:\n\n ```rust.ignore\n if x && y {\n …\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "collapsible_else_if", - "id_span": { - "path": "src/collapsible_if.rs", - "line": 85 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for collapsible `else { if ... }` expressions that can be collapsed to `else if ...`.\n\n **Why is this bad?** Each `if`-statement adds one level of nesting, which\n makes code look more complex than it really is.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n\n if x {\n …\n } else {\n if y {\n …\n }\n }\n ```\n\n Should be written:\n\n ```rust.ignore\n if x {\n …\n } else if y {\n …\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "collapsible_match", - "id_span": { - "path": "src/collapsible_match.rs", - "line": 44 - }, - "group": "clippy::style", - "docs": " **What it does:** Finds nested `match` or `if let` expressions where the patterns may be \"collapsed\" together without adding any branches.\n\n Note that this lint is not intended to find _all_ cases where nested match patterns can be merged, but only\n cases where merging would most likely make the code more readable.\n\n **Why is this bad?** It is unnecessarily verbose and complex.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n fn func(opt: Option>) {\n let n = match opt {\n Some(n) => match n {\n Ok(n) => n,\n _ => return,\n }\n None => return,\n };\n }\n ```\n Use instead:\n ```rust\n fn func(opt: Option>) {\n let n = match opt {\n Some(Ok(n)) => n,\n _ => return,\n };\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "comparison_chain", - "id_span": { - "path": "src/comparison_chain.rs", - "line": 49 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks comparison chains written with `if` that can be rewritten with `match` and `cmp`.\n\n **Why is this bad?** `if` is not guaranteed to be exhaustive and conditionals can get\n repetitive\n\n **Known problems:** The match statement may be slower due to the compiler\n not inlining the call to cmp. See issue [#5354](https://github.com/rust-lang/rust-clippy/issues/5354)\n\n **Example:**\n ```rust,ignore\n # fn a() {}\n # fn b() {}\n # fn c() {}\n fn f(x: u8, y: u8) {\n if x > y {\n a()\n } else if x < y {\n b()\n } else {\n c()\n }\n }\n ```\n\n Could be written:\n\n ```rust,ignore\n use std::cmp::Ordering;\n # fn a() {}\n # fn b() {}\n # fn c() {}\n fn f(x: u8, y: u8) {\n match x.cmp(&y) {\n Ordering::Greater => a(),\n Ordering::Less => b(),\n Ordering::Equal => c()\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "ifs_same_cond", - "id_span": { - "path": "src/copies.rs", - "line": 33 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for consecutive `if`s with the same condition.\n **Why is this bad?** This is probably a copy & paste error.\n\n **Known problems:** Hopefully none.\n\n **Example:**\n ```ignore\n if a == b {\n …\n } else if a == b {\n …\n }\n ```\n\n Note that this lint ignores all conditions with a function call as it could\n have side effects:\n\n ```ignore\n if foo() {\n …\n } else if foo() { // not linted\n …\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "same_functions_in_if_condition", - "id_span": { - "path": "src/copies.rs", - "line": 80 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for consecutive `if`s with the same function call.\n **Why is this bad?** This is probably a copy & paste error.\n Despite the fact that function can have side effects and `if` works as\n intended, such an approach is implicit and can be considered a \"code smell\".\n\n **Known problems:** Hopefully none.\n\n **Example:**\n ```ignore\n if foo() == bar {\n …\n } else if foo() == bar {\n …\n }\n ```\n\n This probably should be:\n ```ignore\n if foo() == bar {\n …\n } else if foo() == baz {\n …\n }\n ```\n\n or if the original code was not a typo and called function mutates a state,\n consider move the mutation out of the `if` condition to avoid similarity to\n a copy & paste error:\n\n ```ignore\n let first = foo();\n if first == bar {\n …\n } else {\n let second = foo();\n if second == bar {\n …\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "if_same_then_else", - "id_span": { - "path": "src/copies.rs", - "line": 101 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for `if/else` with the same body as the *then* part and the *else* part.\n\n **Why is this bad?** This is probably a copy & paste error.\n\n **Known problems:** Hopefully none.\n\n **Example:**\n ```ignore\n let foo = if … {\n 42\n } else {\n 42\n };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "copy_iterator", - "id_span": { - "path": "src/copy_iterator.rs", - "line": 27 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for types that implement `Copy` as well as `Iterator`.\n\n **Why is this bad?** Implicit copies can be confusing when working with\n iterator combinators.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n #[derive(Copy, Clone)]\n struct Countdown(u8);\n\n impl Iterator for Countdown {\n // ...\n }\n\n let a: Vec<_> = my_iterator.take(1).collect();\n let b: Vec<_> = my_iterator.collect();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "create_dir", - "id_span": { - "path": "src/create_dir.rs", - "line": 24 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks usage of `std::fs::create_dir` and suggest using `std::fs::create_dir_all` instead.\n **Why is this bad?** Sometimes `std::fs::create_dir` is mistakenly chosen over `std::fs::create_dir_all`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n std::fs::create_dir(\"foo\");\n ```\n Use instead:\n ```rust\n std::fs::create_dir_all(\"foo\");\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "dbg_macro", - "id_span": { - "path": "src/dbg_macro.rs", - "line": 25 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of dbg!() macro.\n **Why is this bad?** `dbg!` macro is intended as a debugging tool. It\n should not be in version control.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n // Bad\n dbg!(true)\n\n // Good\n true\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "default_trait_access", - "id_span": { - "path": "src/default.rs", - "line": 33 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for literal calls to `Default::default()`.\n **Why is this bad?** It's more clear to the reader to use the name of the type whose default is\n being gotten than the generic `Default`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n let s: String = Default::default();\n\n // Good\n let s = String::default();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "field_reassign_with_default", - "id_span": { - "path": "src/default.rs", - "line": 63 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for immediate reassignment of fields initialized with Default::default().\n\n **Why is this bad?**It's more idiomatic to use the [functional update syntax](https://doc.rust-lang.org/reference/expressions/struct-expr.html#functional-update-syntax).\n\n **Known problems:** Assignments to patterns that are of tuple type are not linted.\n\n **Example:**\n Bad:\n ```\n # #[derive(Default)]\n # struct A { i: i32 }\n let mut a: A = Default::default();\n a.i = 42;\n ```\n Use instead:\n ```\n # #[derive(Default)]\n # struct A { i: i32 }\n let a = A {\n i: 42,\n .. Default::default()\n };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "default_numeric_fallback", - "id_span": { - "path": "src/default_numeric_fallback.rs", - "line": 44 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of unconstrained numeric literals which may cause default numeric fallback in type inference.\n\n Default numeric fallback means that if numeric types have not yet been bound to concrete\n types at the end of type inference, then integer type is bound to `i32`, and similarly\n floating type is bound to `f64`.\n\n See [RFC0212](https://github.com/rust-lang/rfcs/blob/master/text/0212-restore-int-fallback.md) for more information about the fallback.\n\n **Why is this bad?** For those who are very careful about types, default numeric fallback\n can be a pitfall that cause unexpected runtime behavior.\n\n **Known problems:** This lint can only be allowed at the function level or above.\n\n **Example:**\n ```rust\n let i = 10;\n let f = 1.23;\n ```\n\n Use instead:\n ```rust\n let i = 10i32;\n let f = 1.23f64;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "explicit_deref_methods", - "id_span": { - "path": "src/dereference.rs", - "line": 32 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for explicit `deref()` or `deref_mut()` method calls.\n **Why is this bad?** Dereferencing by `&*x` or `&mut *x` is clearer and more concise,\n when not part of a method chain.\n\n **Example:**\n ```rust\n use std::ops::Deref;\n let a: &mut String = &mut String::from(\"foo\");\n let b: &str = a.deref();\n ```\n Could be written as:\n ```rust\n let a: &mut String = &mut String::from(\"foo\");\n let b = &*a;\n ```\n\n This lint excludes\n ```rust,ignore\n let _ = d.unwrap().deref();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "derive_hash_xor_eq", - "id_span": { - "path": "src/derive.rs", - "line": 42 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for deriving `Hash` but implementing `PartialEq` explicitly or vice versa.\n\n **Why is this bad?** The implementation of these traits must agree (for\n example for use with `HashMap`) so it’s probably a bad idea to use a\n default-generated `Hash` implementation with an explicitly defined\n `PartialEq`. In particular, the following must hold for any type:\n\n ```text\n k1 == k2 ⇒ hash(k1) == hash(k2)\n ```\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n #[derive(Hash)]\n struct Foo;\n\n impl PartialEq for Foo {\n ...\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "derive_ord_xor_partial_ord", - "id_span": { - "path": "src/derive.rs", - "line": 93 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for deriving `Ord` but implementing `PartialOrd` explicitly or vice versa.\n\n **Why is this bad?** The implementation of these traits must agree (for\n example for use with `sort`) so it’s probably a bad idea to use a\n default-generated `Ord` implementation with an explicitly defined\n `PartialOrd`. In particular, the following must hold for any type\n implementing `Ord`:\n\n ```text\n k1.cmp(&k2) == k1.partial_cmp(&k2).unwrap()\n ```\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,ignore\n #[derive(Ord, PartialEq, Eq)]\n struct Foo;\n\n impl PartialOrd for Foo {\n ...\n }\n ```\n Use instead:\n ```rust,ignore\n #[derive(PartialEq, Eq)]\n struct Foo;\n\n impl PartialOrd for Foo {\n fn partial_cmp(&self, other: &Foo) -> Option {\n Some(self.cmp(other))\n }\n }\n\n impl Ord for Foo {\n ...\n }\n ```\n or, if you don't need a custom ordering:\n ```rust,ignore\n #[derive(Ord, PartialOrd, PartialEq, Eq)]\n struct Foo;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "expl_impl_clone_on_copy", - "id_span": { - "path": "src/derive.rs", - "line": 119 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for explicit `Clone` implementations for `Copy` types.\n\n **Why is this bad?** To avoid surprising behaviour, these traits should\n agree and the behaviour of `Copy` cannot be overridden. In almost all\n situations a `Copy` type should have a `Clone` implementation that does\n nothing more than copy the object, which is what `#[derive(Copy, Clone)]`\n gets you.\n\n **Known problems:** Bounds of generic types are sometimes wrong: https://github.com/rust-lang/rust/issues/26925\n\n **Example:**\n ```rust,ignore\n #[derive(Copy)]\n struct Foo;\n\n impl Clone for Foo {\n // ..\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "unsafe_derive_deserialize", - "id_span": { - "path": "src/derive.rs", - "line": 153 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for deriving `serde::Deserialize` on a type that has methods using `unsafe`.\n\n **Why is this bad?** Deriving `serde::Deserialize` will create a constructor\n that may violate invariants hold by another constructor.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,ignore\n use serde::Deserialize;\n\n #[derive(Deserialize)]\n pub struct Foo {\n // ..\n }\n\n impl Foo {\n pub fn new() -> Self {\n // setup here ..\n }\n\n pub unsafe fn parts() -> (&str, &str) {\n // assumes invariants hold\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "disallowed_method", - "id_span": { - "path": "src/disallowed_method.rs", - "line": 46 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Denies the configured methods and functions in clippy.toml\n **Why is this bad?** Some methods are undesirable in certain contexts,\n and it's beneficial to lint for them as needed.\n\n **Known problems:** Currently, you must write each function as a\n fully-qualified path. This lint doesn't support aliases or reexported\n names; be aware that many types in `std` are actually reexports.\n\n For example, if you want to disallow `Duration::as_secs`, your clippy.toml\n configuration would look like\n `disallowed-methods = [\"core::time::Duration::as_secs\"]` and not\n `disallowed-methods = [\"std::time::Duration::as_secs\"]` as you might expect.\n\n **Example:**\n\n An example clippy.toml configuration:\n ```toml\n # clippy.toml\n disallowed-methods = [\"alloc::vec::Vec::leak\", \"std::time::Instant::now\"]\n ```\n\n ```rust,ignore\n // Example code where clippy issues a warning\n let xs = vec![1, 2, 3, 4];\n xs.leak(); // Vec::leak is disallowed in the config.\n\n let _now = Instant::now(); // Instant::now is disallowed in the config.\n ```\n\n Use instead:\n ```rust,ignore\n // Example code which does not raise clippy warning\n let mut xs = Vec::new(); // Vec::new is _not_ disallowed in the config.\n xs.push(123); // Vec::push is _not_ disallowed in the config.\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "doc_markdown", - "id_span": { - "path": "src/doc.rs", - "line": 63 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for the presence of `_`, `::` or camel-case words outside ticks in documentation.\n\n **Why is this bad?** *Rustdoc* supports markdown formatting, `_`, `::` and\n camel-case probably indicates some code which should be included between\n ticks. `_` can also be used for emphasis in markdown, this lint tries to\n consider that.\n\n **Known problems:** Lots of bad docs won’t be fixed, what the lint checks\n for is limited, and there are still false positives.\n\n In addition, when writing documentation comments, including `[]` brackets\n inside a link text would trip the parser. Therfore, documenting link with\n `[`SmallVec<[T; INLINE_CAPACITY]>`]` and then [`SmallVec<[T; INLINE_CAPACITY]>`]: SmallVec\n would fail.\n\n **Examples:**\n ```rust\n /// Do something with the foo_bar parameter. See also\n /// that::other::module::foo.\n // ^ `foo_bar` and `that::other::module::foo` should be ticked.\n fn doit(foo_bar: usize) {}\n ```\n\n ```rust\n // Link text with `[]` brackets should be written as following:\n /// Consume the array and return the inner\n /// [`SmallVec<[T; INLINE_CAPACITY]>`][SmallVec].\n /// [SmallVec]: SmallVec\n fn main() {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "missing_safety_doc", - "id_span": { - "path": "src/doc.rs", - "line": 97 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for the doc comments of publicly visible unsafe functions and warns if there is no `# Safety` section.\n\n **Why is this bad?** Unsafe functions should document their safety\n preconditions, so that users can be sure they are using them safely.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust\n# type Universe = ();\n /// This function should really be documented\n pub unsafe fn start_apocalypse(u: &mut Universe) {\n unimplemented!();\n }\n ```\n\n At least write a line about safety:\n\n ```rust\n# type Universe = ();\n /// # Safety\n ///\n /// This function should not be called before the horsemen are ready.\n pub unsafe fn start_apocalypse(u: &mut Universe) {\n unimplemented!();\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "missing_errors_doc", - "id_span": { - "path": "src/doc.rs", - "line": 126 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks the doc comments of publicly visible functions that return a `Result` type and warns if there is no `# Errors` section.\n\n **Why is this bad?** Documenting the type of errors that can be returned from a\n function can help callers write code to handle the errors appropriately.\n\n **Known problems:** None.\n\n **Examples:**\n\n Since the following function returns a `Result` it has an `# Errors` section in\n its doc comment:\n\n ```rust\n# use std::io;\n /// # Errors\n ///\n /// Will return `Err` if `filename` does not exist or the user does not have\n /// permission to read it.\n pub fn read(filename: String) -> io::Result {\n unimplemented!();\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "missing_panics_doc", - "id_span": { - "path": "src/doc.rs", - "line": 157 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks the doc comments of publicly visible functions that may panic and warns if there is no `# Panics` section.\n\n **Why is this bad?** Documenting the scenarios in which panicking occurs\n can help callers who do not want to panic to avoid those situations.\n\n **Known problems:** None.\n\n **Examples:**\n\n Since the following function may panic it has a `# Panics` section in\n its doc comment:\n\n ```rust\n /// # Panics\n ///\n /// Will panic if y is 0\n pub fn divide_by(x: i32, y: i32) -> i32 {\n if y == 0 {\n panic!(\"Cannot divide by 0\")\n } else {\n x / y\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "needless_doctest_main", - "id_span": { - "path": "src/doc.rs", - "line": 185 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `fn main() { .. }` in doctests\n **Why is this bad?** The test can be shorter (and likely more readable)\n if the `fn main()` is left implicit.\n\n **Known problems:** None.\n\n **Examples:**\n ``````rust\n /// An example of a doctest with a `main()` function\n ///\n /// # Examples\n ///\n /// ```\n /// fn main() {\n /// // this needs not be in an `fn`\n /// }\n /// ```\n fn needless_main() {\n unimplemented!();\n }\n ``````\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "double_comparisons", - "id_span": { - "path": "src/double_comparison.rs", - "line": 33 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for double comparisons that could be simplified to a single expression.\n\n **Why is this bad?** Readability.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let x = 1;\n # let y = 2;\n if x == y || x < y {}\n ```\n\n Could be written as:\n\n ```rust\n # let x = 1;\n # let y = 2;\n if x <= y {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "double_parens", - "id_span": { - "path": "src/double_parens.rs", - "line": 35 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for unnecessary double parentheses.\n **Why is this bad?** This makes code harder to read and might indicate a\n mistake.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n fn simple_double_parens() -> i32 {\n ((0))\n }\n\n // Good\n fn simple_no_parens() -> i32 {\n 0\n }\n\n // or\n\n # fn foo(bar: usize) {}\n // Bad\n foo((0));\n\n // Good\n foo(0);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "drop_ref", - "id_span": { - "path": "src/drop_forget_ref.rs", - "line": 26 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for calls to `std::mem::drop` with a reference instead of an owned value.\n\n **Why is this bad?** Calling `drop` on a reference will only drop the\n reference itself, which is a no-op. It will not call the `drop` method (from\n the `Drop` trait implementation) on the underlying referenced value, which\n is likely what was intended.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n let mut lock_guard = mutex.lock();\n std::mem::drop(&lock_guard) // Should have been drop(lock_guard), mutex\n // still locked\n operation_that_requires_mutex_to_be_unlocked();\n ```\n", - "applicability": null - }, - { - "id": "forget_ref", - "id_span": { - "path": "src/drop_forget_ref.rs", - "line": 47 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for calls to `std::mem::forget` with a reference instead of an owned value.\n\n **Why is this bad?** Calling `forget` on a reference will only forget the\n reference itself, which is a no-op. It will not forget the underlying\n referenced\n value, which is likely what was intended.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = Box::new(1);\n std::mem::forget(&x) // Should have been forget(x), x will still be dropped\n ```\n", - "applicability": null - }, - { - "id": "drop_copy", - "id_span": { - "path": "src/drop_forget_ref.rs", - "line": 68 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for calls to `std::mem::drop` with a value that derives the Copy trait\n\n **Why is this bad?** Calling `std::mem::drop` [does nothing for types that\n implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html), since the\n value will be copied and moved into the function on invocation.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x: i32 = 42; // i32 implements Copy\n std::mem::drop(x) // A copy of x is passed to the function, leaving the\n // original unaffected\n ```\n", - "applicability": null - }, - { - "id": "forget_copy", - "id_span": { - "path": "src/drop_forget_ref.rs", - "line": 95 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for calls to `std::mem::forget` with a value that derives the Copy trait\n\n **Why is this bad?** Calling `std::mem::forget` [does nothing for types that\n implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html) since the\n value will be copied and moved into the function on invocation.\n\n An alternative, but also valid, explanation is that Copy types do not\n implement\n the Drop trait, which means they have no destructors. Without a destructor,\n there\n is nothing for `std::mem::forget` to ignore.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x: i32 = 42; // i32 implements Copy\n std::mem::forget(x) // A copy of x is passed to the function, leaving the\n // original unaffected\n ```\n", - "applicability": null - }, - { - "id": "duration_subsec", - "id_span": { - "path": "src/duration_subsec.rs", - "line": 34 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for calculation of subsecond microseconds or milliseconds from other `Duration` methods.\n\n **Why is this bad?** It's more concise to call `Duration::subsec_micros()` or\n `Duration::subsec_millis()` than to calculate them.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # use std::time::Duration;\n let dur = Duration::new(5, 0);\n\n // Bad\n let _micros = dur.subsec_nanos() / 1_000;\n let _millis = dur.subsec_nanos() / 1_000_000;\n\n // Good\n let _micros = dur.subsec_micros();\n let _millis = dur.subsec_millis();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "else_if_without_else", - "id_span": { - "path": "src/else_if_without_else.rs", - "line": 44 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of if expressions with an `else if` branch, but without a final `else` branch.\n\n **Why is this bad?** Some coding guidelines require this (e.g., MISRA-C:2004 Rule 14.10).\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # fn a() {}\n # fn b() {}\n # let x: i32 = 1;\n if x.is_positive() {\n a();\n } else if x.is_negative() {\n b();\n }\n ```\n\n Could be written:\n\n ```rust\n # fn a() {}\n # fn b() {}\n # let x: i32 = 1;\n if x.is_positive() {\n a();\n } else if x.is_negative() {\n b();\n } else {\n // We don't care about zero.\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "empty_enum", - "id_span": { - "path": "src/empty_enum.rs", - "line": 38 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for `enum`s with no variants.\n As of this writing, the `never_type` is still a\n nightly-only experimental API. Therefore, this lint is only triggered\n if the `never_type` is enabled.\n\n **Why is this bad?** If you want to introduce a type which\n can't be instantiated, you should use `!` (the primitive type \"never\"),\n or a wrapper around it, because `!` has more extensive\n compiler support (type inference, etc...) and wrappers\n around it are the conventional way to define an uninhabited type.\n For further information visit [never type documentation](https://doc.rust-lang.org/std/primitive.never.html)\n\n\n **Known problems:** None.\n\n **Example:**\n\n Bad:\n ```rust\n enum Test {}\n ```\n\n Good:\n ```rust\n #![feature(never_type)]\n\n struct Test(!);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "map_entry", - "id_span": { - "path": "src/entry.rs", - "line": 48 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for uses of `contains_key` + `insert` on `HashMap` or `BTreeMap`.\n\n **Why is this bad?** Using `entry` is more efficient.\n\n **Known problems:** Some false negatives, eg.:\n ```rust\n # use std::collections::HashMap;\n # let mut map = HashMap::new();\n # let v = 1;\n # let k = 1;\n if !map.contains_key(&k) {\n map.insert(k.clone(), v);\n }\n ```\n\n **Example:**\n ```rust\n # use std::collections::HashMap;\n # let mut map = HashMap::new();\n # let k = 1;\n # let v = 1;\n if !map.contains_key(&k) {\n map.insert(k, v);\n }\n ```\n can both be rewritten as:\n ```rust\n # use std::collections::HashMap;\n # let mut map = HashMap::new();\n # let k = 1;\n # let v = 1;\n map.entry(k).or_insert(v);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "enum_clike_unportable_variant", - "id_span": { - "path": "src/enum_clike.rs", - "line": 31 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for C-like enumerations that are `repr(isize/usize)` and have values that don't fit into an `i32`.\n\n **Why is this bad?** This will truncate the variant value on 32 bit\n architectures, but works fine on 64 bit.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # #[cfg(target_pointer_width = \"64\")]\n #[repr(usize)]\n enum NonPortable {\n X = 0x1_0000_0000,\n Y = 0,\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "enum_variant_names", - "id_span": { - "path": "src/enum_variants.rs", - "line": 36 - }, - "group": "clippy::style", - "docs": " **What it does:** Detects enumeration variants that are prefixed or suffixed by the same characters.\n\n **Why is this bad?** Enumeration variant names should specify their variant,\n not repeat the enumeration name.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n enum Cake {\n BlackForestCake,\n HummingbirdCake,\n BattenbergCake,\n }\n ```\n Could be written as:\n ```rust\n enum Cake {\n BlackForest,\n Hummingbird,\n Battenberg,\n }\n ```\n", - "applicability": null - }, - { - "id": "pub_enum_variant_names", - "id_span": { - "path": "src/enum_variants.rs", - "line": 66 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Detects public enumeration variants that are prefixed or suffixed by the same characters.\n\n **Why is this bad?** Public enumeration variant names should specify their variant,\n not repeat the enumeration name.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n pub enum Cake {\n BlackForestCake,\n HummingbirdCake,\n BattenbergCake,\n }\n ```\n Could be written as:\n ```rust\n pub enum Cake {\n BlackForest,\n Hummingbird,\n Battenberg,\n }\n ```\n", - "applicability": null - }, - { - "id": "module_name_repetitions", - "id_span": { - "path": "src/enum_variants.rs", - "line": 91 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Detects type names that are prefixed or suffixed by the containing module's name.\n\n **Why is this bad?** It requires the user to type the module name twice.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n mod cake {\n struct BlackForestCake;\n }\n ```\n Could be written as:\n ```rust\n mod cake {\n struct BlackForest;\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "module_inception", - "id_span": { - "path": "src/enum_variants.rs", - "line": 121 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for modules that have the same name as their parent module\n\n **Why is this bad?** A typical beginner mistake is to have `mod foo;` and\n again `mod foo { ..\n }` in `foo.rs`.\n The expectation is that items inside the inner `mod foo { .. }` are then\n available\n through `foo::x`, but they are only available through\n `foo::foo::x`.\n If this is done on purpose, it would be better to choose a more\n representative module name.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n // lib.rs\n mod foo;\n // foo.rs\n mod foo {\n ...\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "eq_op", - "id_span": { - "path": "src/eq_op.rs", - "line": 34 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for equal operands to comparison, logical and bitwise, difference and division binary operators (`==`, `>`, etc., `&&`,\n `||`, `&`, `|`, `^`, `-` and `/`).\n\n **Why is this bad?** This is usually just a typo or a copy and paste error.\n\n **Known problems:** False negatives: We had some false positives regarding\n calls (notably [racer](https://github.com/phildawes/racer) had one instance\n of `x.pop() && x.pop()`), so we removed matching any function or method\n calls. We may introduce a list of known pure functions in the future.\n\n **Example:**\n ```rust\n # let x = 1;\n if x + 1 == x + 1 {}\n ```\n or\n ```rust\n # let a = 3;\n # let b = 4;\n assert_eq!(a, a);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "op_ref", - "id_span": { - "path": "src/eq_op.rs", - "line": 56 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for arguments to `==` which have their address taken to satisfy a bound\n and suggests to dereference the other argument instead\n\n **Why is this bad?** It is more idiomatic to dereference the other argument.\n\n **Known problems:** None\n\n **Example:**\n ```ignore\n // Bad\n &x == y\n\n // Good\n x == *y\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "erasing_op", - "id_span": { - "path": "src/erasing_op.rs", - "line": 25 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for erasing operations, e.g., `x * 0`.\n **Why is this bad?** The whole expression can be replaced by zero.\n This is most likely not the intended outcome and should probably be\n corrected\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = 1;\n 0 / x;\n 0 * x;\n x & 0;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "boxed_local", - "id_span": { - "path": "src/escape.rs", - "line": 43 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for usage of `Box` where an unboxed `T` would work fine.\n\n **Why is this bad?** This is an unnecessary allocation, and bad for\n performance. It is only necessary to allocate if you wish to move the box\n into something.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # fn foo(bar: usize) {}\n // Bad\n let x = Box::new(1);\n foo(*x);\n println!(\"{}\", *x);\n\n // Good\n let x = 1;\n foo(x);\n println!(\"{}\", x);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "redundant_closure", - "id_span": { - "path": "src/eta_reduction.rs", - "line": 37 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for closures which just call another function where the function can be called directly. `unsafe` functions or calls where types\n get adjusted are ignored.\n\n **Why is this bad?** Needlessly creating a closure adds code for no benefit\n and gives the optimizer more work.\n\n **Known problems:** If creating the closure inside the closure has a side-\n effect then moving the closure creation out will change when that side-\n effect runs.\n See [#1439](https://github.com/rust-lang/rust-clippy/issues/1439) for more details.\n\n **Example:**\n ```rust,ignore\n // Bad\n xs.map(|x| foo(x))\n\n // Good\n xs.map(foo)\n ```\n where `foo(_)` is a plain function that takes the exact argument type of\n `x`.\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "redundant_closure_for_method_calls", - "id_span": { - "path": "src/eta_reduction.rs", - "line": 61 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for closures which only invoke a method on the closure argument and can be replaced by referencing the method directly.\n\n **Why is this bad?** It's unnecessary to create the closure.\n\n **Known problems:** [#3071](https://github.com/rust-lang/rust-clippy/issues/3071),\n [#3942](https://github.com/rust-lang/rust-clippy/issues/3942),\n [#4002](https://github.com/rust-lang/rust-clippy/issues/4002)\n\n\n **Example:**\n ```rust,ignore\n Some('a').map(|s| s.to_uppercase());\n ```\n may be rewritten as\n ```rust,ignore\n Some('a').map(char::to_uppercase);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "eval_order_dependence", - "id_span": { - "path": "src/eval_order_dependence.rs", - "line": 38 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for a read and a write to the same variable where whether the read occurs before or after the write depends on the evaluation\n order of sub-expressions.\n\n **Why is this bad?** It is often confusing to read. In addition, the\n sub-expression evaluation order for Rust is not well documented.\n\n **Known problems:** Code which intentionally depends on the evaluation\n order, or which is correct for any evaluation order.\n\n **Example:**\n ```rust\n let mut x = 0;\n\n // Bad\n let a = {\n x = 1;\n 1\n } + x;\n // Unclear whether a is 1 or 2.\n\n // Good\n let tmp = {\n x = 1;\n 1\n };\n let a = tmp + x;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "diverging_sub_expression", - "id_span": { - "path": "src/eval_order_dependence.rs", - "line": 62 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for diverging calls that are not match arms or statements.\n\n **Why is this bad?** It is often confusing to read. In addition, the\n sub-expression evaluation order for Rust is not well documented.\n\n **Known problems:** Someone might want to use `some_bool || panic!()` as a\n shorthand.\n\n **Example:**\n ```rust,no_run\n # fn b() -> bool { true }\n # fn c() -> bool { true }\n let a = b() || panic!() || c();\n // `c()` is dead, `panic!()` is only called if `b()` returns `false`\n let x = (a, b, c, panic!());\n // can simply be replaced by `panic!()`\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "struct_excessive_bools", - "id_span": { - "path": "src/excessive_bools.rs", - "line": 40 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for excessive use of bools in structs.\n\n **Why is this bad?** Excessive bools in a struct\n is often a sign that it's used as a state machine,\n which is much better implemented as an enum.\n If it's not the case, excessive bools usually benefit\n from refactoring into two-variant enums for better\n readability and API.\n\n **Known problems:** None.\n\n **Example:**\n Bad:\n ```rust\n struct S {\n is_pending: bool,\n is_processing: bool,\n is_finished: bool,\n }\n ```\n\n Good:\n ```rust\n enum S {\n Pending,\n Processing,\n Finished,\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "fn_params_excessive_bools", - "id_span": { - "path": "src/excessive_bools.rs", - "line": 78 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for excessive use of bools in function definitions.\n\n **Why is this bad?** Calls to such functions\n are confusing and error prone, because it's\n hard to remember argument order and you have\n no type system support to back you up. Using\n two-variant enums instead of bools often makes\n API easier to use.\n\n **Known problems:** None.\n\n **Example:**\n Bad:\n ```rust,ignore\n fn f(is_round: bool, is_hot: bool) { ... }\n ```\n\n Good:\n ```rust,ignore\n enum Shape {\n Round,\n Spiky,\n }\n\n enum Temperature {\n Hot,\n IceCold,\n }\n\n fn f(shape: Shape, temperature: Temperature) { ... }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "exhaustive_enums", - "id_span": { - "path": "src/exhaustive_items.rs", - "line": 34 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Warns on any exported `enum`s that are not tagged `#[non_exhaustive]`\n **Why is this bad?** Exhaustive enums are typically fine, but a project which does\n not wish to make a stability commitment around exported enums may wish to\n disable them by default.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n enum Foo {\n Bar,\n Baz\n }\n ```\n Use instead:\n ```rust\n #[non_exhaustive]\n enum Foo {\n Bar,\n Baz\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "exhaustive_structs", - "id_span": { - "path": "src/exhaustive_items.rs", - "line": 64 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Warns on any exported `structs`s that are not tagged `#[non_exhaustive]`\n **Why is this bad?** Exhaustive structs are typically fine, but a project which does\n not wish to make a stability commitment around exported structs may wish to\n disable them by default.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n struct Foo {\n bar: u8,\n baz: String,\n }\n ```\n Use instead:\n ```rust\n #[non_exhaustive]\n struct Foo {\n bar: u8,\n baz: String,\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "exit", - "id_span": { - "path": "src/exit.rs", - "line": 20 - }, - "group": "clippy::restriction", - "docs": " **What it does:** `exit()` terminates the program and doesn't provide a stack trace.\n\n **Why is this bad?** Ideally a program is terminated by finishing\n the main function.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n std::process::exit(0)\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "explicit_write", - "id_span": { - "path": "src/explicit_write.rs", - "line": 25 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `write!()` / `writeln()!` which can be replaced with `(e)print!()` / `(e)println!()`\n\n **Why is this bad?** Using `(e)println! is clearer and more concise\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # use std::io::Write;\n # let bar = \"furchtbar\";\n // this would be clearer as `eprintln!(\"foo: {:?}\", bar);`\n writeln!(&mut std::io::stderr(), \"foo: {:?}\", bar).unwrap();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "fallible_impl_from", - "id_span": { - "path": "src/fallible_impl_from.rs", - "line": 45 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Checks for impls of `From<..>` that contain `panic!()` or `unwrap()`\n **Why is this bad?** `TryFrom` should be used if there's a possibility of failure.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n struct Foo(i32);\n\n // Bad\n impl From for Foo {\n fn from(s: String) -> Self {\n Foo(s.parse().unwrap())\n }\n }\n ```\n\n ```rust\n // Good\n struct Foo(i32);\n\n use std::convert::TryFrom;\n impl TryFrom for Foo {\n type Error = ();\n fn try_from(s: String) -> Result {\n if let Ok(parsed) = s.parse() {\n Ok(Foo(parsed))\n } else {\n Err(())\n }\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "float_equality_without_abs", - "id_span": { - "path": "src/float_equality_without_abs.rs", - "line": 37 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for statements of the form `(a - b) < f32::EPSILON` or `(a - b) < f64::EPSILON`. Notes the missing `.abs()`.\n\n **Why is this bad?** The code without `.abs()` is more likely to have a bug.\n\n **Known problems:** If the user can ensure that b is larger than a, the `.abs()` is\n technically unneccessary. However, it will make the code more robust and doesn't have any\n large performance implications. If the abs call was deliberately left out for performance\n reasons, it is probably better to state this explicitly in the code, which then can be done\n with an allow.\n\n **Example:**\n\n ```rust\n pub fn is_roughly_equal(a: f32, b: f32) -> bool {\n (a - b) < f32::EPSILON\n }\n ```\n Use instead:\n ```rust\n pub fn is_roughly_equal(a: f32, b: f32) -> bool {\n (a - b).abs() < f32::EPSILON\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "excessive_precision", - "id_span": { - "path": "src/float_literal.rs", - "line": 30 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for float literals with a precision greater than that supported by the underlying type.\n\n **Why is this bad?** Rust will truncate the literal silently.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let v: f32 = 0.123_456_789_9;\n println!(\"{}\", v); // 0.123_456_789\n\n // Good\n let v: f64 = 0.123_456_789_9;\n println!(\"{}\", v); // 0.123_456_789_9\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "lossy_float_literal", - "id_span": { - "path": "src/float_literal.rs", - "line": 54 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for whole number float literals that cannot be represented as the underlying type without loss.\n\n **Why is this bad?** Rust will silently lose precision during\n conversion to a float.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let _: f32 = 16_777_217.0; // 16_777_216.0\n\n // Good\n let _: f32 = 16_777_216.0;\n let _: f64 = 16_777_217.0;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "imprecise_flops", - "id_span": { - "path": "src/floating_point_arithmetic.rs", - "line": 45 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Looks for floating-point expressions that can be expressed using built-in methods to improve accuracy\n at the cost of performance.\n\n **Why is this bad?** Negatively impacts accuracy.\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n let a = 3f32;\n let _ = a.powf(1.0 / 3.0);\n let _ = (1.0 + a).ln();\n let _ = a.exp() - 1.0;\n ```\n\n is better expressed as\n\n ```rust\n let a = 3f32;\n let _ = a.cbrt();\n let _ = a.ln_1p();\n let _ = a.exp_m1();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "suboptimal_flops", - "id_span": { - "path": "src/floating_point_arithmetic.rs", - "line": 102 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Looks for floating-point expressions that can be expressed using built-in methods to improve both\n accuracy and performance.\n\n **Why is this bad?** Negatively impacts accuracy and performance.\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n use std::f32::consts::E;\n\n let a = 3f32;\n let _ = (2f32).powf(a);\n let _ = E.powf(a);\n let _ = a.powf(1.0 / 2.0);\n let _ = a.log(2.0);\n let _ = a.log(10.0);\n let _ = a.log(E);\n let _ = a.powf(2.0);\n let _ = a * 2.0 + 4.0;\n let _ = if a < 0.0 {\n -a\n } else {\n a\n };\n let _ = if a < 0.0 {\n a\n } else {\n -a\n };\n ```\n\n is better expressed as\n\n ```rust\n use std::f32::consts::E;\n\n let a = 3f32;\n let _ = a.exp2();\n let _ = a.exp();\n let _ = a.sqrt();\n let _ = a.log2();\n let _ = a.log10();\n let _ = a.ln();\n let _ = a.powi(2);\n let _ = a.mul_add(2.0, 4.0);\n let _ = a.abs();\n let _ = -a.abs();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "useless_format", - "id_span": { - "path": "src/format.rs", - "line": 37 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for the use of `format!(\"string literal with no argument\")` and `format!(\"{}\", foo)` where `foo` is a string.\n\n **Why is this bad?** There is no point of doing that. `format!(\"foo\")` can\n be replaced by `\"foo\".to_owned()` if you really need a `String`. The even\n worse `&format!(\"foo\")` is often encountered in the wild. `format!(\"{}\",\n foo)` can be replaced by `foo.clone()` if `foo: String` or `foo.to_owned()`\n if `foo: &str`.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust\n\n // Bad\n # let foo = \"foo\";\n format!(\"{}\", foo);\n\n // Good\n format!(\"foo\");\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "suspicious_assignment_formatting", - "id_span": { - "path": "src/formatting.rs", - "line": 22 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for use of the non-existent `=*`, `=!` and `=-` operators.\n\n **Why is this bad?** This is either a typo of `*=`, `!=` or `-=` or\n confusing.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n a =- 42; // confusing, should it be `a -= 42` or `a = -42`?\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "suspicious_unary_op_formatting", - "id_span": { - "path": "src/formatting.rs", - "line": 44 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks the formatting of a unary operator on the right hand side of a binary operator. It lints if there is no space between the binary and unary operators,\n but there is a space between the unary and its operand.\n\n **Why is this bad?** This is either a typo in the binary operator or confusing.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n if foo <- 30 { // this should be `foo < -30` but looks like a different operator\n }\n\n if foo &&! bar { // this should be `foo && !bar` but looks like a different operator\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "suspicious_else_formatting", - "id_span": { - "path": "src/formatting.rs", - "line": 80 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for formatting of `else`. It lints if the `else` is followed immediately by a newline or the `else` seems to be missing.\n\n **Why is this bad?** This is probably some refactoring remnant, even if the\n code is correct, it might look confusing.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n if foo {\n } { // looks like an `else` is missing here\n }\n\n if foo {\n } if bar { // looks like an `else` is missing here\n }\n\n if foo {\n } else\n\n { // this is the `else` block of the previous `if`, but should it be?\n }\n\n if foo {\n } else\n\n if bar { // this is the `else` block of the previous `if`, but should it be?\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "possible_missing_comma", - "id_span": { - "path": "src/formatting.rs", - "line": 100 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for possible missing comma in an array. It lints if an array element is a binary operator expression and it lies on two lines.\n\n **Why is this bad?** This could lead to unexpected results.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n let a = &[\n -1, -2, -3 // <= no comma here\n -4, -5, -6\n ];\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "from_over_into", - "id_span": { - "path": "src/from_over_into.rs", - "line": 39 - }, - "group": "clippy::style", - "docs": " **What it does:** Searches for implementations of the `Into<..>` trait and suggests to implement `From<..>` instead.\n **Why is this bad?** According the std docs implementing `From<..>` is preferred since it gives you `Into<..>` for free where the reverse isn't true.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n struct StringWrapper(String);\n\n impl Into for String {\n fn into(self) -> StringWrapper {\n StringWrapper(self)\n }\n }\n ```\n Use instead:\n ```rust\n struct StringWrapper(String);\n\n impl From for StringWrapper {\n fn from(s: String) -> StringWrapper {\n StringWrapper(s)\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "from_str_radix_10", - "id_span": { - "path": "src/from_str_radix_10.rs", - "line": 37 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for function invocations of the form `primitive::from_str_radix(s, 10)`\n\n **Why is this bad?**\n This specific common use case can be rewritten as `s.parse::()`\n (and in most cases, the turbofish can be removed), which reduces code length\n and complexity.\n\n **Known problems:**\n This lint may suggest using (&).parse() instead of .parse() directly\n in some cases, which is correct but adds unnecessary complexity to the code.\n\n **Example:**\n\n ```ignore\n let input: &str = get_input();\n let num = u16::from_str_radix(input, 10)?;\n ```\n Use instead:\n ```ignore\n let input: &str = get_input();\n let num: u16 = input.parse()?;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "too_many_arguments", - "id_span": { - "path": "src/functions.rs", - "line": 39 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for functions with too many parameters.\n **Why is this bad?** Functions with lots of parameters are considered bad\n style and reduce readability (“what does the 5th parameter mean?”). Consider\n grouping some parameters into a new type.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # struct Color;\n fn foo(x: u32, y: u32, name: &str, c: Color, w: f32, h: f32, a: f32, b: f32) {\n // ..\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "too_many_lines", - "id_span": { - "path": "src/functions.rs", - "line": 62 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for functions with a large amount of lines.\n **Why is this bad?** Functions with a lot of lines are harder to understand\n due to having to look at a larger amount of code to understand what the\n function is doing. Consider splitting the body of the function into\n multiple functions.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn im_too_long() {\n println!(\"\");\n // ... 100 more LoC\n println!(\"\");\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "not_unsafe_ptr_arg_deref", - "id_span": { - "path": "src/functions.rs", - "line": 96 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for public functions that dereference raw pointer arguments but are not marked unsafe.\n\n **Why is this bad?** The function should probably be marked `unsafe`, since\n for an arbitrary raw pointer, there is no way of telling for sure if it is\n valid.\n\n **Known problems:**\n\n * It does not check functions recursively so if the pointer is passed to a\n private non-`unsafe` function which does the dereferencing, the lint won't\n trigger.\n * It only checks for arguments whose type are raw pointers, not raw pointers\n got from an argument in some other way (`fn foo(bar: &[*const u8])` or\n `some_argument.get_raw_ptr()`).\n\n **Example:**\n ```rust,ignore\n // Bad\n pub fn foo(x: *const u8) {\n println!(\"{}\", unsafe { *x });\n }\n\n // Good\n pub unsafe fn foo(x: *const u8) {\n println!(\"{}\", unsafe { *x });\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "must_use_unit", - "id_span": { - "path": "src/functions.rs", - "line": 117 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for a [`#[must_use]`] attribute on unit-returning functions and methods.\n\n [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute\n\n **Why is this bad?** Unit values are useless. The attribute is likely\n a remnant of a refactoring that removed the return type.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust\n #[must_use]\n fn useless() { }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "double_must_use", - "id_span": { - "path": "src/functions.rs", - "line": 142 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for a [`#[must_use]`] attribute without further information on functions and methods that return a type already\n marked as `#[must_use]`.\n\n [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute\n\n **Why is this bad?** The attribute isn't needed. Not using the result\n will already be reported. Alternatively, one can add some text to the\n attribute to improve the lint message.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust\n #[must_use]\n fn double_must_use() -> Result<(), ()> {\n unimplemented!();\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "must_use_candidate", - "id_span": { - "path": "src/functions.rs", - "line": 170 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for public functions that have no [`#[must_use]`] attribute, but return something not already marked\n must-use, have no mutable arg and mutate no statics.\n\n [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute\n\n **Why is this bad?** Not bad at all, this lint just shows places where\n you could add the attribute.\n\n **Known problems:** The lint only checks the arguments for mutable\n types without looking if they are actually changed. On the other hand,\n it also ignores a broad range of potentially interesting side effects,\n because we cannot decide whether the programmer intends the function to\n be called for the side effect or the result. Expect many false\n positives. At least we don't lint if the result type is unit or already\n `#[must_use]`.\n\n **Examples:**\n ```rust\n // this could be annotated with `#[must_use]`.\n fn id(t: T) -> T { t }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "result_unit_err", - "id_span": { - "path": "src/functions.rs", - "line": 216 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for public functions that return a `Result` with an `Err` type of `()`. It suggests using a custom type that\n implements [`std::error::Error`].\n\n **Why is this bad?** Unit does not implement `Error` and carries no\n further information about what went wrong.\n\n **Known problems:** Of course, this lint assumes that `Result` is used\n for a fallible operation (which is after all the intended use). However\n code may opt to (mis)use it as a basic two-variant-enum. In that case,\n the suggestion is misguided, and the code should use a custom enum\n instead.\n\n **Examples:**\n ```rust\n pub fn read_u8() -> Result { Err(()) }\n ```\n should become\n ```rust,should_panic\n use std::fmt;\n\n #[derive(Debug)]\n pub struct EndOfStream;\n\n impl fmt::Display for EndOfStream {\n fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n write!(f, \"End of Stream\")\n }\n }\n\n impl std::error::Error for EndOfStream { }\n\n pub fn read_u8() -> Result { Err(EndOfStream) }\n# fn main() {\n# read_u8().unwrap();\n# }\n ```\n\n Note that there are crates that simplify creating the error type, e.g.\n [`thiserror`](https://docs.rs/thiserror).\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "future_not_send", - "id_span": { - "path": "src/future_not_send.rs", - "line": 44 - }, - "group": "clippy::nursery", - "docs": " **What it does:** This lint requires Future implementations returned from functions and methods to implement the `Send` marker trait. It is mostly\n used by library authors (public and internal) that target an audience where\n multithreaded executors are likely to be used for running these Futures.\n\n **Why is this bad?** A Future implementation captures some state that it\n needs to eventually produce its final value. When targeting a multithreaded\n executor (which is the norm on non-embedded devices) this means that this\n state may need to be transported to other threads, in other words the\n whole Future needs to implement the `Send` marker trait. If it does not,\n then the resulting Future cannot be submitted to a thread pool in the\n end user’s code.\n\n Especially for generic functions it can be confusing to leave the\n discovery of this problem to the end user: the reported error location\n will be far from its cause and can in many cases not even be fixed without\n modifying the library where the offending Future implementation is\n produced.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n async fn not_send(bytes: std::rc::Rc<[u8]>) {}\n ```\n Use instead:\n ```rust\n async fn is_send(bytes: std::sync::Arc<[u8]>) {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "get_last_with_len", - "id_span": { - "path": "src/get_last_with_len.rs", - "line": 40 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for using `x.get(x.len() - 1)` instead of `x.last()`.\n\n **Why is this bad?** Using `x.last()` is easier to read and has the same\n result.\n\n Note that using `x[x.len() - 1]` is semantically different from\n `x.last()`. Indexing into the array will panic on out-of-bounds\n accesses, while `x.get()` and `x.last()` will return `None`.\n\n There is another lint (get_unwrap) that covers the case of using\n `x.get(index).unwrap()` instead of `x[index]`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let x = vec![2, 3, 5];\n let last_element = x.get(x.len() - 1);\n\n // Good\n let x = vec![2, 3, 5];\n let last_element = x.last();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "identity_op", - "id_span": { - "path": "src/identity_op.rs", - "line": 24 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for identity operations, e.g., `x + 0`.\n **Why is this bad?** This code can be removed without changing the\n meaning. So it just obscures what's going on. Delete it mercilessly.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let x = 1;\n x / 1 + 0 * 1 - 0 | 0;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "if_let_mutex", - "id_span": { - "path": "src/if_let_mutex.rs", - "line": 36 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for `Mutex::lock` calls in `if let` expression with lock calls in any of the else blocks.\n\n **Why is this bad?** The Mutex lock remains held for the whole\n `if let ... else` block and deadlocks.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,ignore\n if let Ok(thing) = mutex.lock() {\n do_thing();\n } else {\n mutex.lock();\n }\n ```\n Should be written\n ```rust,ignore\n let locked = mutex.lock();\n if let Ok(thing) = locked {\n do_thing(thing);\n } else {\n use_locked(locked);\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "if_let_some_result", - "id_span": { - "path": "src/if_let_some_result.rs", - "line": 34 - }, - "group": "clippy::style", - "docs": " **What it does:*** Checks for unnecessary `ok()` in if let.\n **Why is this bad?** Calling `ok()` in if let is unnecessary, instead match\n on `Ok(pat)`\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n for i in iter {\n if let Some(value) = i.parse().ok() {\n vec.push(value)\n }\n }\n ```\n Could be written:\n\n ```ignore\n for i in iter {\n if let Ok(value) = i.parse() {\n vec.push(value)\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "if_not_else", - "id_span": { - "path": "src/if_not_else.rs", - "line": 43 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for usage of `!` or `!=` in an if condition with an else branch.\n\n **Why is this bad?** Negations reduce the readability of statements.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let v: Vec = vec![];\n # fn a() {}\n # fn b() {}\n if !v.is_empty() {\n a()\n } else {\n b()\n }\n ```\n\n Could be written:\n\n ```rust\n # let v: Vec = vec![];\n # fn a() {}\n # fn b() {}\n if v.is_empty() {\n b()\n } else {\n a()\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "implicit_return", - "id_span": { - "path": "src/implicit_return.rs", - "line": 33 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for missing return statements at the end of a block.\n **Why is this bad?** Actually omitting the return keyword is idiomatic Rust code. Programmers\n coming from other languages might prefer the expressiveness of `return`. It's possible to miss\n the last returning statement because the only difference is a missing `;`. Especially in bigger\n code with multiple return paths having a `return` keyword makes it easier to find the\n corresponding statements.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn foo(x: usize) -> usize {\n x\n }\n ```\n add return\n ```rust\n fn foo(x: usize) -> usize {\n return x;\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "implicit_saturating_sub", - "id_span": { - "path": "src/implicit_saturating_sub.rs", - "line": 32 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for implicit saturating subtraction.\n **Why is this bad?** Simplicity and readability. Instead we can easily use an builtin function.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let end: u32 = 10;\n let start: u32 = 5;\n\n let mut i: u32 = end - start;\n\n // Bad\n if i != 0 {\n i -= 1;\n }\n\n // Good\n i = i.saturating_sub(1);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "inconsistent_struct_constructor", - "id_span": { - "path": "src/inconsistent_struct_constructor.rs", - "line": 58 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for struct constructors where the order of the field init shorthand in the constructor is inconsistent with the order in the struct definition.\n\n **Why is this bad?** Since the order of fields in a constructor doesn't affect the\n resulted instance as the below example indicates,\n\n ```rust\n #[derive(Debug, PartialEq, Eq)]\n struct Foo {\n x: i32,\n y: i32,\n }\n let x = 1;\n let y = 2;\n\n // This assertion never fails.\n assert_eq!(Foo { x, y }, Foo { y, x });\n ```\n\n inconsistent order means nothing and just decreases readability and consistency.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n struct Foo {\n x: i32,\n y: i32,\n }\n let x = 1;\n let y = 2;\n Foo { y, x };\n ```\n\n Use instead:\n ```rust\n # struct Foo {\n # x: i32,\n # y: i32,\n # }\n # let x = 1;\n # let y = 2;\n Foo { x, y };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "out_of_bounds_indexing", - "id_span": { - "path": "src/indexing_slicing.rs", - "line": 32 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for out of bounds array indexing with a constant index.\n\n **Why is this bad?** This will always panic at runtime.\n\n **Known problems:** Hopefully none.\n\n **Example:**\n ```no_run\n # #![allow(const_err)]\n let x = [1, 2, 3, 4];\n\n // Bad\n x[9];\n &x[2..9];\n\n // Good\n x[0];\n x[3];\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "indexing_slicing", - "id_span": { - "path": "src/indexing_slicing.rs", - "line": 81 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of indexing or slicing. Arrays are special cases, this lint does report on arrays if we can tell that slicing operations are in bounds and does not\n lint on constant `usize` indexing on arrays because that is handled by rustc's `const_err` lint.\n\n **Why is this bad?** Indexing and slicing can panic at runtime and there are\n safe alternatives.\n\n **Known problems:** Hopefully none.\n\n **Example:**\n ```rust,no_run\n // Vector\n let x = vec![0; 5];\n\n // Bad\n x[2];\n &x[2..100];\n &x[2..];\n &x[..100];\n\n // Good\n x.get(2);\n x.get(2..100);\n x.get(2..);\n x.get(..100);\n\n // Array\n let y = [0, 1, 2, 3];\n\n // Bad\n &y[10..100];\n &y[10..];\n &y[..100];\n\n // Good\n &y[2..];\n &y[..2];\n &y[0..3];\n y.get(10);\n y.get(10..100);\n y.get(10..);\n y.get(..100);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "infinite_iter", - "id_span": { - "path": "src/infinite_iter.rs", - "line": 21 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for iteration that is guaranteed to be infinite.\n **Why is this bad?** While there may be places where this is acceptable\n (e.g., in event streams), in most cases this is simply an error.\n\n **Known problems:** None.\n\n **Example:**\n ```no_run\n use std::iter;\n\n iter::repeat(1_u8).collect::>();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "maybe_infinite_iter", - "id_span": { - "path": "src/infinite_iter.rs", - "line": 40 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for iteration that may be infinite.\n **Why is this bad?** While there may be places where this is acceptable\n (e.g., in event streams), in most cases this is simply an error.\n\n **Known problems:** The code may have a condition to stop iteration, but\n this lint is not clever enough to analyze it.\n\n **Example:**\n ```rust\n let infinite_iter = 0..;\n [0..].iter().zip(infinite_iter.take_while(|x| *x > 5));\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "multiple_inherent_impl", - "id_span": { - "path": "src/inherent_impl.rs", - "line": 37 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for multiple inherent implementations of a struct\n **Why is this bad?** Splitting the implementation of a type makes the code harder to navigate.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n struct X;\n impl X {\n fn one() {}\n }\n impl X {\n fn other() {}\n }\n ```\n\n Could be written:\n\n ```rust\n struct X;\n impl X {\n fn one() {}\n fn other() {}\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "inherent_to_string", - "id_span": { - "path": "src/inherent_to_string.rs", - "line": 44 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for the definition of inherent methods with a signature of `to_string(&self) -> String`.\n **Why is this bad?** This method is also implicitly defined if a type implements the `Display` trait. As the functionality of `Display` is much more versatile, it should be preferred.\n\n **Known problems:** None\n\n ** Example:**\n\n ```rust\n // Bad\n pub struct A;\n\n impl A {\n pub fn to_string(&self) -> String {\n \"I am A\".to_string()\n }\n }\n ```\n\n ```rust\n // Good\n use std::fmt;\n\n pub struct A;\n\n impl fmt::Display for A {\n fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n write!(f, \"I am A\")\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "inherent_to_string_shadow_display", - "id_span": { - "path": "src/inherent_to_string.rs", - "line": 89 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for the definition of inherent methods with a signature of `to_string(&self) -> String` and if the type implementing this method also implements the `Display` trait.\n **Why is this bad?** This method is also implicitly defined if a type implements the `Display` trait. The less versatile inherent method will then shadow the implementation introduced by `Display`.\n\n **Known problems:** None\n\n ** Example:**\n\n ```rust\n // Bad\n use std::fmt;\n\n pub struct A;\n\n impl A {\n pub fn to_string(&self) -> String {\n \"I am A\".to_string()\n }\n }\n\n impl fmt::Display for A {\n fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n write!(f, \"I am A, too\")\n }\n }\n ```\n\n ```rust\n // Good\n use std::fmt;\n\n pub struct A;\n\n impl fmt::Display for A {\n fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n write!(f, \"I am A\")\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "inline_fn_without_body", - "id_span": { - "path": "src/inline_fn_without_body.rs", - "line": 27 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for `#[inline]` on trait methods without bodies\n **Why is this bad?** Only implementations of trait methods may be inlined.\n The inline attribute is ignored for trait methods without bodies.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n trait Animal {\n #[inline]\n fn name(&self) -> &'static str;\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "int_plus_one", - "id_span": { - "path": "src/int_plus_one.rs", - "line": 31 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `x >= y + 1` or `x - 1 >= y` (and `<=`) in a block\n **Why is this bad?** Readability -- better to use `> y` instead of `>= y + 1`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let x = 1;\n # let y = 1;\n if x >= y + 1 {}\n ```\n\n Could be written as:\n\n ```rust\n # let x = 1;\n # let y = 1;\n if x > y {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "integer_division", - "id_span": { - "path": "src/integer_division.rs", - "line": 26 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for division of integers\n **Why is this bad?** When outside of some very specific algorithms,\n integer division is very often a mistake because it discards the\n remainder.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n let x = 3 / 2;\n println!(\"{}\", x);\n\n // Good\n let x = 3f32 / 2f32;\n println!(\"{}\", x);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "items_after_statements", - "id_span": { - "path": "src/items_after_statements.rs", - "line": 48 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for items declared after some statement in a block.\n **Why is this bad?** Items live for the entire scope they are declared\n in. But statements are processed in order. This might cause confusion as\n it's hard to figure out which item is meant in a statement.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n fn foo() {\n println!(\"cake\");\n }\n\n fn main() {\n foo(); // prints \"foo\"\n fn foo() {\n println!(\"foo\");\n }\n foo(); // prints \"foo\"\n }\n ```\n\n ```rust\n // Good\n fn foo() {\n println!(\"cake\");\n }\n\n fn main() {\n fn foo() {\n println!(\"foo\");\n }\n foo(); // prints \"foo\"\n foo(); // prints \"foo\"\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "large_const_arrays", - "id_span": { - "path": "src/large_const_arrays.rs", - "line": 30 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for large `const` arrays that should be defined as `static` instead.\n\n **Why is this bad?** Performance: const variables are inlined upon use.\n Static items result in only one instance and has a fixed location in memory.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n // Bad\n pub const a = [0u32; 1_000_000];\n\n // Good\n pub static a = [0u32; 1_000_000];\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "large_enum_variant", - "id_span": { - "path": "src/large_enum_variant.rs", - "line": 39 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for large size differences between variants on `enum`s.\n\n **Why is this bad?** Enum size is bounded by the largest variant. Having a\n large variant can penalize the memory layout of that enum.\n\n **Known problems:** This lint obviously cannot take the distribution of\n variants in your running program into account. It is possible that the\n smaller variants make up less than 1% of all instances, in which case\n the overhead is negligible and the boxing is counter-productive. Always\n measure the change this lint suggests.\n\n **Example:**\n\n ```rust\n // Bad\n enum Test {\n A(i32),\n B([i32; 8000]),\n }\n\n // Possibly better\n enum Test2 {\n A(i32),\n B(Box<[i32; 8000]>),\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "large_stack_arrays", - "id_span": { - "path": "src/large_stack_arrays.rs", - "line": 23 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for local arrays that may be too large.\n **Why is this bad?** Large local arrays may cause stack overflow.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n let a = [0u32; 1_000_000];\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "len_zero", - "id_span": { - "path": "src/len_zero.rs", - "line": 41 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for getting the length of something via `.len()` just to compare to zero, and suggests using `.is_empty()` where applicable.\n\n **Why is this bad?** Some structures can answer `.is_empty()` much faster\n than calculating their length. So it is good to get into the habit of using\n `.is_empty()`, and having it is cheap.\n Besides, it makes the intent clearer than a manual comparison in some contexts.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n if x.len() == 0 {\n ..\n }\n if y.len() != 0 {\n ..\n }\n ```\n instead use\n ```ignore\n if x.is_empty() {\n ..\n }\n if !y.is_empty() {\n ..\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "len_without_is_empty", - "id_span": { - "path": "src/len_zero.rs", - "line": 66 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for items that implement `.len()` but not `.is_empty()`.\n\n **Why is this bad?** It is good custom to have both methods, because for\n some data structures, asking about the length will be a costly operation,\n whereas `.is_empty()` can usually answer in constant time. Also it used to\n lead to false positives on the [`len_zero`](#len_zero) lint – currently that\n lint will ignore such entities.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n impl X {\n pub fn len(&self) -> usize {\n ..\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "comparison_to_empty", - "id_span": { - "path": "src/len_zero.rs", - "line": 103 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for comparing to an empty slice such as `\"\"` or `[]`, and suggests using `.is_empty()` where applicable.\n\n **Why is this bad?** Some structures can answer `.is_empty()` much faster\n than checking for equality. So it is good to get into the habit of using\n `.is_empty()`, and having it is cheap.\n Besides, it makes the intent clearer than a manual comparison in some contexts.\n\n **Known problems:** None.\n\n **Example:**\n\n ```ignore\n if s == \"\" {\n ..\n }\n\n if arr == [] {\n ..\n }\n ```\n Use instead:\n ```ignore\n if s.is_empty() {\n ..\n }\n\n if arr.is_empty() {\n ..\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "useless_let_if_seq", - "id_span": { - "path": "src/let_if_seq.rs", - "line": 49 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Checks for variable declarations immediately followed by a conditional affectation.\n\n **Why is this bad?** This is not idiomatic Rust.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n let foo;\n\n if bar() {\n foo = 42;\n } else {\n foo = 0;\n }\n\n let mut baz = None;\n\n if bar() {\n baz = Some(42);\n }\n ```\n\n should be written\n\n ```rust,ignore\n let foo = if bar() {\n 42\n } else {\n 0\n };\n\n let baz = if bar() {\n Some(42)\n } else {\n None\n };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "HasPlaceholders" - } - }, - { - "id": "let_underscore_must_use", - "id_span": { - "path": "src/let_underscore.rs", - "line": 29 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for `let _ = ` where expr is #[must_use]\n\n **Why is this bad?** It's better to explicitly\n handle the value of a #[must_use] expr\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn f() -> Result {\n Ok(0)\n }\n\n let _ = f();\n // is_ok() is marked #[must_use]\n let _ = f().is_ok();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "let_underscore_lock", - "id_span": { - "path": "src/let_underscore.rs", - "line": 56 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for `let _ = sync_lock`\n **Why is this bad?** This statement immediately drops the lock instead of\n extending its lifetime to the end of the scope, which is often not intended.\n To extend lock lifetime to the end of the scope, use an underscore-prefixed\n name instead (i.e. _lock). If you want to explicitly drop the lock,\n `std::mem::drop` conveys your intention better and is less error-prone.\n\n **Known problems:** None.\n\n **Example:**\n\n Bad:\n ```rust,ignore\n let _ = mutex.lock();\n ```\n\n Good:\n ```rust,ignore\n let _lock = mutex.lock();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "let_underscore_drop", - "id_span": { - "path": "src/let_underscore.rs", - "line": 97 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for `let _ = ` where expr has a type that implements `Drop`\n\n **Why is this bad?** This statement immediately drops the initializer\n expression instead of extending its lifetime to the end of the scope, which\n is often not intended. To extend the expression's lifetime to the end of the\n scope, use an underscore-prefixed name instead (i.e. _var). If you want to\n explicitly drop the expression, `std::mem::drop` conveys your intention\n better and is less error-prone.\n\n **Known problems:** None.\n\n **Example:**\n\n Bad:\n ```rust,ignore\n struct Droppable;\n impl Drop for Droppable {\n fn drop(&mut self) {}\n }\n {\n let _ = Droppable;\n // ^ dropped here\n /* more code */\n }\n ```\n\n Good:\n ```rust,ignore\n {\n let _droppable = Droppable;\n /* more code */\n // dropped at end of scope\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "needless_lifetimes", - "id_span": { - "path": "src/lifetimes.rs", - "line": 46 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for lifetime annotations which can be removed by relying on lifetime elision.\n\n **Why is this bad?** The additional lifetimes make the code look more\n complicated, while there is nothing out of the ordinary going on. Removing\n them leads to more readable code.\n\n **Known problems:**\n - We bail out if the function has a `where` clause where lifetimes\n are mentioned due to potenial false positives.\n - Lifetime bounds such as `impl Foo + 'a` and `T: 'a` must be elided with the\n placeholder notation `'_` because the fully elided notation leaves the type bound to `'static`.\n\n **Example:**\n ```rust\n // Bad: unnecessary lifetime annotations\n fn in_and_out<'a>(x: &'a u8, y: u8) -> &'a u8 {\n x\n }\n\n // Good\n fn elided(x: &u8, y: u8) -> &u8 {\n x\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "extra_unused_lifetimes", - "id_span": { - "path": "src/lifetimes.rs", - "line": 74 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for lifetimes in generics that are never used anywhere else.\n\n **Why is this bad?** The additional lifetimes make the code look more\n complicated, while there is nothing out of the ordinary going on. Removing\n them leads to more readable code.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad: unnecessary lifetimes\n fn unused_lifetime<'a>(x: u8) {\n // ..\n }\n\n // Good\n fn no_lifetime(x: u8) {\n // ...\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "unreadable_literal", - "id_span": { - "path": "src/literal_representation.rs", - "line": 33 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Warns if a long integral or floating-point constant does not contain underscores.\n\n **Why is this bad?** Reading long numbers is difficult without separators.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let x: u64 = 61864918973511;\n\n // Good\n let x: u64 = 61_864_918_973_511;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "mistyped_literal_suffixes", - "id_span": { - "path": "src/literal_representation.rs", - "line": 57 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Warns for mistyped suffix in literals\n **Why is this bad?** This is most probably a typo\n\n **Known problems:**\n - Recommends a signed suffix, even though the number might be too big and an unsigned\n suffix is required\n - Does not match on `_127` since that is a valid grouping for decimal and octal numbers\n\n **Example:**\n\n ```rust\n // Probably mistyped\n 2_32;\n\n // Good\n 2_i32;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "inconsistent_digit_grouping", - "id_span": { - "path": "src/literal_representation.rs", - "line": 80 - }, - "group": "clippy::style", - "docs": " **What it does:** Warns if an integral or floating-point constant is grouped inconsistently with underscores.\n\n **Why is this bad?** Readers may incorrectly interpret inconsistently\n grouped digits.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let x: u64 = 618_64_9189_73_511;\n\n // Good\n let x: u64 = 61_864_918_973_511;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "unusual_byte_groupings", - "id_span": { - "path": "src/literal_representation.rs", - "line": 99 - }, - "group": "clippy::style", - "docs": " **What it does:** Warns if hexadecimal or binary literals are not grouped by nibble or byte.\n\n **Why is this bad?** Negatively impacts readability.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let x: u32 = 0xFFF_FFF;\n let y: u8 = 0b01_011_101;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "large_digit_groups", - "id_span": { - "path": "src/literal_representation.rs", - "line": 118 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Warns if the digits of an integral or floating-point constant are grouped into groups that\n are too large.\n\n **Why is this bad?** Negatively impacts readability.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let x: u64 = 6186491_8973511;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "decimal_literal_representation", - "id_span": { - "path": "src/literal_representation.rs", - "line": 136 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Warns if there is a better representation for a numeric literal.\n **Why is this bad?** Especially for big powers of 2 a hexadecimal representation is more\n readable than a decimal representation.\n\n **Known problems:** None.\n\n **Example:**\n\n `255` => `0xFF`\n `65_535` => `0xFFFF`\n `4_042_322_160` => `0xF0F0_F0F0`\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "manual_memcpy", - "id_span": { - "path": "src/loops/mod.rs", - "line": 50 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for for-loops that manually copy items between slices that could be optimized by having a memcpy.\n\n **Why is this bad?** It is not as fast as a memcpy.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let src = vec![1];\n # let mut dst = vec![0; 65];\n for i in 0..src.len() {\n dst[i + 64] = src[i];\n }\n ```\n Could be written as:\n ```rust\n # let src = vec![1];\n # let mut dst = vec![0; 65];\n dst[64..(src.len() + 64)].clone_from_slice(&src[..]);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "needless_range_loop", - "id_span": { - "path": "src/loops/mod.rs", - "line": 78 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for looping over the range of `0..len` of some collection just to get the values by index.\n\n **Why is this bad?** Just iterating the collection itself makes the intent\n more clear and is probably faster.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let vec = vec!['a', 'b', 'c'];\n for i in 0..vec.len() {\n println!(\"{}\", vec[i]);\n }\n ```\n Could be written as:\n ```rust\n let vec = vec!['a', 'b', 'c'];\n for i in vec {\n println!(\"{}\", i);\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "explicit_iter_loop", - "id_span": { - "path": "src/loops/mod.rs", - "line": 107 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for loops on `x.iter()` where `&x` will do, and suggests the latter.\n\n **Why is this bad?** Readability.\n\n **Known problems:** False negatives. We currently only warn on some known\n types.\n\n **Example:**\n ```rust\n // with `y` a `Vec` or slice:\n # let y = vec![1];\n for x in y.iter() {\n // ..\n }\n ```\n can be rewritten to\n ```rust\n # let y = vec![1];\n for x in &y {\n // ..\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "explicit_into_iter_loop", - "id_span": { - "path": "src/loops/mod.rs", - "line": 135 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for loops on `y.into_iter()` where `y` will do, and suggests the latter.\n\n **Why is this bad?** Readability.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n # let y = vec![1];\n // with `y` a `Vec` or slice:\n for x in y.into_iter() {\n // ..\n }\n ```\n can be rewritten to\n ```rust\n # let y = vec![1];\n for x in y {\n // ..\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "iter_next_loop", - "id_span": { - "path": "src/loops/mod.rs", - "line": 158 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for loops on `x.next()`.\n **Why is this bad?** `next()` returns either `Some(value)` if there was a\n value, or `None` otherwise. The insidious thing is that `Option<_>`\n implements `IntoIterator`, so that possibly one value will be iterated,\n leading to some hard to find bugs. No one will want to write such code\n [except to win an Underhanded Rust\n Contest](https://www.reddit.com/r/rust/comments/3hb0wm/underhanded_rust_contest/cu5yuhr).\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n for x in y.next() {\n ..\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "for_loops_over_fallibles", - "id_span": { - "path": "src/loops/mod.rs", - "line": 201 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for `for` loops over `Option` or `Result` values.\n **Why is this bad?** Readability. This is more clearly expressed as an `if\n let`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let opt = Some(1);\n\n // Bad\n for x in opt {\n // ..\n }\n\n // Good\n if let Some(x) = opt {\n // ..\n }\n ```\n\n // or\n\n ```rust\n # let res: Result = Ok(1);\n\n // Bad\n for x in &res {\n // ..\n }\n\n // Good\n if let Ok(x) = res {\n // ..\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "while_let_loop", - "id_span": { - "path": "src/loops/mod.rs", - "line": 230 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Detects `loop + match` combinations that are easier written as a `while let` loop.\n\n **Why is this bad?** The `while let` loop is usually shorter and more\n readable.\n\n **Known problems:** Sometimes the wrong binding is displayed ([#383](https://github.com/rust-lang/rust-clippy/issues/383)).\n\n **Example:**\n ```rust,no_run\n # let y = Some(1);\n loop {\n let x = match y {\n Some(x) => x,\n None => break,\n };\n // .. do something with x\n }\n // is easier written as\n while let Some(x) = y {\n // .. do something with x\n };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "HasPlaceholders" - } - }, - { - "id": "needless_collect", - "id_span": { - "path": "src/loops/mod.rs", - "line": 252 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for functions collecting an iterator when collect is not needed.\n\n **Why is this bad?** `collect` causes the allocation of a new data structure,\n when this allocation may not be needed.\n\n **Known problems:**\n None\n\n **Example:**\n ```rust\n # let iterator = vec![1].into_iter();\n let len = iterator.clone().collect::>().len();\n // should be\n let len = iterator.count();\n ```\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": "MachineApplicable" - } - }, - { - "id": "explicit_counter_loop", - "id_span": { - "path": "src/loops/mod.rs", - "line": 282 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks `for` loops over slices with an explicit counter and suggests the use of `.enumerate()`.\n\n **Why is it bad?** Using `.enumerate()` makes the intent more clear,\n declutters the code and may be faster in some instances.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let v = vec![1];\n # fn bar(bar: usize, baz: usize) {}\n let mut i = 0;\n for item in &v {\n bar(i, *item);\n i += 1;\n }\n ```\n Could be written as\n ```rust\n # let v = vec![1];\n # fn bar(bar: usize, baz: usize) {}\n for (i, item) in v.iter().enumerate() { bar(i, *item); }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "empty_loop", - "id_span": { - "path": "src/loops/mod.rs", - "line": 315 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for empty `loop` expressions.\n **Why is this bad?** These busy loops burn CPU cycles without doing\n anything. It is _almost always_ a better idea to `panic!` than to have\n a busy loop.\n\n If panicking isn't possible, think of the environment and either:\n - block on something\n - sleep the thread for some microseconds\n - yield or pause the thread\n\n For `std` targets, this can be done with\n [`std::thread::sleep`](https://doc.rust-lang.org/std/thread/fn.sleep.html)\n or [`std::thread::yield_now`](https://doc.rust-lang.org/std/thread/fn.yield_now.html).\n\n For `no_std` targets, doing this is more complicated, especially because\n `#[panic_handler]`s can't panic. To stop/pause the thread, you will\n probably need to invoke some target-specific intrinsic. Examples include:\n - [`x86_64::instructions::hlt`](https://docs.rs/x86_64/0.12.2/x86_64/instructions/fn.hlt.html)\n - [`cortex_m::asm::wfi`](https://docs.rs/cortex-m/0.6.3/cortex_m/asm/fn.wfi.html)\n\n **Known problems:** None.\n\n **Example:**\n ```no_run\n loop {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "while_let_on_iterator", - "id_span": { - "path": "src/loops/mod.rs", - "line": 334 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `while let` expressions on iterators.\n **Why is this bad?** Readability. A simple `for` loop is shorter and conveys\n the intent better.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n while let Some(val) = iter() {\n ..\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "for_kv_map", - "id_span": { - "path": "src/loops/mod.rs", - "line": 362 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for iterating a map (`HashMap` or `BTreeMap`) and ignoring either the keys or values.\n\n **Why is this bad?** Readability. There are `keys` and `values` methods that\n can be used to express that don't need the values or keys.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n for (k, _) in &map {\n ..\n }\n ```\n\n could be replaced by\n\n ```ignore\n for k in map.keys() {\n ..\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "never_loop", - "id_span": { - "path": "src/loops/mod.rs", - "line": 383 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for loops that will always `break`, `return` or `continue` an outer loop.\n\n **Why is this bad?** This loop never loops, all it does is obfuscating the\n code.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n loop {\n ..;\n break;\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "mut_range_bound", - "id_span": { - "path": "src/loops/mod.rs", - "line": 403 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for loops which have a range bound that is a mutable variable\n **Why is this bad?** One might think that modifying the mutable variable changes the loop bounds\n\n **Known problems:** None\n\n **Example:**\n ```rust\n let mut foo = 42;\n for i in 0..foo {\n foo -= 1;\n println!(\"{}\", i); // prints numbers from 0 to 42, not 0 to 21\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "while_immutable_condition", - "id_span": { - "path": "src/loops/mod.rs", - "line": 426 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks whether variables used within while loop condition can be (and are) mutated in the body.\n\n **Why is this bad?** If the condition is unchanged, entering the body of the loop\n will lead to an infinite loop.\n\n **Known problems:** If the `while`-loop is in a closure, the check for mutation of the\n condition variables in the body can cause false negatives. For example when only `Upvar` `a` is\n in the condition and only `Upvar` `b` gets mutated in the body, the lint will not trigger.\n\n **Example:**\n ```rust\n let i = 0;\n while i > 10 {\n println!(\"let me loop forever!\");\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "same_item_push", - "id_span": { - "path": "src/loops/mod.rs", - "line": 459 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks whether a for loop is being used to push a constant value into a Vec.\n\n **Why is this bad?** This kind of operation can be expressed more succinctly with\n `vec![item;SIZE]` or `vec.resize(NEW_SIZE, item)` and using these alternatives may also\n have better performance.\n **Known problems:** None\n\n **Example:**\n ```rust\n let item1 = 2;\n let item2 = 3;\n let mut vec: Vec = Vec::new();\n for _ in 0..20 {\n vec.push(item1);\n }\n for _ in 0..30 {\n vec.push(item2);\n }\n ```\n could be written as\n ```rust\n let item1 = 2;\n let item2 = 3;\n let mut vec: Vec = vec![item1; 20];\n vec.resize(20 + 30, item2);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "single_element_loop", - "id_span": { - "path": "src/loops/mod.rs", - "line": 484 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks whether a for loop has a single element.\n **Why is this bad?** There is no reason to have a loop of a\n single element.\n **Known problems:** None\n\n **Example:**\n ```rust\n let item1 = 2;\n for item in &[item1] {\n println!(\"{}\", item);\n }\n ```\n could be written as\n ```rust\n let item1 = 2;\n let item = &item1;\n println!(\"{}\", item);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "manual_flatten", - "id_span": { - "path": "src/loops/mod.rs", - "line": 515 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Check for unnecessary `if let` usage in a for loop where only the `Some` or `Ok` variant of the iterator element is used.\n\n **Why is this bad?** It is verbose and can be simplified\n by first calling the `flatten` method on the `Iterator`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let x = vec![Some(1), Some(2), Some(3)];\n for n in x {\n if let Some(n) = n {\n println!(\"{}\", n);\n }\n }\n ```\n Use instead:\n ```rust\n let x = vec![Some(1), Some(2), Some(3)];\n for n in x.into_iter().flatten() {\n println!(\"{}\", n);\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "macro_use_imports", - "id_span": { - "path": "src/macro_use.rs", - "line": 25 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for `#[macro_use] use...`.\n **Why is this bad?** Since the Rust 2018 edition you can import\n macro's directly, this is considered idiomatic.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n #[macro_use]\n use some_macro;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "main_recursion", - "id_span": { - "path": "src/main_recursion.rs", - "line": 22 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for recursion using the entrypoint.\n **Why is this bad?** Apart from special setups (which we could detect following attributes like #![no_std]),\n recursing into main() seems like an unintuitive antipattern we should be able to detect.\n\n **Known problems:** None.\n\n **Example:**\n ```no_run\n fn main() {\n main();\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "manual_async_fn", - "id_span": { - "path": "src/manual_async_fn.rs", - "line": 32 - }, - "group": "clippy::style", - "docs": " **What it does:** It checks for manual implementations of `async` functions.\n **Why is this bad?** It's more idiomatic to use the dedicated syntax.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n use std::future::Future;\n\n fn foo() -> impl Future { async { 42 } }\n ```\n Use instead:\n ```rust\n async fn foo() -> i32 { 42 }\n ```\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": "MachineApplicable" - } - }, - { - "id": "manual_map", - "id_span": { - "path": "src/manual_map.rs", - "line": 44 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for usages of `match` which could be implemented using `map`\n **Why is this bad?** Using the `map` method is clearer and more concise.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n match Some(0) {\n Some(x) => Some(x + 1),\n None => None,\n };\n ```\n Use instead:\n ```rust\n Some(0).map(|x| x + 1);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "manual_non_exhaustive", - "id_span": { - "path": "src/manual_non_exhaustive.rs", - "line": 56 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for manual implementations of the non-exhaustive pattern.\n **Why is this bad?** Using the #[non_exhaustive] attribute expresses better the intent\n and allows possible optimizations when applied to enums.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n struct S {\n pub a: i32,\n pub b: i32,\n _c: (),\n }\n\n enum E {\n A,\n B,\n #[doc(hidden)]\n _C,\n }\n\n struct T(pub i32, pub i32, ());\n ```\n Use instead:\n ```rust\n #[non_exhaustive]\n struct S {\n pub a: i32,\n pub b: i32,\n }\n\n #[non_exhaustive]\n enum E {\n A,\n B,\n }\n\n #[non_exhaustive]\n struct T(pub i32, pub i32);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "manual_ok_or", - "id_span": { - "path": "src/manual_ok_or.rs", - "line": 34 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Finds patterns that reimplement `Option::ok_or`.\n\n **Why is this bad?**\n Concise code helps focusing on behavior instead of boilerplate.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust\n let foo: Option = None;\n foo.map_or(Err(\"error\"), |v| Ok(v));\n ```\n\n Use instead:\n ```rust\n let foo: Option = None;\n foo.ok_or(\"error\");\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "manual_strip", - "id_span": { - "path": "src/manual_strip.rs", - "line": 52 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing using\n the pattern's length.\n\n **Why is this bad?**\n Using `str:strip_{prefix,suffix}` is safer and may have better performance as there is no\n slicing which may panic and the compiler does not need to insert this panic code. It is\n also sometimes more readable as it removes the need for duplicating or storing the pattern\n used by `str::{starts,ends}_with` and in the slicing.\n\n **Known problems:**\n None.\n\n **Example:**\n\n ```rust\n let s = \"hello, world!\";\n if s.starts_with(\"hello, \") {\n assert_eq!(s[\"hello, \".len()..].to_uppercase(), \"WORLD!\");\n }\n ```\n Use instead:\n ```rust\n let s = \"hello, world!\";\n if let Some(end) = s.strip_prefix(\"hello, \") {\n assert_eq!(end.to_uppercase(), \"WORLD!\");\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "manual_unwrap_or", - "id_span": { - "path": "src/manual_unwrap_or.rs", - "line": 36 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Finds patterns that reimplement `Option::unwrap_or` or `Result::unwrap_or`.\n\n **Why is this bad?**\n Concise code helps focusing on behavior instead of boilerplate.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let foo: Option = None;\n match foo {\n Some(v) => v,\n None => 1,\n };\n ```\n\n Use instead:\n ```rust\n let foo: Option = None;\n foo.unwrap_or(1);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "map_clone", - "id_span": { - "path": "src/map_clone.rs", - "line": 40 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for usage of `map(|x| x.clone())` or dereferencing closures for `Copy` types, on `Iterator` or `Option`,\n and suggests `cloned()` or `copied()` instead\n\n **Why is this bad?** Readability, this can be written more concisely\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n let x = vec![42, 43];\n let y = x.iter();\n let z = y.map(|i| *i);\n ```\n\n The correct use would be:\n\n ```rust\n let x = vec![42, 43];\n let y = x.iter();\n let z = y.cloned();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "map_err_ignore", - "id_span": { - "path": "src/map_err_ignore.rs", - "line": 101 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for instances of `map_err(|_| Some::Enum)`\n **Why is this bad?** This `map_err` throws away the original error rather than allowing the enum to contain and report the cause of the error\n\n **Known problems:** None.\n\n **Example:**\n Before:\n ```rust\n use std::fmt;\n\n #[derive(Debug)]\n enum Error {\n Indivisible,\n Remainder(u8),\n }\n\n impl fmt::Display for Error {\n fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n match self {\n Error::Indivisible => write!(f, \"could not divide input by three\"),\n Error::Remainder(remainder) => write!(\n f,\n \"input is not divisible by three, remainder = {}\",\n remainder\n ),\n }\n }\n }\n\n impl std::error::Error for Error {}\n\n fn divisible_by_3(input: &str) -> Result<(), Error> {\n input\n .parse::()\n .map_err(|_| Error::Indivisible)\n .map(|v| v % 3)\n .and_then(|remainder| {\n if remainder == 0 {\n Ok(())\n } else {\n Err(Error::Remainder(remainder as u8))\n }\n })\n }\n ```\n\n After:\n ```rust\n use std::{fmt, num::ParseIntError};\n\n #[derive(Debug)]\n enum Error {\n Indivisible(ParseIntError),\n Remainder(u8),\n }\n\n impl fmt::Display for Error {\n fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n match self {\n Error::Indivisible(_) => write!(f, \"could not divide input by three\"),\n Error::Remainder(remainder) => write!(\n f,\n \"input is not divisible by three, remainder = {}\",\n remainder\n ),\n }\n }\n }\n\n impl std::error::Error for Error {\n fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {\n match self {\n Error::Indivisible(source) => Some(source),\n _ => None,\n }\n }\n }\n\n fn divisible_by_3(input: &str) -> Result<(), Error> {\n input\n .parse::()\n .map_err(Error::Indivisible)\n .map(|v| v % 3)\n .and_then(|remainder| {\n if remainder == 0 {\n Ok(())\n } else {\n Err(Error::Remainder(remainder as u8))\n }\n })\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "map_identity", - "id_span": { - "path": "src/map_identity.rs", - "line": 30 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for instances of `map(f)` where `f` is the identity function.\n **Why is this bad?** It can be written more concisely without the call to `map`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let x = [1, 2, 3];\n let y: Vec<_> = x.iter().map(|x| x).map(|x| 2*x).collect();\n ```\n Use instead:\n ```rust\n let x = [1, 2, 3];\n let y: Vec<_> = x.iter().map(|x| 2*x).collect();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "option_map_unit_fn", - "id_span": { - "path": "src/map_unit_fn.rs", - "line": 48 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `option.map(f)` where f is a function or closure that returns the unit type `()`.\n\n **Why is this bad?** Readability, this can be written more clearly with\n an if let statement\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n # fn do_stuff() -> Option { Some(String::new()) }\n # fn log_err_msg(foo: String) -> Option { Some(foo) }\n # fn format_msg(foo: String) -> String { String::new() }\n let x: Option = do_stuff();\n x.map(log_err_msg);\n # let x: Option = do_stuff();\n x.map(|msg| log_err_msg(format_msg(msg)));\n ```\n\n The correct use would be:\n\n ```rust\n # fn do_stuff() -> Option { Some(String::new()) }\n # fn log_err_msg(foo: String) -> Option { Some(foo) }\n # fn format_msg(foo: String) -> String { String::new() }\n let x: Option = do_stuff();\n if let Some(msg) = x {\n log_err_msg(msg);\n }\n\n # let x: Option = do_stuff();\n if let Some(msg) = x {\n log_err_msg(format_msg(msg));\n }\n ```\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": "MachineApplicable" - } - }, - { - "id": "result_map_unit_fn", - "id_span": { - "path": "src/map_unit_fn.rs", - "line": 89 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `result.map(f)` where f is a function or closure that returns the unit type `()`.\n\n **Why is this bad?** Readability, this can be written more clearly with\n an if let statement\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n # fn do_stuff() -> Result { Ok(String::new()) }\n # fn log_err_msg(foo: String) -> Result { Ok(foo) }\n # fn format_msg(foo: String) -> String { String::new() }\n let x: Result = do_stuff();\n x.map(log_err_msg);\n # let x: Result = do_stuff();\n x.map(|msg| log_err_msg(format_msg(msg)));\n ```\n\n The correct use would be:\n\n ```rust\n # fn do_stuff() -> Result { Ok(String::new()) }\n # fn log_err_msg(foo: String) -> Result { Ok(foo) }\n # fn format_msg(foo: String) -> String { String::new() }\n let x: Result = do_stuff();\n if let Ok(msg) = x {\n log_err_msg(msg);\n };\n # let x: Result = do_stuff();\n if let Ok(msg) = x {\n log_err_msg(format_msg(msg));\n };\n ```\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": "MachineApplicable" - } - }, - { - "id": "match_on_vec_items", - "id_span": { - "path": "src/match_on_vec_items.rs", - "line": 41 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for `match vec[idx]` or `match vec[n..m]`.\n **Why is this bad?** This can panic at runtime.\n\n **Known problems:** None.\n\n **Example:**\n ```rust, no_run\n let arr = vec![0, 1, 2, 3];\n let idx = 1;\n\n // Bad\n match arr[idx] {\n 0 => println!(\"{}\", 0),\n 1 => println!(\"{}\", 3),\n _ => {},\n }\n ```\n Use instead:\n ```rust, no_run\n let arr = vec![0, 1, 2, 3];\n let idx = 1;\n\n // Good\n match arr.get(idx) {\n Some(0) => println!(\"{}\", 0),\n Some(1) => println!(\"{}\", 3),\n _ => {},\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "single_match", - "id_span": { - "path": "src/matches.rs", - "line": 55 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for matches with a single arm where an `if let` will usually suffice.\n\n **Why is this bad?** Just readability – `if let` nests less than a `match`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # fn bar(stool: &str) {}\n # let x = Some(\"abc\");\n // Bad\n match x {\n Some(ref foo) => bar(foo),\n _ => (),\n }\n\n // Good\n if let Some(ref foo) = x {\n bar(foo);\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "HasPlaceholders" - } - }, - { - "id": "single_match_else", - "id_span": { - "path": "src/matches.rs", - "line": 94 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for matches with two arms where an `if let else` will usually suffice.\n\n **Why is this bad?** Just readability – `if let` nests less than a `match`.\n\n **Known problems:** Personal style preferences may differ.\n\n **Example:**\n\n Using `match`:\n\n ```rust\n # fn bar(foo: &usize) {}\n # let other_ref: usize = 1;\n # let x: Option<&usize> = Some(&1);\n match x {\n Some(ref foo) => bar(foo),\n _ => bar(&other_ref),\n }\n ```\n\n Using `if let` with `else`:\n\n ```rust\n # fn bar(foo: &usize) {}\n # let other_ref: usize = 1;\n # let x: Option<&usize> = Some(&1);\n if let Some(ref foo) = x {\n bar(foo);\n } else {\n bar(&other_ref);\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "HasPlaceholders" - } - }, - { - "id": "match_ref_pats", - "id_span": { - "path": "src/matches.rs", - "line": 125 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for matches where all arms match a reference, suggesting to remove the reference and deref the matched expression\n instead. It also checks for `if let &foo = bar` blocks.\n\n **Why is this bad?** It just makes the code less readable. That reference\n destructuring adds nothing to the code.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n // Bad\n match x {\n &A(ref y) => foo(y),\n &B => bar(),\n _ => frob(&x),\n }\n\n // Good\n match *x {\n A(ref y) => foo(y),\n B => bar(),\n _ => frob(x),\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "match_bool", - "id_span": { - "path": "src/matches.rs", - "line": 159 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for matches where match expression is a `bool`. It suggests to replace the expression with an `if...else` block.\n\n **Why is this bad?** It makes the code less readable.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # fn foo() {}\n # fn bar() {}\n let condition: bool = true;\n match condition {\n true => foo(),\n false => bar(),\n }\n ```\n Use if/else instead:\n ```rust\n # fn foo() {}\n # fn bar() {}\n let condition: bool = true;\n if condition {\n foo();\n } else {\n bar();\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "HasPlaceholders" - } - }, - { - "id": "match_overlapping_arm", - "id_span": { - "path": "src/matches.rs", - "line": 181 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for overlapping match arms.\n **Why is this bad?** It is likely to be an error and if not, makes the code\n less obvious.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = 5;\n match x {\n 1...10 => println!(\"1 ... 10\"),\n 5...15 => println!(\"5 ... 15\"),\n _ => (),\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "match_wild_err_arm", - "id_span": { - "path": "src/matches.rs", - "line": 203 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for arm which matches all errors with `Err(_)` and take drastic actions like `panic!`.\n\n **Why is this bad?** It is generally a bad practice, similar to\n catching all exceptions in java with `catch(Exception)`\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x: Result = Ok(3);\n match x {\n Ok(_) => println!(\"ok\"),\n Err(_) => panic!(\"err\"),\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "match_as_ref", - "id_span": { - "path": "src/matches.rs", - "line": 229 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for match which is used to add a reference to an `Option` value.\n\n **Why is this bad?** Using `as_ref()` or `as_mut()` instead is shorter.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x: Option<()> = None;\n\n // Bad\n let r: Option<&()> = match x {\n None => None,\n Some(ref v) => Some(v),\n };\n\n // Good\n let r: Option<&()> = x.as_ref();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "wildcard_enum_match_arm", - "id_span": { - "path": "src/matches.rs", - "line": 258 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for wildcard enum matches using `_`.\n **Why is this bad?** New enum variants added by library updates can be missed.\n\n **Known problems:** Suggested replacements may be incorrect if guards exhaustively cover some\n variants, and also may not use correct path to enum if it's not present in the current scope.\n\n **Example:**\n ```rust\n # enum Foo { A(usize), B(usize) }\n # let x = Foo::B(1);\n // Bad\n match x {\n Foo::A(_) => {},\n _ => {},\n }\n\n // Good\n match x {\n Foo::A(_) => {},\n Foo::B(_) => {},\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "match_wildcard_for_single_variants", - "id_span": { - "path": "src/matches.rs", - "line": 290 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for wildcard enum matches for a single variant.\n **Why is this bad?** New enum variants added by library updates can be missed.\n\n **Known problems:** Suggested replacements may not use correct path to enum\n if it's not present in the current scope.\n\n **Example:**\n\n ```rust\n # enum Foo { A, B, C }\n # let x = Foo::B;\n // Bad\n match x {\n Foo::A => {},\n Foo::B => {},\n _ => {},\n }\n\n // Good\n match x {\n Foo::A => {},\n Foo::B => {},\n Foo::C => {},\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "wildcard_in_or_patterns", - "id_span": { - "path": "src/matches.rs", - "line": 317 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for wildcard pattern used with others patterns in same match arm.\n **Why is this bad?** Wildcard pattern already covers any other pattern as it will match anyway.\n It makes the code less readable, especially to spot wildcard pattern use in match arm.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n match \"foo\" {\n \"a\" => {},\n \"bar\" | _ => {},\n }\n\n // Good\n match \"foo\" {\n \"a\" => {},\n _ => {},\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "infallible_destructuring_match", - "id_span": { - "path": "src/matches.rs", - "line": 352 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for matches being used to destructure a single-variant enum or tuple struct where a `let` will suffice.\n\n **Why is this bad?** Just readability – `let` doesn't nest, whereas a `match` does.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n enum Wrapper {\n Data(i32),\n }\n\n let wrapper = Wrapper::Data(42);\n\n let data = match wrapper {\n Wrapper::Data(i) => i,\n };\n ```\n\n The correct use would be:\n ```rust\n enum Wrapper {\n Data(i32),\n }\n\n let wrapper = Wrapper::Data(42);\n let Wrapper::Data(data) = wrapper;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "match_single_binding", - "id_span": { - "path": "src/matches.rs", - "line": 380 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for useless match that binds to only one value.\n **Why is this bad?** Readability and needless complexity.\n\n **Known problems:** Suggested replacements may be incorrect when `match`\n is actually binding temporary value, bringing a 'dropped while borrowed' error.\n\n **Example:**\n ```rust\n # let a = 1;\n # let b = 2;\n\n // Bad\n match (a, b) {\n (c, d) => {\n // useless match\n }\n }\n\n // Good\n let (c, d) = (a, b);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "rest_pat_in_fully_bound_structs", - "id_span": { - "path": "src/matches.rs", - "line": 410 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for unnecessary '..' pattern binding on struct when all fields are explicitly matched.\n **Why is this bad?** Correctness and readability. It's like having a wildcard pattern after\n matching all enum variants explicitly.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # struct A { a: i32 }\n let a = A { a: 5 };\n\n // Bad\n match a {\n A { a: 5, .. } => {},\n _ => {},\n }\n\n // Good\n match a {\n A { a: 5 } => {},\n _ => {},\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "redundant_pattern_matching", - "id_span": { - "path": "src/matches.rs", - "line": 458 - }, - "group": "clippy::style", - "docs": " **What it does:** Lint for redundant pattern matching over `Result`, `Option`, `std::task::Poll` or `std::net::IpAddr`\n\n **Why is this bad?** It's more concise and clear to just use the proper\n utility function\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n # use std::task::Poll;\n # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};\n if let Ok(_) = Ok::(42) {}\n if let Err(_) = Err::(42) {}\n if let None = None::<()> {}\n if let Some(_) = Some(42) {}\n if let Poll::Pending = Poll::Pending::<()> {}\n if let Poll::Ready(_) = Poll::Ready(42) {}\n if let IpAddr::V4(_) = IpAddr::V4(Ipv4Addr::LOCALHOST) {}\n if let IpAddr::V6(_) = IpAddr::V6(Ipv6Addr::LOCALHOST) {}\n match Ok::(42) {\n Ok(_) => true,\n Err(_) => false,\n };\n ```\n\n The more idiomatic use would be:\n\n ```rust\n # use std::task::Poll;\n # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};\n if Ok::(42).is_ok() {}\n if Err::(42).is_err() {}\n if None::<()>.is_none() {}\n if Some(42).is_some() {}\n if Poll::Pending::<()>.is_pending() {}\n if Poll::Ready(42).is_ready() {}\n if IpAddr::V4(Ipv4Addr::LOCALHOST).is_ipv4() {}\n if IpAddr::V6(Ipv6Addr::LOCALHOST).is_ipv6() {}\n Ok::(42).is_ok();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "match_like_matches_macro", - "id_span": { - "path": "src/matches.rs", - "line": 491 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `match` or `if let` expressions producing a `bool` that could be written using `matches!`\n\n **Why is this bad?** Readability and needless complexity.\n\n **Known problems:** This lint falsely triggers, if there are arms with\n `cfg` attributes that remove an arm evaluating to `false`.\n\n **Example:**\n ```rust\n let x = Some(5);\n\n // Bad\n let a = match x {\n Some(0) => true,\n _ => false,\n };\n\n let a = if let Some(0) = x {\n true\n } else {\n false\n };\n\n // Good\n let a = matches!(x, Some(0));\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "match_same_arms", - "id_span": { - "path": "src/matches.rs", - "line": 532 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for `match` with identical arm bodies.\n **Why is this bad?** This is probably a copy & paste error. If arm bodies\n are the same on purpose, you can factor them\n [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns).\n\n **Known problems:** False positive possible with order dependent `match`\n (see issue\n [#860](https://github.com/rust-lang/rust-clippy/issues/860)).\n\n **Example:**\n ```rust,ignore\n match foo {\n Bar => bar(),\n Quz => quz(),\n Baz => bar(), // <= oops\n }\n ```\n\n This should probably be\n ```rust,ignore\n match foo {\n Bar => bar(),\n Quz => quz(),\n Baz => baz(), // <= fixed\n }\n ```\n\n or if the original code was not a typo:\n ```rust,ignore\n match foo {\n Bar | Baz => bar(), // <= shows the intent better\n Quz => quz(),\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "mem_discriminant_non_enum", - "id_span": { - "path": "src/mem_discriminant.rs", - "line": 25 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for calls of `mem::discriminant()` on a non-enum type.\n **Why is this bad?** The value of `mem::discriminant()` on non-enum types\n is unspecified.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n use std::mem;\n\n mem::discriminant(&\"hello\");\n mem::discriminant(&&Some(2));\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "mem_forget", - "id_span": { - "path": "src/mem_forget.rs", - "line": 21 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of `std::mem::forget(t)` where `t` is `Drop`.\n\n **Why is this bad?** `std::mem::forget(t)` prevents `t` from running its\n destructor, possibly causing leaks.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # use std::mem;\n # use std::rc::Rc;\n mem::forget(Rc::new(55))\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "mem_replace_option_with_none", - "id_span": { - "path": "src/mem_replace.rs", - "line": 37 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `mem::replace()` on an `Option` with `None`.\n\n **Why is this bad?** `Option` already has the method `take()` for\n taking its current value (Some(..) or None) and replacing it with\n `None`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n use std::mem;\n\n let mut an_option = Some(0);\n let replaced = mem::replace(&mut an_option, None);\n ```\n Is better expressed with:\n ```rust\n let mut an_option = Some(0);\n let taken = an_option.take();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "mem_replace_with_uninit", - "id_span": { - "path": "src/mem_replace.rs", - "line": 69 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for `mem::replace(&mut _, mem::uninitialized())` and `mem::replace(&mut _, mem::zeroed())`.\n\n **Why is this bad?** This will lead to undefined behavior even if the\n value is overwritten later, because the uninitialized value may be\n observed in the case of a panic.\n\n **Known problems:** None.\n\n **Example:**\n\n ```\n use std::mem;\n# fn may_panic(v: Vec) -> Vec { v }\n\n #[allow(deprecated, invalid_value)]\n fn myfunc (v: &mut Vec) {\n let taken_v = unsafe { mem::replace(v, mem::uninitialized()) };\n let new_v = may_panic(taken_v); // undefined behavior on panic\n mem::forget(mem::replace(v, new_v));\n }\n ```\n\n The [take_mut](https://docs.rs/take_mut) crate offers a sound solution,\n at the cost of either lazily creating a replacement value or aborting\n on panic, to ensure that the uninitialized value cannot be observed.\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "mem_replace_with_default", - "id_span": { - "path": "src/mem_replace.rs", - "line": 93 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `std::mem::replace` on a value of type `T` with `T::default()`.\n\n **Why is this bad?** `std::mem` module already has the method `take` to\n take the current value and replace it with the default value of that type.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let mut text = String::from(\"foo\");\n let replaced = std::mem::replace(&mut text, String::default());\n ```\n Is better expressed with:\n ```rust\n let mut text = String::from(\"foo\");\n let taken = std::mem::take(&mut text);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "unwrap_used", - "id_span": { - "path": "src/methods/mod.rs", - "line": 84 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for `.unwrap()` calls on `Option`s and on `Result`s.\n **Why is this bad?** It is better to handle the `None` or `Err` case,\n or at least call `.expect(_)` with a more helpful message. Still, for a lot of\n quick-and-dirty code, `unwrap` is a good choice, which is why this lint is\n `Allow` by default.\n\n `result.unwrap()` will let the thread panic on `Err` values.\n Normally, you want to implement more sophisticated error handling,\n and propagate errors upwards with `?` operator.\n\n Even if you want to panic on errors, not all `Error`s implement good\n messages on display. Therefore, it may be beneficial to look at the places\n where they may get displayed. Activate this lint to do just that.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust\n # let opt = Some(1);\n\n // Bad\n opt.unwrap();\n\n // Good\n opt.expect(\"more helpful message\");\n ```\n\n // or\n\n ```rust\n # let res: Result = Ok(1);\n\n // Bad\n res.unwrap();\n\n // Good\n res.expect(\"more helpful message\");\n ```\n", - "applicability": null - }, - { - "id": "expect_used", - "id_span": { - "path": "src/methods/mod.rs", - "line": 126 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for `.expect()` calls on `Option`s and `Result`s.\n **Why is this bad?** Usually it is better to handle the `None` or `Err` case.\n Still, for a lot of quick-and-dirty code, `expect` is a good choice, which is why\n this lint is `Allow` by default.\n\n `result.expect()` will let the thread panic on `Err`\n values. Normally, you want to implement more sophisticated error handling,\n and propagate errors upwards with `?` operator.\n\n **Known problems:** None.\n\n **Examples:**\n ```rust,ignore\n # let opt = Some(1);\n\n // Bad\n opt.expect(\"one\");\n\n // Good\n let opt = Some(1);\n opt?;\n ```\n\n // or\n\n ```rust\n # let res: Result = Ok(1);\n\n // Bad\n res.expect(\"one\");\n\n // Good\n res?;\n # Ok::<(), ()>(())\n ```\n", - "applicability": null - }, - { - "id": "should_implement_trait", - "id_span": { - "path": "src/methods/mod.rs", - "line": 155 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for methods that should live in a trait implementation of a `std` trait (see [llogiq's blog\n post](http://llogiq.github.io/2015/07/30/traits.html) for further\n information) instead of an inherent implementation.\n\n **Why is this bad?** Implementing the traits improve ergonomics for users of\n the code, often with very little cost. Also people seeing a `mul(...)`\n method\n may expect `*` to work equally, so you should have good reason to disappoint\n them.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n struct X;\n impl X {\n fn add(&self, other: &X) -> X {\n // ..\n # X\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "wrong_self_convention", - "id_span": { - "path": "src/methods/mod.rs", - "line": 188 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for methods with certain name prefixes and which doesn't match how self is taken. The actual rules are:\n\n |Prefix |`self` taken |\n |-------|----------------------|\n |`as_` |`&self` or `&mut self`|\n |`from_`| none |\n |`into_`|`self` |\n |`is_` |`&self` or none |\n |`to_` |`&self` |\n\n **Why is this bad?** Consistency breeds readability. If you follow the\n conventions, your users won't be surprised that they, e.g., need to supply a\n mutable reference to a `as_..` function.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # struct X;\n impl X {\n fn as_str(self) -> &'static str {\n // ..\n # \"\"\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "wrong_pub_self_convention", - "id_span": { - "path": "src/methods/mod.rs", - "line": 212 - }, - "group": "clippy::restriction", - "docs": " **What it does:** This is the same as [`wrong_self_convention`](#wrong_self_convention), but for public items.\n\n **Why is this bad?** See [`wrong_self_convention`](#wrong_self_convention).\n\n **Known problems:** Actually *renaming* the function may break clients if\n the function is part of the public interface. In that case, be mindful of\n the stability guarantees you've given your users.\n\n **Example:**\n ```rust\n # struct X;\n impl<'a> X {\n pub fn as_str(self) -> &'a str {\n \"foo\"\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "ok_expect", - "id_span": { - "path": "src/methods/mod.rs", - "line": 235 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for usage of `ok().expect(..)`.\n **Why is this bad?** Because you usually call `expect()` on the `Result`\n directly to get a better error message.\n\n **Known problems:** The error type needs to implement `Debug`\n\n **Example:**\n ```rust\n # let x = Ok::<_, ()>(());\n\n // Bad\n x.ok().expect(\"why did I do this again?\");\n\n // Good\n x.expect(\"why did I do this again?\");\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "map_unwrap_or", - "id_span": { - "path": "src/methods/mod.rs", - "line": 272 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for usage of `option.map(_).unwrap_or(_)` or `option.map(_).unwrap_or_else(_)` or `result.map(_).unwrap_or_else(_)`.\n\n **Why is this bad?** Readability, these can be written more concisely (resp.) as\n `option.map_or(_, _)`, `option.map_or_else(_, _)` and `result.map_or_else(_, _)`.\n\n **Known problems:** The order of the arguments is not in execution order\n\n **Examples:**\n ```rust\n # let x = Some(1);\n\n // Bad\n x.map(|a| a + 1).unwrap_or(0);\n\n // Good\n x.map_or(0, |a| a + 1);\n ```\n\n // or\n\n ```rust\n # let x: Result = Ok(1);\n # fn some_function(foo: ()) -> usize { 1 }\n\n // Bad\n x.map(|a| a + 1).unwrap_or_else(some_function);\n\n // Good\n x.map_or_else(some_function, |a| a + 1);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "option_map_or_none", - "id_span": { - "path": "src/methods/mod.rs", - "line": 295 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for usage of `_.map_or(None, _)`.\n **Why is this bad?** Readability, this can be written more concisely as\n `_.and_then(_)`.\n\n **Known problems:** The order of the arguments is not in execution order.\n\n **Example:**\n ```rust\n # let opt = Some(1);\n\n // Bad\n opt.map_or(None, |a| Some(a + 1));\n\n // Good\n opt.and_then(|a| Some(a + 1));\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "result_map_or_into_option", - "id_span": { - "path": "src/methods/mod.rs", - "line": 321 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for usage of `_.map_or(None, Some)`.\n **Why is this bad?** Readability, this can be written more concisely as\n `_.ok()`.\n\n **Known problems:** None.\n\n **Example:**\n\n Bad:\n ```rust\n # let r: Result = Ok(1);\n assert_eq!(Some(1), r.map_or(None, Some));\n ```\n\n Good:\n ```rust\n # let r: Result = Ok(1);\n assert_eq!(Some(1), r.ok());\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "bind_instead_of_map", - "id_span": { - "path": "src/methods/mod.rs", - "line": 354 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `_.and_then(|x| Some(y))`, `_.and_then(|x| Ok(y))` or `_.or_else(|x| Err(y))`.\n\n **Why is this bad?** Readability, this can be written more concisely as\n `_.map(|x| y)` or `_.map_err(|x| y)`.\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n # fn opt() -> Option<&'static str> { Some(\"42\") }\n # fn res() -> Result<&'static str, &'static str> { Ok(\"42\") }\n let _ = opt().and_then(|s| Some(s.len()));\n let _ = res().and_then(|s| if s.len() == 42 { Ok(10) } else { Ok(20) });\n let _ = res().or_else(|s| if s.len() == 42 { Err(10) } else { Err(20) });\n ```\n\n The correct use would be:\n\n ```rust\n # fn opt() -> Option<&'static str> { Some(\"42\") }\n # fn res() -> Result<&'static str, &'static str> { Ok(\"42\") }\n let _ = opt().map(|s| s.len());\n let _ = res().map(|s| if s.len() == 42 { 10 } else { 20 });\n let _ = res().map_err(|s| if s.len() == 42 { 10 } else { 20 });\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "filter_next", - "id_span": { - "path": "src/methods/mod.rs", - "line": 377 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `_.filter(_).next()`.\n **Why is this bad?** Readability, this can be written more concisely as\n `_.find(_)`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let vec = vec![1];\n vec.iter().filter(|x| **x == 0).next();\n ```\n Could be written as\n ```rust\n # let vec = vec![1];\n vec.iter().find(|x| **x == 0);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "skip_while_next", - "id_span": { - "path": "src/methods/mod.rs", - "line": 400 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `_.skip_while(condition).next()`.\n **Why is this bad?** Readability, this can be written more concisely as\n `_.find(!condition)`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let vec = vec![1];\n vec.iter().skip_while(|x| **x == 0).next();\n ```\n Could be written as\n ```rust\n # let vec = vec![1];\n vec.iter().find(|x| **x != 0);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "map_flatten", - "id_span": { - "path": "src/methods/mod.rs", - "line": 423 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for usage of `_.map(_).flatten(_)`,\n **Why is this bad?** Readability, this can be written more concisely as\n `_.flat_map(_)`\n\n **Known problems:**\n\n **Example:**\n ```rust\n let vec = vec![vec![1]];\n\n // Bad\n vec.iter().map(|x| x.iter()).flatten();\n\n // Good\n vec.iter().flat_map(|x| x.iter());\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "filter_map", - "id_span": { - "path": "src/methods/mod.rs", - "line": 452 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for usage of `_.filter(_).map(_)`, `_.filter(_).flat_map(_)`, `_.filter_map(_).flat_map(_)` and similar.\n\n **Why is this bad?** Readability, this can be written more concisely as\n `_.filter_map(_)`.\n\n **Known problems:** Often requires a condition + Option/Iterator creation\n inside the closure.\n\n **Example:**\n ```rust\n let vec = vec![1];\n\n // Bad\n vec.iter().filter(|x| **x == 0).map(|x| *x * 2);\n\n // Good\n vec.iter().filter_map(|x| if *x == 0 {\n Some(*x * 2)\n } else {\n None\n });\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "manual_filter_map", - "id_span": { - "path": "src/methods/mod.rs", - "line": 478 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `_.filter(_).map(_)` that can be written more simply as `filter_map(_)`.\n\n **Why is this bad?** Redundant code in the `filter` and `map` operations is poor style and\n less performant.\n\n **Known problems:** None.\n\n **Example:**\n Bad:\n ```rust\n (0_i32..10)\n .filter(|n| n.checked_add(1).is_some())\n .map(|n| n.checked_add(1).unwrap());\n ```\n\n Good:\n ```rust\n (0_i32..10).filter_map(|n| n.checked_add(1));\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "manual_find_map", - "id_span": { - "path": "src/methods/mod.rs", - "line": 504 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `_.find(_).map(_)` that can be written more simply as `find_map(_)`.\n\n **Why is this bad?** Redundant code in the `find` and `map` operations is poor style and\n less performant.\n\n **Known problems:** None.\n\n **Example:**\n Bad:\n ```rust\n (0_i32..10)\n .find(|n| n.checked_add(1).is_some())\n .map(|n| n.checked_add(1).unwrap());\n ```\n\n Good:\n ```rust\n (0_i32..10).find_map(|n| n.checked_add(1));\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "filter_map_next", - "id_span": { - "path": "src/methods/mod.rs", - "line": 526 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for usage of `_.filter_map(_).next()`.\n **Why is this bad?** Readability, this can be written more concisely as\n `_.find_map(_)`.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n (0..3).filter_map(|x| if x == 2 { Some(x) } else { None }).next();\n ```\n Can be written as\n\n ```rust\n (0..3).find_map(|x| if x == 2 { Some(x) } else { None });\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "flat_map_identity", - "id_span": { - "path": "src/methods/mod.rs", - "line": 548 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `flat_map(|x| x)`.\n **Why is this bad?** Readability, this can be written more concisely by using `flatten`.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n # let iter = vec![vec![0]].into_iter();\n iter.flat_map(|x| x);\n ```\n Can be written as\n ```rust\n # let iter = vec![vec![0]].into_iter();\n iter.flatten();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "search_is_some", - "id_span": { - "path": "src/methods/mod.rs", - "line": 572 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for an iterator or string search (such as `find()`, `position()`, or `rposition()`) followed by a call to `is_some()`.\n\n **Why is this bad?** Readability, this can be written more concisely as\n `_.any(_)` or `_.contains(_)`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let vec = vec![1];\n vec.iter().find(|x| **x == 0).is_some();\n ```\n Could be written as\n ```rust\n # let vec = vec![1];\n vec.iter().any(|x| *x == 0);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "chars_next_cmp", - "id_span": { - "path": "src/methods/mod.rs", - "line": 596 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for usage of `.chars().next()` on a `str` to check if it starts with a given char.\n\n **Why is this bad?** Readability, this can be written more concisely as\n `_.starts_with(_)`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let name = \"foo\";\n if name.chars().next() == Some('_') {};\n ```\n Could be written as\n ```rust\n let name = \"foo\";\n if name.starts_with('_') {};\n ```\n", - "applicability": null - }, - { - "id": "or_fun_call", - "id_span": { - "path": "src/methods/mod.rs", - "line": 627 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for calls to `.or(foo(..))`, `.unwrap_or(foo(..))`, etc., and suggests to use `or_else`, `unwrap_or_else`, etc., or\n `unwrap_or_default` instead.\n\n **Why is this bad?** The function will always be called and potentially\n allocate an object acting as the default.\n\n **Known problems:** If the function has side-effects, not calling it will\n change the semantic of the program, but you shouldn't rely on that anyway.\n\n **Example:**\n ```rust\n # let foo = Some(String::new());\n foo.unwrap_or(String::new());\n ```\n this can instead be written:\n ```rust\n # let foo = Some(String::new());\n foo.unwrap_or_else(String::new);\n ```\n or\n ```rust\n # let foo = Some(String::new());\n foo.unwrap_or_default();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "HasPlaceholders" - } - }, - { - "id": "expect_fun_call", - "id_span": { - "path": "src/methods/mod.rs", - "line": 662 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for calls to `.expect(&format!(...))`, `.expect(foo(..))`, etc., and suggests to use `unwrap_or_else` instead\n\n **Why is this bad?** The function will always be called.\n\n **Known problems:** If the function has side-effects, not calling it will\n change the semantics of the program, but you shouldn't rely on that anyway.\n\n **Example:**\n ```rust\n # let foo = Some(String::new());\n # let err_code = \"418\";\n # let err_msg = \"I'm a teapot\";\n foo.expect(&format!(\"Err {}: {}\", err_code, err_msg));\n ```\n or\n ```rust\n # let foo = Some(String::new());\n # let err_code = \"418\";\n # let err_msg = \"I'm a teapot\";\n foo.expect(format!(\"Err {}: {}\", err_code, err_msg).as_str());\n ```\n this can instead be written:\n ```rust\n # let foo = Some(String::new());\n # let err_code = \"418\";\n # let err_msg = \"I'm a teapot\";\n foo.unwrap_or_else(|| panic!(\"Err {}: {}\", err_code, err_msg));\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "clone_on_copy", - "id_span": { - "path": "src/methods/mod.rs", - "line": 679 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `.clone()` on a `Copy` type.\n **Why is this bad?** The only reason `Copy` types implement `Clone` is for\n generics, not for using the `clone` method on a concrete type.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n 42u64.clone();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "clone_on_ref_ptr", - "id_span": { - "path": "src/methods/mod.rs", - "line": 704 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of `.clone()` on a ref-counted pointer, (`Rc`, `Arc`, `rc::Weak`, or `sync::Weak`), and suggests calling Clone via unified\n function syntax instead (e.g., `Rc::clone(foo)`).\n\n **Why is this bad?** Calling '.clone()' on an Rc, Arc, or Weak\n can obscure the fact that only the pointer is being cloned, not the underlying\n data.\n\n **Example:**\n ```rust\n # use std::rc::Rc;\n let x = Rc::new(1);\n\n // Bad\n x.clone();\n\n // Good\n Rc::clone(&x);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "clone_double_ref", - "id_span": { - "path": "src/methods/mod.rs", - "line": 726 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for usage of `.clone()` on an `&&T`.\n **Why is this bad?** Cloning an `&&T` copies the inner `&T`, instead of\n cloning the underlying `T`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn main() {\n let x = vec![1];\n let y = &&x;\n let z = y.clone();\n println!(\"{:p} {:p}\", *y, z); // prints out the same pointer\n }\n ```\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "inefficient_to_string", - "id_span": { - "path": "src/methods/mod.rs", - "line": 749 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for usage of `.to_string()` on an `&&T` where `T` implements `ToString` directly (like `&&str` or `&&String`).\n\n **Why is this bad?** This bypasses the specialized implementation of\n `ToString` and instead goes through the more expensive string formatting\n facilities.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Generic implementation for `T: Display` is used (slow)\n [\"foo\", \"bar\"].iter().map(|s| s.to_string());\n\n // OK, the specialized impl is used\n [\"foo\", \"bar\"].iter().map(|&s| s.to_string());\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "new_ret_no_self", - "id_span": { - "path": "src/methods/mod.rs", - "line": 810 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `new` not returning a type that contains `Self`.\n **Why is this bad?** As a convention, `new` methods are used to make a new\n instance of a type.\n\n **Known problems:** None.\n\n **Example:**\n In an impl block:\n ```rust\n # struct Foo;\n # struct NotAFoo;\n impl Foo {\n fn new() -> NotAFoo {\n # NotAFoo\n }\n }\n ```\n\n ```rust\n # struct Foo;\n struct Bar(Foo);\n impl Foo {\n // Bad. The type name must contain `Self`\n fn new() -> Bar {\n # Bar(Foo)\n }\n }\n ```\n\n ```rust\n # struct Foo;\n # struct FooError;\n impl Foo {\n // Good. Return type contains `Self`\n fn new() -> Result {\n # Ok(Foo)\n }\n }\n ```\n\n Or in a trait definition:\n ```rust\n pub trait Trait {\n // Bad. The type name must contain `Self`\n fn new();\n }\n ```\n\n ```rust\n pub trait Trait {\n // Good. Return type contains `Self`\n fn new() -> Self;\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "single_char_pattern", - "id_span": { - "path": "src/methods/mod.rs", - "line": 831 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for string methods that receive a single-character `str` as an argument, e.g., `_.split(\"x\")`.\n\n **Why is this bad?** Performing these methods using a `char` is faster than\n using a `str`.\n\n **Known problems:** Does not catch multi-byte unicode characters.\n\n **Example:**\n ```rust,ignore\n // Bad\n _.split(\"x\");\n\n // Good\n _.split('x');\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "iterator_step_by_zero", - "id_span": { - "path": "src/methods/mod.rs", - "line": 850 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for calling `.step_by(0)` on iterators which panics.\n **Why is this bad?** This very much looks like an oversight. Use `panic!()` instead if you\n actually intend to panic.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,should_panic\n for x in (0..100).step_by(0) {\n //..\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "iter_nth_zero", - "id_span": { - "path": "src/methods/mod.rs", - "line": 878 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for the use of `iter.nth(0)`.\n **Why is this bad?** `iter.next()` is equivalent to\n `iter.nth(0)`, as they both consume the next element,\n but is more readable.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n # use std::collections::HashSet;\n // Bad\n # let mut s = HashSet::new();\n # s.insert(1);\n let x = s.iter().nth(0);\n\n // Good\n # let mut s = HashSet::new();\n # s.insert(1);\n let x = s.iter().next();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "iter_nth", - "id_span": { - "path": "src/methods/mod.rs", - "line": 904 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for use of `.iter().nth()` (and the related `.iter_mut().nth()`) on standard library types with O(1) element access.\n\n **Why is this bad?** `.get()` and `.get_mut()` are more efficient and more\n readable.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let some_vec = vec![0, 1, 2, 3];\n let bad_vec = some_vec.iter().nth(3);\n let bad_slice = &some_vec[..].iter().nth(3);\n ```\n The correct use would be:\n ```rust\n let some_vec = vec![0, 1, 2, 3];\n let bad_vec = some_vec.get(3);\n let bad_slice = &some_vec[..].get(3);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "iter_skip_next", - "id_span": { - "path": "src/methods/mod.rs", - "line": 928 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for use of `.skip(x).next()` on iterators.\n **Why is this bad?** `.nth(x)` is cleaner\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let some_vec = vec![0, 1, 2, 3];\n let bad_vec = some_vec.iter().skip(3).next();\n let bad_slice = &some_vec[..].iter().skip(3).next();\n ```\n The correct use would be:\n ```rust\n let some_vec = vec![0, 1, 2, 3];\n let bad_vec = some_vec.iter().nth(3);\n let bad_slice = &some_vec[..].iter().nth(3);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "get_unwrap", - "id_span": { - "path": "src/methods/mod.rs", - "line": 961 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for use of `.get().unwrap()` (or `.get_mut().unwrap`) on a standard library type which implements `Index`\n\n **Why is this bad?** Using the Index trait (`[]`) is more clear and more\n concise.\n\n **Known problems:** Not a replacement for error handling: Using either\n `.unwrap()` or the Index trait (`[]`) carries the risk of causing a `panic`\n if the value being accessed is `None`. If the use of `.get().unwrap()` is a\n temporary placeholder for dealing with the `Option` type, then this does\n not mitigate the need for error handling. If there is a chance that `.get()`\n will be `None` in your program, then it is advisable that the `None` case\n is handled in a future refactor instead of using `.unwrap()` or the Index\n trait.\n\n **Example:**\n ```rust\n let mut some_vec = vec![0, 1, 2, 3];\n let last = some_vec.get(3).unwrap();\n *some_vec.get_mut(0).unwrap() = 1;\n ```\n The correct use would be:\n ```rust\n let mut some_vec = vec![0, 1, 2, 3];\n let last = some_vec[3];\n some_vec[0] = 1;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "string_extend_chars", - "id_span": { - "path": "src/methods/mod.rs", - "line": 990 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for the use of `.extend(s.chars())` where s is a `&str` or `String`.\n\n **Why is this bad?** `.push_str(s)` is clearer\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let abc = \"abc\";\n let def = String::from(\"def\");\n let mut s = String::new();\n s.extend(abc.chars());\n s.extend(def.chars());\n ```\n The correct use would be:\n ```rust\n let abc = \"abc\";\n let def = String::from(\"def\");\n let mut s = String::new();\n s.push_str(abc);\n s.push_str(&def);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "iter_cloned_collect", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1013 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for the use of `.cloned().collect()` on slice to create a `Vec`.\n\n **Why is this bad?** `.to_vec()` is clearer\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let s = [1, 2, 3, 4, 5];\n let s2: Vec = s[..].iter().cloned().collect();\n ```\n The better use would be:\n ```rust\n let s = [1, 2, 3, 4, 5];\n let s2: Vec = s.to_vec();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "chars_last_cmp", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1037 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for usage of `_.chars().last()` or `_.chars().next_back()` on a `str` to check if it ends with a given char.\n\n **Why is this bad?** Readability, this can be written more concisely as\n `_.ends_with(_)`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let name = \"_\";\n\n // Bad\n name.chars().last() == Some('_') || name.chars().next_back() == Some('-');\n\n // Good\n name.ends_with('_') || name.ends_with('-');\n ```\n", - "applicability": null - }, - { - "id": "useless_asref", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1062 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `.as_ref()` or `.as_mut()` where the types before and after the call are the same.\n\n **Why is this bad?** The call is unnecessary.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # fn do_stuff(x: &[i32]) {}\n let x: &[i32] = &[1, 2, 3, 4, 5];\n do_stuff(x.as_ref());\n ```\n The correct use would be:\n ```rust\n # fn do_stuff(x: &[i32]) {}\n let x: &[i32] = &[1, 2, 3, 4, 5];\n do_stuff(x);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "unnecessary_fold", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1084 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for using `fold` when a more succinct alternative exists. Specifically, this checks for `fold`s which could be replaced by `any`, `all`,\n `sum` or `product`.\n\n **Why is this bad?** Readability.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let _ = (0..3).fold(false, |acc, x| acc || x > 2);\n ```\n This could be written as:\n ```rust\n let _ = (0..3).any(|x| x > 2);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "unnecessary_filter_map", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1113 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for `filter_map` calls which could be replaced by `filter` or `map`. More specifically it checks if the closure provided is only performing one of the\n filter or map operations and suggests the appropriate option.\n\n **Why is this bad?** Complexity. The intent is also clearer if only a single\n operation is being performed.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n let _ = (0..3).filter_map(|x| if x > 2 { Some(x) } else { None });\n\n // As there is no transformation of the argument this could be written as:\n let _ = (0..3).filter(|&x| x > 2);\n ```\n\n ```rust\n let _ = (0..4).filter_map(|x| Some(x + 1));\n\n // As there is no conditional check on the argument this could be written as:\n let _ = (0..4).map(|x| x + 1);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "into_iter_on_ref", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1137 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `into_iter` calls on references which should be replaced by `iter` or `iter_mut`.\n\n **Why is this bad?** Readability. Calling `into_iter` on a reference will not move out its\n content into the resulting iterator, which is confusing. It is better just call `iter` or\n `iter_mut` directly.\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n // Bad\n let _ = (&vec![3, 4, 5]).into_iter();\n\n // Good\n let _ = (&vec![3, 4, 5]).iter();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "suspicious_map", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1156 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for calls to `map` followed by a `count`.\n **Why is this bad?** It looks suspicious. Maybe `map` was confused with `filter`.\n If the `map` call is intentional, this should be rewritten. Or, if you intend to\n drive the iterator to completion, you can just use `for_each` instead.\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n let _ = (0..3).map(|x| x + 2).count();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "uninit_assumed_init", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1188 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for `MaybeUninit::uninit().assume_init()`.\n **Why is this bad?** For most types, this is undefined behavior.\n\n **Known problems:** For now, we accept empty tuples and tuples / arrays\n of `MaybeUninit`. There may be other types that allow uninitialized\n data, but those are not yet rigorously defined.\n\n **Example:**\n\n ```rust\n // Beware the UB\n use std::mem::MaybeUninit;\n\n let _: usize = unsafe { MaybeUninit::uninit().assume_init() };\n ```\n\n Note that the following is OK:\n\n ```rust\n use std::mem::MaybeUninit;\n\n let _: [MaybeUninit; 5] = unsafe {\n MaybeUninit::uninit().assume_init()\n };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "manual_saturating_arithmetic", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1215 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `.checked_add/sub(x).unwrap_or(MAX/MIN)`.\n **Why is this bad?** These can be written simply with `saturating_add/sub` methods.\n\n **Example:**\n\n ```rust\n # let y: u32 = 0;\n # let x: u32 = 100;\n let add = x.checked_add(y).unwrap_or(u32::MAX);\n let sub = x.checked_sub(y).unwrap_or(u32::MIN);\n ```\n\n can be written using dedicated methods for saturating addition/subtraction as:\n\n ```rust\n # let y: u32 = 0;\n # let x: u32 = 100;\n let add = x.saturating_add(y);\n let sub = x.saturating_sub(y);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "zst_offset", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1232 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for `offset(_)`, `wrapping_`{`add`, `sub`}, etc. on raw pointers to zero-sized types\n\n **Why is this bad?** This is a no-op, and likely unintended\n\n **Known problems:** None\n\n **Example:**\n ```rust\n unsafe { (&() as *const ()).offset(1) };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "filetype_is_file", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1272 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for `FileType::is_file()`.\n **Why is this bad?** When people testing a file type with `FileType::is_file`\n they are testing whether a path is something they can get bytes from. But\n `is_file` doesn't cover special file types in unix-like systems, and doesn't cover\n symlink in windows. Using `!FileType::is_dir()` is a better way to that intention.\n\n **Example:**\n\n ```rust\n # || {\n let metadata = std::fs::metadata(\"foo.txt\")?;\n let filetype = metadata.file_type();\n\n if filetype.is_file() {\n // read file\n }\n # Ok::<_, std::io::Error>(())\n # };\n ```\n\n should be written as:\n\n ```rust\n # || {\n let metadata = std::fs::metadata(\"foo.txt\")?;\n let filetype = metadata.file_type();\n\n if !filetype.is_dir() {\n // read file\n }\n # Ok::<_, std::io::Error>(())\n # };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "option_as_ref_deref", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1297 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `_.as_ref().map(Deref::deref)` or it's aliases (such as String::as_str).\n **Why is this bad?** Readability, this can be written more concisely as\n `_.as_deref()`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let opt = Some(\"\".to_string());\n opt.as_ref().map(String::as_str)\n # ;\n ```\n Can be written as\n ```rust\n # let opt = Some(\"\".to_string());\n opt.as_deref()\n # ;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "iter_next_slice", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1323 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for usage of `iter().next()` on a Slice or an Array\n **Why is this bad?** These can be shortened into `.get()`\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let a = [1, 2, 3];\n # let b = vec![1, 2, 3];\n a[2..].iter().next();\n b.iter().next();\n ```\n should be written as:\n ```rust\n # let a = [1, 2, 3];\n # let b = vec![1, 2, 3];\n a.get(2);\n b.get(0);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "single_char_add_str", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1348 - }, - "group": "clippy::style", - "docs": " **What it does:** Warns when using `push_str`/`insert_str` with a single-character string literal where `push`/`insert` with a `char` would work fine.\n\n **Why is this bad?** It's less clear that we are pushing a single character.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n let mut string = String::new();\n string.insert_str(0, \"R\");\n string.push_str(\"R\");\n ```\n Could be written as\n ```rust\n let mut string = String::new();\n string.insert(0, 'R');\n string.push('R');\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "unnecessary_lazy_evaluations", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1384 - }, - "group": "clippy::style", - "docs": " **What it does:** As the counterpart to `or_fun_call`, this lint looks for unnecessary lazily evaluated closures on `Option` and `Result`.\n\n This lint suggests changing the following functions, when eager evaluation results in\n simpler code:\n - `unwrap_or_else` to `unwrap_or`\n - `and_then` to `and`\n - `or_else` to `or`\n - `get_or_insert_with` to `get_or_insert`\n - `ok_or_else` to `ok_or`\n\n **Why is this bad?** Using eager evaluation is shorter and simpler in some cases.\n\n **Known problems:** It is possible, but not recommended for `Deref` and `Index` to have\n side effects. Eagerly evaluating them can change the semantics of the program.\n\n **Example:**\n\n ```rust\n // example code where clippy issues a warning\n let opt: Option = None;\n\n opt.unwrap_or_else(|| 42);\n ```\n Use instead:\n ```rust\n let opt: Option = None;\n\n opt.unwrap_or(42);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "map_collect_result_unit", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1405 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for usage of `_.map(_).collect::()`.\n **Why is this bad?** Using `try_for_each` instead is more readable and idiomatic.\n\n **Known problems:** None\n\n **Example:**\n\n ```rust\n (0..3).map(|t| Err(t)).collect::>();\n ```\n Use instead:\n ```rust\n (0..3).try_for_each(|t| Err(t));\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "from_iter_instead_of_collect", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1438 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `from_iter()` function calls on types that implement the `FromIterator` trait.\n\n **Why is this bad?** It is recommended style to use collect. See\n [FromIterator documentation](https://doc.rust-lang.org/std/iter/trait.FromIterator.html)\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n use std::iter::FromIterator;\n\n let five_fives = std::iter::repeat(5).take(5);\n\n let v = Vec::from_iter(five_fives);\n\n assert_eq!(v, vec![5, 5, 5, 5, 5]);\n ```\n Use instead:\n ```rust\n let five_fives = std::iter::repeat(5).take(5);\n\n let v: Vec = five_fives.collect();\n\n assert_eq!(v, vec![5, 5, 5, 5, 5]);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "inspect_for_each", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1468 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `inspect().for_each()`.\n **Why is this bad?** It is the same as performing the computation\n inside `inspect` at the beginning of the closure in `for_each`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n [1,2,3,4,5].iter()\n .inspect(|&x| println!(\"inspect the number: {}\", x))\n .for_each(|&x| {\n assert!(x >= 0);\n });\n ```\n Can be written as\n ```rust\n [1,2,3,4,5].iter()\n .for_each(|&x| {\n println!(\"inspect the number: {}\", x);\n assert!(x >= 0);\n });\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "filter_map_identity", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1491 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `filter_map(|x| x)`.\n **Why is this bad?** Readability, this can be written more concisely by using `flatten`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n # let iter = vec![Some(1)].into_iter();\n iter.filter_map(|x| x);\n ```\n Use instead:\n ```rust\n # let iter = vec![Some(1)].into_iter();\n iter.flatten();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "bytes_nth", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1513 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for the use of `.bytes().nth()`.\n **Why is this bad?** `.as_bytes().get()` is more efficient and more\n readable.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let _ = \"Hello\".bytes().nth(3);\n\n // Good\n let _ = \"Hello\".as_bytes().get(3);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "implicit_clone", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1539 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for the usage of `_.to_owned()`, `vec.to_vec()`, or similar when calling `_.clone()` would be clearer.\n **Why is this bad?** These methods do the same thing as `_.clone()` but may be confusing as\n to why we are calling `to_vec` on something that is already a `Vec` or calling `to_owned` on something that is already owned.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let a = vec![1, 2, 3];\n let b = a.to_vec();\n let c = a.to_owned();\n ```\n Use instead:\n ```rust\n let a = vec![1, 2, 3];\n let b = a.clone();\n let c = a.clone();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "iter_count", - "id_span": { - "path": "src/methods/mod.rs", - "line": 1565 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for the use of `.iter().count()`.\n **Why is this bad?** `.len()` is more efficient and more\n readable.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let some_vec = vec![0, 1, 2, 3];\n let _ = some_vec.iter().count();\n let _ = &some_vec[..].iter().count();\n\n // Good\n let some_vec = vec![0, 1, 2, 3];\n let _ = some_vec.len();\n let _ = &some_vec[..].len();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "min_max", - "id_span": { - "path": "src/minmax.rs", - "line": 28 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for expressions where `std::cmp::min` and `max` are used to clamp values, but switched so that the result is constant.\n\n **Why is this bad?** This is in all probability not the intended outcome. At\n the least it hurts readability of the code.\n\n **Known problems:** None\n\n **Example:**\n ```ignore\n min(0, max(100, x))\n ```\n or\n ```ignore\n x.max(100).min(0)\n ```\n It will always be equal to `0`. Probably the author meant to clamp the value\n between 0 and 100, but has erroneously swapped `min` and `max`.\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "toplevel_ref_arg", - "id_span": { - "path": "src/misc.rs", - "line": 53 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for function arguments and let bindings denoted as `ref`.\n\n **Why is this bad?** The `ref` declaration makes the function take an owned\n value, but turns the argument into a reference (which means that the value\n is destroyed when exiting the function). This adds not much value: either\n take a reference type, or take an owned value and create references in the\n body.\n\n For let bindings, `let x = &foo;` is preferred over `let ref x = foo`. The\n type of `x` is more obvious with the former.\n\n **Known problems:** If the argument is dereferenced within the function,\n removing the `ref` will lead to errors. This can be fixed by removing the\n dereferences, e.g., changing `*x` to `x` within the function.\n\n **Example:**\n ```rust,ignore\n // Bad\n fn foo(ref x: u8) -> bool {\n true\n }\n\n // Good\n fn foo(x: &u8) -> bool {\n true\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "cmp_nan", - "id_span": { - "path": "src/misc.rs", - "line": 76 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for comparisons to NaN.\n **Why is this bad?** NaN does not compare meaningfully to anything – not\n even itself – so those comparisons are simply wrong.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let x = 1.0;\n\n // Bad\n if x == f32::NAN { }\n\n // Good\n if x.is_nan() { }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "float_cmp", - "id_span": { - "path": "src/misc.rs", - "line": 109 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for (in-)equality comparisons on floating-point values (apart from zero), except in functions called `*eq*` (which probably\n implement equality for a type involving floats).\n\n **Why is this bad?** Floating point calculations are usually imprecise, so\n asking if two values are *exactly* equal is asking for trouble. For a good\n guide on what to do, see [the floating point\n guide](http://www.floating-point-gui.de/errors/comparison).\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = 1.2331f64;\n let y = 1.2332f64;\n\n // Bad\n if y == 1.23f64 { }\n if y != x {} // where both are floats\n\n // Good\n let error_margin = f64::EPSILON; // Use an epsilon for comparison\n // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.\n // let error_margin = std::f64::EPSILON;\n if (y - 1.23f64).abs() < error_margin { }\n if (y - x).abs() > error_margin { }\n ```\n", - "applicability": null - }, - { - "id": "cmp_owned", - "id_span": { - "path": "src/misc.rs", - "line": 136 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for conversions to owned values just for the sake of a comparison.\n\n **Why is this bad?** The comparison can operate on a reference, so creating\n an owned value effectively throws it away directly afterwards, which is\n needlessly consuming code and heap space.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let x = \"foo\";\n # let y = String::from(\"foo\");\n if x.to_owned() == y {}\n ```\n Could be written as\n ```rust\n # let x = \"foo\";\n # let y = String::from(\"foo\");\n if x == y {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "modulo_one", - "id_span": { - "path": "src/misc.rs", - "line": 159 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for getting the remainder of a division by one or minus one.\n\n **Why is this bad?** The result for a divisor of one can only ever be zero; for\n minus one it can cause panic/overflow (if the left operand is the minimal value of\n the respective integer type) or results in zero. No one will write such code\n deliberately, unless trying to win an Underhanded Rust Contest. Even for that\n contest, it's probably a bad idea. Use something more underhanded.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let x = 1;\n let a = x % 1;\n let a = x % -1;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "used_underscore_binding", - "id_span": { - "path": "src/misc.rs", - "line": 181 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for the use of bindings with a single leading underscore.\n\n **Why is this bad?** A single leading underscore is usually used to indicate\n that a binding will not be used. Using such a binding breaks this\n expectation.\n\n **Known problems:** The lint does not work properly with desugaring and\n macro, it has been allowed in the mean time.\n\n **Example:**\n ```rust\n let _x = 0;\n let y = _x + 1; // Here we are using `_x`, even though it has a leading\n // underscore. We should rename `_x` to `x`\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "short_circuit_statement", - "id_span": { - "path": "src/misc.rs", - "line": 201 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for the use of short circuit boolean conditions as a\n statement.\n\n **Why is this bad?** Using a short circuit boolean condition as a statement\n may hide the fact that the second part is executed or not depending on the\n outcome of the first part.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n f() && g(); // We should write `if f() { g(); }`.\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "zero_ptr", - "id_span": { - "path": "src/misc.rs", - "line": 223 - }, - "group": "clippy::style", - "docs": " **What it does:** Catch casts from `0` to some pointer type\n **Why is this bad?** This generally means `null` and is better expressed as\n {`std`, `core`}`::ptr::`{`null`, `null_mut`}.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n let a = 0 as *const u32;\n\n // Good\n let a = std::ptr::null::();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "float_cmp_const", - "id_span": { - "path": "src/misc.rs", - "line": 254 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for (in-)equality comparisons on floating-point value and constant, except in functions called `*eq*` (which probably\n implement equality for a type involving floats).\n\n **Why is this bad?** Floating point calculations are usually imprecise, so\n asking if two values are *exactly* equal is asking for trouble. For a good\n guide on what to do, see [the floating point\n guide](http://www.floating-point-gui.de/errors/comparison).\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x: f64 = 1.0;\n const ONE: f64 = 1.00;\n\n // Bad\n if x == ONE { } // where both are floats\n\n // Good\n let error_margin = f64::EPSILON; // Use an epsilon for comparison\n // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.\n // let error_margin = std::f64::EPSILON;\n if (x - ONE).abs() < error_margin { }\n ```\n", - "applicability": null - }, - { - "id": "unneeded_field_pattern", - "id_span": { - "path": "src/misc_early.rs", - "line": 44 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for structure field patterns bound to wildcards.\n **Why is this bad?** Using `..` instead is shorter and leaves the focus on\n the fields that are actually bound.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # struct Foo {\n # a: i32,\n # b: i32,\n # c: i32,\n # }\n let f = Foo { a: 0, b: 0, c: 0 };\n\n // Bad\n match f {\n Foo { a: _, b: 0, .. } => {},\n Foo { a: _, b: _, c: _ } => {},\n }\n\n // Good\n match f {\n Foo { b: 0, .. } => {},\n Foo { .. } => {},\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "duplicate_underscore_argument", - "id_span": { - "path": "src/misc_early.rs", - "line": 65 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for function arguments having the similar names differing by an underscore.\n\n **Why is this bad?** It affects code readability.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n fn foo(a: i32, _a: i32) {}\n\n // Good\n fn bar(a: i32, _b: i32) {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "double_neg", - "id_span": { - "path": "src/misc_early.rs", - "line": 83 - }, - "group": "clippy::style", - "docs": " **What it does:** Detects expressions of the form `--x`.\n **Why is this bad?** It can mislead C/C++ programmers to think `x` was\n decremented.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let mut x = 3;\n --x;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "mixed_case_hex_literals", - "id_span": { - "path": "src/misc_early.rs", - "line": 104 - }, - "group": "clippy::style", - "docs": " **What it does:** Warns on hexadecimal literals with mixed-case letter digits.\n\n **Why is this bad?** It looks confusing.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n let y = 0x1a9BAcD;\n\n // Good\n let y = 0x1A9BACD;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "unseparated_literal_suffix", - "id_span": { - "path": "src/misc_early.rs", - "line": 125 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Warns if literal suffixes are not separated by an underscore.\n\n **Why is this bad?** It is much less readable.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n let y = 123832i32;\n\n // Good\n let y = 123832_i32;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "zero_prefixed_literal", - "id_span": { - "path": "src/misc_early.rs", - "line": 163 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Warns if an integral constant literal starts with `0`.\n **Why is this bad?** In some languages (including the infamous C language\n and most of its\n family), this marks an octal constant. In Rust however, this is a decimal\n constant. This could\n be confusing for both the writer and a reader of the constant.\n\n **Known problems:** None.\n\n **Example:**\n\n In Rust:\n ```rust\n fn main() {\n let a = 0123;\n println!(\"{}\", a);\n }\n ```\n\n prints `123`, while in C:\n\n ```c\n #include \n\n int main() {\n int a = 0123;\n printf(\"%d\\n\", a);\n }\n ```\n\n prints `83` (as `83 == 0o123` while `123 == 0o173`).\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "builtin_type_shadow", - "id_span": { - "path": "src/misc_early.rs", - "line": 184 - }, - "group": "clippy::style", - "docs": " **What it does:** Warns if a generic shadows a built-in type.\n **Why is this bad?** This gives surprising type errors.\n\n **Known problems:** None.\n\n **Example:**\n\n ```ignore\n impl Foo {\n fn impl_func(&self) -> u32 {\n 42\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "redundant_pattern", - "id_span": { - "path": "src/misc_early.rs", - "line": 213 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for patterns in the form `name @ _`.\n **Why is this bad?** It's almost always more readable to just use direct\n bindings.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let v = Some(\"abc\");\n\n // Bad\n match v {\n Some(x) => (),\n y @ _ => (),\n }\n\n // Good\n match v {\n Some(x) => (),\n y => (),\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "unneeded_wildcard_pattern", - "id_span": { - "path": "src/misc_early.rs", - "line": 247 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for tuple patterns with a wildcard pattern (`_`) is next to a rest pattern (`..`).\n\n _NOTE_: While `_, ..` means there is at least one element left, `..`\n means there are 0 or more elements left. This can make a difference\n when refactoring, but shouldn't result in errors in the refactored code,\n since the wildcard pattern isn't used anyway.\n **Why is this bad?** The wildcard pattern is unneeded as the rest pattern\n can match that element as well.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # struct TupleStruct(u32, u32, u32);\n # let t = TupleStruct(1, 2, 3);\n // Bad\n match t {\n TupleStruct(0, .., _) => (),\n _ => (),\n }\n\n // Good\n match t {\n TupleStruct(0, ..) => (),\n _ => (),\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "missing_const_for_fn", - "id_span": { - "path": "src/missing_const_for_fn.rs", - "line": 72 - }, - "group": "clippy::nursery", - "docs": " **What it does:**\n Suggests the use of `const` in functions and methods where possible.\n\n **Why is this bad?**\n\n Not having the function const prevents callers of the function from being const as well.\n\n **Known problems:**\n\n Const functions are currently still being worked on, with some features only being available\n on nightly. This lint does not consider all edge cases currently and the suggestions may be\n incorrect if you are using this lint on stable.\n\n Also, the lint only runs one pass over the code. Consider these two non-const functions:\n\n ```rust\n fn a() -> i32 {\n 0\n }\n fn b() -> i32 {\n a()\n }\n ```\n\n When running Clippy, the lint will only suggest to make `a` const, because `b` at this time\n can't be const as it calls a non-const function. Making `a` const and running Clippy again,\n will suggest to make `b` const, too.\n\n **Example:**\n\n ```rust\n # struct Foo {\n # random_number: usize,\n # }\n # impl Foo {\n fn new() -> Self {\n Self { random_number: 42 }\n }\n # }\n ```\n\n Could be a const fn:\n\n ```rust\n # struct Foo {\n # random_number: usize,\n # }\n # impl Foo {\n const fn new() -> Self {\n Self { random_number: 42 }\n }\n # }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "missing_docs_in_private_items", - "id_span": { - "path": "src/missing_doc.rs", - "line": 29 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Warns if there is missing doc for any documentable item (public or private).\n\n **Why is this bad?** Doc is good. *rustc* has a `MISSING_DOCS`\n allowed-by-default lint for\n public members, but has no way to enforce documentation of private items.\n This lint fixes that.\n\n **Known problems:** None.\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "missing_inline_in_public_items", - "id_span": { - "path": "src/missing_inline.rs", - "line": 55 - }, - "group": "clippy::restriction", - "docs": " **What it does:** it lints if an exported function, method, trait method with default impl, or trait method impl is not `#[inline]`.\n\n **Why is this bad?** In general, it is not. Functions can be inlined across\n crates when that's profitable as long as any form of LTO is used. When LTO is disabled,\n functions that are not `#[inline]` cannot be inlined across crates. Certain types of crates\n might intend for most of the methods in their public API to be able to be inlined across\n crates even when LTO is disabled. For these types of crates, enabling this lint might make\n sense. It allows the crate to require all exported methods to be `#[inline]` by default, and\n then opt out for specific methods where this might not make sense.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n pub fn foo() {} // missing #[inline]\n fn ok() {} // ok\n #[inline] pub fn bar() {} // ok\n #[inline(always)] pub fn baz() {} // ok\n\n pub trait Bar {\n fn bar(); // ok\n fn def_bar() {} // missing #[inline]\n }\n\n struct Baz;\n impl Baz {\n fn private() {} // ok\n }\n\n impl Bar for Baz {\n fn bar() {} // ok - Baz is not exported\n }\n\n pub struct PubBaz;\n impl PubBaz {\n fn private() {} // ok\n pub fn not_ptrivate() {} // missing #[inline]\n }\n\n impl Bar for PubBaz {\n fn bar() {} // missing #[inline]\n fn def_bar() {} // missing #[inline]\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "modulo_arithmetic", - "id_span": { - "path": "src/modulo_arithmetic.rs", - "line": 26 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for modulo arithmetic.\n **Why is this bad?** The results of modulo (%) operation might differ\n depending on the language, when negative numbers are involved.\n If you interop with different languages it might be beneficial\n to double check all places that use modulo arithmetic.\n\n For example, in Rust `17 % -3 = 2`, but in Python `17 % -3 = -1`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = -17 % 3;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "multiple_crate_versions", - "id_span": { - "path": "src/multiple_crate_versions.rs", - "line": 32 - }, - "group": "clippy::cargo", - "docs": " **What it does:** Checks to see if multiple versions of a crate are being used.\n\n **Why is this bad?** This bloats the size of targets, and can lead to\n confusing error messages when structs or traits are used interchangeably\n between different versions of a crate.\n\n **Known problems:** Because this can be caused purely by the dependencies\n themselves, it's not always possible to fix this issue.\n\n **Example:**\n ```toml\n # This will pull in both winapi v0.3.x and v0.2.x, triggering a warning.\n [dependencies]\n ctrlc = \"=3.1.0\"\n ansi_term = \"=0.11.0\"\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "mutable_key_type", - "id_span": { - "path": "src/mut_key.rs", - "line": 50 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for sets/maps with mutable key types.\n **Why is this bad?** All of `HashMap`, `HashSet`, `BTreeMap` and\n `BtreeSet` rely on either the hash or the order of keys be unchanging,\n so having types with interior mutability is a bad idea.\n\n **Known problems:** It's correct to use a struct, that contains interior mutability\n as a key, when its `Hash` implementation doesn't access any of the interior mutable types.\n However, this lint is unable to recognize this, so it causes a false positive in theses cases.\n The `bytes` crate is a great example of this.\n\n **Example:**\n ```rust\n use std::cmp::{PartialEq, Eq};\n use std::collections::HashSet;\n use std::hash::{Hash, Hasher};\n use std::sync::atomic::AtomicUsize;\n# #[allow(unused)]\n\n struct Bad(AtomicUsize);\n impl PartialEq for Bad {\n fn eq(&self, rhs: &Self) -> bool {\n ..\n ; unimplemented!();\n }\n }\n\n impl Eq for Bad {}\n\n impl Hash for Bad {\n fn hash(&self, h: &mut H) {\n ..\n ; unimplemented!();\n }\n }\n\n fn main() {\n let _: HashSet = HashSet::new();\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "mut_mut", - "id_span": { - "path": "src/mut_mut.rs", - "line": 24 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for instances of `mut mut` references.\n **Why is this bad?** Multiple `mut`s don't add anything meaningful to the\n source. This is either a copy'n'paste error, or it shows a fundamental\n misunderstanding of references.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let mut y = 1;\n let x = &mut &mut y;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "mut_mutex_lock", - "id_span": { - "path": "src/mut_mutex_lock.rs", - "line": 40 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `&mut Mutex::lock` calls\n **Why is this bad?** `Mutex::lock` is less efficient than\n calling `Mutex::get_mut`. In addition you also have a statically\n guarantee that the mutex isn't locked, instead of just a runtime\n guarantee.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n use std::sync::{Arc, Mutex};\n\n let mut value_rc = Arc::new(Mutex::new(42_u8));\n let value_mutex = Arc::get_mut(&mut value_rc).unwrap();\n\n let mut value = value_mutex.lock().unwrap();\n *value += 1;\n ```\n Use instead:\n ```rust\n use std::sync::{Arc, Mutex};\n\n let mut value_rc = Arc::new(Mutex::new(42_u8));\n let value_mutex = Arc::get_mut(&mut value_rc).unwrap();\n\n let value = value_mutex.get_mut().unwrap();\n *value += 1;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "unnecessary_mut_passed", - "id_span": { - "path": "src/mut_reference.rs", - "line": 25 - }, - "group": "clippy::style", - "docs": " **What it does:** Detects passing a mutable reference to a function that only requires an immutable reference.\n\n **Why is this bad?** The mutable reference rules out all other references to\n the value. Also the code misleads about the intent of the call site.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n // Bad\n my_vec.push(&mut value)\n\n // Good\n my_vec.push(&value)\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "debug_assert_with_mut_call", - "id_span": { - "path": "src/mutable_debug_assertion.rs", - "line": 28 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Checks for function/method calls with a mutable parameter in `debug_assert!`, `debug_assert_eq!` and `debug_assert_ne!` macros.\n\n **Why is this bad?** In release builds `debug_assert!` macros are optimized out by the\n compiler.\n Therefore mutating something in a `debug_assert!` macro results in different behaviour\n between a release and debug build.\n\n **Known problems:** None\n\n **Example:**\n ```rust,ignore\n debug_assert_eq!(vec![3].pop(), Some(3));\n // or\n fn take_a_mut_parameter(_: &mut u32) -> bool { unimplemented!() }\n debug_assert!(take_a_mut_parameter(&mut 5));\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "mutex_atomic", - "id_span": { - "path": "src/mutex_atomic.rs", - "line": 34 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for usages of `Mutex` where an atomic will do.\n **Why is this bad?** Using a mutex just to make access to a plain bool or\n reference sequential is shooting flies with cannons.\n `std::sync::atomic::AtomicBool` and `std::sync::atomic::AtomicPtr` are leaner and\n faster.\n\n **Known problems:** This lint cannot detect if the mutex is actually used\n for waiting before a critical section.\n\n **Example:**\n ```rust\n # let y = true;\n\n // Bad\n # use std::sync::Mutex;\n let x = Mutex::new(&y);\n\n // Good\n # use std::sync::atomic::AtomicBool;\n let x = AtomicBool::new(y);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "mutex_integer", - "id_span": { - "path": "src/mutex_atomic.rs", - "line": 59 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Checks for usages of `Mutex` where `X` is an integral type.\n\n **Why is this bad?** Using a mutex just to make access to a plain integer\n sequential is\n shooting flies with cannons. `std::sync::atomic::AtomicUsize` is leaner and faster.\n\n **Known problems:** This lint cannot detect if the mutex is actually used\n for waiting before a critical section.\n\n **Example:**\n ```rust\n # use std::sync::Mutex;\n let x = Mutex::new(0usize);\n\n // Good\n # use std::sync::atomic::AtomicUsize;\n let x = AtomicUsize::new(0usize);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "needless_arbitrary_self_type", - "id_span": { - "path": "src/needless_arbitrary_self_type.rs", - "line": 55 - }, - "group": "clippy::complexity", - "docs": " **What it does:** The lint checks for `self` in fn parameters that specify the `Self`-type explicitly\n **Why is this bad?** Increases the amount and decreases the readability of code\n\n **Known problems:** None\n\n **Example:**\n ```rust\n enum ValType {\n I32,\n I64,\n F32,\n F64,\n }\n\n impl ValType {\n pub fn bytes(self: Self) -> usize {\n match self {\n Self::I32 | Self::F32 => 4,\n Self::I64 | Self::F64 => 8,\n }\n }\n }\n ```\n\n Could be rewritten as\n\n ```rust\n enum ValType {\n I32,\n I64,\n F32,\n F64,\n }\n\n impl ValType {\n pub fn bytes(self) -> usize {\n match self {\n Self::I32 | Self::F32 => 4,\n Self::I64 | Self::F64 => 8,\n }\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "needless_bool", - "id_span": { - "path": "src/needless_bool.rs", - "line": 38 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for expressions of the form `if c { true } else { false }` (or vice versa) and suggests using the condition directly.\n\n **Why is this bad?** Redundant code.\n\n **Known problems:** Maybe false positives: Sometimes, the two branches are\n painstakingly documented (which we, of course, do not detect), so they *may*\n have some value. Even then, the documentation can be rewritten to match the\n shorter code.\n\n **Example:**\n ```rust,ignore\n if x {\n false\n } else {\n true\n }\n ```\n Could be written as\n ```rust,ignore\n !x\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "bool_comparison", - "id_span": { - "path": "src/needless_bool.rs", - "line": 62 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for expressions of the form `x == true`, `x != true` and order comparisons such as `x < true` (or vice versa) and\n suggest using the variable directly.\n\n **Why is this bad?** Unnecessary code.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n if x == true {}\n if y == false {}\n ```\n use `x` directly:\n ```rust,ignore\n if x {}\n if !y {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "needless_borrow", - "id_span": { - "path": "src/needless_borrow.rs", - "line": 32 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Checks for address of operations (`&`) that are going to be dereferenced immediately by the compiler.\n\n **Why is this bad?** Suggests that the receiver of the expression borrows\n the expression.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n let x: &i32 = &&&&&&5;\n\n // Good\n let x: &i32 = &5;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "needless_borrowed_reference", - "id_span": { - "path": "src/needless_borrowed_ref.rs", - "line": 48 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for useless borrowed references.\n **Why is this bad?** It is mostly useless and make the code look more\n complex than it\n actually is.\n\n **Known problems:** It seems that the `&ref` pattern is sometimes useful.\n For instance in the following snippet:\n ```rust,ignore\n enum Animal {\n Cat(u64),\n Dog(u64),\n }\n\n fn foo(a: &Animal, b: &Animal) {\n match (a, b) {\n (&Animal::Cat(v), k) | (k, &Animal::Cat(v)) => (), // lifetime mismatch error\n (&Animal::Dog(ref c), &Animal::Dog(_)) => ()\n }\n }\n ```\n There is a lifetime mismatch error for `k` (indeed a and b have distinct\n lifetime).\n This can be fixed by using the `&ref` pattern.\n However, the code can also be fixed by much cleaner ways\n\n **Example:**\n ```rust\n let mut v = Vec::::new();\n let _ = v.iter_mut().filter(|&ref a| a.is_empty());\n ```\n This closure takes a reference on something that has been matched as a\n reference and\n de-referenced.\n As such, it could just be |a| a.is_empty()\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "needless_continue", - "id_span": { - "path": "src/needless_continue.rs", - "line": 114 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** The lint checks for `if`-statements appearing in loops that contain a `continue` statement in either their main blocks or their\n `else`-blocks, when omitting the `else`-block possibly with some\n rearrangement of code can make the code easier to understand.\n\n **Why is this bad?** Having explicit `else` blocks for `if` statements\n containing `continue` in their THEN branch adds unnecessary branching and\n nesting to the code. Having an else block containing just `continue` can\n also be better written by grouping the statements following the whole `if`\n statement within the THEN block and omitting the else block completely.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n # fn condition() -> bool { false }\n # fn update_condition() {}\n # let x = false;\n while condition() {\n update_condition();\n if x {\n // ...\n } else {\n continue;\n }\n println!(\"Hello, world\");\n }\n ```\n\n Could be rewritten as\n\n ```rust\n # fn condition() -> bool { false }\n # fn update_condition() {}\n # let x = false;\n while condition() {\n update_condition();\n if x {\n // ...\n println!(\"Hello, world\");\n }\n }\n ```\n\n As another example, the following code\n\n ```rust\n # fn waiting() -> bool { false }\n loop {\n if waiting() {\n continue;\n } else {\n // Do something useful\n }\n # break;\n }\n ```\n Could be rewritten as\n\n ```rust\n # fn waiting() -> bool { false }\n loop {\n if waiting() {\n continue;\n }\n // Do something useful\n # break;\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "needless_pass_by_value", - "id_span": { - "path": "src/needless_pass_by_value.rs", - "line": 50 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for functions taking arguments by value, but not consuming them in its\n body.\n\n **Why is this bad?** Taking arguments by reference is more flexible and can\n sometimes avoid\n unnecessary allocations.\n\n **Known problems:**\n * This lint suggests taking an argument by reference,\n however sometimes it is better to let users decide the argument type\n (by using `Borrow` trait, for example), depending on how the function is used.\n\n **Example:**\n ```rust\n fn foo(v: Vec) {\n assert_eq!(v.len(), 42);\n }\n ```\n should be\n ```rust\n fn foo(v: &[i32]) {\n assert_eq!(v.len(), 42);\n }\n ```\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": null - } - }, - { - "id": "needless_question_mark", - "id_span": { - "path": "src/needless_question_mark.rs", - "line": 57 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Suggests alternatives for useless applications of `?` in terminating expressions\n\n **Why is this bad?** There's no reason to use `?` to short-circuit when execution of the body will end there anyway.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n struct TO {\n magic: Option,\n }\n\n fn f(to: TO) -> Option {\n Some(to.magic?)\n }\n\n struct TR {\n magic: Result,\n }\n\n fn g(tr: Result) -> Result {\n tr.and_then(|t| Ok(t.magic?))\n }\n\n ```\n Use instead:\n ```rust\n struct TO {\n magic: Option,\n }\n\n fn f(to: TO) -> Option {\n to.magic\n }\n\n struct TR {\n magic: Result,\n }\n\n fn g(tr: Result) -> Result {\n tr.and_then(|t| t.magic)\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "needless_update", - "id_span": { - "path": "src/needless_update.rs", - "line": 43 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for needlessly including a base struct on update when all fields are changed anyway.\n\n This lint is not applied to structs marked with\n [non_exhaustive](https://doc.rust-lang.org/reference/attributes/type_system.html).\n\n **Why is this bad?** This will cost resources (because the base has to be\n somewhere), and make the code less readable.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # struct Point {\n # x: i32,\n # y: i32,\n # z: i32,\n # }\n # let zero_point = Point { x: 0, y: 0, z: 0 };\n\n // Bad\n Point {\n x: 1,\n y: 1,\n z: 1,\n ..zero_point\n };\n\n // Ok\n Point {\n x: 1,\n y: 1,\n ..zero_point\n };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "neg_cmp_op_on_partial_ord", - "id_span": { - "path": "src/neg_cmp_op_on_partial_ord.rs", - "line": 41 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for the usage of negated comparison operators on types which only implement\n `PartialOrd` (e.g., `f64`).\n\n **Why is this bad?**\n These operators make it easy to forget that the underlying types actually allow not only three\n potential Orderings (Less, Equal, Greater) but also a fourth one (Uncomparable). This is\n especially easy to miss if the operator based comparison result is negated.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n use std::cmp::Ordering;\n\n // Bad\n let a = 1.0;\n let b = f64::NAN;\n\n let _not_less_or_equal = !(a <= b);\n\n // Good\n let a = 1.0;\n let b = f64::NAN;\n\n let _not_less_or_equal = match a.partial_cmp(&b) {\n None | Some(Ordering::Greater) => true,\n _ => false,\n };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "neg_multiply", - "id_span": { - "path": "src/neg_multiply.rs", - "line": 21 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for multiplication by -1 as a form of negation.\n **Why is this bad?** It's more readable to just negate.\n\n **Known problems:** This only catches integers (for now).\n\n **Example:**\n ```ignore\n x * -1\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "new_without_default", - "id_span": { - "path": "src/new_without_default.rs", - "line": 48 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for types with a `fn new() -> Self` method and no implementation of\n [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html).\n\n **Why is this bad?** The user might expect to be able to use\n [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) as the\n type can be constructed without arguments.\n\n **Known problems:** Hopefully none.\n\n **Example:**\n\n ```ignore\n struct Foo(Bar);\n\n impl Foo {\n fn new() -> Self {\n Foo(Bar::new())\n }\n }\n ```\n\n To fix the lint, add a `Default` implementation that delegates to `new`:\n\n ```ignore\n struct Foo(Bar);\n\n impl Default for Foo {\n fn default() -> Self {\n Foo::new()\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "no_effect", - "id_span": { - "path": "src/no_effect.rs", - "line": 22 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for statements which have no effect.\n **Why is this bad?** Similar to dead code, these statements are actually\n executed. However, as they have no effect, all they do is make the code less\n readable.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n 0;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "unnecessary_operation", - "id_span": { - "path": "src/no_effect.rs", - "line": 40 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for expression statements that can be reduced to a sub-expression.\n\n **Why is this bad?** Expressions by themselves often have no side-effects.\n Having such expressions reduces readability.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n compute_array()[0];\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "declare_interior_mutable_const", - "id_span": { - "path": "src/non_copy_const.rs", - "line": 69 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for declaration of `const` items which is interior mutable (e.g., contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.).\n\n **Why is this bad?** Consts are copied everywhere they are referenced, i.e.,\n every time you refer to the const a fresh instance of the `Cell` or `Mutex`\n or `AtomicXxxx` will be created, which defeats the whole purpose of using\n these types in the first place.\n\n The `const` should better be replaced by a `static` item if a global\n variable is wanted, or replaced by a `const fn` if a constructor is wanted.\n\n **Known problems:** A \"non-constant\" const item is a legacy way to supply an\n initialized value to downstream `static` items (e.g., the\n `std::sync::ONCE_INIT` constant). In this case the use of `const` is legit,\n and this lint should be suppressed.\n\n Even though the lint avoids triggering on a constant whose type has enums that have variants\n with interior mutability, and its value uses non interior mutable variants (see\n [#3962](https://github.com/rust-lang/rust-clippy/issues/3962) and\n [#3825](https://github.com/rust-lang/rust-clippy/issues/3825) for examples);\n it complains about associated constants without default values only based on its types;\n which might not be preferable.\n There're other enums plus associated constants cases that the lint cannot handle.\n\n Types that have underlying or potential interior mutability trigger the lint whether\n the interior mutable field is used or not. See issues\n [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and\n\n **Example:**\n ```rust\n use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};\n\n // Bad.\n const CONST_ATOM: AtomicUsize = AtomicUsize::new(12);\n CONST_ATOM.store(6, SeqCst); // the content of the atomic is unchanged\n assert_eq!(CONST_ATOM.load(SeqCst), 12); // because the CONST_ATOM in these lines are distinct\n\n // Good.\n static STATIC_ATOM: AtomicUsize = AtomicUsize::new(15);\n STATIC_ATOM.store(9, SeqCst);\n assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance\n ```\n", - "applicability": null - }, - { - "id": "borrow_interior_mutable_const", - "id_span": { - "path": "src/non_copy_const.rs", - "line": 110 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks if `const` items which is interior mutable (e.g., contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.) has been borrowed directly.\n\n **Why is this bad?** Consts are copied everywhere they are referenced, i.e.,\n every time you refer to the const a fresh instance of the `Cell` or `Mutex`\n or `AtomicXxxx` will be created, which defeats the whole purpose of using\n these types in the first place.\n\n The `const` value should be stored inside a `static` item.\n\n **Known problems:** When an enum has variants with interior mutability, use of its non\n interior mutable variants can generate false positives. See issue\n [#3962](https://github.com/rust-lang/rust-clippy/issues/3962)\n\n Types that have underlying or potential interior mutability trigger the lint whether\n the interior mutable field is used or not. See issues\n [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and\n [#3825](https://github.com/rust-lang/rust-clippy/issues/3825)\n\n **Example:**\n ```rust\n use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};\n const CONST_ATOM: AtomicUsize = AtomicUsize::new(12);\n\n // Bad.\n CONST_ATOM.store(6, SeqCst); // the content of the atomic is unchanged\n assert_eq!(CONST_ATOM.load(SeqCst), 12); // because the CONST_ATOM in these lines are distinct\n\n // Good.\n static STATIC_ATOM: AtomicUsize = CONST_ATOM;\n STATIC_ATOM.store(9, SeqCst);\n assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance\n ```\n", - "applicability": null - }, - { - "id": "similar_names", - "id_span": { - "path": "src/non_expressive_names.rs", - "line": 27 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for names that are very similar and thus confusing.\n **Why is this bad?** It's hard to distinguish between names that differ only\n by a single character.\n\n **Known problems:** None?\n\n **Example:**\n ```ignore\n let checked_exp = something;\n let checked_expr = something_else;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "many_single_char_names", - "id_span": { - "path": "src/non_expressive_names.rs", - "line": 45 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for too many variables whose name consists of a single character.\n\n **Why is this bad?** It's hard to memorize what a variable means without a\n descriptive name.\n\n **Known problems:** None?\n\n **Example:**\n ```ignore\n let (a, b, c, d, e, f, g) = (...);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "just_underscores_and_digits", - "id_span": { - "path": "src/non_expressive_names.rs", - "line": 65 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks if you have variables whose name consists of just underscores and digits.\n\n **Why is this bad?** It's hard to memorize what a variable means without a\n descriptive name.\n\n **Known problems:** None?\n\n **Example:**\n ```rust\n let _1 = 1;\n let ___1 = 1;\n let __1___2 = 11;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "nonsensical_open_options", - "id_span": { - "path": "src/open_options.rs", - "line": 23 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for duplicate open options as well as combinations that make no sense.\n\n **Why is this bad?** In the best case, the code will be harder to read than\n necessary. I don't know the worst case.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n use std::fs::OpenOptions;\n\n OpenOptions::new().read(true).truncate(true);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "option_env_unwrap", - "id_span": { - "path": "src/option_env_unwrap.rs", - "line": 28 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for usage of `option_env!(...).unwrap()` and suggests usage of the `env!` macro.\n\n **Why is this bad?** Unwrapping the result of `option_env!` will panic\n at run-time if the environment variable doesn't exist, whereas `env!`\n catches it at compile-time.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,no_run\n let _ = option_env!(\"HOME\").unwrap();\n ```\n\n Is better expressed as:\n\n ```rust,no_run\n let _ = env!(\"HOME\");\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "option_if_let_else", - "id_span": { - "path": "src/option_if_let_else.rs", - "line": 59 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Lints usage of `if let Some(v) = ... { y } else { x }` which is more\n idiomatically done with `Option::map_or` (if the else bit is a pure\n expression) or `Option::map_or_else` (if the else bit is an impure\n expression).\n\n **Why is this bad?**\n Using the dedicated functions of the Option type is clearer and\n more concise than an `if let` expression.\n\n **Known problems:**\n This lint uses a deliberately conservative metric for checking\n if the inside of either body contains breaks or continues which will\n cause it to not suggest a fix if either block contains a loop with\n continues or breaks contained within the loop.\n\n **Example:**\n\n ```rust\n # let optional: Option = Some(0);\n # fn do_complicated_function() -> u32 { 5 };\n let _ = if let Some(foo) = optional {\n foo\n } else {\n 5\n };\n let _ = if let Some(foo) = optional {\n foo\n } else {\n let y = do_complicated_function();\n y*y\n };\n ```\n\n should be\n\n ```rust\n # let optional: Option = Some(0);\n # fn do_complicated_function() -> u32 { 5 };\n let _ = optional.map_or(5, |foo| foo);\n let _ = optional.map_or_else(||{\n let y = do_complicated_function();\n y*y\n }, |foo| foo);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "overflow_check_conditional", - "id_span": { - "path": "src/overflow_check_conditional.rs", - "line": 21 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Detects classic underflow/overflow checks.\n **Why is this bad?** Most classic C underflow/overflow checks will fail in\n Rust. Users can use functions like `overflowing_*` and `wrapping_*` instead.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let a = 1;\n # let b = 2;\n a + b < a;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "panic_in_result_fn", - "id_span": { - "path": "src/panic_in_result_fn.rs", - "line": 29 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of `panic!`, `unimplemented!`, `todo!`, `unreachable!` or assertions in a function of type result.\n **Why is this bad?** For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence panicking macros should be avoided.\n\n **Known problems:** Functions called from a function returning a `Result` may invoke a panicking macro. This is not checked.\n\n **Example:**\n\n ```rust\n fn result_with_panic() -> Result\n {\n panic!(\"error\");\n }\n ```\n Use instead:\n ```rust\n fn result_without_panic() -> Result {\n Err(String::from(\"error\"))\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "panic", - "id_span": { - "path": "src/panic_unimplemented.rs", - "line": 19 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of `panic!`.\n **Why is this bad?** `panic!` will stop the execution of the executable\n\n **Known problems:** None.\n\n **Example:**\n ```no_run\n panic!(\"even with a good reason\");\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "unimplemented", - "id_span": { - "path": "src/panic_unimplemented.rs", - "line": 35 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of `unimplemented!`.\n **Why is this bad?** This macro should not be present in production code\n\n **Known problems:** None.\n\n **Example:**\n ```no_run\n unimplemented!();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "todo", - "id_span": { - "path": "src/panic_unimplemented.rs", - "line": 51 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of `todo!`.\n **Why is this bad?** This macro should not be present in production code\n\n **Known problems:** None.\n\n **Example:**\n ```no_run\n todo!();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "unreachable", - "id_span": { - "path": "src/panic_unimplemented.rs", - "line": 67 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for usage of `unreachable!`.\n **Why is this bad?** This macro can cause code to panic\n\n **Known problems:** None.\n\n **Example:**\n ```no_run\n unreachable!();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "partialeq_ne_impl", - "id_span": { - "path": "src/partialeq_ne_impl.rs", - "line": 27 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for manual re-implementations of `PartialEq::ne`.\n **Why is this bad?** `PartialEq::ne` is required to always return the\n negated result of `PartialEq::eq`, which is exactly what the default\n implementation does. Therefore, there should never be any need to\n re-implement it.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n struct Foo;\n\n impl PartialEq for Foo {\n fn eq(&self, other: &Foo) -> bool { true }\n fn ne(&self, other: &Foo) -> bool { !(self == other) }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "trivially_copy_pass_by_ref", - "id_span": { - "path": "src/pass_by_ref_or_value.rs", - "line": 52 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for functions taking arguments by reference, where the argument type is `Copy` and small enough to be more efficient to always\n pass by value.\n\n **Why is this bad?** In many calling conventions instances of structs will\n be passed through registers if they fit into two or less general purpose\n registers.\n\n **Known problems:** This lint is target register size dependent, it is\n limited to 32-bit to try and reduce portability problems between 32 and\n 64-bit, but if you are compiling for 8 or 16-bit targets then the limit\n will be different.\n\n The configuration option `trivial_copy_size_limit` can be set to override\n this limit for a project.\n\n This lint attempts to allow passing arguments by reference if a reference\n to that argument is returned. This is implemented by comparing the lifetime\n of the argument and return value for equality. However, this can cause\n false positives in cases involving multiple lifetimes that are bounded by\n each other.\n\n **Example:**\n\n ```rust\n // Bad\n fn foo(v: &u32) {}\n ```\n\n ```rust\n // Better\n fn foo(v: u32) {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "large_types_passed_by_value", - "id_span": { - "path": "src/pass_by_ref_or_value.rs", - "line": 84 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for functions taking arguments by value, where the argument type is `Copy` and large enough to be worth considering\n passing by reference. Does not trigger if the function is being exported,\n because that might induce API breakage, if the parameter is declared as mutable,\n or if the argument is a `self`.\n\n **Why is this bad?** Arguments passed by value might result in an unnecessary\n shallow copy, taking up more space in the stack and requiring a call to\n `memcpy`, which can be expensive.\n\n **Example:**\n\n ```rust\n #[derive(Clone, Copy)]\n struct TooLarge([u8; 2048]);\n\n // Bad\n fn foo(v: TooLarge) {}\n ```\n ```rust\n #[derive(Clone, Copy)]\n struct TooLarge([u8; 2048]);\n\n // Good\n fn foo(v: &TooLarge) {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "path_buf_push_overwrite", - "id_span": { - "path": "src/path_buf_push_overwrite.rs", - "line": 36 - }, - "group": "clippy::nursery", - "docs": " **What it does:*** Checks for [push](https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.push) calls on `PathBuf` that can cause overwrites.\n\n **Why is this bad?** Calling `push` with a root path at the start can overwrite the\n previous defined path.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n use std::path::PathBuf;\n\n let mut x = PathBuf::from(\"/foo\");\n x.push(\"/bar\");\n assert_eq!(x, PathBuf::from(\"/bar\"));\n ```\n Could be written:\n\n ```rust\n use std::path::PathBuf;\n\n let mut x = PathBuf::from(\"/foo\");\n x.push(\"bar\");\n assert_eq!(x, PathBuf::from(\"/foo/bar\"));\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "pattern_type_mismatch", - "id_span": { - "path": "src/pattern_type_mismatch.rs", - "line": 79 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for patterns that aren't exact representations of the types they are applied to.\n\n To satisfy this lint, you will have to adjust either the expression that is matched\n against or the pattern itself, as well as the bindings that are introduced by the\n adjusted patterns. For matching you will have to either dereference the expression\n with the `*` operator, or amend the patterns to explicitly match against `&`\n or `&mut ` depending on the reference mutability. For the bindings you need\n to use the inverse. You can leave them as plain bindings if you wish for the value\n to be copied, but you must use `ref mut ` or `ref ` to construct\n a reference into the matched structure.\n\n If you are looking for a way to learn about ownership semantics in more detail, it\n is recommended to look at IDE options available to you to highlight types, lifetimes\n and reference semantics in your code. The available tooling would expose these things\n in a general way even outside of the various pattern matching mechanics. Of course\n this lint can still be used to highlight areas of interest and ensure a good understanding\n of ownership semantics.\n\n **Why is this bad?** It isn't bad in general. But in some contexts it can be desirable\n because it increases ownership hints in the code, and will guard against some changes\n in ownership.\n\n **Known problems:** None.\n\n **Example:**\n\n This example shows the basic adjustments necessary to satisfy the lint. Note how\n the matched expression is explicitly dereferenced with `*` and the `inner` variable\n is bound to a shared borrow via `ref inner`.\n\n ```rust,ignore\n // Bad\n let value = &Some(Box::new(23));\n match value {\n Some(inner) => println!(\"{}\", inner),\n None => println!(\"none\"),\n }\n\n // Good\n let value = &Some(Box::new(23));\n match *value {\n Some(ref inner) => println!(\"{}\", inner),\n None => println!(\"none\"),\n }\n ```\n\n The following example demonstrates one of the advantages of the more verbose style.\n Note how the second version uses `ref mut a` to explicitly declare `a` a shared mutable\n borrow, while `b` is simply taken by value. This ensures that the loop body cannot\n accidentally modify the wrong part of the structure.\n\n ```rust,ignore\n // Bad\n let mut values = vec![(2, 3), (3, 4)];\n for (a, b) in &mut values {\n *a += *b;\n }\n\n // Good\n let mut values = vec![(2, 3), (3, 4)];\n for &mut (ref mut a, b) in &mut values {\n *a += b;\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "precedence", - "id_span": { - "path": "src/precedence.rs", - "line": 44 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for operations where precedence may be unclear and suggests to add parentheses. Currently it catches the following:\n * mixed usage of arithmetic and bit shifting/combining operators without\n parentheses\n * a \"negative\" numeric literal (which is really a unary `-` followed by a\n numeric literal)\n followed by a method call\n\n **Why is this bad?** Not everyone knows the precedence of those operators by\n heart, so expressions like these may trip others trying to reason about the\n code.\n\n **Known problems:** None.\n\n **Example:**\n * `1 << 2 + 3` equals 32, while `(1 << 2) + 3` equals 7\n * `-1i32.abs()` equals -1, while `(-1i32).abs()` equals 1\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "ptr_arg", - "id_span": { - "path": "src/ptr.rs", - "line": 69 - }, - "group": "clippy::style", - "docs": " **What it does:** This lint checks for function arguments of type `&String` or `&Vec` unless the references are mutable. It will also suggest you\n replace `.clone()` calls with the appropriate `.to_owned()`/`to_string()`\n calls.\n\n **Why is this bad?** Requiring the argument to be of the specific size\n makes the function less useful for no benefit; slices in the form of `&[T]`\n or `&str` usually suffice and can be obtained from other types, too.\n\n **Known problems:** The lint does not follow data. So if you have an\n argument `x` and write `let y = x; y.clone()` the lint will not suggest\n changing that `.clone()` to `.to_owned()`.\n\n Other functions called from this function taking a `&String` or `&Vec`\n argument may also fail to compile if you change the argument. Applying\n this lint on them will fix the problem, but they may be in other crates.\n\n One notable example of a function that may cause issues, and which cannot\n easily be changed due to being in the standard library is `Vec::contains`.\n when called on a `Vec>`. If a `&Vec` is passed to that method then\n it will compile, but if a `&[T]` is passed then it will not compile.\n\n ```ignore\n fn cannot_take_a_slice(v: &Vec) -> bool {\n let vec_of_vecs: Vec> = some_other_fn();\n\n vec_of_vecs.contains(v)\n }\n ```\n\n Also there may be `fn(&Vec)`-typed references pointing to your function.\n If you have them, you will get a compiler error after applying this lint's\n suggestions. You then have the choice to undo your changes or change the\n type of the reference.\n\n Note that if the function is part of your public interface, there may be\n other crates referencing it, of which you may not be aware. Carefully\n deprecate the function before applying the lint suggestions in this case.\n\n **Example:**\n ```ignore\n // Bad\n fn foo(&Vec) { .. }\n\n // Good\n fn foo(&[u32]) { .. }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "cmp_null", - "id_span": { - "path": "src/ptr.rs", - "line": 95 - }, - "group": "clippy::style", - "docs": " **What it does:** This lint checks for equality comparisons with `ptr::null`\n **Why is this bad?** It's easier and more readable to use the inherent\n `.is_null()`\n method instead\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n // Bad\n if x == ptr::null {\n ..\n }\n\n // Good\n if x.is_null() {\n ..\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "mut_from_ref", - "id_span": { - "path": "src/ptr.rs", - "line": 117 - }, - "group": "clippy::correctness", - "docs": " **What it does:** This lint checks for functions that take immutable references and return mutable ones.\n\n **Why is this bad?** This is trivially unsound, as one can create two\n mutable references from the same (immutable!) source.\n This [error](https://github.com/rust-lang/rust/issues/39465)\n actually lead to an interim Rust release 1.15.1.\n\n **Known problems:** To be on the conservative side, if there's at least one\n mutable reference with the output lifetime, this lint will not trigger.\n In practice, this case is unlikely anyway.\n\n **Example:**\n ```ignore\n fn foo(&Foo) -> &mut Bar { .. }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "ptr_eq", - "id_span": { - "path": "src/ptr_eq.rs", - "line": 32 - }, - "group": "clippy::style", - "docs": " **What it does:** Use `std::ptr::eq` when applicable\n **Why is this bad?** `ptr::eq` can be used to compare `&T` references\n (which coerce to `*const T` implicitly) by their address rather than\n comparing the values they point to.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let a = &[1, 2, 3];\n let b = &[1, 2, 3];\n\n assert!(a as *const _ as usize == b as *const _ as usize);\n ```\n Use instead:\n ```rust\n let a = &[1, 2, 3];\n let b = &[1, 2, 3];\n\n assert!(std::ptr::eq(a, b));\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "ptr_offset_with_cast", - "id_span": { - "path": "src/ptr_offset_with_cast.rs", - "line": 40 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of the `offset` pointer method with a `usize` casted to an `isize`.\n\n **Why is this bad?** If we’re always increasing the pointer address, we can avoid the numeric\n cast by using the `add` method instead.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n let vec = vec![b'a', b'b', b'c'];\n let ptr = vec.as_ptr();\n let offset = 1_usize;\n\n unsafe {\n ptr.offset(offset as isize);\n }\n ```\n\n Could be written:\n\n ```rust\n let vec = vec![b'a', b'b', b'c'];\n let ptr = vec.as_ptr();\n let offset = 1_usize;\n\n unsafe {\n ptr.add(offset);\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "question_mark", - "id_span": { - "path": "src/question_mark.rs", - "line": 34 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for expressions that could be replaced by the question mark operator.\n **Why is this bad?** Question mark usage is more idiomatic.\n\n **Known problems:** None\n\n **Example:**\n ```ignore\n if option.is_none() {\n return None;\n }\n ```\n\n Could be written:\n\n ```ignore\n option?;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "range_zip_with_len", - "id_span": { - "path": "src/ranges.rs", - "line": 40 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for zipping a collection with the range of `0.._.len()`.\n\n **Why is this bad?** The code is better expressed with `.enumerate()`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let x = vec![1];\n x.iter().zip(0..x.len());\n ```\n Could be written as\n ```rust\n # let x = vec![1];\n x.iter().enumerate();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "range_plus_one", - "id_span": { - "path": "src/ranges.rs", - "line": 74 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for exclusive ranges where 1 is added to the upper bound, e.g., `x..(y+1)`.\n\n **Why is this bad?** The code is more readable with an inclusive range\n like `x..=y`.\n\n **Known problems:** Will add unnecessary pair of parentheses when the\n expression is not wrapped in a pair but starts with a opening parenthesis\n and ends with a closing one.\n I.e., `let _ = (f()+1)..(f()+1)` results in `let _ = ((f()+1)..=f())`.\n\n Also in many cases, inclusive ranges are still slower to run than\n exclusive ranges, because they essentially add an extra branch that\n LLVM may fail to hoist out of the loop.\n\n This will cause a warning that cannot be fixed if the consumer of the\n range only accepts a specific range type, instead of the generic\n `RangeBounds` trait\n ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)).\n\n **Example:**\n ```rust,ignore\n for x..(y+1) { .. }\n ```\n Could be written as\n ```rust,ignore\n for x..=y { .. }\n ```\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": "MachineApplicable" - } - }, - { - "id": "range_minus_one", - "id_span": { - "path": "src/ranges.rs", - "line": 99 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for inclusive ranges where 1 is subtracted from the upper bound, e.g., `x..=(y-1)`.\n\n **Why is this bad?** The code is more readable with an exclusive range\n like `x..y`.\n\n **Known problems:** This will cause a warning that cannot be fixed if\n the consumer of the range only accepts a specific range type, instead of\n the generic `RangeBounds` trait\n ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)).\n\n **Example:**\n ```rust,ignore\n for x..=(y-1) { .. }\n ```\n Could be written as\n ```rust,ignore\n for x..y { .. }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "reversed_empty_ranges", - "id_span": { - "path": "src/ranges.rs", - "line": 132 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for range expressions `x..y` where both `x` and `y` are constant and `x` is greater or equal to `y`.\n\n **Why is this bad?** Empty ranges yield no values so iterating them is a no-op.\n Moreover, trying to use a reversed range to index a slice will panic at run-time.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,no_run\n fn main() {\n (10..=0).for_each(|x| println!(\"{}\", x));\n\n let arr = [1, 2, 3, 4, 5];\n let sub = &arr[3..1];\n }\n ```\n Use instead:\n ```rust\n fn main() {\n (0..=10).rev().for_each(|x| println!(\"{}\", x));\n\n let arr = [1, 2, 3, 4, 5];\n let sub = &arr[1..3];\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "manual_range_contains", - "id_span": { - "path": "src/ranges.rs", - "line": 159 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for expressions like `x >= 3 && x < 8` that could be more readably expressed as `(3..8).contains(x)`.\n\n **Why is this bad?** `contains` expresses the intent better and has less\n failure modes (such as fencepost errors or using `||` instead of `&&`).\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // given\n let x = 6;\n\n assert!(x >= 3 && x < 8);\n ```\n Use instead:\n ```rust\n# let x = 6;\n assert!((3..8).contains(&x));\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "redundant_clone", - "id_span": { - "path": "src/redundant_clone.rs", - "line": 63 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for a redundant `clone()` (and its relatives) which clones an owned value that is going to be dropped without further use.\n\n **Why is this bad?** It is not always possible for the compiler to eliminate useless\n allocations and deallocations generated by redundant `clone()`s.\n\n **Known problems:**\n\n False-negatives: analysis performed by this lint is conservative and limited.\n\n **Example:**\n ```rust\n # use std::path::Path;\n # #[derive(Clone)]\n # struct Foo;\n # impl Foo {\n # fn new() -> Self { Foo {} }\n # }\n # fn call(x: Foo) {}\n {\n let x = Foo::new();\n call(x.clone());\n call(x.clone()); // this can just pass `x`\n }\n\n [\"lorem\", \"ipsum\"].join(\" \").to_string();\n\n Path::new(\"/a/b\").join(\"c\").to_path_buf();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "redundant_closure_call", - "id_span": { - "path": "src/redundant_closure_call.rs", - "line": 32 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Detects closures called in the same expression where they are defined.\n\n **Why is this bad?** It is unnecessarily adding to the expression's\n complexity.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n // Bad\n let a = (|| 42)()\n\n // Good\n let a = 42\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "redundant_else", - "id_span": { - "path": "src/redundant_else.rs", - "line": 37 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for `else` blocks that can be removed without changing semantics.\n **Why is this bad?** The `else` block adds unnecessary indentation and verbosity.\n\n **Known problems:** Some may prefer to keep the `else` block for clarity.\n\n **Example:**\n\n ```rust\n fn my_func(count: u32) {\n if count == 0 {\n print!(\"Nothing to do\");\n return;\n } else {\n print!(\"Moving on...\");\n }\n }\n ```\n Use instead:\n ```rust\n fn my_func(count: u32) {\n if count == 0 {\n print!(\"Nothing to do\");\n return;\n }\n print!(\"Moving on...\");\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "redundant_field_names", - "id_span": { - "path": "src/redundant_field_names.rs", - "line": 34 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for fields in struct literals where shorthands could be used.\n\n **Why is this bad?** If the field and variable names are the same,\n the field name is redundant.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let bar: u8 = 123;\n\n struct Foo {\n bar: u8,\n }\n\n let foo = Foo { bar: bar };\n ```\n the last line can be simplified to\n ```ignore\n let foo = Foo { bar };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "redundant_pub_crate", - "id_span": { - "path": "src/redundant_pub_crate.rs", - "line": 30 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Checks for items declared `pub(crate)` that are not crate visible because they are inside a private module.\n\n **Why is this bad?** Writing `pub(crate)` is misleading when it's redundant due to the parent\n module's visibility.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n mod internal {\n pub(crate) fn internal_fn() { }\n }\n ```\n This function is not visible outside the module and it can be declared with `pub` or\n private visibility\n ```rust\n mod internal {\n pub fn internal_fn() { }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "redundant_slicing", - "id_span": { - "path": "src/redundant_slicing.rs", - "line": 33 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for redundant slicing expressions which use the full range, and do not change the type.\n\n **Why is this bad?** It unnecessarily adds complexity to the expression.\n\n **Known problems:** If the type being sliced has an implementation of `Index`\n that actually changes anything then it can't be removed. However, this would be surprising\n to people reading the code and should have a note with it.\n\n **Example:**\n\n ```ignore\n fn get_slice(x: &[u32]) -> &[u32] {\n &x[..]\n }\n ```\n Use instead:\n ```ignore\n fn get_slice(x: &[u32]) -> &[u32] {\n x\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "redundant_static_lifetimes", - "id_span": { - "path": "src/redundant_static_lifetimes.rs", - "line": 30 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for constants and statics with an explicit `'static` lifetime.\n **Why is this bad?** Adding `'static` to every reference can create very\n complicated types.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n const FOO: &'static [(&'static str, &'static str, fn(&Bar) -> bool)] =\n &[...]\n static FOO: &'static [(&'static str, &'static str, fn(&Bar) -> bool)] =\n &[...]\n ```\n This code can be rewritten as\n ```ignore\n const FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...]\n static FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...]\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "ref_option_ref", - "id_span": { - "path": "src/ref_option_ref.rs", - "line": 28 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for usage of `&Option<&T>`.\n **Why is this bad?** Since `&` is Copy, it's useless to have a\n reference on `Option<&T>`.\n\n **Known problems:** It may be irrelevant to use this lint on\n public API code as it will make a breaking change to apply it.\n\n **Example:**\n\n ```rust,ignore\n let x: &Option<&u32> = &Some(&0u32);\n ```\n Use instead:\n ```rust,ignore\n let x: Option<&u32> = Some(&0u32);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "deref_addrof", - "id_span": { - "path": "src/reference.rs", - "line": 29 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `*&` and `*&mut` in expressions.\n **Why is this bad?** Immediately dereferencing a reference is no-op and\n makes the code less clear.\n\n **Known problems:** Multiple dereference/addrof pairs are not handled so\n the suggested fix for `x = **&&y` is `x = *&y`, which is still incorrect.\n\n **Example:**\n ```rust,ignore\n // Bad\n let a = f(*&mut b);\n let c = *&d;\n\n // Good\n let a = f(b);\n let c = d;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "ref_in_deref", - "id_span": { - "path": "src/reference.rs", - "line": 120 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for references in expressions that use auto dereference.\n\n **Why is this bad?** The reference is a no-op and is automatically\n dereferenced by the compiler and makes the code less clear.\n\n **Example:**\n ```rust\n struct Point(u32, u32);\n let point = Point(30, 20);\n let x = (&point).0;\n ```\n Use instead:\n ```rust\n # struct Point(u32, u32);\n # let point = Point(30, 20);\n let x = point.0;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "invalid_regex", - "id_span": { - "path": "src/regex.rs", - "line": 25 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks [regex](https://crates.io/crates/regex) creation (with `Regex::new`, `RegexBuilder::new`, or `RegexSet::new`) for correct\n regex syntax.\n\n **Why is this bad?** This will lead to a runtime panic.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n Regex::new(\"|\")\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "trivial_regex", - "id_span": { - "path": "src/regex.rs", - "line": 46 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Checks for trivial [regex](https://crates.io/crates/regex) creation (with `Regex::new`, `RegexBuilder::new`, or `RegexSet::new`).\n\n **Why is this bad?** Matching the regex can likely be replaced by `==` or\n `str::starts_with`, `str::ends_with` or `std::contains` or other `str`\n methods.\n\n **Known problems:** If the same regex is going to be applied to multiple\n inputs, the precomputations done by `Regex` construction can give\n significantly better performance than any of the `str`-based methods.\n\n **Example:**\n ```ignore\n Regex::new(\"^foobar\")\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "repeat_once", - "id_span": { - "path": "src/repeat_once.rs", - "line": 33 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for usage of `.repeat(1)` and suggest the following method for each types. - `.to_string()` for `str`\n - `.clone()` for `String`\n - `.to_vec()` for `slice`\n\n **Why is this bad?** For example, `String.repeat(1)` is equivalent to `.clone()`. If cloning the string is the intention behind this, `clone()` should be used.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n fn main() {\n let x = String::from(\"hello world\").repeat(1);\n }\n ```\n Use instead:\n ```rust\n fn main() {\n let x = String::from(\"hello world\").clone();\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "let_and_return", - "id_span": { - "path": "src/returns.rs", - "line": 38 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `let`-bindings, which are subsequently returned.\n\n **Why is this bad?** It is just extraneous code. Remove it to make your code\n more rusty.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn foo() -> String {\n let x = String::new();\n x\n }\n ```\n instead, use\n ```\n fn foo() -> String {\n String::new()\n }\n ```\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": "MachineApplicable" - } - }, - { - "id": "needless_return", - "id_span": { - "path": "src/returns.rs", - "line": 63 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for return statements at the end of a block.\n **Why is this bad?** Removing the `return` and semicolon will make the code\n more rusty.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn foo(x: usize) -> usize {\n return x;\n }\n ```\n simplify to\n ```rust\n fn foo(x: usize) -> usize {\n x\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "self_assignment", - "id_span": { - "path": "src/self_assignment.rs", - "line": 29 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for explicit self-assignments.\n **Why is this bad?** Self-assignments are redundant and unlikely to be\n intentional.\n\n **Known problems:** If expression contains any deref coercions or\n indexing operations they are assumed not to have any side effects.\n\n **Example:**\n\n ```rust\n struct Event {\n id: usize,\n x: i32,\n y: i32,\n }\n\n fn copy_position(a: &mut Event, b: &Event) {\n a.x = b.x;\n a.y = a.y;\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "semicolon_if_nothing_returned", - "id_span": { - "path": "src/semicolon_if_nothing_returned.rs", - "line": 30 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Looks for blocks of expressions and fires if the last expression returns `()` but is not followed by a semicolon.\n\n **Why is this bad?** The semicolon might be optional but when\n extending the block with new code, it doesn't require a change in previous last line.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n fn main() {\n println!(\"Hello world\")\n }\n ```\n Use instead:\n ```rust\n fn main() {\n println!(\"Hello world\");\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "serde_api_misuse", - "id_span": { - "path": "src/serde_api.rs", - "line": 16 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for mis-uses of the serde API.\n **Why is this bad?** Serde is very finnicky about how its API should be\n used, but the type system can't be used to enforce it (yet?).\n\n **Known problems:** None.\n\n **Example:** Implementing `Visitor::visit_string` but not\n `Visitor::visit_str`.\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "shadow_same", - "id_span": { - "path": "src/shadow.rs", - "line": 34 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for bindings that shadow other bindings already in scope, while just changing reference level or mutability.\n\n **Why is this bad?** Not much, in fact it's a very common pattern in Rust\n code. Still, some may opt to avoid it in their code base, they can set this\n lint to `Warn`.\n\n **Known problems:** This lint, as the other shadowing related lints,\n currently only catches very simple patterns.\n\n **Example:**\n ```rust\n # let x = 1;\n // Bad\n let x = &x;\n\n // Good\n let y = &x; // use different variable name\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "shadow_reuse", - "id_span": { - "path": "src/shadow.rs", - "line": 61 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for bindings that shadow other bindings already in scope, while reusing the original value.\n\n **Why is this bad?** Not too much, in fact it's a common pattern in Rust\n code. Still, some argue that name shadowing like this hurts readability,\n because a value may be bound to different things depending on position in\n the code.\n\n **Known problems:** This lint, as the other shadowing related lints,\n currently only catches very simple patterns.\n\n **Example:**\n ```rust\n let x = 2;\n let x = x + 1;\n ```\n use different variable name:\n ```rust\n let x = 2;\n let y = x + 1;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "shadow_unrelated", - "id_span": { - "path": "src/shadow.rs", - "line": 93 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for bindings that shadow other bindings already in scope, either without a initialization or with one that does not even use\n the original value.\n\n **Why is this bad?** Name shadowing can hurt readability, especially in\n large code bases, because it is easy to lose track of the active binding at\n any place in the code. This can be alleviated by either giving more specific\n names to bindings or introducing more scopes to contain the bindings.\n\n **Known problems:** This lint, as the other shadowing related lints,\n currently only catches very simple patterns. Note that\n `allow`/`warn`/`deny`/`forbid` attributes only work on the function level\n for this lint.\n\n **Example:**\n ```rust\n # let y = 1;\n # let z = 2;\n let x = y;\n\n // Bad\n let x = z; // shadows the earlier binding\n\n // Good\n let w = z; // use different variable name\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "single_component_path_imports", - "id_span": { - "path": "src/single_component_path_imports.rs", - "line": 32 - }, - "group": "clippy::style", - "docs": " **What it does:** Checking for imports with single component use path.\n **Why is this bad?** Import with single component use path such as `use cratename;`\n is not necessary, and thus should be removed.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,ignore\n use regex;\n\n fn main() {\n regex::Regex::new(r\"^\\d{4}-\\d{2}-\\d{2}$\").unwrap();\n }\n ```\n Better as\n ```rust,ignore\n fn main() {\n regex::Regex::new(r\"^\\d{4}-\\d{2}-\\d{2}$\").unwrap();\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "size_of_in_element_count", - "id_span": { - "path": "src/size_of_in_element_count.rs", - "line": 31 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Detects expressions where `size_of::` or `size_of_val::` is used as a\n count of elements of type `T`\n\n **Why is this bad?** These functions expect a count\n of `T` and not a number of bytes\n\n **Known problems:** None.\n\n **Example:**\n ```rust,no_run\n # use std::ptr::copy_nonoverlapping;\n # use std::mem::size_of;\n const SIZE: usize = 128;\n let x = [2u8; SIZE];\n let mut y = [2u8; SIZE];\n unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::() * SIZE) };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "slow_vector_initialization", - "id_span": { - "path": "src/slow_vector_initialization.rs", - "line": 37 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks slow zero-filled vector initialization\n **Why is this bad?** These structures are non-idiomatic and less efficient than simply using\n `vec![0; len]`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # use core::iter::repeat;\n # let len = 4;\n\n // Bad\n let mut vec1 = Vec::with_capacity(len);\n vec1.resize(len, 0);\n\n let mut vec2 = Vec::with_capacity(len);\n vec2.extend(repeat(0).take(len));\n\n // Good\n let mut vec1 = vec![0; len];\n let mut vec2 = vec![0; len];\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "stable_sort_primitive", - "id_span": { - "path": "src/stable_sort_primitive.rs", - "line": 36 - }, - "group": "clippy::perf", - "docs": " **What it does:** When sorting primitive values (integers, bools, chars, as well\n as arrays, slices, and tuples of such items), it is better to\n use an unstable sort than a stable sort.\n\n **Why is this bad?**\n Using a stable sort consumes more memory and cpu cycles. Because\n values which compare equal are identical, preserving their\n relative order (the guarantee that a stable sort provides) means\n nothing, while the extra costs still apply.\n\n **Known problems:**\n None\n\n **Example:**\n\n ```rust\n let mut vec = vec![2, 1, 3];\n vec.sort();\n ```\n Use instead:\n ```rust\n let mut vec = vec![2, 1, 3];\n vec.sort_unstable();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "string_add_assign", - "id_span": { - "path": "src/strings.rs", - "line": 37 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for string appends of the form `x = x + y` (without `let`!).\n\n **Why is this bad?** It's not really bad, but some people think that the\n `.push_str(_)` method is more readable.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let mut x = \"Hello\".to_owned();\n x = x + \", World\";\n\n // More readable\n x += \", World\";\n x.push_str(\", World\");\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "string_add", - "id_span": { - "path": "src/strings.rs", - "line": 65 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for all instances of `x + _` where `x` is of type `String`, but only if [`string_add_assign`](#string_add_assign) does *not*\n match.\n\n **Why is this bad?** It's not bad in and of itself. However, this particular\n `Add` implementation is asymmetric (the other operand need not be `String`,\n but `x` does), while addition as mathematically defined is symmetric, also\n the `String::push_str(_)` function is a perfectly good replacement.\n Therefore, some dislike it and wish not to have it in their code.\n\n That said, other people think that string addition, having a long tradition\n in other languages is actually fine, which is why we decided to make this\n particular lint `allow` by default.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let x = \"Hello\".to_owned();\n x + \", World\";\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "string_lit_as_bytes", - "id_span": { - "path": "src/strings.rs", - "line": 107 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Checks for the `as_bytes` method called on string literals that contain only ASCII characters.\n\n **Why is this bad?** Byte string literals (e.g., `b\"foo\"`) can be used\n instead. They are shorter but less discoverable than `as_bytes()`.\n\n **Known Problems:**\n `\"str\".as_bytes()` and the suggested replacement of `b\"str\"` are not\n equivalent because they have different types. The former is `&[u8]`\n while the latter is `&[u8; 3]`. That means in general they will have a\n different set of methods and different trait implementations.\n\n ```compile_fail\n fn f(v: Vec) {}\n\n f(\"...\".as_bytes().to_owned()); // works\n f(b\"...\".to_owned()); // does not work, because arg is [u8; 3] not Vec\n\n fn g(r: impl std::io::Read) {}\n\n g(\"...\".as_bytes()); // works\n g(b\"...\"); // does not work\n ```\n\n The actual equivalent of `\"str\".as_bytes()` with the same type is not\n `b\"str\"` but `&b\"str\"[..]`, which is a great deal of punctuation and not\n more readable than a function call.\n\n **Example:**\n ```rust\n // Bad\n let bs = \"a byte string\".as_bytes();\n\n // Good\n let bs = b\"a byte string\";\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "string_from_utf8_as_bytes", - "id_span": { - "path": "src/strings.rs", - "line": 196 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Check if the string is transformed to byte array and casted back to string.\n **Why is this bad?** It's unnecessary, the string can be used directly.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n let _ = std::str::from_utf8(&\"Hello World!\".as_bytes()[6..11]).unwrap();\n ```\n could be written as\n ```rust\n let _ = &\"Hello World!\"[6..11];\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "str_to_string", - "id_span": { - "path": "src/strings.rs", - "line": 313 - }, - "group": "clippy::restriction", - "docs": " **What it does:** This lint checks for `.to_string()` method calls on values of type `&str`.\n **Why is this bad?** The `to_string` method is also used on other types to convert them to a string.\n When called on a `&str` it turns the `&str` into the owned variant `String`, which can be better\n expressed with `.to_owned()`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // example code where clippy issues a warning\n let _ = \"str\".to_string();\n ```\n Use instead:\n ```rust\n // example code which does not raise clippy warning\n let _ = \"str\".to_owned();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "string_to_string", - "id_span": { - "path": "src/strings.rs", - "line": 362 - }, - "group": "clippy::restriction", - "docs": " **What it does:** This lint checks for `.to_string()` method calls on values of type `String`.\n **Why is this bad?** The `to_string` method is also used on other types to convert them to a string.\n When called on a `String` it only clones the `String`, which can be better expressed with `.clone()`.\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // example code where clippy issues a warning\n let msg = String::from(\"Hello World\");\n let _ = msg.to_string();\n ```\n Use instead:\n ```rust\n // example code which does not raise clippy warning\n let msg = String::from(\"Hello World\");\n let _ = msg.clone();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "suspicious_operation_groupings", - "id_span": { - "path": "src/suspicious_operation_groupings.rs", - "line": 60 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for unlikely usages of binary operators that are almost\n certainly typos and/or copy/paste errors, given the other usages\n of binary operators nearby.\n **Why is this bad?**\n They are probably bugs and if they aren't then they look like bugs\n and you should add a comment explaining why you are doing such an\n odd set of operations.\n **Known problems:**\n There may be some false positives if you are trying to do something\n unusual that happens to look like a typo.\n\n **Example:**\n\n ```rust\n struct Vec3 {\n x: f64,\n y: f64,\n z: f64,\n }\n\n impl Eq for Vec3 {}\n\n impl PartialEq for Vec3 {\n fn eq(&self, other: &Self) -> bool {\n // This should trigger the lint because `self.x` is compared to `other.y`\n self.x == other.y && self.y == other.y && self.z == other.z\n }\n }\n ```\n Use instead:\n ```rust\n # struct Vec3 {\n # x: f64,\n # y: f64,\n # z: f64,\n # }\n // same as above except:\n impl PartialEq for Vec3 {\n fn eq(&self, other: &Self) -> bool {\n // Note we now compare other.x to self.x\n self.x == other.x && self.y == other.y && self.z == other.z\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "suspicious_arithmetic_impl", - "id_span": { - "path": "src/suspicious_trait_impl.rs", - "line": 27 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Lints for suspicious operations in impls of arithmetic operators, e.g. subtracting elements in an Add impl.\n\n **Why this is bad?** This is probably a typo or copy-and-paste error and not intended.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n impl Add for Foo {\n type Output = Foo;\n\n fn add(self, other: Foo) -> Foo {\n Foo(self.0 - other.0)\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "suspicious_op_assign_impl", - "id_span": { - "path": "src/suspicious_trait_impl.rs", - "line": 48 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Lints for suspicious operations in impls of OpAssign, e.g. subtracting elements in an AddAssign impl.\n\n **Why this is bad?** This is probably a typo or copy-and-paste error and not intended.\n\n **Known problems:** None.\n\n **Example:**\n ```ignore\n impl AddAssign for Foo {\n fn add_assign(&mut self, other: Foo) {\n *self = *self - other;\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "manual_swap", - "id_span": { - "path": "src/swap.rs", - "line": 36 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for manual swapping.\n **Why is this bad?** The `std::mem::swap` function exposes the intent better\n without deinitializing or copying either variable.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let mut a = 42;\n let mut b = 1337;\n\n let t = b;\n b = a;\n a = t;\n ```\n Use std::mem::swap():\n ```rust\n let mut a = 1;\n let mut b = 2;\n std::mem::swap(&mut a, &mut b);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "almost_swapped", - "id_span": { - "path": "src/swap.rs", - "line": 61 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for `foo = bar; bar = foo` sequences.\n **Why is this bad?** This looks like a failed attempt to swap.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let mut a = 1;\n # let mut b = 2;\n a = b;\n b = a;\n ```\n If swapping is intended, use `swap()` instead:\n ```rust\n # let mut a = 1;\n # let mut b = 2;\n std::mem::swap(&mut a, &mut b);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "tabs_in_doc_comments", - "id_span": { - "path": "src/tabs_in_doc_comments.rs", - "line": 54 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks doc comments for usage of tab characters.\n **Why is this bad?** The rust style-guide promotes spaces instead of tabs for indentation.\n To keep a consistent view on the source, also doc comments should not have tabs.\n Also, explaining ascii-diagrams containing tabs can get displayed incorrectly when the\n display settings of the author and reader differ.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n ///\n /// Struct to hold two strings:\n /// \t- first\t\tone\n /// \t- second\tone\n pub struct DoubleString {\n ///\n /// \t- First String:\n /// \t\t- needs to be inside here\n first_string: String,\n ///\n /// \t- Second String:\n /// \t\t- needs to be inside here\n second_string: String,\n}\n ```\n\n Will be converted to:\n ```rust\n ///\n /// Struct to hold two strings:\n /// - first one\n /// - second one\n pub struct DoubleString {\n ///\n /// - First String:\n /// - needs to be inside here\n first_string: String,\n ///\n /// - Second String:\n /// - needs to be inside here\n second_string: String,\n}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "temporary_assignment", - "id_span": { - "path": "src/temporary_assignment.rs", - "line": 19 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for construction of a structure or tuple just to assign a value in it.\n\n **Why is this bad?** Readability. If the structure is only created to be\n updated, why not write the structure you want in the first place?\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n (0, 0).0 = 1\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "to_digit_is_some", - "id_span": { - "path": "src/to_digit_is_some.rs", - "line": 27 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for `.to_digit(..).is_some()` on `char`s.\n **Why is this bad?** This is a convoluted way of checking if a `char` is a digit. It's\n more straight forward to use the dedicated `is_digit` method.\n\n **Example:**\n ```rust\n # let c = 'c';\n # let radix = 10;\n let is_digit = c.to_digit(radix).is_some();\n ```\n can be written as:\n ```\n # let c = 'c';\n # let radix = 10;\n let is_digit = c.is_digit(radix);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "to_string_in_display", - "id_span": { - "path": "src/to_string_in_display.rs", - "line": 40 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for uses of `to_string()` in `Display` traits.\n **Why is this bad?** Usually `to_string` is implemented indirectly\n via `Display`. Hence using it while implementing `Display` would\n lead to infinite recursion.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n use std::fmt;\n\n struct Structure(i32);\n impl fmt::Display for Structure {\n fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n write!(f, \"{}\", self.to_string())\n }\n }\n\n ```\n Use instead:\n ```rust\n use std::fmt;\n\n struct Structure(i32);\n impl fmt::Display for Structure {\n fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n write!(f, \"{}\", self.0)\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "type_repetition_in_bounds", - "id_span": { - "path": "src/trait_bounds.rs", - "line": 28 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** This lint warns about unnecessary type repetitions in trait bounds\n **Why is this bad?** Repeating the type for every bound makes the code\n less readable than combining the bounds\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n pub fn foo(t: T) where T: Copy, T: Clone {}\n ```\n\n Could be written as:\n\n ```rust\n pub fn foo(t: T) where T: Copy + Clone {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "trait_duplication_in_bounds", - "id_span": { - "path": "src/trait_bounds.rs", - "line": 57 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for cases where generics are being used and multiple syntax specifications for trait bounds are used simultaneously.\n\n **Why is this bad?** Duplicate bounds makes the code\n less readable than specifing them only once.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn func(arg: T) where T: Clone + Default {}\n ```\n\n Could be written as:\n\n ```rust\n fn func(arg: T) {}\n ```\n or\n\n ```rust\n fn func(arg: T) where T: Clone + Default {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "wrong_transmute", - "id_span": { - "path": "src/transmute/mod.rs", - "line": 34 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for transmutes that can't ever be correct on any architecture.\n\n **Why is this bad?** It's basically guaranteed to be undefined behaviour.\n\n **Known problems:** When accessing C, users might want to store pointer\n sized objects in `extradata` arguments to save an allocation.\n\n **Example:**\n ```ignore\n let ptr: *const T = core::intrinsics::transmute('x')\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "useless_transmute", - "id_span": { - "path": "src/transmute/mod.rs", - "line": 53 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Checks for transmutes to the original type of the object and transmutes that could be a cast.\n\n **Why is this bad?** Readability. The code tricks people into thinking that\n something complex is going on.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n core::intrinsics::transmute(t); // where the result type is the same as `t`'s\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "transmutes_expressible_as_ptr_casts", - "id_span": { - "path": "src/transmute/mod.rs", - "line": 78 - }, - "group": "clippy::complexity", - "docs": " **What it does:**Checks for transmutes that could be a pointer cast.\n **Why is this bad?** Readability. The code tricks people into thinking that\n something complex is going on.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n # let p: *const [i32] = &[];\n unsafe { std::mem::transmute::<*const [i32], *const [u16]>(p) };\n ```\n Use instead:\n ```rust\n # let p: *const [i32] = &[];\n p as *const [u16];\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "crosspointer_transmute", - "id_span": { - "path": "src/transmute/mod.rs", - "line": 96 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for transmutes between a type `T` and `*T`.\n **Why is this bad?** It's easy to mistakenly transmute between a type and a\n pointer to that type.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n core::intrinsics::transmute(t) // where the result type is the same as\n // `*t` or `&t`'s\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "transmute_ptr_to_ref", - "id_span": { - "path": "src/transmute/mod.rs", - "line": 121 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for transmutes from a pointer to a reference.\n **Why is this bad?** This can always be rewritten with `&` and `*`.\n\n **Known problems:**\n - `mem::transmute` in statics and constants is stable from Rust 1.46.0,\n while dereferencing raw pointer is not stable yet.\n If you need to do this in those places,\n you would have to use `transmute` instead.\n\n **Example:**\n ```rust,ignore\n unsafe {\n let _: &T = std::mem::transmute(p); // where p: *const T\n }\n\n // can be written:\n let _: &T = &*p;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "transmute_int_to_char", - "id_span": { - "path": "src/transmute/mod.rs", - "line": 152 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for transmutes from an integer to a `char`.\n **Why is this bad?** Not every integer is a Unicode scalar value.\n\n **Known problems:**\n - [`from_u32`] which this lint suggests using is slower than `transmute`\n as it needs to validate the input.\n If you are certain that the input is always a valid Unicode scalar value,\n use [`from_u32_unchecked`] which is as fast as `transmute`\n but has a semantically meaningful name.\n - You might want to handle `None` returned from [`from_u32`] instead of calling `unwrap`.\n\n [`from_u32`]: https://doc.rust-lang.org/std/char/fn.from_u32.html\n [`from_u32_unchecked`]: https://doc.rust-lang.org/std/char/fn.from_u32_unchecked.html\n\n **Example:**\n ```rust\n let x = 1_u32;\n unsafe {\n let _: char = std::mem::transmute(x); // where x: u32\n }\n\n // should be:\n let _ = std::char::from_u32(x).unwrap();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "transmute_bytes_to_str", - "id_span": { - "path": "src/transmute/mod.rs", - "line": 183 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for transmutes from a `&[u8]` to a `&str`.\n **Why is this bad?** Not every byte slice is a valid UTF-8 string.\n\n **Known problems:**\n - [`from_utf8`] which this lint suggests using is slower than `transmute`\n as it needs to validate the input.\n If you are certain that the input is always a valid UTF-8,\n use [`from_utf8_unchecked`] which is as fast as `transmute`\n but has a semantically meaningful name.\n - You might want to handle errors returned from [`from_utf8`] instead of calling `unwrap`.\n\n [`from_utf8`]: https://doc.rust-lang.org/std/str/fn.from_utf8.html\n [`from_utf8_unchecked`]: https://doc.rust-lang.org/std/str/fn.from_utf8_unchecked.html\n\n **Example:**\n ```rust\n let b: &[u8] = &[1_u8, 2_u8];\n unsafe {\n let _: &str = std::mem::transmute(b); // where b: &[u8]\n }\n\n // should be:\n let _ = std::str::from_utf8(b).unwrap();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "transmute_int_to_bool", - "id_span": { - "path": "src/transmute/mod.rs", - "line": 205 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for transmutes from an integer to a `bool`.\n **Why is this bad?** This might result in an invalid in-memory representation of a `bool`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = 1_u8;\n unsafe {\n let _: bool = std::mem::transmute(x); // where x: u8\n }\n\n // should be:\n let _: bool = x != 0;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "transmute_int_to_float", - "id_span": { - "path": "src/transmute/mod.rs", - "line": 227 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for transmutes from an integer to a float.\n **Why is this bad?** Transmutes are dangerous and error-prone, whereas `from_bits` is intuitive\n and safe.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n unsafe {\n let _: f32 = std::mem::transmute(1_u32); // where x: u32\n }\n\n // should be:\n let _: f32 = f32::from_bits(1_u32);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "transmute_float_to_int", - "id_span": { - "path": "src/transmute/mod.rs", - "line": 249 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for transmutes from a float to an integer.\n **Why is this bad?** Transmutes are dangerous and error-prone, whereas `to_bits` is intuitive\n and safe.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n unsafe {\n let _: u32 = std::mem::transmute(1f32);\n }\n\n // should be:\n let _: u32 = 1f32.to_bits();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "transmute_ptr_to_ptr", - "id_span": { - "path": "src/transmute/mod.rs", - "line": 276 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for transmutes from a pointer to a pointer, or from a reference to a reference.\n\n **Why is this bad?** Transmutes are dangerous, and these can instead be\n written as casts.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let ptr = &1u32 as *const u32;\n unsafe {\n // pointer-to-pointer transmute\n let _: *const f32 = std::mem::transmute(ptr);\n // ref-ref transmute\n let _: &f32 = std::mem::transmute(&1u32);\n }\n // These can be respectively written:\n let _ = ptr as *const f32;\n let _ = unsafe{ &*(&1u32 as *const u32 as *const f32) };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "unsound_collection_transmute", - "id_span": { - "path": "src/transmute/mod.rs", - "line": 304 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for transmutes between collections whose types have different ABI, size or alignment.\n\n **Why is this bad?** This is undefined behavior.\n\n **Known problems:** Currently, we cannot know whether a type is a\n collection, so we just lint the ones that come with `std`.\n\n **Example:**\n ```rust\n // different size, therefore likely out-of-bounds memory access\n // You absolutely do not want this in your code!\n unsafe {\n std::mem::transmute::<_, Vec>(vec![2_u16])\n };\n ```\n\n You must always iterate, map and collect the values:\n\n ```rust\n vec![2_u16].into_iter().map(u32::from).collect::>();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "transmuting_null", - "id_span": { - "path": "src/transmuting_null.rs", - "line": 23 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for transmute calls which would receive a null pointer.\n **Why is this bad?** Transmuting a null pointer is undefined behavior.\n\n **Known problems:** Not all cases can be detected at the moment of this writing.\n For example, variables which hold a null pointer and are then fed to a `transmute`\n call, aren't detectable yet.\n\n **Example:**\n ```rust\n let null_ref: &u64 = unsafe { std::mem::transmute(0 as *const u64) };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "try_err", - "id_span": { - "path": "src/try_err.rs", - "line": 43 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for usages of `Err(x)?`.\n **Why is this bad?** The `?` operator is designed to allow calls that\n can fail to be easily chained. For example, `foo()?.bar()` or\n `foo(bar()?)`. Because `Err(x)?` can't be used that way (it will\n always return), it is more clear to write `return Err(x)`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn foo(fail: bool) -> Result {\n if fail {\n Err(\"failed\")?;\n }\n Ok(0)\n }\n ```\n Could be written:\n\n ```rust\n fn foo(fail: bool) -> Result {\n if fail {\n return Err(\"failed\".into());\n }\n Ok(0)\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "box_vec", - "id_span": { - "path": "src/types.rs", - "line": 66 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for use of `Box>` anywhere in the code. Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information.\n\n **Why is this bad?** `Vec` already keeps its contents in a separate area on\n the heap. So if you `Box` it, you just add another level of indirection\n without any benefit whatsoever.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n struct X {\n values: Box>,\n }\n ```\n\n Better:\n\n ```rust,ignore\n struct X {\n values: Vec,\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "vec_box", - "id_span": { - "path": "src/types.rs", - "line": 95 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for use of `Vec>` where T: Sized anywhere in the code. Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information.\n\n **Why is this bad?** `Vec` already keeps its contents in a separate area on\n the heap. So if you `Box` its contents, you just add another level of indirection.\n\n **Known problems:** Vec> makes sense if T is a large type (see [#3530](https://github.com/rust-lang/rust-clippy/issues/3530),\n 1st comment).\n\n **Example:**\n ```rust\n struct X {\n values: Vec>,\n }\n ```\n\n Better:\n\n ```rust\n struct X {\n values: Vec,\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "option_option", - "id_span": { - "path": "src/types.rs", - "line": 133 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for use of `Option>` in function signatures and type definitions\n\n **Why is this bad?** `Option<_>` represents an optional value. `Option>`\n represents an optional optional value which is logically the same thing as an optional\n value but has an unneeded extra level of wrapping.\n\n If you have a case where `Some(Some(_))`, `Some(None)` and `None` are distinct cases,\n consider a custom `enum` instead, with clear names for each case.\n\n **Known problems:** None.\n\n **Example**\n ```rust\n fn get_data() -> Option> {\n None\n }\n ```\n\n Better:\n\n ```rust\n pub enum Contents {\n Data(Vec), // Was Some(Some(Vec))\n NotYetFetched, // Was Some(None)\n None, // Was None\n }\n\n fn get_data() -> Contents {\n Contents::None\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "linkedlist", - "id_span": { - "path": "src/types.rs", - "line": 169 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for usage of any `LinkedList`, suggesting to use a `Vec` or a `VecDeque` (formerly called `RingBuf`).\n\n **Why is this bad?** Gankro says:\n\n > The TL;DR of `LinkedList` is that it's built on a massive amount of\n pointers and indirection.\n > It wastes memory, it has terrible cache locality, and is all-around slow.\n `RingBuf`, while\n > \"only\" amortized for push/pop, should be faster in the general case for\n almost every possible\n > workload, and isn't even amortized at all if you can predict the capacity\n you need.\n >\n > `LinkedList`s are only really good if you're doing a lot of merging or\n splitting of lists.\n > This is because they can just mangle some pointers instead of actually\n copying the data. Even\n > if you're doing a lot of insertion in the middle of the list, `RingBuf`\n can still be better\n > because of how expensive it is to seek to the middle of a `LinkedList`.\n\n **Known problems:** False positives – the instances where using a\n `LinkedList` makes sense are few and far between, but they can still happen.\n\n **Example:**\n ```rust\n # use std::collections::LinkedList;\n let x: LinkedList = LinkedList::new();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "borrowed_box", - "id_span": { - "path": "src/types.rs", - "line": 193 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for use of `&Box` anywhere in the code. Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information.\n\n **Why is this bad?** Any `&Box` can also be a `&T`, which is more\n general.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n fn foo(bar: &Box) { ... }\n ```\n\n Better:\n\n ```rust,ignore\n fn foo(bar: &T) { ... }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "Unspecified" - } - }, - { - "id": "redundant_allocation", - "id_span": { - "path": "src/types.rs", - "line": 217 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for use of redundant allocations anywhere in the code.\n **Why is this bad?** Expressions such as `Rc<&T>`, `Rc>`, `Rc>`, `Box<&T>`\n add an unnecessary level of indirection.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # use std::rc::Rc;\n fn foo(bar: Rc<&usize>) {}\n ```\n\n Better:\n\n ```rust\n fn foo(bar: &usize) {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "rc_buffer", - "id_span": { - "path": "src/types.rs", - "line": 248 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for `Rc` and `Arc` when `T` is a mutable buffer type such as `String` or `Vec`.\n **Why is this bad?** Expressions such as `Rc` usually have no advantage over `Rc`, since\n it is larger and involves an extra level of indirection, and doesn't implement `Borrow`.\n\n While mutating a buffer type would still be possible with `Rc::get_mut()`, it only\n works if there are no additional references yet, which usually defeats the purpose of\n enclosing it in a shared ownership type. Instead, additionally wrapping the inner\n type with an interior mutable container (such as `RefCell` or `Mutex`) would normally\n be used.\n\n **Known problems:** This pattern can be desirable to avoid the overhead of a `RefCell` or `Mutex` for\n cases where mutation only happens before there are any additional references.\n\n **Example:**\n ```rust,ignore\n # use std::rc::Rc;\n fn foo(interned: Rc) { ... }\n ```\n\n Better:\n\n ```rust,ignore\n fn foo(interned: Rc) { ... }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "let_unit_value", - "id_span": { - "path": "src/types.rs", - "line": 768 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for binding a unit value.\n **Why is this bad?** A unit value cannot usefully be used anywhere. So\n binding one is kind of pointless.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = {\n 1;\n };\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "unit_cmp", - "id_span": { - "path": "src/types.rs", - "line": 849 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for comparisons to unit. This includes all binary comparisons (like `==` and `<`) and asserts.\n\n **Why is this bad?** Unit is always equal to itself, and thus is just a\n clumsily written constant. Mostly this happens when someone accidentally\n adds semicolons at the end of the operands.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # fn foo() {};\n # fn bar() {};\n # fn baz() {};\n if {\n foo();\n } == {\n bar();\n } {\n baz();\n }\n ```\n is equal to\n ```rust\n # fn foo() {};\n # fn bar() {};\n # fn baz() {};\n {\n foo();\n bar();\n baz();\n }\n ```\n\n For asserts:\n ```rust\n # fn foo() {};\n # fn bar() {};\n assert_eq!({ foo(); }, { bar(); });\n ```\n will always succeed\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "unit_arg", - "id_span": { - "path": "src/types.rs", - "line": 922 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for passing a unit value as an argument to a function without using a unit literal (`()`).\n\n **Why is this bad?** This is likely the result of an accidental semicolon.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n foo({\n let a = bar();\n baz(a);\n })\n ```\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": "MachineApplicable" - } - }, - { - "id": "cast_precision_loss", - "id_span": { - "path": "src/types.rs", - "line": 1158 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for casts from any numerical to a float type where the receiving type cannot store all values from the original type without\n rounding errors. This possible rounding is to be expected, so this lint is\n `Allow` by default.\n\n Basically, this warns on casting any integer with 32 or more bits to `f32`\n or any 64-bit integer to `f64`.\n\n **Why is this bad?** It's not bad at all. But in some applications it can be\n helpful to know where precision loss can take place. This lint can help find\n those places in the code.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = u64::MAX;\n x as f64;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "cast_sign_loss", - "id_span": { - "path": "src/types.rs", - "line": 1179 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for casts from a signed to an unsigned numerical type. In this case, negative values wrap around to large positive values,\n which can be quite surprising in practice. However, as the cast works as\n defined, this lint is `Allow` by default.\n\n **Why is this bad?** Possibly surprising results. You can activate this lint\n as a one-time check to see where numerical wrapping can arise.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let y: i8 = -1;\n y as u128; // will return 18446744073709551615\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "cast_possible_truncation", - "id_span": { - "path": "src/types.rs", - "line": 1201 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for casts between numerical types that may truncate large values. This is expected behavior, so the cast is `Allow` by\n default.\n\n **Why is this bad?** In some problem domains, it is good practice to avoid\n truncation. This lint can be activated to help assess where additional\n checks could be beneficial.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn as_u8(x: u64) -> u8 {\n x as u8\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "cast_possible_wrap", - "id_span": { - "path": "src/types.rs", - "line": 1224 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for casts from an unsigned type to a signed type of the same size. Performing such a cast is a 'no-op' for the compiler,\n i.e., nothing is changed at the bit level, and the binary representation of\n the value is reinterpreted. This can cause wrapping if the value is too big\n for the target signed type. However, the cast works as defined, so this lint\n is `Allow` by default.\n\n **Why is this bad?** While such a cast is not bad in itself, the results can\n be surprising when this is not the intended behavior, as demonstrated by the\n example below.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n u32::MAX as i32; // will yield a value of `-1`\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "cast_lossless", - "id_span": { - "path": "src/types.rs", - "line": 1256 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for casts between numerical types that may be replaced by safe conversion functions.\n\n **Why is this bad?** Rust's `as` keyword will perform many kinds of\n conversions, including silently lossy conversions. Conversion functions such\n as `i32::from` will only perform lossless conversions. Using the conversion\n functions prevents conversions from turning into silent lossy conversions if\n the types of the input expressions ever change, and make it easier for\n people reading the code to know that the conversion is lossless.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn as_u64(x: u8) -> u64 {\n x as u64\n }\n ```\n\n Using `::from` would look like this:\n\n ```rust\n fn as_u64(x: u8) -> u64 {\n u64::from(x)\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "unnecessary_cast", - "id_span": { - "path": "src/types.rs", - "line": 1281 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for casts to the same type, casts of int literals to integer types and casts of float literals to float types.\n\n **Why is this bad?** It's just unnecessary.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let _ = 2i32 as i32;\n let _ = 0.5 as f32;\n ```\n\n Better:\n\n ```rust\n let _ = 2_i32;\n let _ = 0.5_f32;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "cast_ptr_alignment", - "id_span": { - "path": "src/types.rs", - "line": 1305 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for casts, using `as` or `pointer::cast`, from a less-strictly-aligned pointer to a more-strictly-aligned pointer\n\n **Why is this bad?** Dereferencing the resulting pointer may be undefined\n behavior.\n\n **Known problems:** Using `std::ptr::read_unaligned` and `std::ptr::write_unaligned` or similar\n on the resulting pointer is fine. Is over-zealous: Casts with manual alignment checks or casts like\n u64-> u8 -> u16 can be fine. Miri is able to do a more in-depth analysis.\n\n **Example:**\n ```rust\n let _ = (&1u8 as *const u8) as *const u16;\n let _ = (&mut 1u8 as *mut u8) as *mut u16;\n\n (&1u8 as *const u8).cast::();\n (&mut 1u8 as *mut u8).cast::();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "fn_to_numeric_cast", - "id_span": { - "path": "src/types.rs", - "line": 1332 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for casts of function pointers to something other than usize\n **Why is this bad?**\n Casting a function pointer to anything other than usize/isize is not portable across\n architectures, because you end up losing bits if the target type is too small or end up with a\n bunch of extra bits that waste space and add more instructions to the final binary than\n strictly necessary for the problem\n\n Casting to isize also doesn't make sense since there are no signed addresses.\n\n **Example**\n\n ```rust\n // Bad\n fn fun() -> i32 { 1 }\n let a = fun as i64;\n\n // Good\n fn fun2() -> i32 { 1 }\n let a = fun2 as usize;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "fn_to_numeric_cast_with_truncation", - "id_span": { - "path": "src/types.rs", - "line": 1362 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for casts of a function pointer to a numeric type not wide enough to store address.\n\n **Why is this bad?**\n Such a cast discards some bits of the function's address. If this is intended, it would be more\n clearly expressed by casting to usize first, then casting the usize to the intended type (with\n a comment) to perform the truncation.\n\n **Example**\n\n ```rust\n // Bad\n fn fn1() -> i16 {\n 1\n };\n let _ = fn1 as i32;\n\n // Better: Cast to usize first, then comment with the reason for the truncation\n fn fn2() -> i16 {\n 1\n };\n let fn_ptr = fn2 as usize;\n let fn_ptr_truncated = fn_ptr as i32;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "type_complexity", - "id_span": { - "path": "src/types.rs", - "line": 1897 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for types used in structs, parameters and `let` declarations above a certain complexity threshold.\n\n **Why is this bad?** Too complex types make the code less readable. Consider\n using a `type` definition to simplify them.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # use std::rc::Rc;\n struct Foo {\n inner: Rc>>>,\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "char_lit_as_u8", - "id_span": { - "path": "src/types.rs", - "line": 2068 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for expressions where a character literal is cast to `u8` and suggests using a byte literal instead.\n\n **Why is this bad?** In general, casting values to smaller types is\n error-prone and should be avoided where possible. In the particular case of\n converting a character literal to u8, it is easy to avoid by just using a\n byte literal instead. As an added bonus, `b'a'` is even slightly shorter\n than `'a' as u8`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n 'x' as u8\n ```\n\n A better version, using the byte literal:\n\n ```rust,ignore\n b'x'\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "absurd_extreme_comparisons", - "id_span": { - "path": "src/types.rs", - "line": 2133 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for comparisons where one side of the relation is either the minimum or maximum value for its type and warns if it involves a\n case that is always true or always false. Only integer and boolean types are\n checked.\n\n **Why is this bad?** An expression like `min <= x` may misleadingly imply\n that it is possible for `x` to be less than the minimum. Expressions like\n `max < x` are probably mistakes.\n\n **Known problems:** For `usize` the size of the current compile target will\n be assumed (e.g., 64 bits on 64 bit systems). This means code that uses such\n a comparison to detect target pointer width will trigger this lint. One can\n use `mem::sizeof` and compare its value or conditional compilation\n attributes\n like `#[cfg(target_pointer_width = \"64\")] ..` instead.\n\n **Example:**\n\n ```rust\n let vec: Vec = Vec::new();\n if vec.len() <= 0 {}\n if 100 > i32::MAX {}\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "invalid_upcast_comparisons", - "id_span": { - "path": "src/types.rs", - "line": 2294 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for comparisons where the relation is always either true or false, but where one side has been upcast so that the comparison is\n necessary. Only integer types are checked.\n\n **Why is this bad?** An expression like `let x : u8 = ...; (x as u32) > 300`\n will mistakenly imply that it is possible for `x` to be outside the range of\n `u8`.\n\n **Known problems:**\n https://github.com/rust-lang/rust-clippy/issues/886\n\n **Example:**\n ```rust\n let x: u8 = 1;\n (x as u32) > 300;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "implicit_hasher", - "id_span": { - "path": "src/types.rs", - "line": 2515 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for public `impl` or `fn` missing generalization over different hashers and implicitly defaulting to the default hashing\n algorithm (`SipHash`).\n\n **Why is this bad?** `HashMap` or `HashSet` with custom hashers cannot be\n used with them.\n\n **Known problems:** Suggestions for replacing constructors can contain\n false-positives. Also applying suggestions can require modification of other\n pieces of code, possibly including external crates.\n\n **Example:**\n ```rust\n # use std::collections::HashMap;\n # use std::hash::{Hash, BuildHasher};\n # trait Serialize {};\n impl Serialize for HashMap { }\n\n pub fn foo(map: &mut HashMap) { }\n ```\n could be rewritten as\n ```rust\n # use std::collections::HashMap;\n # use std::hash::{Hash, BuildHasher};\n # trait Serialize {};\n impl Serialize for HashMap { }\n\n pub fn foo(map: &mut HashMap) { }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "cast_ref_to_mut", - "id_span": { - "path": "src/types.rs", - "line": 2866 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for casts of `&T` to `&mut T` anywhere in the code.\n **Why is this bad?** It’s basically guaranteed to be undefined behaviour.\n `UnsafeCell` is the only way to obtain aliasable data that is considered\n mutable.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n fn x(r: &i32) {\n unsafe {\n *(r as *const _ as *mut _) += 1;\n }\n }\n ```\n\n Instead consider using interior mutability types.\n\n ```rust\n use std::cell::UnsafeCell;\n\n fn x(r: &UnsafeCell) {\n unsafe {\n *r.get() += 1;\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "ptr_as_ptr", - "id_span": { - "path": "src/types.rs", - "line": 2922 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for `as` casts between raw pointers without changing its mutability,\n namely `*const T` to `*const U` and `*mut T` to `*mut U`.\n\n **Why is this bad?**\n Though `as` casts between raw pointers is not terrible, `pointer::cast` is safer because\n it cannot accidentally change the pointer's mutability nor cast the pointer to other types like `usize`.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let ptr: *const u32 = &42_u32;\n let mut_ptr: *mut u32 = &mut 42_u32;\n let _ = ptr as *const i32;\n let _ = mut_ptr as *mut i32;\n ```\n Use instead:\n ```rust\n let ptr: *const u32 = &42_u32;\n let mut_ptr: *mut u32 = &mut 42_u32;\n let _ = ptr.cast::();\n let _ = mut_ptr.cast::();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "undropped_manually_drops", - "id_span": { - "path": "src/undropped_manually_drops.rs", - "line": 27 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Prevents the safe `std::mem::drop` function from being called on `std::mem::ManuallyDrop`.\n **Why is this bad?** The safe `drop` function does not drop the inner value of a `ManuallyDrop`.\n\n **Known problems:** Does not catch cases if the user binds `std::mem::drop`\n to a different name and calls it that way.\n\n **Example:**\n\n ```rust\n struct S;\n drop(std::mem::ManuallyDrop::new(S));\n ```\n Use instead:\n ```rust\n struct S;\n unsafe {\n std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S));\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "invisible_characters", - "id_span": { - "path": "src/unicode.rs", - "line": 20 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for invisible Unicode characters in the code.\n **Why is this bad?** Having an invisible character in the code makes for all\n sorts of April fools, but otherwise is very much frowned upon.\n\n **Known problems:** None.\n\n **Example:** You don't see it, but there may be a zero-width space or soft hyphen\n some­where in this text.\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "non_ascii_literal", - "id_span": { - "path": "src/unicode.rs", - "line": 44 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for non-ASCII characters in string literals.\n **Why is this bad?** Yeah, we know, the 90's called and wanted their charset\n back. Even so, there still are editors and other programs out there that\n don't work well with Unicode. So if the code is meant to be used\n internationally, on multiple operating systems, or has other portability\n requirements, activating this lint could be useful.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n let x = String::from(\"€\");\n ```\n Could be written as:\n ```rust\n let x = String::from(\"\\u{20ac}\");\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "unicode_not_nfc", - "id_span": { - "path": "src/unicode.rs", - "line": 61 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for string literals that contain Unicode in a form that is not equal to its\n [NFC-recomposition](http://www.unicode.org/reports/tr15/#Norm_Forms).\n\n **Why is this bad?** If such a string is compared to another, the results\n may be surprising.\n\n **Known problems** None.\n\n **Example:** You may not see it, but \"à\"\" and \"à\"\" aren't the same string. The\n former when escaped is actually `\"a\\u{300}\"` while the latter is `\"\\u{e0}\"`.\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "unit_return_expecting_ord", - "id_span": { - "path": "src/unit_return_expecting_ord.rs", - "line": 30 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for functions that expect closures of type Fn(...) -> Ord where the implemented closure returns the unit type.\n The lint also suggests to remove the semi-colon at the end of the statement if present.\n\n **Why is this bad?** Likely, returning the unit type is unintentional, and\n could simply be caused by an extra semi-colon. Since () implements Ord\n it doesn't cause a compilation error.\n This is the same reasoning behind the unit_cmp lint.\n\n **Known problems:** If returning unit is intentional, then there is no\n way of specifying this without triggering needless_return lint\n\n **Example:**\n\n ```rust\n let mut twins = vec!((1, 1), (2, 2));\n twins.sort_by_key(|x| { x.1; });\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "fn_address_comparisons", - "id_span": { - "path": "src/unnamed_address.rs", - "line": 27 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for comparisons with an address of a function item.\n **Why is this bad?** Function item address is not guaranteed to be unique and could vary\n between different code generation units. Furthermore different function items could have\n the same address after being merged together.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n type F = fn();\n fn a() {}\n let f: F = a;\n if f == a {\n // ...\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "vtable_address_comparisons", - "id_span": { - "path": "src/unnamed_address.rs", - "line": 51 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for comparisons with an address of a trait vtable.\n **Why is this bad?** Comparing trait objects pointers compares an vtable addresses which\n are not guaranteed to be unique and could vary between different code generation units.\n Furthermore vtables for different types could have the same address after being merged\n together.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,ignore\n let a: Rc = ...\n let b: Rc = ...\n if Rc::ptr_eq(&a, &b) {\n ...\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "unnecessary_sort_by", - "id_span": { - "path": "src/unnecessary_sort_by.rs", - "line": 41 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Detects uses of `Vec::sort_by` passing in a closure\n which compares the two arguments, either directly or indirectly.\n\n **Why is this bad?**\n It is more clear to use `Vec::sort_by_key` (or `Vec::sort` if\n possible) than to use `Vec::sort_by` and a more complicated\n closure.\n\n **Known problems:**\n If the suggested `Vec::sort_by_key` uses Reverse and it isn't already\n imported by a use statement, then it will need to be added manually.\n\n **Example:**\n\n ```rust\n # struct A;\n # impl A { fn foo(&self) {} }\n # let mut vec: Vec = Vec::new();\n vec.sort_by(|a, b| a.foo().cmp(&b.foo()));\n ```\n Use instead:\n ```rust\n # struct A;\n # impl A { fn foo(&self) {} }\n # let mut vec: Vec = Vec::new();\n vec.sort_by_key(|a| a.foo());\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "unnecessary_wraps", - "id_span": { - "path": "src/unnecessary_wraps.rs", - "line": 50 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for private functions that only return `Ok` or `Some`.\n **Why is this bad?** It is not meaningful to wrap values when no `None` or `Err` is returned.\n\n **Known problems:** There can be false positives if the function signature is designed to\n fit some external requirement.\n\n **Example:**\n\n ```rust\n fn get_cool_number(a: bool, b: bool) -> Option {\n if a && b {\n return Some(50);\n }\n if a {\n Some(0)\n } else {\n Some(10)\n }\n }\n ```\n Use instead:\n ```rust\n fn get_cool_number(a: bool, b: bool) -> i32 {\n if a && b {\n return 50;\n }\n if a {\n 0\n } else {\n 10\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "unnested_or_patterns", - "id_span": { - "path": "src/unnested_or_patterns.rs", - "line": 47 - }, - "group": "clippy::pedantic", - "docs": " **What it does:**\n Checks for unnested or-patterns, e.g., `Some(0) | Some(2)` and\n suggests replacing the pattern with a nested one, `Some(0 | 2)`.\n\n Another way to think of this is that it rewrites patterns in\n *disjunctive normal form (DNF)* into *conjunctive normal form (CNF)*.\n\n **Why is this bad?**\n\n In the example above, `Some` is repeated, which unncessarily complicates the pattern.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n fn main() {\n if let Some(0) | Some(2) = Some(0) {}\n }\n ```\n Use instead:\n ```rust\n #![feature(or_patterns)]\n\n fn main() {\n if let Some(0 | 2) = Some(0) {}\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "unsafe_removed_from_name", - "id_span": { - "path": "src/unsafe_removed_from_name.rs", - "line": 24 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for imports that remove \"unsafe\" from an item's name.\n\n **Why is this bad?** Renaming makes it less clear which traits and\n structures are unsafe.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n use std::cell::{UnsafeCell as TotallySafeCell};\n\n extern crate crossbeam;\n use crossbeam::{spawn_unsafe as spawn};\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "unused_io_amount", - "id_span": { - "path": "src/unused_io_amount.rs", - "line": 28 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for unused written/read amount.\n **Why is this bad?** `io::Write::write(_vectored)` and\n `io::Read::read(_vectored)` are not guaranteed to\n process the entire buffer. They return how many bytes were processed, which\n might be smaller\n than a given buffer's length. If you don't need to deal with\n partial-write/read, use\n `write_all`/`read_exact` instead.\n\n **Known problems:** Detects only common patterns.\n\n **Example:**\n ```rust,ignore\n use std::io;\n fn foo(w: &mut W) -> io::Result<()> {\n // must be `w.write_all(b\"foo\")?;`\n w.write(b\"foo\")?;\n Ok(())\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "unused_self", - "id_span": { - "path": "src/unused_self.rs", - "line": 33 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks methods that contain a `self` argument but don't use it\n **Why is this bad?** It may be clearer to define the method as an associated function instead\n of an instance method if it doesn't require `self`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust,ignore\n struct A;\n impl A {\n fn method(&self) {}\n }\n ```\n\n Could be written:\n\n ```rust,ignore\n struct A;\n impl A {\n fn method() {}\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "unused_unit", - "id_span": { - "path": "src/unused_unit.rs", - "line": 27 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for unit (`()`) expressions that can be removed.\n **Why is this bad?** Such expressions add no value, but can make the code\n less readable. Depending on formatting they can make a `break` or `return`\n statement look like a function call.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n fn return_unit() -> () {\n ()\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "unnecessary_unwrap", - "id_span": { - "path": "src/unwrap.rs", - "line": 40 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for calls of `unwrap[_err]()` that cannot fail.\n **Why is this bad?** Using `if let` or `match` is more idiomatic.\n\n **Known problems:** None\n\n **Example:**\n ```rust\n # let option = Some(0);\n # fn do_something_with(_x: usize) {}\n if option.is_some() {\n do_something_with(option.unwrap())\n }\n ```\n\n Could be written:\n\n ```rust\n # let option = Some(0);\n # fn do_something_with(_x: usize) {}\n if let Some(value) = option {\n do_something_with(value)\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "panicking_unwrap", - "id_span": { - "path": "src/unwrap.rs", - "line": 63 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Checks for calls of `unwrap[_err]()` that will always fail.\n **Why is this bad?** If panicking is desired, an explicit `panic!()` should be used.\n\n **Known problems:** This lint only checks `if` conditions not assignments.\n So something like `let x: Option<()> = None; x.unwrap();` will not be recognized.\n\n **Example:**\n ```rust\n # let option = Some(0);\n # fn do_something_with(_x: usize) {}\n if option.is_none() {\n do_something_with(option.unwrap())\n }\n ```\n\n This code will always panic. The if condition should probably be inverted.\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "unwrap_in_result", - "id_span": { - "path": "src/unwrap_in_result.rs", - "line": 47 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for functions of type Result that contain `expect()` or `unwrap()`\n **Why is this bad?** These functions promote recoverable errors to non-recoverable errors which may be undesirable in code bases which wish to avoid panics.\n\n **Known problems:** This can cause false positives in functions that handle both recoverable and non recoverable errors.\n\n **Example:**\n Before:\n ```rust\n fn divisible_by_3(i_str: String) -> Result<(), String> {\n let i = i_str\n .parse::()\n .expect(\"cannot divide the input by three\");\n\n if i % 3 != 0 {\n Err(\"Number is not divisible by 3\")?\n }\n\n Ok(())\n }\n ```\n\n After:\n ```rust\n fn divisible_by_3(i_str: String) -> Result<(), String> {\n let i = i_str\n .parse::()\n .map_err(|e| format!(\"cannot divide the input by three: {}\", e))?;\n\n if i % 3 != 0 {\n Err(\"Number is not divisible by 3\")?\n }\n\n Ok(())\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "upper_case_acronyms", - "id_span": { - "path": "src/upper_case_acronyms.rs", - "line": 35 - }, - "group": "clippy::style", - "docs": " **What it does:** Checks for fully capitalized names and optionally names containing a capitalized acronym.\n **Why is this bad?** In CamelCase, acronyms count as one word.\n See [naming conventions](https://rust-lang.github.io/api-guidelines/naming.html#casing-conforms-to-rfc-430-c-case)\n for more.\n\n By default, the lint only triggers on fully-capitalized names.\n You can use the `upper-case-acronyms-aggressive: true` config option to enable linting\n on all camel case names\n\n **Known problems:** When two acronyms are contiguous, the lint can't tell where\n the first acronym ends and the second starts, so it suggests to lowercase all of\n the letters in the second acronym.\n\n **Example:**\n\n ```rust\n struct HTTPResponse;\n ```\n Use instead:\n ```rust\n struct HttpResponse;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "use_self", - "id_span": { - "path": "src/use_self.rs", - "line": 53 - }, - "group": "clippy::nursery", - "docs": " **What it does:** Checks for unnecessary repetition of structure name when a replacement with `Self` is applicable.\n\n **Why is this bad?** Unnecessary repetition. Mixed use of `Self` and struct\n name\n feels inconsistent.\n\n **Known problems:**\n - Unaddressed false negative in fn bodies of trait implementations\n - False positive with assotiated types in traits (#4140)\n\n **Example:**\n\n ```rust\n struct Foo {}\n impl Foo {\n fn new() -> Foo {\n Foo {}\n }\n }\n ```\n could be\n ```rust\n struct Foo {}\n impl Foo {\n fn new() -> Self {\n Self {}\n }\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "useless_conversion", - "id_span": { - "path": "src/useless_conversion.rs", - "line": 32 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for `Into`, `TryInto`, `From`, `TryFrom`, or `IntoIter` calls which uselessly convert to the same type.\n\n **Why is this bad?** Redundant code.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n // Bad\n // format!() returns a `String`\n let s: String = format!(\"hello\").into();\n\n // Good\n let s: String = format!(\"hello\");\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "useless_vec", - "id_span": { - "path": "src/vec.rs", - "line": 36 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for usage of `&vec![..]` when using `&[..]` would be possible.\n\n **Why is this bad?** This is less efficient.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # fn foo(my_vec: &[u8]) {}\n\n // Bad\n foo(&vec![1, 2]);\n\n // Good\n foo(&[1, 2]);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "vec_init_then_push", - "id_span": { - "path": "src/vec_init_then_push.rs", - "line": 32 - }, - "group": "clippy::perf", - "docs": " **What it does:** Checks for calls to `push` immediately after creating a new `Vec`.\n **Why is this bad?** The `vec![]` macro is both more performant and easier to read than\n multiple `push` calls.\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust\n let mut v = Vec::new();\n v.push(0);\n ```\n Use instead:\n ```rust\n let v = vec![0];\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "HasPlaceholders" - } - }, - { - "id": "vec_resize_to_zero", - "id_span": { - "path": "src/vec_resize_to_zero.rs", - "line": 24 - }, - "group": "clippy::correctness", - "docs": " **What it does:** Finds occurrences of `Vec::resize(0, an_int)`\n **Why is this bad?** This is probably an argument inversion mistake.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n vec!(1, 2, 3, 4, 5).resize(0, 5)\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MaybeIncorrect" - } - }, - { - "id": "verbose_file_reads", - "id_span": { - "path": "src/verbose_file_reads.rs", - "line": 29 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for use of File::read_to_end and File::read_to_string.\n **Why is this bad?** `fs::{read, read_to_string}` provide the same functionality when `buf` is empty with fewer imports and no intermediate values.\n See also: [fs::read docs](https://doc.rust-lang.org/std/fs/fn.read.html), [fs::read_to_string docs](https://doc.rust-lang.org/std/fs/fn.read_to_string.html)\n\n **Known problems:** None.\n\n **Example:**\n\n ```rust,no_run\n # use std::io::Read;\n # use std::fs::File;\n let mut f = File::open(\"foo.txt\").unwrap();\n let mut bytes = Vec::new();\n f.read_to_end(&mut bytes).unwrap();\n ```\n Can be written more concisely as\n ```rust,no_run\n # use std::fs;\n let mut bytes = fs::read(\"foo.txt\").unwrap();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "wildcard_dependencies", - "id_span": { - "path": "src/wildcard_dependencies.rs", - "line": 24 - }, - "group": "clippy::cargo", - "docs": " **What it does:** Checks for wildcard dependencies in the `Cargo.toml`.\n **Why is this bad?** [As the edition guide says](https://rust-lang-nursery.github.io/edition-guide/rust-2018/cargo-and-crates-io/crates-io-disallows-wildcard-dependencies.html),\n it is highly unlikely that you work with any possible version of your dependency,\n and wildcard dependencies would cause unnecessary breakage in the ecosystem.\n\n **Known problems:** None.\n\n **Example:**\n\n ```toml\n [dependencies]\n regex = \"*\"\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "enum_glob_use", - "id_span": { - "path": "src/wildcard_imports.rs", - "line": 32 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for `use Enum::*`.\n **Why is this bad?** It is usually better style to use the prefixed name of\n an enumeration variant, rather than importing variants.\n\n **Known problems:** Old-style enumerations that prefix the variants are\n still around.\n\n **Example:**\n ```rust,ignore\n // Bad\n use std::cmp::Ordering::*;\n foo(Less);\n\n // Good\n use std::cmp::Ordering;\n foo(Ordering::Less)\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "wildcard_imports", - "id_span": { - "path": "src/wildcard_imports.rs", - "line": 83 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for wildcard imports `use _::*`.\n **Why is this bad?** wildcard imports can pollute the namespace. This is especially bad if\n you try to import something through a wildcard, that already has been imported by name from\n a different source:\n\n ```rust,ignore\n use crate1::foo; // Imports a function named foo\n use crate2::*; // Has a function named foo\n\n foo(); // Calls crate1::foo\n ```\n\n This can lead to confusing error messages at best and to unexpected behavior at worst.\n\n **Exceptions:**\n\n Wildcard imports are allowed from modules named `prelude`. Many crates (including the standard library)\n provide modules named \"prelude\" specifically designed for wildcard import.\n\n `use super::*` is allowed in test modules. This is defined as any module with \"test\" in the name.\n\n These exceptions can be disabled using the `warn-on-all-wildcard-imports` configuration flag.\n\n **Known problems:** If macros are imported through the wildcard, this macro is not included\n by the suggestion and has to be added by hand.\n\n Applying the suggestion when explicit imports of the things imported with a glob import\n exist, may result in `unused_imports` warnings.\n\n **Example:**\n\n ```rust,ignore\n // Bad\n use crate1::*;\n\n foo();\n ```\n\n ```rust,ignore\n // Good\n use crate1::foo;\n\n foo();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "println_empty_string", - "id_span": { - "path": "src/write.rs", - "line": 33 - }, - "group": "clippy::style", - "docs": " **What it does:** This lint warns when you use `println!(\"\")` to print a newline.\n\n **Why is this bad?** You should use `println!()`, which is simpler.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n println!(\"\");\n\n // Good\n println!();\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "print_with_newline", - "id_span": { - "path": "src/write.rs", - "line": 57 - }, - "group": "clippy::style", - "docs": " **What it does:** This lint warns when you use `print!()` with a format string that ends in a newline.\n\n **Why is this bad?** You should use `println!()` instead, which appends the\n newline.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # let name = \"World\";\n print!(\"Hello {}!\\n\", name);\n ```\n use println!() instead\n ```rust\n # let name = \"World\";\n println!(\"Hello {}!\", name);\n ```\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": "MachineApplicable" - } - }, - { - "id": "print_stdout", - "id_span": { - "path": "src/write.rs", - "line": 75 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for printing on *stdout*. The purpose of this lint is to catch debugging remnants.\n\n **Why is this bad?** People often print on *stdout* while debugging an\n application and might forget to remove those prints afterward.\n\n **Known problems:** Only catches `print!` and `println!` calls.\n\n **Example:**\n ```rust\n println!(\"Hello world!\");\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "print_stderr", - "id_span": { - "path": "src/write.rs", - "line": 93 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for printing on *stderr*. The purpose of this lint is to catch debugging remnants.\n\n **Why is this bad?** People often print on *stderr* while debugging an\n application and might forget to remove those prints afterward.\n\n **Known problems:** Only catches `eprint!` and `eprintln!` calls.\n\n **Example:**\n ```rust\n eprintln!(\"Hello world!\");\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "use_debug", - "id_span": { - "path": "src/write.rs", - "line": 110 - }, - "group": "clippy::restriction", - "docs": " **What it does:** Checks for use of `Debug` formatting. The purpose of this lint is to catch debugging remnants.\n\n **Why is this bad?** The purpose of the `Debug` trait is to facilitate\n debugging Rust code. It should not be used in user-facing output.\n\n **Example:**\n ```rust\n # let foo = \"bar\";\n println!(\"{:?}\", foo);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "print_literal", - "id_span": { - "path": "src/write.rs", - "line": 133 - }, - "group": "clippy::style", - "docs": " **What it does:** This lint warns about the use of literals as `print!`/`println!` args.\n **Why is this bad?** Using literals as `println!` args is inefficient\n (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary\n (i.e., just put the literal in the format string)\n\n **Known problems:** Will also warn with macro calls as arguments that expand to literals\n -- e.g., `println!(\"{}\", env!(\"FOO\"))`.\n\n **Example:**\n ```rust\n println!(\"{}\", \"foo\");\n ```\n use the literal without formatting:\n ```rust\n println!(\"foo\");\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "writeln_empty_string", - "id_span": { - "path": "src/write.rs", - "line": 156 - }, - "group": "clippy::style", - "docs": " **What it does:** This lint warns when you use `writeln!(buf, \"\")` to print a newline.\n\n **Why is this bad?** You should use `writeln!(buf)`, which is simpler.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # use std::fmt::Write;\n # let mut buf = String::new();\n // Bad\n writeln!(buf, \"\");\n\n // Good\n writeln!(buf);\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": "MachineApplicable" - } - }, - { - "id": "write_with_newline", - "id_span": { - "path": "src/write.rs", - "line": 182 - }, - "group": "clippy::style", - "docs": " **What it does:** This lint warns when you use `write!()` with a format string that\n ends in a newline.\n\n **Why is this bad?** You should use `writeln!()` instead, which appends the\n newline.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n # use std::fmt::Write;\n # let mut buf = String::new();\n # let name = \"World\";\n // Bad\n write!(buf, \"Hello {}!\\n\", name);\n\n // Good\n writeln!(buf, \"Hello {}!\", name);\n ```\n", - "applicability": { - "is_multi_suggestion": true, - "applicability": "MachineApplicable" - } - }, - { - "id": "write_literal", - "id_span": { - "path": "src/write.rs", - "line": 207 - }, - "group": "clippy::style", - "docs": " **What it does:** This lint warns about the use of literals as `write!`/`writeln!` args.\n **Why is this bad?** Using literals as `writeln!` args is inefficient\n (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary\n (i.e., just put the literal in the format string)\n\n **Known problems:** Will also warn with macro calls as arguments that expand to literals\n -- e.g., `writeln!(buf, \"{}\", env!(\"FOO\"))`.\n\n **Example:**\n ```rust\n # use std::fmt::Write;\n # let mut buf = String::new();\n // Bad\n writeln!(buf, \"{}\", \"foo\");\n\n // Good\n writeln!(buf, \"foo\");\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "zero_divided_by_zero", - "id_span": { - "path": "src/zero_div_zero.rs", - "line": 23 - }, - "group": "clippy::complexity", - "docs": " **What it does:** Checks for `0.0 / 0.0`.\n **Why is this bad?** It's less readable than `f32::NAN` or `f64::NAN`.\n\n **Known problems:** None.\n\n **Example:**\n ```rust\n // Bad\n let nan = 0.0f32 / 0.0;\n\n // Good\n let nan = f32::NAN;\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - }, - { - "id": "zero_sized_map_values", - "id_span": { - "path": "src/zero_sized_map_values.rs", - "line": 37 - }, - "group": "clippy::pedantic", - "docs": " **What it does:** Checks for maps with zero-sized value types anywhere in the code.\n **Why is this bad?** Since there is only a single value for a zero-sized type, a map\n containing zero sized values is effectively a set. Using a set in that case improves\n readability and communicates intent more clearly.\n\n **Known problems:**\n * A zero-sized type cannot be recovered later if it contains private fields.\n * This lints the signature of public items\n\n **Example:**\n\n ```rust\n # use std::collections::HashMap;\n fn unique_words(text: &str) -> HashMap<&str, ()> {\n todo!();\n }\n ```\n Use instead:\n ```rust\n # use std::collections::HashSet;\n fn unique_words(text: &str) -> HashSet<&str> {\n todo!();\n }\n ```\n", - "applicability": { - "is_multi_suggestion": false, - "applicability": null - } - } -] diff --git a/tests/dogfood.rs b/tests/dogfood.rs index b0da0bfcc35..604968ad8b0 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -41,10 +41,6 @@ fn dogfood_clippy() { command.args(&["-D", "clippy::internal"]); } - if cfg!(feature = "metadata-collector-lint") { - command.args(&["-D", "clippy::internal"]); - } - let output = command.output().unwrap(); println!("status: {}", output.status); diff --git a/tests/ui-internal/metadata-collector/track_applicability_value.rs b/tests/ui-internal/metadata-collector/track_applicability_value.rs deleted file mode 100644 index b0f59e5cf8a..00000000000 --- a/tests/ui-internal/metadata-collector/track_applicability_value.rs +++ /dev/null @@ -1,50 +0,0 @@ -#![deny(clippy::internal)] -#![feature(rustc_private)] - -extern crate rustc_ast; -extern crate rustc_errors; -extern crate rustc_lint; -extern crate rustc_session; -extern crate rustc_span; - -use rustc_ast::ast::Expr; -use rustc_errors::{Applicability, DiagnosticBuilder}; -use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Span; - -fn producer_fn() -> Applicability { - Applicability::MachineApplicable -} - -fn modifier_fn(applicability: &mut Applicability) { - if let Applicability::MaybeIncorrect = applicability { - *applicability = Applicability::HasPlaceholders; - } -} - -fn consumer_fn(_applicability: Applicability) {} - -struct Muh; - -impl Muh { - fn producer_method() -> Applicability { - Applicability::MachineApplicable - } -} - -fn main() { - let mut applicability = producer_fn(); - applicability = Applicability::MachineApplicable; - applicability = Muh::producer_method(); - - applicability = if true { - Applicability::HasPlaceholders - } else { - Applicability::MaybeIncorrect - }; - - modifier_fn(&mut applicability); - - consumer_fn(applicability); -} -- cgit 1.4.1-3-g733a5 From c1fa1102d484b74cb7bc425994fa4246dd690ad3 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Fri, 12 Mar 2021 21:52:01 +0100 Subject: ENABLE_METADATA_COLLECTION env-value to disable default metadata collection --- clippy_lints/src/lib.rs | 6 +++++- .../src/utils/internal_lints/metadata_collector.rs | 18 ------------------ tests/dogfood.rs | 2 ++ 3 files changed, 7 insertions(+), 19 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 7668d4dd0b1..1a74f641554 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1005,7 +1005,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass); } #[cfg(feature = "metadata-collector-lint")] - store.register_late_pass(|| box utils::internal_lints::metadata_collector::MetadataCollector::default()); + { + if std::env::var("ENABLE_METADATA_COLLECTION").eq(&Ok("1".to_string())) { + store.register_late_pass(|| box utils::internal_lints::metadata_collector::MetadataCollector::default()); + } + } store.register_late_pass(|| box utils::author::Author); store.register_late_pass(|| box await_holding_invalid::AwaitHolding); diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 37e90bf8686..07abae31386 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -148,24 +148,6 @@ struct LintMetadata { applicability: Option, } -// impl Ord for LintMetadata { -// fn cmp(&self, other: &Self) -> Ordering { -// self.id.cmp(&other.id) -// } -// } -// -// impl PartialOrd for LintMetadata { -// fn partial_cmp(&self, other: &Self) -> Option { -// Some(self.cmp(other)) -// } -// } -// -// impl PartialEq for LintMetadata { -// fn eq(&self, other: &Self) -> bool { -// self.id == other.id -// } -// } - impl LintMetadata { fn new(id: String, id_span: SerializableSpan, group: String, docs: String) -> Self { Self { diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 604968ad8b0..6524fd4706c 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -22,12 +22,14 @@ fn dogfood_clippy() { return; } let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let enable_metadata_collection = std::env::var("ENABLE_METADATA_COLLECTION").unwrap_or_else(|_| "0".to_string()); let mut command = Command::new(&*CLIPPY_PATH); command .current_dir(root_dir) .env("CLIPPY_DOGFOOD", "1") .env("CARGO_INCREMENTAL", "0") + .env("ENABLE_METADATA_COLLECTION", &enable_metadata_collection) .arg("clippy") .arg("--all-targets") .arg("--all-features") -- cgit 1.4.1-3-g733a5 From 2b38399920ad97cc9db61e50549fd4af54d4b38d Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 5 May 2021 14:44:32 -0500 Subject: Improve eval_order_dependence output --- clippy_lints/src/eval_order_dependence.rs | 2 +- tests/ui/eval_order_dependence.stderr | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/eval_order_dependence.rs b/clippy_lints/src/eval_order_dependence.rs index 762f64fe37a..8815ca27494 100644 --- a/clippy_lints/src/eval_order_dependence.rs +++ b/clippy_lints/src/eval_order_dependence.rs @@ -305,7 +305,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ReadVisitor<'a, 'tcx> { self.cx, EVAL_ORDER_DEPENDENCE, expr.span, - "unsequenced read of a variable", + &format!("unsequenced read of `{}`", self.cx.tcx.hir().name(self.var)), Some(self.write_expr.span), "whether read occurs before this write depends on evaluation order", ); diff --git a/tests/ui/eval_order_dependence.stderr b/tests/ui/eval_order_dependence.stderr index 8f4fa2228f7..cf4adbdfa24 100644 --- a/tests/ui/eval_order_dependence.stderr +++ b/tests/ui/eval_order_dependence.stderr @@ -1,4 +1,4 @@ -error: unsequenced read of a variable +error: unsequenced read of `x` --> $DIR/eval_order_dependence.rs:15:9 | LL | } + x; @@ -11,7 +11,7 @@ note: whether read occurs before this write depends on evaluation order LL | x = 1; | ^^^^^ -error: unsequenced read of a variable +error: unsequenced read of `x` --> $DIR/eval_order_dependence.rs:18:5 | LL | x += { @@ -23,7 +23,7 @@ note: whether read occurs before this write depends on evaluation order LL | x = 20; | ^^^^^^ -error: unsequenced read of a variable +error: unsequenced read of `x` --> $DIR/eval_order_dependence.rs:31:12 | LL | a: x, @@ -35,7 +35,7 @@ note: whether read occurs before this write depends on evaluation order LL | x = 6; | ^^^^^ -error: unsequenced read of a variable +error: unsequenced read of `x` --> $DIR/eval_order_dependence.rs:40:9 | LL | x += { -- cgit 1.4.1-3-g733a5 From 7a7b8bd3e885ec97725579d89ba3ac12a1e6f93f Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 5 May 2021 15:02:47 -0500 Subject: Fix eval_order_dependence async false positive --- clippy_lints/src/eval_order_dependence.rs | 28 ++++++++++++++-------------- tests/ui/eval_order_dependence.rs | 6 ++++++ tests/ui/eval_order_dependence.stderr | 16 ++++++++-------- 3 files changed, 28 insertions(+), 22 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/eval_order_dependence.rs b/clippy_lints/src/eval_order_dependence.rs index 8815ca27494..41acf55dd7d 100644 --- a/clippy_lints/src/eval_order_dependence.rs +++ b/clippy_lints/src/eval_order_dependence.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_note}; use clippy_utils::{get_parent_expr, path_to_local, path_to_local_id}; +use if_chain::if_chain; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, Guard, HirId, Local, Node, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -70,20 +71,19 @@ declare_lint_pass!(EvalOrderDependence => [EVAL_ORDER_DEPENDENCE, DIVERGING_SUB_ impl<'tcx> LateLintPass<'tcx> for EvalOrderDependence { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // Find a write to a local variable. - match expr.kind { - ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) => { - if let Some(var) = path_to_local(lhs) { - let mut visitor = ReadVisitor { - cx, - var, - write_expr: expr, - last_expr: expr, - }; - check_for_unsequenced_reads(&mut visitor); - } - }, - _ => {}, - } + let var = if_chain! { + if let ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) = expr.kind; + if let Some(var) = path_to_local(lhs); + if expr.span.desugaring_kind().is_none(); + then { var } else { return; } + }; + let mut visitor = ReadVisitor { + cx, + var, + write_expr: expr, + last_expr: expr, + }; + check_for_unsequenced_reads(&mut visitor); } fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { match stmt.kind { diff --git a/tests/ui/eval_order_dependence.rs b/tests/ui/eval_order_dependence.rs index d806bc6d401..d742856bc41 100644 --- a/tests/ui/eval_order_dependence.rs +++ b/tests/ui/eval_order_dependence.rs @@ -1,3 +1,5 @@ +// edition:2018 + #[warn(clippy::eval_order_dependence)] #[allow( unused_assignments, @@ -107,3 +109,7 @@ fn main() { }, ); } + +async fn issue_6925() { + let _ = vec![async { true }.await, async { false }.await]; +} diff --git a/tests/ui/eval_order_dependence.stderr b/tests/ui/eval_order_dependence.stderr index cf4adbdfa24..35eb85e95a3 100644 --- a/tests/ui/eval_order_dependence.stderr +++ b/tests/ui/eval_order_dependence.stderr @@ -1,48 +1,48 @@ error: unsequenced read of `x` - --> $DIR/eval_order_dependence.rs:15:9 + --> $DIR/eval_order_dependence.rs:17:9 | LL | } + x; | ^ | = note: `-D clippy::eval-order-dependence` implied by `-D warnings` note: whether read occurs before this write depends on evaluation order - --> $DIR/eval_order_dependence.rs:13:9 + --> $DIR/eval_order_dependence.rs:15:9 | LL | x = 1; | ^^^^^ error: unsequenced read of `x` - --> $DIR/eval_order_dependence.rs:18:5 + --> $DIR/eval_order_dependence.rs:20:5 | LL | x += { | ^ | note: whether read occurs before this write depends on evaluation order - --> $DIR/eval_order_dependence.rs:19:9 + --> $DIR/eval_order_dependence.rs:21:9 | LL | x = 20; | ^^^^^^ error: unsequenced read of `x` - --> $DIR/eval_order_dependence.rs:31:12 + --> $DIR/eval_order_dependence.rs:33:12 | LL | a: x, | ^ | note: whether read occurs before this write depends on evaluation order - --> $DIR/eval_order_dependence.rs:33:13 + --> $DIR/eval_order_dependence.rs:35:13 | LL | x = 6; | ^^^^^ error: unsequenced read of `x` - --> $DIR/eval_order_dependence.rs:40:9 + --> $DIR/eval_order_dependence.rs:42:9 | LL | x += { | ^ | note: whether read occurs before this write depends on evaluation order - --> $DIR/eval_order_dependence.rs:41:13 + --> $DIR/eval_order_dependence.rs:43:13 | LL | x = 20; | ^^^^^^ -- cgit 1.4.1-3-g733a5 From d66d37303c7074e8e26a0f67bba5a36cd6b12e29 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 5 May 2021 16:05:16 -0500 Subject: Fix unnecessary_filter_map false positive --- clippy_lints/src/methods/unnecessary_filter_map.rs | 40 ++++++++++++---------- tests/ui/unnecessary_filter_map.rs | 4 +++ 2 files changed, 26 insertions(+), 18 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index b61c4ffe9b3..8b66587bfd1 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -6,6 +6,7 @@ use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; +use rustc_middle::ty::{self, TyS}; use rustc_span::sym; use super::UNNECESSARY_FILTER_MAP; @@ -28,25 +29,28 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr< found_mapping |= return_visitor.found_mapping; found_filtering |= return_visitor.found_filtering; - if !found_filtering { - span_lint( - cx, - UNNECESSARY_FILTER_MAP, - expr.span, - "this `.filter_map` can be written more simply using `.map`", - ); - return; - } - - if !found_mapping && !mutates_arg { - span_lint( - cx, - UNNECESSARY_FILTER_MAP, - expr.span, - "this `.filter_map` can be written more simply using `.filter`", - ); + let sugg = if !found_filtering { + "map" + } else if !found_mapping && !mutates_arg { + 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_type, adt.did) + && TyS::same_type(in_ty, subst.type_at(0)) => + { + "filter" + }, + _ => return, + } + } else { return; - } + }; + span_lint( + cx, + UNNECESSARY_FILTER_MAP, + expr.span, + &format!("this `.filter_map` can be written more simply using `.{}`", sugg), + ); } } diff --git a/tests/ui/unnecessary_filter_map.rs b/tests/ui/unnecessary_filter_map.rs index af858e4abcf..c58181f518d 100644 --- a/tests/ui/unnecessary_filter_map.rs +++ b/tests/ui/unnecessary_filter_map.rs @@ -15,3 +15,7 @@ fn main() { let _ = (0..4).filter_map(i32::checked_abs); } + +fn filter_map_none_changes_item_type() -> impl Iterator { + "".chars().filter_map(|_| None) +} -- cgit 1.4.1-3-g733a5 From ab3094b3dbf0bf6423a9cf0509a170eee89f106e Mon Sep 17 00:00:00 2001 From: Mateusz Gacek <96mateusz.gacek@gmail.com> Date: Thu, 6 May 2021 10:49:31 -0700 Subject: wrong_self_convention: For `to_*` variant don't lint in trait impl taking `self` when non-`Copy` type It relaxes rules for `to_*` variant, so it doesn't lint in trait definitions and implementations anymore. Although, non-`Copy` type implementing trait's `to_*` method taking `self` feels not good (consumes ownership, so should be rather named `into_`), it would be better if this case was a pedantic lint (allow-by-default) instead. --- clippy_lints/src/methods/wrong_self_convention.rs | 2 +- tests/ui/wrong_self_convention2.rs | 2 +- tests/ui/wrong_self_convention2.stderr | 11 ----------- 3 files changed, 2 insertions(+), 13 deletions(-) delete mode 100644 tests/ui/wrong_self_convention2.stderr (limited to 'tests') diff --git a/clippy_lints/src/methods/wrong_self_convention.rs b/clippy_lints/src/methods/wrong_self_convention.rs index 6e2bcb113c2..1773c26c251 100644 --- a/clippy_lints/src/methods/wrong_self_convention.rs +++ b/clippy_lints/src/methods/wrong_self_convention.rs @@ -22,7 +22,7 @@ const CONVENTIONS: [(&[Convention], &[SelfKind]); 9] = [ // Conversion using `to_` can use borrowed (non-Copy types) or owned (Copy types). // Source: https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(false), - Convention::IsTraitItem(false)], &[SelfKind::Ref]), + Convention::IsTraitItem(false), Convention::ImplementsTrait(false)], &[SelfKind::Ref]), (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(true), Convention::IsTraitItem(false), Convention::ImplementsTrait(false)], &[SelfKind::Value]), ]; diff --git a/tests/ui/wrong_self_convention2.rs b/tests/ui/wrong_self_convention2.rs index ae3a740d405..18202ef2989 100644 --- a/tests/ui/wrong_self_convention2.rs +++ b/tests/ui/wrong_self_convention2.rs @@ -23,7 +23,7 @@ mod issue6983 { } struct FooNoCopy; - // trigger lint + // don't trigger impl ToU64 for FooNoCopy { fn to_u64(self) -> u64 { 2 diff --git a/tests/ui/wrong_self_convention2.stderr b/tests/ui/wrong_self_convention2.stderr deleted file mode 100644 index 0ca1a390974..00000000000 --- a/tests/ui/wrong_self_convention2.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference - --> $DIR/wrong_self_convention2.rs:28:19 - | -LL | fn to_u64(self) -> u64 { - | ^^^^ - | - = note: `-D clippy::wrong-self-convention` implied by `-D warnings` - = help: consider choosing a less ambiguous name - -error: aborting due to previous error - -- cgit 1.4.1-3-g733a5 From 5f3aae61af8ab129b743d13b14ec5519e97cc445 Mon Sep 17 00:00:00 2001 From: Thomas Otto Date: Thu, 6 May 2021 23:51:01 +0200 Subject: Handle write!(buf, "\n") case better Make `write!(buf, "\n")` suggest `writeln!(buf)` by removing the trailing comma from `writeln!(buf, )`. changelog: [`write_with_newline`] suggestion on only "\n" improved --- clippy_lints/src/write.rs | 23 ++++++++++++++--------- tests/ui/write_with_newline.stderr | 4 ++-- 2 files changed, 16 insertions(+), 11 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 7e962472c07..d0e79efa70d 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -279,8 +279,15 @@ impl EarlyLintPass for Write { span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprintln!`"); self.lint_println_empty_string(cx, mac); } else if mac.path == sym!(write) { - if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), true) { + if let (Some(fmt_str), dest) = self.check_tts(cx, mac.args.inner_tokens(), true) { if check_newlines(&fmt_str) { + let (nl_span, only_nl) = newline_span(&fmt_str); + let nl_span = match (dest, only_nl) { + // Special case of `write!(buf, "\n")`: Mark everything from the end of + // `buf` for removal so no trailing comma [`writeln!(buf, )`] remains. + (Some(dest_expr), true) => Span::new(dest_expr.span.hi(), nl_span.hi(), nl_span.ctxt()), + _ => nl_span, + }; span_lint_and_then( cx, WRITE_WITH_NEWLINE, @@ -289,10 +296,7 @@ impl EarlyLintPass for Write { |err| { err.multipart_suggestion( "use `writeln!()` instead", - vec![ - (mac.path.span, String::from("writeln")), - (newline_span(&fmt_str), String::new()), - ], + vec![(mac.path.span, String::from("writeln")), (nl_span, String::new())], Applicability::MachineApplicable, ); }, @@ -329,12 +333,13 @@ impl EarlyLintPass for Write { /// Given a format string that ends in a newline and its span, calculates the span of the /// newline, or the format string itself if the format string consists solely of a newline. -fn newline_span(fmtstr: &StrLit) -> Span { +/// Return this and a boolean indicating whether it only consisted of a newline. +fn newline_span(fmtstr: &StrLit) -> (Span, bool) { let sp = fmtstr.span; let contents = &fmtstr.symbol.as_str(); if *contents == r"\n" { - return sp; + return (sp, true); } let newline_sp_hi = sp.hi() @@ -351,7 +356,7 @@ fn newline_span(fmtstr: &StrLit) -> Span { panic!("expected format string to contain a newline"); }; - sp.with_lo(newline_sp_hi - newline_sp_len).with_hi(newline_sp_hi) + (sp.with_lo(newline_sp_hi - newline_sp_len).with_hi(newline_sp_hi), false) } /// Stores a list of replacement spans for each argument, but only if all the replacements used an @@ -613,7 +618,7 @@ impl Write { |err| { err.multipart_suggestion( &format!("use `{}!` instead", suggested), - vec![(mac.path.span, suggested), (newline_span(&fmt_str), String::new())], + vec![(mac.path.span, suggested), (newline_span(&fmt_str).0, String::new())], Applicability::MachineApplicable, ); }, diff --git a/tests/ui/write_with_newline.stderr b/tests/ui/write_with_newline.stderr index a14e86122ee..cecc2ea9406 100644 --- a/tests/ui/write_with_newline.stderr +++ b/tests/ui/write_with_newline.stderr @@ -51,8 +51,8 @@ LL | write!(&mut v, "/n"); | help: use `writeln!()` instead | -LL | writeln!(&mut v, ); - | ^^^^^^^ -- +LL | writeln!(&mut v); + | ^^^^^^^ -- error: using `write!()` with a format string that ends in a single newline --> $DIR/write_with_newline.rs:36:5 -- cgit 1.4.1-3-g733a5 From a21607d9b504281a00325065955dd825334ad6ef Mon Sep 17 00:00:00 2001 From: Mateusz Gacek <96mateusz.gacek@gmail.com> Date: Wed, 5 May 2021 12:08:24 -0700 Subject: needless_collect: For `BTreeMap` and `HashMap` lint only `is_empty` - `len` might produce different results than `count` - they don't have `contain` but `contains_key` method --- clippy_lints/src/loops/needless_collect.rs | 64 ++++++++++++++++++------------ tests/ui/needless_collect.fixed | 10 ++++- tests/ui/needless_collect.rs | 8 +++- tests/ui/needless_collect.stderr | 14 +++++-- 4 files changed, 63 insertions(+), 33 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/loops/needless_collect.rs index 9662a0b22a3..1ed58faa925 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/loops/needless_collect.rs @@ -10,7 +10,6 @@ use rustc_hir::intravisit::{walk_block, walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::{Block, Expr, ExprKind, GenericArg, GenericArgs, HirId, Local, Pat, PatKind, QPath, StmtKind, Ty}; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; - use rustc_span::symbol::{sym, Ident}; use rustc_span::{MultiSpan, Span}; @@ -28,32 +27,45 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont if let Some(generic_args) = chain_method.args; if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0); if let Some(ty) = cx.typeck_results().node_type_opt(ty.hir_id); - if is_type_diagnostic_item(cx, ty, sym::vec_type) - || is_type_diagnostic_item(cx, ty, sym::vecdeque_type) - || match_type(cx, ty, &paths::BTREEMAP) - || is_type_diagnostic_item(cx, ty, sym::hashmap_type); - if let Some(sugg) = match &*method.ident.name.as_str() { - "len" => Some("count()".to_string()), - "is_empty" => Some("next().is_none()".to_string()), - "contains" => { - let contains_arg = snippet(cx, args[1].span, "??"); - let (arg, pred) = contains_arg - .strip_prefix('&') - .map_or(("&x", &*contains_arg), |s| ("x", s)); - Some(format!("any(|{}| x == {})", arg, pred)) - } - _ => None, - }; then { - span_lint_and_sugg( - cx, - NEEDLESS_COLLECT, - method0_span.with_hi(expr.span.hi()), - NEEDLESS_COLLECT_MSG, - "replace with", - sugg, - Applicability::MachineApplicable, - ); + let is_empty_sugg = Some("next().is_none()".to_string()); + let method_name = &*method.ident.name.as_str(); + let sugg = if is_type_diagnostic_item(cx, ty, sym::vec_type) || + is_type_diagnostic_item(cx, ty, sym::vecdeque_type) { + match method_name { + "len" => Some("count()".to_string()), + "is_empty" => is_empty_sugg, + "contains" => { + let contains_arg = snippet(cx, args[1].span, "??"); + let (arg, pred) = contains_arg + .strip_prefix('&') + .map_or(("&x", &*contains_arg), |s| ("x", s)); + Some(format!("any(|{}| x == {})", arg, pred)) + } + _ => None, + } + } + else if match_type(cx, ty, &paths::BTREEMAP) || + is_type_diagnostic_item(cx, ty, sym::hashmap_type) { + match method_name { + "is_empty" => is_empty_sugg, + _ => None, + } + } + else { + None + }; + if let Some(sugg) = sugg { + span_lint_and_sugg( + cx, + NEEDLESS_COLLECT, + method0_span.with_hi(expr.span.hi()), + NEEDLESS_COLLECT_MSG, + "replace with", + sugg, + Applicability::MachineApplicable, + ); + } } } } diff --git a/tests/ui/needless_collect.fixed b/tests/ui/needless_collect.fixed index af6c7bf15ea..d7595569681 100644 --- a/tests/ui/needless_collect.fixed +++ b/tests/ui/needless_collect.fixed @@ -2,7 +2,7 @@ #![allow(unused, clippy::suspicious_map, clippy::iter_count)] -use std::collections::{BTreeSet, HashMap, HashSet}; +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; #[warn(clippy::needless_collect)] #[allow(unused_variables, clippy::iter_cloned_collect, clippy::iter_next_slice)] @@ -13,7 +13,13 @@ fn main() { // Empty } sample.iter().cloned().any(|x| x == 1); - sample.iter().map(|x| (x, x)).count(); + // #7164 HashMap's and BTreeMap's `len` usage should not be linted + sample.iter().map(|x| (x, x)).collect::>().len(); + sample.iter().map(|x| (x, x)).collect::>().len(); + + sample.iter().map(|x| (x, x)).next().is_none(); + sample.iter().map(|x| (x, x)).next().is_none(); + // Notice the `HashSet`--this should not be linted sample.iter().collect::>().len(); // Neither should this diff --git a/tests/ui/needless_collect.rs b/tests/ui/needless_collect.rs index 6ae14f370b1..9883c75b745 100644 --- a/tests/ui/needless_collect.rs +++ b/tests/ui/needless_collect.rs @@ -2,7 +2,7 @@ #![allow(unused, clippy::suspicious_map, clippy::iter_count)] -use std::collections::{BTreeSet, HashMap, HashSet}; +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; #[warn(clippy::needless_collect)] #[allow(unused_variables, clippy::iter_cloned_collect, clippy::iter_next_slice)] @@ -13,7 +13,13 @@ fn main() { // Empty } sample.iter().cloned().collect::>().contains(&1); + // #7164 HashMap's and BTreeMap's `len` usage should not be linted sample.iter().map(|x| (x, x)).collect::>().len(); + sample.iter().map(|x| (x, x)).collect::>().len(); + + sample.iter().map(|x| (x, x)).collect::>().is_empty(); + sample.iter().map(|x| (x, x)).collect::>().is_empty(); + // Notice the `HashSet`--this should not be linted sample.iter().collect::>().len(); // Neither should this diff --git a/tests/ui/needless_collect.stderr b/tests/ui/needless_collect.stderr index 2a9539d5975..3acdf66a42e 100644 --- a/tests/ui/needless_collect.stderr +++ b/tests/ui/needless_collect.stderr @@ -19,10 +19,16 @@ LL | sample.iter().cloned().collect::>().contains(&1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == 1)` error: avoid using `collect()` when not needed - --> $DIR/needless_collect.rs:16:35 + --> $DIR/needless_collect.rs:20:35 | -LL | sample.iter().map(|x| (x, x)).collect::>().len(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `count()` +LL | sample.iter().map(|x| (x, x)).collect::>().is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` -error: aborting due to 4 previous errors +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:21:35 + | +LL | sample.iter().map(|x| (x, x)).collect::>().is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` + +error: aborting due to 5 previous errors -- cgit 1.4.1-3-g733a5 From 171789eb4526fe4ef65514f26056ca4551915cca Mon Sep 17 00:00:00 2001 From: Mateusz Gacek <96mateusz.gacek@gmail.com> Date: Wed, 5 May 2021 12:17:49 -0700 Subject: needless_collect: Lint `LinkedList` and `BinaryHeap` in direct usage. Those two types are supported already when used indirectly. This commit adds support for direct usage as well. --- clippy_lints/src/loops/needless_collect.rs | 4 +++- tests/ui/needless_collect.fixed | 11 ++++++++- tests/ui/needless_collect.rs | 11 ++++++++- tests/ui/needless_collect.stderr | 38 +++++++++++++++++++++++++++++- 4 files changed, 60 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/loops/needless_collect.rs index 6a9aa08426c..89c95f3d127 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/loops/needless_collect.rs @@ -31,7 +31,9 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont let is_empty_sugg = Some("next().is_none()".to_string()); let method_name = &*method.ident.name.as_str(); let sugg = if is_type_diagnostic_item(cx, ty, sym::vec_type) || - is_type_diagnostic_item(cx, ty, sym::vecdeque_type) { + is_type_diagnostic_item(cx, ty, sym::vecdeque_type) || + is_type_diagnostic_item(cx, ty, sym::LinkedList) || + is_type_diagnostic_item(cx, ty, sym::BinaryHeap) { match method_name { "len" => Some("count()".to_string()), "is_empty" => is_empty_sugg, diff --git a/tests/ui/needless_collect.fixed b/tests/ui/needless_collect.fixed index d7595569681..6ecbbcb6249 100644 --- a/tests/ui/needless_collect.fixed +++ b/tests/ui/needless_collect.fixed @@ -2,7 +2,7 @@ #![allow(unused, clippy::suspicious_map, clippy::iter_count)] -use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; +use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList}; #[warn(clippy::needless_collect)] #[allow(unused_variables, clippy::iter_cloned_collect, clippy::iter_next_slice)] @@ -24,4 +24,13 @@ fn main() { sample.iter().collect::>().len(); // Neither should this sample.iter().collect::>().len(); + + sample.iter().count(); + sample.iter().next().is_none(); + sample.iter().cloned().any(|x| x == 1); + sample.iter().any(|x| x == &1); + + // `BinaryHeap` doesn't have `contains` method + sample.iter().count(); + sample.iter().next().is_none(); } diff --git a/tests/ui/needless_collect.rs b/tests/ui/needless_collect.rs index 9883c75b745..8dc69bcf5b3 100644 --- a/tests/ui/needless_collect.rs +++ b/tests/ui/needless_collect.rs @@ -2,7 +2,7 @@ #![allow(unused, clippy::suspicious_map, clippy::iter_count)] -use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; +use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList}; #[warn(clippy::needless_collect)] #[allow(unused_variables, clippy::iter_cloned_collect, clippy::iter_next_slice)] @@ -24,4 +24,13 @@ fn main() { sample.iter().collect::>().len(); // Neither should this sample.iter().collect::>().len(); + + sample.iter().collect::>().len(); + sample.iter().collect::>().is_empty(); + sample.iter().cloned().collect::>().contains(&1); + sample.iter().collect::>().contains(&&1); + + // `BinaryHeap` doesn't have `contains` method + sample.iter().collect::>().len(); + sample.iter().collect::>().is_empty(); } diff --git a/tests/ui/needless_collect.stderr b/tests/ui/needless_collect.stderr index 3acdf66a42e..039091627a8 100644 --- a/tests/ui/needless_collect.stderr +++ b/tests/ui/needless_collect.stderr @@ -30,5 +30,41 @@ error: avoid using `collect()` when not needed LL | sample.iter().map(|x| (x, x)).collect::>().is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` -error: aborting due to 5 previous errors +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:28:19 + | +LL | sample.iter().collect::>().len(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `count()` + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:29:19 + | +LL | sample.iter().collect::>().is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:30:28 + | +LL | sample.iter().cloned().collect::>().contains(&1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == 1)` + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:31:19 + | +LL | sample.iter().collect::>().contains(&&1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == &1)` + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:34:19 + | +LL | sample.iter().collect::>().len(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `count()` + +error: avoid using `collect()` when not needed + --> $DIR/needless_collect.rs:35:19 + | +LL | sample.iter().collect::>().is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` + +error: aborting due to 11 previous errors -- cgit 1.4.1-3-g733a5 From 5ba236f3039241077302fddd7acb671e62195af6 Mon Sep 17 00:00:00 2001 From: Arya Kumar Date: Tue, 11 May 2021 19:34:14 +0000 Subject: added `needless_bitwise_bool` lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 4 ++ clippy_lints/src/needless_bitwise_bool.rs | 84 +++++++++++++++++++++++++++++++ tests/ui/needless_bitwise_bool.fixed | 40 +++++++++++++++ tests/ui/needless_bitwise_bool.rs | 40 +++++++++++++++ tests/ui/needless_bitwise_bool.stderr | 10 ++++ 6 files changed, 179 insertions(+) create mode 100644 clippy_lints/src/needless_bitwise_bool.rs create mode 100644 tests/ui/needless_bitwise_bool.fixed create mode 100644 tests/ui/needless_bitwise_bool.rs create mode 100644 tests/ui/needless_bitwise_bool.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a328a20dd4..3645f27427d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2549,6 +2549,7 @@ Released 2018-09-13 [`mutex_integer`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_integer [`naive_bytecount`]: https://rust-lang.github.io/rust-clippy/master/index.html#naive_bytecount [`needless_arbitrary_self_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_arbitrary_self_type +[`needless_bitwise_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bitwise_bool [`needless_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool [`needless_borrow`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow [`needless_borrowed_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 783bef73214..5b5fc452771 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -288,6 +288,7 @@ mod mut_reference; mod mutable_debug_assertion; mod mutex_atomic; mod needless_arbitrary_self_type; +mod needless_bitwise_bool; mod needless_bool; mod needless_borrow; mod needless_borrowed_ref; @@ -833,6 +834,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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, @@ -1018,6 +1020,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let type_complexity_threshold = conf.type_complexity_threshold; store.register_late_pass(move || box types::Types::new(vec_box_size_threshold, type_complexity_threshold)); store.register_late_pass(|| box booleans::NonminimalBool); + store.register_late_pass(|| box needless_bitwise_bool::NeedlessBitwiseBool); store.register_late_pass(|| box eq_op::EqOp); store.register_late_pass(|| box enum_clike::UnportableVariant); store.register_late_pass(|| box float_literal::FloatLiteral); @@ -1392,6 +1395,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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_continue::NEEDLESS_CONTINUE), LintId::of(needless_for_each::NEEDLESS_FOR_EACH), LintId::of(needless_pass_by_value::NEEDLESS_PASS_BY_VALUE), diff --git a/clippy_lints/src/needless_bitwise_bool.rs b/clippy_lints/src/needless_bitwise_bool.rs new file mode 100644 index 00000000000..95febf4a2ad --- /dev/null +++ b/clippy_lints/src/needless_bitwise_bool.rs @@ -0,0 +1,84 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::in_macro; +use clippy_utils::source::snippet_opt; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** + /// Checks for uses of bitwise and/or operators between booleans, where performance may be improved by using + /// a lazy and. + /// + /// **Why is this bad?** + /// The bitwise operators do not support short-circuiting, so it may hinder code performance. + /// Additionally, boolean logic "masked" as bitwise logic is not caught by lints like `unnecessary_fold` + /// + /// **Known problems:** + /// This lint evaluates only when the right side is determined to have no side effects. At this time, that + /// determination is quite conservative. + /// + /// **Example:** + /// + /// ```rust + /// if x & !y {} // where both x and y are booleans + /// ``` + /// Use instead: + /// ```rust + /// if x && !y {} + /// ``` + pub NEEDLESS_BITWISE_BOOL, + pedantic, + "Boolean expressions that use bitwise rather than lazy operators" +} + +declare_lint_pass!(NeedlessBitwiseBool => [NEEDLESS_BITWISE_BOOL]); + +fn is_bitwise_operation(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let ty = cx.typeck_results().expr_ty(expr); + if_chain! { + if !in_macro(expr.span); + if let (&ExprKind::Binary(ref op, _, right), &ty::Bool) = (&expr.kind, &ty.kind()); + if op.node == BinOpKind::BitAnd || op.node == BinOpKind::BitOr; + if let ExprKind::Call(..) | ExprKind::MethodCall(..) | ExprKind::Binary(..) | ExprKind::Unary(..) = right.kind; + if !right.can_have_side_effects(); + then { + return true; + } + } + false +} + +fn suggession_snippet(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { + if let ExprKind::Binary(ref op, left, right) = expr.kind { + if let (Some(l_snippet), Some(r_snippet)) = (snippet_opt(cx, left.span), snippet_opt(cx, right.span)) { + let op_snippet = match op.node { + BinOpKind::BitAnd => "&&", + _ => "||", + }; + return Some(format!("{} {} {}", l_snippet, op_snippet, r_snippet)); + } + } + None +} + +impl LateLintPass<'_> for NeedlessBitwiseBool { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if is_bitwise_operation(cx, expr) { + span_lint_and_then( + cx, + NEEDLESS_BITWISE_BOOL, + expr.span, + "use of bitwise operator instead of lazy operator between booleans", + |diag| { + if let Some(sugg) = suggession_snippet(cx, expr) { + diag.span_suggestion(expr.span, "try", sugg, Applicability::MachineApplicable); + } + }, + ); + } + } +} diff --git a/tests/ui/needless_bitwise_bool.fixed b/tests/ui/needless_bitwise_bool.fixed new file mode 100644 index 00000000000..5e1ea663a10 --- /dev/null +++ b/tests/ui/needless_bitwise_bool.fixed @@ -0,0 +1,40 @@ +// run-rustfix + +#![warn(clippy::needless_bitwise_bool)] + +fn returns_bool() -> bool { + true +} + +const fn const_returns_bool() -> bool { + false +} + +fn main() { + let (x, y) = (false, true); + if x & y { + println!("true") + } + if returns_bool() & x { + println!("true") + } + if !returns_bool() & returns_bool() { + println!("true") + } + if y && !x { + println!("true") + } + + // BELOW: lints we hope to catch as `Expr::can_have_side_effects` improves. + if y & !const_returns_bool() { + println!("true") // This is a const function, in an UnOp + } + + if y & "abcD".is_empty() { + println!("true") // This is a const method call + } + + if y & (0 < 1) { + println!("true") // This is a BinOp with no side effects + } +} diff --git a/tests/ui/needless_bitwise_bool.rs b/tests/ui/needless_bitwise_bool.rs new file mode 100644 index 00000000000..f3075fba0a2 --- /dev/null +++ b/tests/ui/needless_bitwise_bool.rs @@ -0,0 +1,40 @@ +// run-rustfix + +#![warn(clippy::needless_bitwise_bool)] + +fn returns_bool() -> bool { + true +} + +const fn const_returns_bool() -> bool { + false +} + +fn main() { + let (x, y) = (false, true); + if x & y { + println!("true") + } + if returns_bool() & x { + println!("true") + } + if !returns_bool() & returns_bool() { + println!("true") + } + if y & !x { + println!("true") + } + + // BELOW: lints we hope to catch as `Expr::can_have_side_effects` improves. + if y & !const_returns_bool() { + println!("true") // This is a const function, in an UnOp + } + + if y & "abcD".is_empty() { + println!("true") // This is a const method call + } + + if y & (0 < 1) { + println!("true") // This is a BinOp with no side effects + } +} diff --git a/tests/ui/needless_bitwise_bool.stderr b/tests/ui/needless_bitwise_bool.stderr new file mode 100644 index 00000000000..63c88ef63f5 --- /dev/null +++ b/tests/ui/needless_bitwise_bool.stderr @@ -0,0 +1,10 @@ +error: use of bitwise operator instead of lazy operator between booleans + --> $DIR/needless_bitwise_bool.rs:24:8 + | +LL | if y & !x { + | ^^^^^^ help: try: `y && !x` + | + = note: `-D clippy::needless-bitwise-bool` implied by `-D warnings` + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From 0d4604dc05acd45d2401f4973d6085a4eba50016 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Thu, 13 May 2021 01:41:22 +0200 Subject: Added `cargo collect-metadata` as a alias for the metadata collection lint --- .cargo/config | 1 + tests/dogfood.rs | 68 +++++++++++++++++++++++++++++++++----------------------- 2 files changed, 41 insertions(+), 28 deletions(-) (limited to 'tests') diff --git a/.cargo/config b/.cargo/config index 9b5add4df1c..e95ea224cb6 100644 --- a/.cargo/config +++ b/.cargo/config @@ -2,6 +2,7 @@ uitest = "test --test compile-test" dev = "run --target-dir clippy_dev/target --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --" lintcheck = "run --target-dir lintcheck/target --package lintcheck --bin lintcheck --manifest-path lintcheck/Cargo.toml -- " +collect-metadata = "test --test dogfood --features metadata-collector-lint -- run_metadata_collection_lint --ignored" [build] rustflags = ["-Zunstable-options"] diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 6524fd4706c..5d9f128753f 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -22,14 +22,12 @@ fn dogfood_clippy() { return; } let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - let enable_metadata_collection = std::env::var("ENABLE_METADATA_COLLECTION").unwrap_or_else(|_| "0".to_string()); let mut command = Command::new(&*CLIPPY_PATH); command .current_dir(root_dir) .env("CLIPPY_DOGFOOD", "1") .env("CARGO_INCREMENTAL", "0") - .env("ENABLE_METADATA_COLLECTION", &enable_metadata_collection) .arg("clippy") .arg("--all-targets") .arg("--all-features") @@ -157,10 +155,9 @@ fn dogfood_subprojects() { if cargo::is_rustc_test_suite() { return; } - let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); // NOTE: `path_dep` crate is omitted on purpose here - for d in &[ + for project in &[ "clippy_workspace_tests", "clippy_workspace_tests/src", "clippy_workspace_tests/subcrate", @@ -170,34 +167,49 @@ fn dogfood_subprojects() { "clippy_utils", "rustc_tools_util", ] { - let mut command = Command::new(&*CLIPPY_PATH); - command - .current_dir(root_dir.join(d)) - .env("CLIPPY_DOGFOOD", "1") - .env("CARGO_INCREMENTAL", "0") - .arg("clippy") - .arg("--all-targets") - .arg("--all-features") - .arg("--") - .args(&["-D", "clippy::all"]) - .args(&["-D", "clippy::pedantic"]) - .arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir + run_clippy_for_project(project); + } + + // NOTE: Since tests run in parallel we can't run cargo commands on the same workspace at the + // same time, so we test this immediately after the dogfood for workspaces. + test_no_deps_ignores_path_deps_in_workspaces(); +} - // internal lints only exist if we build with the internal-lints feature - if cfg!(feature = "internal-lints") { - command.args(&["-D", "clippy::internal"]); - } +#[test] +#[ignore] +#[cfg(feature = "metadata-collector-lint")] +fn run_metadata_collection_lint() { + std::env::set_var("ENABLE_METADATA_COLLECTION", "1"); + run_clippy_for_project("clippy_lints"); +} - let output = command.output().unwrap(); +fn run_clippy_for_project(project: &str) { + let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - println!("status: {}", output.status); - println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); - println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); + let mut command = Command::new(&*CLIPPY_PATH); - assert!(output.status.success()); + command + .current_dir(root_dir.join(project)) + .env("CLIPPY_DOGFOOD", "1") + .env("CARGO_INCREMENTAL", "0") + .arg("clippy") + .arg("--all-targets") + .arg("--all-features") + .arg("--") + .args(&["-D", "clippy::all"]) + .args(&["-D", "clippy::pedantic"]) + .arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir + + // internal lints only exist if we build with the internal-lints feature + if cfg!(feature = "internal-lints") { + command.args(&["-D", "clippy::internal"]); } - // NOTE: Since tests run in parallel we can't run cargo commands on the same workspace at the - // same time, so we test this immediately after the dogfood for workspaces. - test_no_deps_ignores_path_deps_in_workspaces(); + let output = command.output().unwrap(); + + println!("status: {}", output.status); + println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); + println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); + + assert!(output.status.success()); } -- cgit 1.4.1-3-g733a5 From 8214bf04450db9e57975993290d3c35a66a40707 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Fri, 16 Apr 2021 16:07:20 +0900 Subject: match_single_binding: Fix invalid suggestion when match scrutinee has side effects --- clippy_lints/src/matches.rs | 37 ++++++++++++++++++++++++++--------- tests/ui/match_single_binding.fixed | 5 +---- tests/ui/match_single_binding.rs | 10 +--------- tests/ui/match_single_binding.stderr | 13 +----------- tests/ui/match_single_binding2.fixed | 16 +++++++++++++++ tests/ui/match_single_binding2.rs | 18 +++++++++++++++++ tests/ui/match_single_binding2.stderr | 36 +++++++++++++++++++++++++++++++++- 7 files changed, 100 insertions(+), 35 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 44b4eb29035..345b360e633 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -1479,15 +1479,34 @@ fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[A ); }, PatKind::Wild => { - span_lint_and_sugg( - cx, - MATCH_SINGLE_BINDING, - expr.span, - "this match could be replaced by its body itself", - "consider using the match body instead", - snippet_body, - Applicability::MachineApplicable, - ); + if ex.can_have_side_effects() { + let indent = " ".repeat(indent_of(cx, expr.span).unwrap_or(0)); + let sugg = format!( + "{};\n{}{}", + snippet_with_applicability(cx, ex.span, "..", &mut applicability), + indent, + snippet_body + ); + span_lint_and_sugg( + cx, + MATCH_SINGLE_BINDING, + expr.span, + "this match could be replaced by its scrutinee and body", + "consider using the scrutinee and body instead", + sugg, + applicability, + ) + } else { + span_lint_and_sugg( + cx, + MATCH_SINGLE_BINDING, + expr.span, + "this match could be replaced by its body itself", + "consider using the match body instead", + snippet_body, + Applicability::MachineApplicable, + ); + } }, _ => (), } diff --git a/tests/ui/match_single_binding.fixed b/tests/ui/match_single_binding.fixed index 526e94b10bd..30bf6402253 100644 --- a/tests/ui/match_single_binding.fixed +++ b/tests/ui/match_single_binding.fixed @@ -94,10 +94,7 @@ fn main() { 0 => println!("Disabled branch"), _ => println!("Enabled branch"), } - // Lint - let x = 1; - let y = 1; - println!("Single branch"); + // Ok let x = 1; let y = 1; diff --git a/tests/ui/match_single_binding.rs b/tests/ui/match_single_binding.rs index 6a2ca7c5e93..d8bb80d8b96 100644 --- a/tests/ui/match_single_binding.rs +++ b/tests/ui/match_single_binding.rs @@ -106,15 +106,7 @@ fn main() { 0 => println!("Disabled branch"), _ => println!("Enabled branch"), } - // Lint - let x = 1; - let y = 1; - match match y { - 0 => 1, - _ => 2, - } { - _ => println!("Single branch"), - } + // Ok let x = 1; let y = 1; diff --git a/tests/ui/match_single_binding.stderr b/tests/ui/match_single_binding.stderr index cbbf5d29c02..795c8c3e24d 100644 --- a/tests/ui/match_single_binding.stderr +++ b/tests/ui/match_single_binding.stderr @@ -167,16 +167,5 @@ LL | unwrapped LL | }) | -error: this match could be replaced by its body itself - --> $DIR/match_single_binding.rs:112:5 - | -LL | / match match y { -LL | | 0 => 1, -LL | | _ => 2, -LL | | } { -LL | | _ => println!("Single branch"), -LL | | } - | |_____^ help: consider using the match body instead: `println!("Single branch");` - -error: aborting due to 12 previous errors +error: aborting due to 11 previous errors diff --git a/tests/ui/match_single_binding2.fixed b/tests/ui/match_single_binding2.fixed index e73a85b73d7..a91fcc2125d 100644 --- a/tests/ui/match_single_binding2.fixed +++ b/tests/ui/match_single_binding2.fixed @@ -34,4 +34,20 @@ fn main() { }, None => println!("nothing"), } + + fn side_effects() {} + + // Lint (scrutinee has side effects) + // issue #7094 + side_effects(); + println!("Side effects"); + + // Lint (scrutinee has side effects) + // issue #7094 + let x = 1; + match x { + 0 => 1, + _ => 2, + }; + println!("Single branch"); } diff --git a/tests/ui/match_single_binding2.rs b/tests/ui/match_single_binding2.rs index 7362cb390e5..476386ebabe 100644 --- a/tests/ui/match_single_binding2.rs +++ b/tests/ui/match_single_binding2.rs @@ -34,4 +34,22 @@ fn main() { }, None => println!("nothing"), } + + fn side_effects() {} + + // Lint (scrutinee has side effects) + // issue #7094 + match side_effects() { + _ => println!("Side effects"), + } + + // Lint (scrutinee has side effects) + // issue #7094 + let x = 1; + match match x { + 0 => 1, + _ => 2, + } { + _ => println!("Single branch"), + } } diff --git a/tests/ui/match_single_binding2.stderr b/tests/ui/match_single_binding2.stderr index bc18d191aa3..4372f55af87 100644 --- a/tests/ui/match_single_binding2.stderr +++ b/tests/ui/match_single_binding2.stderr @@ -30,5 +30,39 @@ LL | let (a, b) = get_tup(); LL | println!("a {:?} and b {:?}", a, b); | -error: aborting due to 2 previous errors +error: this match could be replaced by its scrutinee and body + --> $DIR/match_single_binding2.rs:42:5 + | +LL | / match side_effects() { +LL | | _ => println!("Side effects"), +LL | | } + | |_____^ + | +help: consider using the scrutinee and body instead + | +LL | side_effects(); +LL | println!("Side effects"); + | + +error: this match could be replaced by its scrutinee and body + --> $DIR/match_single_binding2.rs:49:5 + | +LL | / match match x { +LL | | 0 => 1, +LL | | _ => 2, +LL | | } { +LL | | _ => println!("Single branch"), +LL | | } + | |_____^ + | +help: consider using the scrutinee and body instead + | +LL | match x { +LL | 0 => 1, +LL | _ => 2, +LL | }; +LL | println!("Single branch"); + | + +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From daca50a515f28218142d9945ca714f7420b4fc75 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Wed, 24 Mar 2021 09:32:29 -0400 Subject: Improvements to `while_let_on_iterator` * Suggest `&mut iter` when the iterator is used after the loop. * Suggest `&mut iter` when the iterator is a field in a struct. * Don't lint when the iterator is a field in a struct, and the struct is used in the loop. * Lint when the loop is nested in another loop, but suggest `&mut iter` unless the iterator is from a local declared inside the loop. --- clippy_lints/src/doc.rs | 2 +- clippy_lints/src/loops/while_let_on_iterator.rs | 445 +++++++++++++++++------- clippy_utils/src/lib.rs | 18 + clippy_utils/src/sugg.rs | 2 +- clippy_utils/src/visitors.rs | 36 +- tests/ui/while_let_on_iterator.fixed | 164 +++++++-- tests/ui/while_let_on_iterator.rs | 164 +++++++-- tests/ui/while_let_on_iterator.stderr | 72 +++- 8 files changed, 706 insertions(+), 197 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index fb53b55ebd6..e67ec4e06c5 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -383,7 +383,7 @@ pub fn strip_doc_comment_decoration(doc: &str, comment_kind: CommentKind, span: let mut no_stars = String::with_capacity(doc.len()); for line in doc.lines() { let mut chars = line.chars(); - while let Some(c) = chars.next() { + for c in &mut chars { if c.is_whitespace() { no_stars.push(c); } else { diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index 82715d9bafa..75e92f08df1 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -1,170 +1,353 @@ -use super::utils::{LoopNestVisitor, Nesting}; use super::WHILE_LET_ON_ITERATOR; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::implements_trait; -use clippy_utils::usage::mutated_variables; -use clippy_utils::{ - get_enclosing_block, is_refutable, is_trait_method, last_path_segment, path_to_local, path_to_local_id, -}; -use if_chain::if_chain; +use clippy_utils::{get_enclosing_loop, is_refutable, is_trait_method, match_def_path, paths, visitors::is_res_used}; use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_block, walk_expr, NestedVisitorMap, Visitor}; -use rustc_hir::{Expr, ExprKind, HirId, MatchSource, Node, PatKind}; +use rustc_hir::intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor}; +use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, MatchSource, Node, PatKind, QPath, UnOp}; use rustc_lint::LateContext; -use rustc_middle::hir::map::Map; -use rustc_span::symbol::sym; +use rustc_span::{symbol::sym, Span, Symbol}; pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Match(match_expr, arms, MatchSource::WhileLetDesugar) = expr.kind { - let pat = &arms[0].pat.kind; - if let (&PatKind::TupleStruct(ref qpath, pat_args, _), &ExprKind::MethodCall(method_path, _, method_args, _)) = - (pat, &match_expr.kind) - { - let iter_expr = &method_args[0]; - - // Don't lint when the iterator is recreated on every iteration - if_chain! { - if let ExprKind::MethodCall(..) | ExprKind::Call(..) = iter_expr.kind; - if let Some(iter_def_id) = cx.tcx.get_diagnostic_item(sym::Iterator); - if implements_trait(cx, cx.typeck_results().expr_ty(iter_expr), iter_def_id, &[]); - then { - return; - } - } + if let ExprKind::Match(scrutinee_expr, [arm, _], MatchSource::WhileLetDesugar) = expr.kind { + let some_pat = match arm.pat.kind { + PatKind::TupleStruct(QPath::Resolved(None, path), sub_pats, _) => match path.res { + Res::Def(_, id) if match_def_path(cx, id, &paths::OPTION_SOME) => sub_pats.first(), + _ => return, + }, + _ => return, + }; - let lhs_constructor = last_path_segment(qpath); - if method_path.ident.name == sym::next - && is_trait_method(cx, match_expr, sym::Iterator) - && lhs_constructor.ident.name == sym::Some - && (pat_args.is_empty() - || !is_refutable(cx, pat_args[0]) - && !is_used_inside(cx, iter_expr, arms[0].body) - && !is_iterator_used_after_while_let(cx, iter_expr) - && !is_nested(cx, expr, &method_args[0])) + let iter_expr = match scrutinee_expr.kind { + ExprKind::MethodCall(name, _, [iter_expr], _) + if name.ident.name == sym::next && is_trait_method(cx, scrutinee_expr, sym::Iterator) => { - let mut applicability = Applicability::MachineApplicable; - let iterator = snippet_with_applicability(cx, method_args[0].span, "_", &mut applicability); - let loop_var = if pat_args.is_empty() { - "_".to_string() + if let Some(iter_expr) = try_parse_iter_expr(cx, iter_expr) { + iter_expr } else { - snippet_with_applicability(cx, pat_args[0].span, "_", &mut applicability).into_owned() - }; - span_lint_and_sugg( - cx, - WHILE_LET_ON_ITERATOR, - expr.span.with_hi(match_expr.span.hi()), - "this loop could be written as a `for` loop", - "try", - format!("for {} in {}", loop_var, iterator), - applicability, - ); + return; + } } + _ => return, + }; + + // Needed to find an outer loop, if there are any. + let loop_expr = if let Some((_, Node::Expr(e))) = cx.tcx.hir().parent_iter(expr.hir_id).nth(1) { + e + } else { + return; + }; + + // Refutable patterns don't work with for loops. + // The iterator also can't be accessed withing the loop. + if some_pat.map_or(true, |p| is_refutable(cx, p)) || uses_iter(cx, &iter_expr, arm.body) { + return; } + + // If the iterator is a field or the iterator is accessed after the loop is complete it needs to be + // borrowed mutably. + // TODO: If the struct can be partially moved from and the struct isn't used afterwards a mutable + // borrow of a field isn't necessary. + let ref_mut = if !iter_expr.fields.is_empty() || needs_mutable_borrow(cx, &iter_expr, loop_expr) { + "&mut " + } else { + "" + }; + let mut applicability = Applicability::MachineApplicable; + let iterator = snippet_with_applicability(cx, iter_expr.span, "_", &mut applicability); + let loop_var = some_pat.map_or_else( + || "_".into(), + |pat| snippet_with_applicability(cx, pat.span, "_", &mut applicability).into_owned(), + ); + span_lint_and_sugg( + cx, + WHILE_LET_ON_ITERATOR, + expr.span.with_hi(scrutinee_expr.span.hi()), + "this loop could be written as a `for` loop", + "try", + format!("for {} in {}{}", loop_var, ref_mut, iterator), + applicability, + ); } } -fn is_used_inside<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, container: &'tcx Expr<'_>) -> bool { - let def_id = match path_to_local(expr) { - Some(id) => id, - None => return false, - }; - if let Some(used_mutably) = mutated_variables(container, cx) { - if used_mutably.contains(&def_id) { - return true; +#[derive(Debug)] +struct IterExpr { + /// The span of the whole expression, not just the path and fields stored here. + span: Span, + /// The fields used, in order of child to parent. + fields: Vec, + /// The path being used. + path: Res, +} +/// Parses any expression to find out which field of which variable is used. Will return `None` if +/// the expression might have side effects. +fn try_parse_iter_expr(cx: &LateContext<'_>, mut e: &Expr<'_>) -> Option { + let span = e.span; + let mut fields = Vec::new(); + loop { + match e.kind { + ExprKind::Path(ref path) => { + break Some(IterExpr { + span, + fields, + path: cx.qpath_res(path, e.hir_id), + }); + }, + ExprKind::Field(base, name) => { + fields.push(name.name); + e = base; + }, + // Dereferencing a pointer has no side effects and doesn't affect which field is being used. + ExprKind::Unary(UnOp::Deref, base) if cx.typeck_results().expr_ty(base).is_ref() => e = base, + + // Shouldn't have side effects, but there's no way to trace which field is used. So forget which fields have + // already been seen. + ExprKind::Index(base, idx) if !idx.can_have_side_effects() => { + fields.clear(); + e = base; + }, + ExprKind::Unary(UnOp::Deref, base) => { + fields.clear(); + e = base; + }, + + // No effect and doesn't affect which field is being used. + ExprKind::DropTemps(base) | ExprKind::AddrOf(_, _, base) | ExprKind::Type(base, _) => e = base, + _ => break None, } } - false } -fn is_iterator_used_after_while_let<'tcx>(cx: &LateContext<'tcx>, iter_expr: &'tcx Expr<'_>) -> bool { - let def_id = match path_to_local(iter_expr) { - Some(id) => id, - None => return false, - }; - let mut visitor = VarUsedAfterLoopVisitor { - def_id, - iter_expr_id: iter_expr.hir_id, - past_while_let: false, - var_used_after_while_let: false, - }; - if let Some(enclosing_block) = get_enclosing_block(cx, def_id) { - walk_block(&mut visitor, enclosing_block); +fn is_expr_same_field(cx: &LateContext<'_>, mut e: &Expr<'_>, mut fields: &[Symbol], path_res: Res) -> bool { + loop { + match (&e.kind, fields) { + (&ExprKind::Field(base, name), [head_field, tail_fields @ ..]) if name.name == *head_field => { + e = base; + fields = tail_fields; + }, + (ExprKind::Path(path), []) => { + break cx.qpath_res(path, e.hir_id) == path_res; + }, + (&(ExprKind::DropTemps(base) | ExprKind::AddrOf(_, _, base) | ExprKind::Type(base, _)), _) => e = base, + _ => break false, + } } - visitor.var_used_after_while_let } -fn is_nested(cx: &LateContext<'_>, match_expr: &Expr<'_>, iter_expr: &Expr<'_>) -> bool { - if_chain! { - if let Some(loop_block) = get_enclosing_block(cx, match_expr.hir_id); - let parent_node = cx.tcx.hir().get_parent_node(loop_block.hir_id); - if let Some(Node::Expr(loop_expr)) = cx.tcx.hir().find(parent_node); - then { - return is_loop_nested(cx, loop_expr, iter_expr) - } +/// Checks if the given expression is the same field as, is a child of, of the parent of the given +/// field. Used to check if the expression can be used while the given field is borrowed. +fn is_expr_same_child_or_parent_field(cx: &LateContext<'_>, expr: &Expr<'_>, fields: &[Symbol], path_res: Res) -> bool { + match expr.kind { + ExprKind::Field(base, name) => { + if let Some((head_field, tail_fields)) = fields.split_first() { + if name.name == *head_field && is_expr_same_field(cx, base, fields, path_res) { + return true; + } + // Check if the expression is a parent field + let mut fields_iter = tail_fields.iter(); + while let Some(field) = fields_iter.next() { + if *field == name.name && is_expr_same_field(cx, base, fields_iter.as_slice(), path_res) { + return true; + } + } + } + + // Check if the expression is a child field. + let mut e = base; + loop { + match e.kind { + ExprKind::Field(..) if is_expr_same_field(cx, e, fields, path_res) => break true, + ExprKind::Field(base, _) | ExprKind::DropTemps(base) | ExprKind::Type(base, _) => e = base, + ExprKind::Path(ref path) if fields.is_empty() => { + break cx.qpath_res(path, e.hir_id) == path_res; + }, + _ => break false, + } + } + }, + // If the path matches, this is either an exact match, of the expression is a parent of the field. + ExprKind::Path(ref path) => cx.qpath_res(path, expr.hir_id) == path_res, + ExprKind::DropTemps(base) | ExprKind::Type(base, _) | ExprKind::AddrOf(_, _, base) => { + is_expr_same_child_or_parent_field(cx, base, fields, path_res) + }, + _ => false, } - false } -fn is_loop_nested(cx: &LateContext<'_>, loop_expr: &Expr<'_>, iter_expr: &Expr<'_>) -> bool { - let mut id = loop_expr.hir_id; - let iter_id = if let Some(id) = path_to_local(iter_expr) { - id - } else { - return true; +/// Strips off all field and path expressions. Used to skip them after failing to check for +/// equality. +fn skip_fields_and_path(expr: &'tcx Expr<'_>) -> (Option<&'tcx Expr<'tcx>>, bool) { + let mut e = expr; + let e = loop { + match e.kind { + ExprKind::Field(base, _) | ExprKind::DropTemps(base) | ExprKind::Type(base, _) => e = base, + ExprKind::Path(_) => return (None, true), + _ => break e, + } }; - loop { - let parent = cx.tcx.hir().get_parent_node(id); - if parent == id { - return false; + (Some(e), e.hir_id != expr.hir_id) +} + +/// Checks if the given expression uses the iterator. +fn uses_iter(cx: &LateContext<'tcx>, iter_expr: &IterExpr, container: &'tcx Expr<'_>) -> bool { + struct V<'a, 'b, 'tcx> { + cx: &'a LateContext<'tcx>, + iter_expr: &'b IterExpr, + uses_iter: bool, + } + impl Visitor<'tcx> for V<'_, '_, 'tcx> { + type Map = ErasedMap<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None } - match cx.tcx.hir().find(parent) { - Some(Node::Expr(expr)) => { - if let ExprKind::Loop(..) = expr.kind { - return true; - }; - }, - Some(Node::Block(block)) => { - let mut block_visitor = LoopNestVisitor { - hir_id: id, - iterator: iter_id, - nesting: Nesting::Unknown, - }; - walk_block(&mut block_visitor, block); - if block_visitor.nesting == Nesting::RuledOut { - return false; + + fn visit_expr(&mut self, e: &'tcx Expr<'_>) { + if self.uses_iter { + // return + } else if is_expr_same_child_or_parent_field(self.cx, e, &self.iter_expr.fields, self.iter_expr.path) { + self.uses_iter = true; + } else if let (e, true) = skip_fields_and_path(e) { + if let Some(e) = e { + self.visit_expr(e); } - }, - Some(Node::Stmt(_)) => (), - _ => { - return false; - }, + } else if let ExprKind::Closure(_, _, id, _, _) = e.kind { + if is_res_used(self.cx, self.iter_expr.path, id) { + self.uses_iter = true; + } + } else { + walk_expr(self, e); + } } - id = parent; } -} -struct VarUsedAfterLoopVisitor { - def_id: HirId, - iter_expr_id: HirId, - past_while_let: bool, - var_used_after_while_let: bool, + let mut v = V { + cx, + iter_expr, + uses_iter: false, + }; + v.visit_expr(container); + v.uses_iter } -impl<'tcx> Visitor<'tcx> for VarUsedAfterLoopVisitor { - type Map = Map<'tcx>; +#[allow(clippy::too_many_lines)] +fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr: &'tcx Expr<'_>) -> bool { + struct AfterLoopVisitor<'a, 'b, 'tcx> { + cx: &'a LateContext<'tcx>, + iter_expr: &'b IterExpr, + loop_id: HirId, + after_loop: bool, + used_iter: bool, + } + impl Visitor<'tcx> for AfterLoopVisitor<'_, '_, 'tcx> { + type Map = ErasedMap<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + + fn visit_expr(&mut self, e: &'tcx Expr<'_>) { + if self.used_iter { + return; + } + if self.after_loop { + if is_expr_same_child_or_parent_field(self.cx, e, &self.iter_expr.fields, self.iter_expr.path) { + self.used_iter = true; + } else if let (e, true) = skip_fields_and_path(e) { + if let Some(e) = e { + self.visit_expr(e); + } + } else if let ExprKind::Closure(_, _, id, _, _) = e.kind { + self.used_iter |= is_res_used(self.cx, self.iter_expr.path, id); + } else { + walk_expr(self, e); + } + } else if self.loop_id == e.hir_id { + self.after_loop = true; + } else { + walk_expr(self, e); + } + } + } + + struct NestedLoopVisitor<'a, 'b, 'tcx> { + cx: &'a LateContext<'tcx>, + iter_expr: &'b IterExpr, + local_id: HirId, + loop_id: HirId, + after_loop: bool, + found_local: bool, + used_after: bool, + } + impl Visitor<'tcx> for NestedLoopVisitor<'a, 'b, 'tcx> { + type Map = ErasedMap<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if self.past_while_let { - if path_to_local_id(expr, self.def_id) { - self.var_used_after_while_let = true; + fn visit_local(&mut self, l: &'tcx Local<'_>) { + if !self.after_loop { + l.pat.each_binding_or_first(&mut |_, id, _, _| { + if id == self.local_id { + self.found_local = true; + } + }); + } + if let Some(e) = l.init { + self.visit_expr(e); + } + } + + fn visit_expr(&mut self, e: &'tcx Expr<'_>) { + if self.used_after { + return; + } + if self.after_loop { + if is_expr_same_child_or_parent_field(self.cx, e, &self.iter_expr.fields, self.iter_expr.path) { + self.used_after = true; + } else if let (e, true) = skip_fields_and_path(e) { + if let Some(e) = e { + self.visit_expr(e); + } + } else if let ExprKind::Closure(_, _, id, _, _) = e.kind { + self.used_after |= is_res_used(self.cx, self.iter_expr.path, id); + } else { + walk_expr(self, e); + } + } else if e.hir_id == self.loop_id { + self.after_loop = true; + } else { + walk_expr(self, e); } - } else if self.iter_expr_id == expr.hir_id { - self.past_while_let = true; } - walk_expr(self, expr); } - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None + + if let Some(e) = get_enclosing_loop(cx.tcx, loop_expr) { + // The iterator expression will be used on the next iteration unless it is declared within the outer + // loop. + let local_id = match iter_expr.path { + Res::Local(id) => id, + _ => return true, + }; + let mut v = NestedLoopVisitor { + cx, + iter_expr, + local_id, + loop_id: loop_expr.hir_id, + after_loop: false, + found_local: false, + used_after: false, + }; + v.visit_expr(e); + v.used_after || !v.found_local + } else { + let mut v = AfterLoopVisitor { + cx, + iter_expr, + loop_id: loop_expr.hir_id, + after_loop: false, + used_iter: false, + }; + v.visit_expr(&cx.tcx.hir().body(cx.enclosing_body.unwrap()).value); + v.used_iter } } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 7ca9d3a860d..371fe23bedc 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -855,6 +855,24 @@ pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio }) } +/// Gets the loop enclosing the given expression, if any. +pub fn get_enclosing_loop(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> { + let map = tcx.hir(); + for (_, node) in map.parent_iter(expr.hir_id) { + match node { + Node::Expr( + e @ Expr { + kind: ExprKind::Loop(..), + .. + }, + ) => return Some(e), + Node::Expr(_) | Node::Stmt(_) | Node::Block(_) | Node::Local(_) | Node::Arm(_) => (), + _ => break, + } + } + None +} + /// Gets the parent node if it's an impl block. pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> { let map = tcx.hir(); diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 0633a19391f..0c950661757 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -289,7 +289,7 @@ fn has_enclosing_paren(sugg: impl AsRef) -> bool { let mut chars = sugg.as_ref().chars(); if let Some('(') = chars.next() { let mut depth = 1; - while let Some(c) = chars.next() { + for c in &mut chars { if c == '(' { depth += 1; } else if c == ')' { diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index d431bdf34ee..ffd15076026 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -1,7 +1,7 @@ use crate::path_to_local_id; use rustc_hir as hir; use rustc_hir::intravisit::{self, walk_expr, ErasedMap, NestedVisitorMap, Visitor}; -use rustc_hir::{Arm, Block, Body, Destination, Expr, ExprKind, HirId, Stmt}; +use rustc_hir::{def::Res, Arm, Block, Body, BodyId, Destination, Expr, ExprKind, HirId, Stmt}; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; @@ -218,6 +218,7 @@ impl<'tcx> Visitable<'tcx> for &'tcx Arm<'tcx> { } } +/// Calls the given function for each break expression. pub fn visit_break_exprs<'tcx>( node: impl Visitable<'tcx>, f: impl FnMut(&'tcx Expr<'tcx>, Destination, Option<&'tcx Expr<'tcx>>), @@ -239,3 +240,36 @@ pub fn visit_break_exprs<'tcx>( node.visit(&mut V(f)); } + +/// Checks if the given resolved path is used the body. +pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool { + struct V<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + res: Res, + found: bool, + } + impl Visitor<'tcx> for V<'_, 'tcx> { + type Map = Map<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) + } + + fn visit_expr(&mut self, e: &'tcx Expr<'_>) { + if self.found { + return; + } + + if let ExprKind::Path(p) = &e.kind { + if self.cx.qpath_res(p, e.hir_id) == self.res { + self.found = true; + } + } else { + walk_expr(self, e) + } + } + } + + let mut v = V { cx, res, found: false }; + v.visit_expr(&cx.tcx.hir().body(body).value); + v.found +} diff --git a/tests/ui/while_let_on_iterator.fixed b/tests/ui/while_let_on_iterator.fixed index 749393db124..0562db48a88 100644 --- a/tests/ui/while_let_on_iterator.fixed +++ b/tests/ui/while_let_on_iterator.fixed @@ -1,7 +1,7 @@ // run-rustfix #![warn(clippy::while_let_on_iterator)] -#![allow(clippy::never_loop, unreachable_code, unused_mut)] +#![allow(clippy::never_loop, unreachable_code, unused_mut, dead_code)] fn base() { let mut iter = 1..20; @@ -38,13 +38,6 @@ fn base() { println!("next: {:?}", iter.next()); } - // or this - let mut iter = 1u32..20; - while let Some(_) = iter.next() { - break; - } - println!("Remaining iter {:?}", iter); - // or this let mut iter = 1u32..20; while let Some(_) = iter.next() { @@ -135,18 +128,6 @@ fn refutable2() { fn nested_loops() { let a = [42, 1337]; - let mut y = a.iter(); - loop { - // x is reused, so don't lint here - while let Some(_) = y.next() {} - } - - let mut y = a.iter(); - for _ in 0..2 { - while let Some(_) = y.next() { - // y is reused, don't lint - } - } loop { let mut y = a.iter(); @@ -205,13 +186,138 @@ fn issue1654() { } } -fn main() { - base(); - refutable(); - refutable2(); - nested_loops(); - issue1121(); - issue2965(); - issue3670(); - issue1654(); +fn issue6491() { + // Used in outer loop, needs &mut + let mut it = 1..40; + while let Some(n) = it.next() { + for m in &mut it { + if m % 10 == 0 { + break; + } + println!("doing something with m: {}", m); + } + println!("n still is {}", n); + } + + // This is fine, inner loop uses a new iterator. + let mut it = 1..40; + for n in it { + let mut it = 1..40; + for m in it { + if m % 10 == 0 { + break; + } + println!("doing something with m: {}", m); + } + + // Weird binding shouldn't change anything. + let (mut it, _) = (1..40, 0); + for m in it { + if m % 10 == 0 { + break; + } + println!("doing something with m: {}", m); + } + + // Used after the loop, needs &mut. + let mut it = 1..40; + for m in &mut it { + if m % 10 == 0 { + break; + } + println!("doing something with m: {}", m); + } + println!("next item {}", it.next().unwrap()); + + println!("n still is {}", n); + } +} + +fn issue6231() { + // Closure in the outer loop, needs &mut + let mut it = 1..40; + let mut opt = Some(0); + while let Some(n) = opt.take().or_else(|| it.next()) { + for m in &mut it { + if n % 10 == 0 { + break; + } + println!("doing something with m: {}", m); + } + println!("n still is {}", n); + } } + +fn issue1924() { + struct S(T); + impl> S { + fn f(&mut self) -> Option { + // Used as a field. + for i in &mut self.0 { + if !(3..=7).contains(&i) { + return Some(i); + } + } + None + } + + fn f2(&mut self) -> Option { + // Don't lint, self borrowed inside the loop + while let Some(i) = self.0.next() { + if i == 1 { + return self.f(); + } + } + None + } + } + impl> S<(S, Option)> { + fn f3(&mut self) -> Option { + // Don't lint, self borrowed inside the loop + while let Some(i) = self.0.0.0.next() { + if i == 1 { + return self.0.0.f(); + } + } + while let Some(i) = self.0.0.0.next() { + if i == 1 { + return self.f3(); + } + } + // This one is fine, a different field is borrowed + for i in &mut self.0.0.0 { + if i == 1 { + return self.0.1.take(); + } + } + None + } + } + + struct S2(T, u32); + impl> Iterator for S2 { + type Item = u32; + fn next(&mut self) -> Option { + self.0.next() + } + } + + // Don't lint, field of the iterator is accessed in the loop + let mut it = S2(1..40, 0); + while let Some(n) = it.next() { + if n == it.1 { + break; + } + } + + // Needs &mut, field of the iterator is accessed after the loop + let mut it = S2(1..40, 0); + for n in &mut it { + if n == 0 { + break; + } + } + println!("iterator field {}", it.1); +} + +fn main() {} diff --git a/tests/ui/while_let_on_iterator.rs b/tests/ui/while_let_on_iterator.rs index 30e3b82a7cc..a7c217dc7fd 100644 --- a/tests/ui/while_let_on_iterator.rs +++ b/tests/ui/while_let_on_iterator.rs @@ -1,7 +1,7 @@ // run-rustfix #![warn(clippy::while_let_on_iterator)] -#![allow(clippy::never_loop, unreachable_code, unused_mut)] +#![allow(clippy::never_loop, unreachable_code, unused_mut, dead_code)] fn base() { let mut iter = 1..20; @@ -38,13 +38,6 @@ fn base() { println!("next: {:?}", iter.next()); } - // or this - let mut iter = 1u32..20; - while let Some(_) = iter.next() { - break; - } - println!("Remaining iter {:?}", iter); - // or this let mut iter = 1u32..20; while let Some(_) = iter.next() { @@ -135,18 +128,6 @@ fn refutable2() { fn nested_loops() { let a = [42, 1337]; - let mut y = a.iter(); - loop { - // x is reused, so don't lint here - while let Some(_) = y.next() {} - } - - let mut y = a.iter(); - for _ in 0..2 { - while let Some(_) = y.next() { - // y is reused, don't lint - } - } loop { let mut y = a.iter(); @@ -205,13 +186,138 @@ fn issue1654() { } } -fn main() { - base(); - refutable(); - refutable2(); - nested_loops(); - issue1121(); - issue2965(); - issue3670(); - issue1654(); +fn issue6491() { + // Used in outer loop, needs &mut + let mut it = 1..40; + while let Some(n) = it.next() { + while let Some(m) = it.next() { + if m % 10 == 0 { + break; + } + println!("doing something with m: {}", m); + } + println!("n still is {}", n); + } + + // This is fine, inner loop uses a new iterator. + let mut it = 1..40; + while let Some(n) = it.next() { + let mut it = 1..40; + while let Some(m) = it.next() { + if m % 10 == 0 { + break; + } + println!("doing something with m: {}", m); + } + + // Weird binding shouldn't change anything. + let (mut it, _) = (1..40, 0); + while let Some(m) = it.next() { + if m % 10 == 0 { + break; + } + println!("doing something with m: {}", m); + } + + // Used after the loop, needs &mut. + let mut it = 1..40; + while let Some(m) = it.next() { + if m % 10 == 0 { + break; + } + println!("doing something with m: {}", m); + } + println!("next item {}", it.next().unwrap()); + + println!("n still is {}", n); + } } + +fn issue6231() { + // Closure in the outer loop, needs &mut + let mut it = 1..40; + let mut opt = Some(0); + while let Some(n) = opt.take().or_else(|| it.next()) { + while let Some(m) = it.next() { + if n % 10 == 0 { + break; + } + println!("doing something with m: {}", m); + } + println!("n still is {}", n); + } +} + +fn issue1924() { + struct S(T); + impl> S { + fn f(&mut self) -> Option { + // Used as a field. + while let Some(i) = self.0.next() { + if i < 3 || i > 7 { + return Some(i); + } + } + None + } + + fn f2(&mut self) -> Option { + // Don't lint, self borrowed inside the loop + while let Some(i) = self.0.next() { + if i == 1 { + return self.f(); + } + } + None + } + } + impl> S<(S, Option)> { + fn f3(&mut self) -> Option { + // Don't lint, self borrowed inside the loop + while let Some(i) = self.0.0.0.next() { + if i == 1 { + return self.0.0.f(); + } + } + while let Some(i) = self.0.0.0.next() { + if i == 1 { + return self.f3(); + } + } + // This one is fine, a different field is borrowed + while let Some(i) = self.0.0.0.next() { + if i == 1 { + return self.0.1.take(); + } + } + None + } + } + + struct S2(T, u32); + impl> Iterator for S2 { + type Item = u32; + fn next(&mut self) -> Option { + self.0.next() + } + } + + // Don't lint, field of the iterator is accessed in the loop + let mut it = S2(1..40, 0); + while let Some(n) = it.next() { + if n == it.1 { + break; + } + } + + // Needs &mut, field of the iterator is accessed after the loop + let mut it = S2(1..40, 0); + while let Some(n) = it.next() { + if n == 0 { + break; + } + } + println!("iterator field {}", it.1); +} + +fn main() {} diff --git a/tests/ui/while_let_on_iterator.stderr b/tests/ui/while_let_on_iterator.stderr index 6554977c798..0feca257410 100644 --- a/tests/ui/while_let_on_iterator.stderr +++ b/tests/ui/while_let_on_iterator.stderr @@ -19,28 +19,90 @@ 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:101:9 + --> $DIR/while_let_on_iterator.rs:94: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:108:9 + --> $DIR/while_let_on_iterator.rs:101: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:121:9 + --> $DIR/while_let_on_iterator.rs:114: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:153:9 + --> $DIR/while_let_on_iterator.rs:134:9 | LL | while let Some(_) = y.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in y` -error: aborting due to 7 previous errors +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:193:9 + | +LL | while let Some(m) = it.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in &mut it` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:204: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:206: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:215: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:224:9 + | +LL | while let Some(m) = it.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in &mut it` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:241:9 + | +LL | while let Some(m) = it.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in &mut it` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:256:13 + | +LL | while let Some(i) = self.0.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in &mut self.0` + +error: manual `!RangeInclusive::contains` implementation + --> $DIR/while_let_on_iterator.rs:257:20 + | +LL | if i < 3 || i > 7 { + | ^^^^^^^^^^^^^^ help: use: `!(3..=7).contains(&i)` + | + = 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:288:13 + | +LL | while let Some(i) = self.0.0.0.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in &mut self.0.0.0` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:315:5 + | +LL | while let Some(n) = it.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in &mut it` + +error: aborting due to 17 previous errors -- cgit 1.4.1-3-g733a5 From 4713e25ab07badc863fff05fcd5bcf9852cf375e Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 13 Apr 2021 09:30:01 -0400 Subject: Cleanup of `while_let_on_iterator` --- clippy_lints/src/loops/while_let_on_iterator.rs | 116 +++++++++++------------- clippy_utils/src/visitors.rs | 2 +- tests/ui/while_let_on_iterator.fixed | 9 +- tests/ui/while_let_on_iterator.rs | 9 +- tests/ui/while_let_on_iterator.stderr | 10 +- 5 files changed, 80 insertions(+), 66 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index 75e92f08df1..63560047578 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -2,6 +2,7 @@ use super::WHILE_LET_ON_ITERATOR; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{get_enclosing_loop, is_refutable, is_trait_method, match_def_path, paths, visitors::is_res_used}; +use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor}; use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, MatchSource, Node, PatKind, QPath, UnOp}; @@ -9,66 +10,57 @@ use rustc_lint::LateContext; use rustc_span::{symbol::sym, Span, Symbol}; pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Match(scrutinee_expr, [arm, _], MatchSource::WhileLetDesugar) = expr.kind { - let some_pat = match arm.pat.kind { - PatKind::TupleStruct(QPath::Resolved(None, path), sub_pats, _) => match path.res { - Res::Def(_, id) if match_def_path(cx, id, &paths::OPTION_SOME) => sub_pats.first(), - _ => return, - }, - _ => return, - }; - - let iter_expr = match scrutinee_expr.kind { - ExprKind::MethodCall(name, _, [iter_expr], _) - if name.ident.name == sym::next && is_trait_method(cx, scrutinee_expr, sym::Iterator) => - { - if let Some(iter_expr) = try_parse_iter_expr(cx, iter_expr) { - iter_expr - } else { - return; - } - } - _ => return, - }; - - // Needed to find an outer loop, if there are any. - let loop_expr = if let Some((_, Node::Expr(e))) = cx.tcx.hir().parent_iter(expr.hir_id).nth(1) { - e + let (scrutinee_expr, iter_expr, some_pat, loop_expr) = if_chain! { + if let ExprKind::Match(scrutinee_expr, [arm, _], MatchSource::WhileLetDesugar) = expr.kind; + // check for `Some(..)` pattern + if let PatKind::TupleStruct(QPath::Resolved(None, pat_path), some_pat, _) = arm.pat.kind; + if let Res::Def(_, pat_did) = pat_path.res; + if match_def_path(cx, pat_did, &paths::OPTION_SOME); + // check for call to `Iterator::next` + if let ExprKind::MethodCall(method_name, _, [iter_expr], _) = scrutinee_expr.kind; + if method_name.ident.name == sym::next; + if is_trait_method(cx, scrutinee_expr, sym::Iterator); + if let Some(iter_expr) = try_parse_iter_expr(cx, iter_expr); + // get the loop containing the match expression + if let Some((_, Node::Expr(loop_expr))) = cx.tcx.hir().parent_iter(expr.hir_id).nth(1); + if !uses_iter(cx, &iter_expr, arm.body); + then { + (scrutinee_expr, iter_expr, some_pat, loop_expr) } else { return; - }; + } + }; - // Refutable patterns don't work with for loops. - // The iterator also can't be accessed withing the loop. - if some_pat.map_or(true, |p| is_refutable(cx, p)) || uses_iter(cx, &iter_expr, arm.body) { + let mut applicability = Applicability::MachineApplicable; + let loop_var = if let Some(some_pat) = some_pat.first() { + if is_refutable(cx, some_pat) { + // Refutable patterns don't work with for loops. return; } + snippet_with_applicability(cx, some_pat.span, "..", &mut applicability) + } else { + "_".into() + }; - // If the iterator is a field or the iterator is accessed after the loop is complete it needs to be - // borrowed mutably. - // TODO: If the struct can be partially moved from and the struct isn't used afterwards a mutable - // borrow of a field isn't necessary. - let ref_mut = if !iter_expr.fields.is_empty() || needs_mutable_borrow(cx, &iter_expr, loop_expr) { - "&mut " - } else { - "" - }; - let mut applicability = Applicability::MachineApplicable; - let iterator = snippet_with_applicability(cx, iter_expr.span, "_", &mut applicability); - let loop_var = some_pat.map_or_else( - || "_".into(), - |pat| snippet_with_applicability(cx, pat.span, "_", &mut applicability).into_owned(), - ); - span_lint_and_sugg( - cx, - WHILE_LET_ON_ITERATOR, - expr.span.with_hi(scrutinee_expr.span.hi()), - "this loop could be written as a `for` loop", - "try", - format!("for {} in {}{}", loop_var, ref_mut, iterator), - applicability, - ); - } + // If the iterator is a field or the iterator is accessed after the loop is complete it needs to be + // borrowed mutably. TODO: If the struct can be partially moved from and the struct isn't used + // afterwards a mutable borrow of a field isn't necessary. + let ref_mut = if !iter_expr.fields.is_empty() || needs_mutable_borrow(cx, &iter_expr, loop_expr) { + "&mut " + } else { + "" + }; + + let iterator = snippet_with_applicability(cx, iter_expr.span, "_", &mut applicability); + span_lint_and_sugg( + cx, + WHILE_LET_ON_ITERATOR, + expr.span.with_hi(scrutinee_expr.span.hi()), + "this loop could be written as a `for` loop", + "try", + format!("for {} in {}{}", loop_var, ref_mut, iterator), + applicability, + ); } #[derive(Debug)] @@ -135,8 +127,10 @@ fn is_expr_same_field(cx: &LateContext<'_>, mut e: &Expr<'_>, mut fields: &[Symb } } -/// Checks if the given expression is the same field as, is a child of, of the parent of the given -/// field. Used to check if the expression can be used while the given field is borrowed. +/// Checks if the given expression is the same field as, is a child of, or is the parent of the +/// given field. Used to check if the expression can be used while the given field is borrowed +/// mutably. e.g. if checking for `x.y`, then `x.y`, `x.y.z`, and `x` will all return true, but +/// `x.z`, and `y` will return false. fn is_expr_same_child_or_parent_field(cx: &LateContext<'_>, expr: &Expr<'_>, fields: &[Symbol], path_res: Res) -> bool { match expr.kind { ExprKind::Field(base, name) => { @@ -166,7 +160,7 @@ fn is_expr_same_child_or_parent_field(cx: &LateContext<'_>, expr: &Expr<'_>, fie } } }, - // If the path matches, this is either an exact match, of the expression is a parent of the field. + // If the path matches, this is either an exact match, or the expression is a parent of the field. ExprKind::Path(ref path) => cx.qpath_res(path, expr.hir_id) == path_res, ExprKind::DropTemps(base) | ExprKind::Type(base, _) | ExprKind::AddrOf(_, _, base) => { is_expr_same_child_or_parent_field(cx, base, fields, path_res) @@ -175,8 +169,8 @@ fn is_expr_same_child_or_parent_field(cx: &LateContext<'_>, expr: &Expr<'_>, fie } } -/// Strips off all field and path expressions. Used to skip them after failing to check for -/// equality. +/// Strips off all field and path expressions. This will return true if a field or path has been +/// skipped. Used to skip them after failing to check for equality. fn skip_fields_and_path(expr: &'tcx Expr<'_>) -> (Option<&'tcx Expr<'tcx>>, bool) { let mut e = expr; let e = loop { @@ -257,7 +251,7 @@ fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr: self.visit_expr(e); } } else if let ExprKind::Closure(_, _, id, _, _) = e.kind { - self.used_iter |= is_res_used(self.cx, self.iter_expr.path, id); + self.used_iter = is_res_used(self.cx, self.iter_expr.path, id); } else { walk_expr(self, e); } @@ -309,7 +303,7 @@ fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr: self.visit_expr(e); } } else if let ExprKind::Closure(_, _, id, _, _) = e.kind { - self.used_after |= is_res_used(self.cx, self.iter_expr.path, id); + self.used_after = is_res_used(self.cx, self.iter_expr.path, id); } else { walk_expr(self, e); } diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index ffd15076026..ecdc666b5f6 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -241,7 +241,7 @@ pub fn visit_break_exprs<'tcx>( node.visit(&mut V(f)); } -/// Checks if the given resolved path is used the body. +/// Checks if the given resolved path is used in the given body. pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool { struct V<'a, 'tcx> { cx: &'a LateContext<'tcx>, diff --git a/tests/ui/while_let_on_iterator.fixed b/tests/ui/while_let_on_iterator.fixed index 0562db48a88..389297eff0c 100644 --- a/tests/ui/while_let_on_iterator.fixed +++ b/tests/ui/while_let_on_iterator.fixed @@ -288,6 +288,8 @@ fn issue1924() { for i in &mut self.0.0.0 { if i == 1 { return self.0.1.take(); + } else { + self.0.1 = Some(i); } } None @@ -320,4 +322,9 @@ fn issue1924() { println!("iterator field {}", it.1); } -fn main() {} +fn main() { + let mut it = 0..20; + for _ in it { + println!("test"); + } +} diff --git a/tests/ui/while_let_on_iterator.rs b/tests/ui/while_let_on_iterator.rs index a7c217dc7fd..df932724a0d 100644 --- a/tests/ui/while_let_on_iterator.rs +++ b/tests/ui/while_let_on_iterator.rs @@ -288,6 +288,8 @@ fn issue1924() { while let Some(i) = self.0.0.0.next() { if i == 1 { return self.0.1.take(); + } else { + self.0.1 = Some(i); } } None @@ -320,4 +322,9 @@ fn issue1924() { println!("iterator field {}", it.1); } -fn main() {} +fn main() { + let mut it = 0..20; + while let Some(..) = it.next() { + println!("test"); + } +} diff --git a/tests/ui/while_let_on_iterator.stderr b/tests/ui/while_let_on_iterator.stderr index 0feca257410..e8741f74981 100644 --- a/tests/ui/while_let_on_iterator.stderr +++ b/tests/ui/while_let_on_iterator.stderr @@ -99,10 +99,16 @@ LL | while let Some(i) = self.0.0.0.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in &mut self.0.0.0` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:315:5 + --> $DIR/while_let_on_iterator.rs:317:5 | LL | while let Some(n) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in &mut it` -error: aborting due to 17 previous errors +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:327:5 + | +LL | while let Some(..) = it.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in it` + +error: aborting due to 18 previous errors -- cgit 1.4.1-3-g733a5 From cd0db8a4599619dd43af5f39834a7566d06e5b50 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Wed, 12 May 2021 21:41:59 -0400 Subject: Fix test comment for `while_let_on_iterator` --- tests/ui/while_let_on_iterator.fixed | 6 ++---- tests/ui/while_let_on_iterator.rs | 6 ++---- tests/ui/while_let_on_iterator.stderr | 22 +++++++++++----------- 3 files changed, 15 insertions(+), 19 deletions(-) (limited to 'tests') diff --git a/tests/ui/while_let_on_iterator.fixed b/tests/ui/while_let_on_iterator.fixed index 389297eff0c..c3e2cf0c4a4 100644 --- a/tests/ui/while_let_on_iterator.fixed +++ b/tests/ui/while_let_on_iterator.fixed @@ -148,10 +148,8 @@ fn issue1121() { } fn issue2965() { - // This should not cause an ICE and suggest: - // - // for _ in values.iter() {} - // + // This should not cause an ICE + use std::collections::HashSet; let mut values = HashSet::new(); values.insert(1); diff --git a/tests/ui/while_let_on_iterator.rs b/tests/ui/while_let_on_iterator.rs index df932724a0d..1717006a449 100644 --- a/tests/ui/while_let_on_iterator.rs +++ b/tests/ui/while_let_on_iterator.rs @@ -148,10 +148,8 @@ fn issue1121() { } fn issue2965() { - // This should not cause an ICE and suggest: - // - // for _ in values.iter() {} - // + // This should not cause an ICE + use std::collections::HashSet; let mut values = HashSet::new(); values.insert(1); diff --git a/tests/ui/while_let_on_iterator.stderr b/tests/ui/while_let_on_iterator.stderr index e8741f74981..eff559bef7e 100644 --- a/tests/ui/while_let_on_iterator.stderr +++ b/tests/ui/while_let_on_iterator.stderr @@ -43,49 +43,49 @@ 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:193:9 + --> $DIR/while_let_on_iterator.rs:191:9 | LL | while let Some(m) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in &mut it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:204:5 + --> $DIR/while_let_on_iterator.rs:202: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:206:9 + --> $DIR/while_let_on_iterator.rs:204: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:215:9 + --> $DIR/while_let_on_iterator.rs:213: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:224:9 + --> $DIR/while_let_on_iterator.rs:222:9 | LL | while let Some(m) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in &mut it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:241:9 + --> $DIR/while_let_on_iterator.rs:239:9 | LL | while let Some(m) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in &mut it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:256:13 + --> $DIR/while_let_on_iterator.rs:254:13 | LL | while let Some(i) = self.0.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in &mut self.0` error: manual `!RangeInclusive::contains` implementation - --> $DIR/while_let_on_iterator.rs:257:20 + --> $DIR/while_let_on_iterator.rs:255:20 | LL | if i < 3 || i > 7 { | ^^^^^^^^^^^^^^ help: use: `!(3..=7).contains(&i)` @@ -93,19 +93,19 @@ 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:288:13 + --> $DIR/while_let_on_iterator.rs:286:13 | LL | while let Some(i) = self.0.0.0.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in &mut self.0.0.0` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:317:5 + --> $DIR/while_let_on_iterator.rs:315:5 | LL | while let Some(n) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in &mut it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:327:5 + --> $DIR/while_let_on_iterator.rs:325:5 | LL | while let Some(..) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in it` -- cgit 1.4.1-3-g733a5 From cd241b33cb8379311286f53c4afc6e671ddfded1 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Thu, 13 May 2021 10:24:29 +0200 Subject: Trigger `wrong_self_convention` only if it has implicit self --- clippy_lints/src/methods/mod.rs | 24 ++++++++++++++---------- tests/ui/wrong_self_convention2.rs | 23 +++++++++++++++++++++++ tests/ui/wrong_self_convention2.stderr | 19 +++++++++++++++++++ 3 files changed, 56 insertions(+), 10 deletions(-) create mode 100644 tests/ui/wrong_self_convention2.stderr (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 0b1b6304def..e0d29682146 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1838,16 +1838,18 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } } - wrong_self_convention::check( - cx, - &name, - item.vis.node.is_pub(), - self_ty, - first_arg_ty, - first_arg.pat.span, - implements_trait, - false - ); + if sig.decl.implicit_self.has_implicit_self() { + wrong_self_convention::check( + cx, + &name, + item.vis.node.is_pub(), + self_ty, + first_arg_ty, + first_arg.pat.span, + implements_trait, + false + ); + } } } @@ -1903,7 +1905,9 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if_chain! { if let TraitItemKind::Fn(ref sig, _) = item.kind; + if sig.decl.implicit_self.has_implicit_self(); if let Some(first_arg_ty) = sig.decl.inputs.iter().next(); + then { let first_arg_span = first_arg_ty.span; let first_arg_ty = hir_ty_to_ty(cx.tcx, first_arg_ty); diff --git a/tests/ui/wrong_self_convention2.rs b/tests/ui/wrong_self_convention2.rs index 18202ef2989..b2c6503de49 100644 --- a/tests/ui/wrong_self_convention2.rs +++ b/tests/ui/wrong_self_convention2.rs @@ -42,3 +42,26 @@ mod issue7032 { } } } + +mod issue7179 { + pub struct S(i32); + + impl S { + // don't trigger (`s` is not `self`) + pub fn from_be(s: Self) -> Self { + S(i32::from_be(s.0)) + } + + // lint + pub fn from_be_self(self) -> Self { + S(i32::from_be(self.0)) + } + } + + trait T { + // don't trigger (`s` is not `self`) + fn from_be(s: Self) -> Self; + // lint + fn from_be_self(self) -> Self; + } +} diff --git a/tests/ui/wrong_self_convention2.stderr b/tests/ui/wrong_self_convention2.stderr new file mode 100644 index 00000000000..d2d74ce099e --- /dev/null +++ b/tests/ui/wrong_self_convention2.stderr @@ -0,0 +1,19 @@ +error: methods called `from_*` usually take no `self` + --> $DIR/wrong_self_convention2.rs:56:29 + | +LL | pub fn from_be_self(self) -> Self { + | ^^^^ + | + = note: `-D clippy::wrong-self-convention` implied by `-D warnings` + = help: consider choosing a less ambiguous name + +error: methods called `from_*` usually take no `self` + --> $DIR/wrong_self_convention2.rs:65:25 + | +LL | fn from_be_self(self) -> Self; + | ^^^^ + | + = help: consider choosing a less ambiguous name + +error: aborting due to 2 previous errors + -- cgit 1.4.1-3-g733a5 From 368e621265ab7f3066d1e3e56b0633bca3f7e831 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Thu, 13 May 2021 11:50:25 +0200 Subject: Stop linting `else if let` pattern in `option_if_let_else` lint --- clippy_lints/src/option_if_let_else.rs | 4 +++- tests/ui/option_if_let_else.fixed | 6 +++++- tests/ui/option_if_let_else.stderr | 13 +------------ 3 files changed, 9 insertions(+), 14 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index e527adbb892..2d98b275de7 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::contains_return_break_continue_macro; -use clippy_utils::{eager_or_lazy, get_enclosing_block, in_macro, is_lang_ctor}; +use clippy_utils::{eager_or_lazy, get_enclosing_block, in_macro, is_else_clause, is_lang_ctor}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionSome; @@ -161,6 +161,7 @@ fn detect_option_if_let_else<'tcx>( if_chain! { if !in_macro(expr.span); // Don't lint macros, because it behaves weirdly if let ExprKind::Match(cond_expr, arms, MatchSource::IfLetDesugar{contains_else_clause: true}) = &expr.kind; + if !is_else_clause(cx.tcx, expr); if arms.len() == 2; if !is_result_ok(cx, cond_expr); // Don't lint on Result::ok because a different lint does it already if let PatKind::TupleStruct(struct_qpath, &[inner_pat], _) = &arms[0].pat.kind; @@ -168,6 +169,7 @@ fn detect_option_if_let_else<'tcx>( if let PatKind::Binding(bind_annotation, _, id, _) = &inner_pat.kind; if !contains_return_break_continue_macro(arms[0].body); if !contains_return_break_continue_macro(arms[1].body); + then { let capture_mut = if bind_annotation == &BindingAnnotation::Mutable { "mut " } else { "" }; let some_body = extract_body_from_arm(&arms[0])?; diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed index 47e7460fa7a..769ccc14bc1 100644 --- a/tests/ui/option_if_let_else.fixed +++ b/tests/ui/option_if_let_else.fixed @@ -10,7 +10,11 @@ fn bad1(string: Option<&str>) -> (bool, &str) { fn else_if_option(string: Option<&str>) -> Option<(bool, &str)> { if string.is_none() { None - } else { string.map_or(Some((false, "")), |x| Some((true, x))) } + } else if let Some(x) = string { + Some((true, x)) + } else { + Some((false, "")) + } } fn unop_bad(string: &Option<&str>, mut num: Option) { diff --git a/tests/ui/option_if_let_else.stderr b/tests/ui/option_if_let_else.stderr index 7aab068800a..4ebb068d22e 100644 --- a/tests/ui/option_if_let_else.stderr +++ b/tests/ui/option_if_let_else.stderr @@ -10,17 +10,6 @@ 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:17:12 - | -LL | } else if let Some(x) = string { - | ____________^ -LL | | Some((true, x)) -LL | | } else { -LL | | Some((false, "")) -LL | | } - | |_____^ help: try: `{ string.map_or(Some((false, "")), |x| Some((true, x))) }` - error: use Option::map_or instead of an if let/else --> $DIR/option_if_let_else.rs:25:13 | @@ -159,5 +148,5 @@ error: use Option::map_or instead of an if let/else LL | let _ = if let Some(x) = optional { x + 2 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)` -error: aborting due to 12 previous errors +error: aborting due to 11 previous errors -- cgit 1.4.1-3-g733a5 From 18c702955b6c557fe864d73964c472f9455c2208 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Thu, 13 May 2021 23:28:40 +0200 Subject: Add sized trait for `wrong_self_convention` lint test --- tests/ui/wrong_self_convention2.rs | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'tests') diff --git a/tests/ui/wrong_self_convention2.rs b/tests/ui/wrong_self_convention2.rs index b2c6503de49..3a72174d03d 100644 --- a/tests/ui/wrong_self_convention2.rs +++ b/tests/ui/wrong_self_convention2.rs @@ -64,4 +64,8 @@ mod issue7179 { // lint fn from_be_self(self) -> Self; } + + trait Foo: Sized { + fn as_byte_slice(slice: &[Self]) -> &[u8]; + } } -- cgit 1.4.1-3-g733a5 From c6e0e843d279c0c4703e2b3326826423b791dee2 Mon Sep 17 00:00:00 2001 From: Jackson Lewis Date: Fri, 14 May 2021 16:45:18 -0700 Subject: Implement unnecessary-async and UI test --- clippy_lints/src/lib.rs | 4 ++ clippy_lints/src/unnecessary_async.rs | 92 +++++++++++++++++++++++++++++++++++ tests/ui/unnecessary_async.rs | 15 ++++++ tests/ui/unnecessary_async.stderr | 13 +++++ 4 files changed, 124 insertions(+) create mode 100644 clippy_lints/src/unnecessary_async.rs create mode 100644 tests/ui/unnecessary_async.rs create mode 100644 tests/ui/unnecessary_async.stderr (limited to 'tests') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 1a74f641554..e8b8b2e9117 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -360,6 +360,7 @@ mod unnamed_address; mod unnecessary_self_imports; mod unnecessary_sort_by; mod unnecessary_wraps; +mod unnecessary_async; mod unnested_or_patterns; mod unsafe_removed_from_name; mod unused_io_amount; @@ -954,6 +955,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: unit_types::UNIT_CMP, unnamed_address::FN_ADDRESS_COMPARISONS, unnamed_address::VTABLE_ADDRESS_COMPARISONS, + unnecessary_async::UNNECESSARY_ASYNC, unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS, unnecessary_sort_by::UNNECESSARY_SORT_BY, unnecessary_wraps::UNNECESSARY_WRAPS, @@ -1271,6 +1273,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box manual_map::ManualMap); store.register_late_pass(move || box if_then_some_else_none::IfThenSomeElseNone::new(msrv)); store.register_early_pass(|| box bool_assert_comparison::BoolAssertComparison); + store.register_late_pass(|| box unnecessary_async::UnnecessaryAsync); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(arithmetic::FLOAT_ARITHMETIC), @@ -1412,6 +1415,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(unicode::NON_ASCII_LITERAL), LintId::of(unicode::UNICODE_NOT_NFC), LintId::of(unit_types::LET_UNIT_VALUE), + LintId::of(unnecessary_async::UNNECESSARY_ASYNC), LintId::of(unnecessary_wraps::UNNECESSARY_WRAPS), LintId::of(unnested_or_patterns::UNNESTED_OR_PATTERNS), LintId::of(unused_self::UNUSED_SELF), diff --git a/clippy_lints/src/unnecessary_async.rs b/clippy_lints/src/unnecessary_async.rs new file mode 100644 index 00000000000..15a90ba42d2 --- /dev/null +++ b/clippy_lints/src/unnecessary_async.rs @@ -0,0 +1,92 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, NestedVisitorMap, Visitor}; +use rustc_hir::{Body, Expr, ExprKind, FnDecl, FnHeader, HirId, IsAsync, Item, ItemKind, YieldSource}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for functions that are declared `async` but have no `.await`s inside of them. + /// + /// **Why is this bad?** Async functions with no async code create overhead, both mentally and computationally. + /// Callers of async methods either need to be calling from an async function themselves or run it on an executor, both of which + /// causes runtime overhead and hassle for the caller. + /// + /// **Known problems:** None + /// + /// **Example:** + /// + /// ```rust + /// // Bad + /// async fn get_random_number() -> i64 { + /// 4 // Chosen by fair dice roll. Guaranteed to be random. + /// } + /// let number_future = get_random_number(); + /// + /// // Good + /// fn get_random_number_improved() -> i64 { + /// 4 // Chosen by fair dice roll. Guaranteed to be random. + /// } + /// let number_future = async { get_random_number_improved() }; + /// ``` + pub UNNECESSARY_ASYNC, + pedantic, + "finds async functions with no await statements" +} + +declare_lint_pass!(UnnecessaryAsync => [UNNECESSARY_ASYNC]); + +struct AsyncFnVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + found_await: bool, +} + +impl<'a, 'tcx> Visitor<'tcx> for AsyncFnVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { + if let ExprKind::Yield(_, YieldSource::Await { .. }) = ex.kind { + self.found_await = true; + } + walk_expr(self, ex); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) + } +} + +impl<'tcx> LateLintPass<'tcx> for UnnecessaryAsync { + fn check_item(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if let ItemKind::Trait(..) = item.kind { + return; + } + } + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + fn_kind: FnKind<'tcx>, + fn_decl: &'tcx FnDecl<'tcx>, + body: &Body<'tcx>, + span: Span, + hir_id: HirId, + ) { + if let FnKind::ItemFn(_, _, FnHeader { asyncness, .. }, _) = &fn_kind { + if matches!(asyncness, IsAsync::Async) { + let mut visitor = AsyncFnVisitor { cx, found_await: false }; + walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), span, hir_id); + if !visitor.found_await { + span_lint_and_help( + cx, + UNNECESSARY_ASYNC, + span, + "unnecessary `async` for function with no await statements", + None, + "consider removing the `async` from this function", + ); + } + } + } + } +} diff --git a/tests/ui/unnecessary_async.rs b/tests/ui/unnecessary_async.rs new file mode 100644 index 00000000000..7d63083b13e --- /dev/null +++ b/tests/ui/unnecessary_async.rs @@ -0,0 +1,15 @@ +// edition:2018 +#![warn(clippy::unnecessary_async)] + +async fn foo() -> i32 { + 4 +} + +async fn bar() -> i32 { + foo().await +} + +fn main() { + foo(); + bar(); +} diff --git a/tests/ui/unnecessary_async.stderr b/tests/ui/unnecessary_async.stderr new file mode 100644 index 00000000000..5542580e45d --- /dev/null +++ b/tests/ui/unnecessary_async.stderr @@ -0,0 +1,13 @@ +error: unnecessary `async` for function with no await statements + --> $DIR/unnecessary_async.rs:4:1 + | +LL | / async fn foo() -> i32 { +LL | | 4 +LL | | } + | |_^ + | + = note: `-D clippy::unnecessary-async` implied by `-D warnings` + = help: consider removing the `async` from this function + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From 1d8f3b51e662bb66e2b696d0c13057d6ba89f979 Mon Sep 17 00:00:00 2001 From: Jackson Lewis Date: Fri, 14 May 2021 17:07:30 -0700 Subject: Unnecessary -> Unused --- clippy_lints/src/lib.rs | 8 +-- clippy_lints/src/unnecessary_async.rs | 92 ----------------------------------- clippy_lints/src/unused_async.rs | 92 +++++++++++++++++++++++++++++++++++ tests/ui/unnecessary_async.rs | 15 ------ tests/ui/unnecessary_async.stderr | 13 ----- tests/ui/unused_async.rs | 15 ++++++ tests/ui/unused_async.stderr | 13 +++++ 7 files changed, 124 insertions(+), 124 deletions(-) delete mode 100644 clippy_lints/src/unnecessary_async.rs create mode 100644 clippy_lints/src/unused_async.rs delete mode 100644 tests/ui/unnecessary_async.rs delete mode 100644 tests/ui/unnecessary_async.stderr create mode 100644 tests/ui/unused_async.rs create mode 100644 tests/ui/unused_async.stderr (limited to 'tests') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index e8b8b2e9117..52e64d6a48f 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -360,7 +360,7 @@ mod unnamed_address; mod unnecessary_self_imports; mod unnecessary_sort_by; mod unnecessary_wraps; -mod unnecessary_async; +mod unused_async; mod unnested_or_patterns; mod unsafe_removed_from_name; mod unused_io_amount; @@ -955,7 +955,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: unit_types::UNIT_CMP, unnamed_address::FN_ADDRESS_COMPARISONS, unnamed_address::VTABLE_ADDRESS_COMPARISONS, - unnecessary_async::UNNECESSARY_ASYNC, + unused_async::UNUSED_ASYNC, unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS, unnecessary_sort_by::UNNECESSARY_SORT_BY, unnecessary_wraps::UNNECESSARY_WRAPS, @@ -1273,7 +1273,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box manual_map::ManualMap); store.register_late_pass(move || box if_then_some_else_none::IfThenSomeElseNone::new(msrv)); store.register_early_pass(|| box bool_assert_comparison::BoolAssertComparison); - store.register_late_pass(|| box unnecessary_async::UnnecessaryAsync); + store.register_late_pass(|| box unused_async::UnusedAsync); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(arithmetic::FLOAT_ARITHMETIC), @@ -1415,7 +1415,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(unicode::NON_ASCII_LITERAL), LintId::of(unicode::UNICODE_NOT_NFC), LintId::of(unit_types::LET_UNIT_VALUE), - LintId::of(unnecessary_async::UNNECESSARY_ASYNC), + LintId::of(unused_async::UNUSED_ASYNC), LintId::of(unnecessary_wraps::UNNECESSARY_WRAPS), LintId::of(unnested_or_patterns::UNNESTED_OR_PATTERNS), LintId::of(unused_self::UNUSED_SELF), diff --git a/clippy_lints/src/unnecessary_async.rs b/clippy_lints/src/unnecessary_async.rs deleted file mode 100644 index 15a90ba42d2..00000000000 --- a/clippy_lints/src/unnecessary_async.rs +++ /dev/null @@ -1,92 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_help; -use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, NestedVisitorMap, Visitor}; -use rustc_hir::{Body, Expr, ExprKind, FnDecl, FnHeader, HirId, IsAsync, Item, ItemKind, YieldSource}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::hir::map::Map; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::Span; - -declare_clippy_lint! { - /// **What it does:** Checks for functions that are declared `async` but have no `.await`s inside of them. - /// - /// **Why is this bad?** Async functions with no async code create overhead, both mentally and computationally. - /// Callers of async methods either need to be calling from an async function themselves or run it on an executor, both of which - /// causes runtime overhead and hassle for the caller. - /// - /// **Known problems:** None - /// - /// **Example:** - /// - /// ```rust - /// // Bad - /// async fn get_random_number() -> i64 { - /// 4 // Chosen by fair dice roll. Guaranteed to be random. - /// } - /// let number_future = get_random_number(); - /// - /// // Good - /// fn get_random_number_improved() -> i64 { - /// 4 // Chosen by fair dice roll. Guaranteed to be random. - /// } - /// let number_future = async { get_random_number_improved() }; - /// ``` - pub UNNECESSARY_ASYNC, - pedantic, - "finds async functions with no await statements" -} - -declare_lint_pass!(UnnecessaryAsync => [UNNECESSARY_ASYNC]); - -struct AsyncFnVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - found_await: bool, -} - -impl<'a, 'tcx> Visitor<'tcx> for AsyncFnVisitor<'a, 'tcx> { - type Map = Map<'tcx>; - - fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { - if let ExprKind::Yield(_, YieldSource::Await { .. }) = ex.kind { - self.found_await = true; - } - walk_expr(self, ex); - } - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) - } -} - -impl<'tcx> LateLintPass<'tcx> for UnnecessaryAsync { - fn check_item(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - if let ItemKind::Trait(..) = item.kind { - return; - } - } - fn check_fn( - &mut self, - cx: &LateContext<'tcx>, - fn_kind: FnKind<'tcx>, - fn_decl: &'tcx FnDecl<'tcx>, - body: &Body<'tcx>, - span: Span, - hir_id: HirId, - ) { - if let FnKind::ItemFn(_, _, FnHeader { asyncness, .. }, _) = &fn_kind { - if matches!(asyncness, IsAsync::Async) { - let mut visitor = AsyncFnVisitor { cx, found_await: false }; - walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), span, hir_id); - if !visitor.found_await { - span_lint_and_help( - cx, - UNNECESSARY_ASYNC, - span, - "unnecessary `async` for function with no await statements", - None, - "consider removing the `async` from this function", - ); - } - } - } - } -} diff --git a/clippy_lints/src/unused_async.rs b/clippy_lints/src/unused_async.rs new file mode 100644 index 00000000000..18ee07d3a95 --- /dev/null +++ b/clippy_lints/src/unused_async.rs @@ -0,0 +1,92 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, NestedVisitorMap, Visitor}; +use rustc_hir::{Body, Expr, ExprKind, FnDecl, FnHeader, HirId, IsAsync, Item, ItemKind, YieldSource}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for functions that are declared `async` but have no `.await`s inside of them. + /// + /// **Why is this bad?** Async functions with no async code create overhead, both mentally and computationally. + /// Callers of async methods either need to be calling from an async function themselves or run it on an executor, both of which + /// causes runtime overhead and hassle for the caller. + /// + /// **Known problems:** None + /// + /// **Example:** + /// + /// ```rust + /// // Bad + /// async fn get_random_number() -> i64 { + /// 4 // Chosen by fair dice roll. Guaranteed to be random. + /// } + /// let number_future = get_random_number(); + /// + /// // Good + /// fn get_random_number_improved() -> i64 { + /// 4 // Chosen by fair dice roll. Guaranteed to be random. + /// } + /// let number_future = async { get_random_number_improved() }; + /// ``` + pub UNUSED_ASYNC, + pedantic, + "finds async functions with no await statements" +} + +declare_lint_pass!(UnusedAsync => [UNUSED_ASYNC]); + +struct AsyncFnVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + found_await: bool, +} + +impl<'a, 'tcx> Visitor<'tcx> for AsyncFnVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { + if let ExprKind::Yield(_, YieldSource::Await { .. }) = ex.kind { + self.found_await = true; + } + walk_expr(self, ex); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) + } +} + +impl<'tcx> LateLintPass<'tcx> for UnusedAsync { + fn check_item(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if let ItemKind::Trait(..) = item.kind { + return; + } + } + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + fn_kind: FnKind<'tcx>, + fn_decl: &'tcx FnDecl<'tcx>, + body: &Body<'tcx>, + span: Span, + hir_id: HirId, + ) { + if let FnKind::ItemFn(_, _, FnHeader { asyncness, .. }, _) = &fn_kind { + if matches!(asyncness, IsAsync::Async) { + let mut visitor = AsyncFnVisitor { cx, found_await: false }; + walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), span, hir_id); + if !visitor.found_await { + span_lint_and_help( + cx, + UNUSED_ASYNC, + span, + "unused `async` for function with no await statements", + None, + "consider removing the `async` from this function", + ); + } + } + } + } +} diff --git a/tests/ui/unnecessary_async.rs b/tests/ui/unnecessary_async.rs deleted file mode 100644 index 7d63083b13e..00000000000 --- a/tests/ui/unnecessary_async.rs +++ /dev/null @@ -1,15 +0,0 @@ -// edition:2018 -#![warn(clippy::unnecessary_async)] - -async fn foo() -> i32 { - 4 -} - -async fn bar() -> i32 { - foo().await -} - -fn main() { - foo(); - bar(); -} diff --git a/tests/ui/unnecessary_async.stderr b/tests/ui/unnecessary_async.stderr deleted file mode 100644 index 5542580e45d..00000000000 --- a/tests/ui/unnecessary_async.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error: unnecessary `async` for function with no await statements - --> $DIR/unnecessary_async.rs:4:1 - | -LL | / async fn foo() -> i32 { -LL | | 4 -LL | | } - | |_^ - | - = note: `-D clippy::unnecessary-async` implied by `-D warnings` - = help: consider removing the `async` from this function - -error: aborting due to previous error - diff --git a/tests/ui/unused_async.rs b/tests/ui/unused_async.rs new file mode 100644 index 00000000000..4f4203f5fdb --- /dev/null +++ b/tests/ui/unused_async.rs @@ -0,0 +1,15 @@ +// edition:2018 +#![warn(clippy::unused_async)] + +async fn foo() -> i32 { + 4 +} + +async fn bar() -> i32 { + foo().await +} + +fn main() { + foo(); + bar(); +} diff --git a/tests/ui/unused_async.stderr b/tests/ui/unused_async.stderr new file mode 100644 index 00000000000..8b834d205b1 --- /dev/null +++ b/tests/ui/unused_async.stderr @@ -0,0 +1,13 @@ +error: unused `async` for function with no await statements + --> $DIR/unused_async.rs:4:1 + | +LL | / async fn foo() -> i32 { +LL | | 4 +LL | | } + | |_^ + | + = note: `-D clippy::unused-async` implied by `-D warnings` + = help: consider removing the `async` from this function + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From ebf065e5172227a99743f4032c5bfff80d5630e6 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Mon, 17 May 2021 22:18:50 +0900 Subject: Fix a `manual_unwrap_or` FP with deref coercion --- clippy_lints/src/manual_unwrap_or.rs | 3 +++ tests/ui/manual_unwrap_or.fixed | 8 ++++++++ tests/ui/manual_unwrap_or.rs | 8 ++++++++ 3 files changed, 19 insertions(+) (limited to 'tests') diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index 520162559e5..cf183d4c16f 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -11,6 +11,7 @@ use rustc_hir::{Arm, Expr, ExprKind, PatKind}; use rustc_lint::LintContext; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::adjustment::Adjust; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -87,6 +88,8 @@ fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind; if path_to_local_id(unwrap_arm.body, binding_hir_id); if !contains_return_break_continue_macro(or_arm.body); + if !cx.typeck_results().expr_adjustments(unwrap_arm.body).iter() + .any(|a| matches!(a.kind, Adjust::Deref(Some(..)))); then { Some(or_arm) } else { diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index e7a29596b73..1efecb0ba12 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -163,4 +163,12 @@ mod issue6965 { } } +use std::rc::Rc; +fn format_name(name: Option<&Rc>) -> &str { + match name { + None => "", + Some(name) => name, + } +} + fn main() {} diff --git a/tests/ui/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs index 66006b6c616..95d585ad18a 100644 --- a/tests/ui/manual_unwrap_or.rs +++ b/tests/ui/manual_unwrap_or.rs @@ -205,4 +205,12 @@ mod issue6965 { } } +use std::rc::Rc; +fn format_name(name: Option<&Rc>) -> &str { + match name { + None => "", + Some(name) => name, + } +} + fn main() {} -- cgit 1.4.1-3-g733a5 From 2fb35ce4f0ea8d33bbe207c8a1c8822ebb90c813 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Thu, 13 May 2021 21:40:20 +0200 Subject: Add generic args for comparison in `use_self` and `useless_conversion` lints --- clippy_lints/src/use_self.rs | 10 +++++----- clippy_lints/src/useless_conversion.rs | 15 ++++++++------- clippy_utils/src/ty.rs | 24 ++++++++++++++++++++++++ tests/ui/use_self.fixed | 30 ++++++++++++++++++++++++++++++ tests/ui/use_self.rs | 30 ++++++++++++++++++++++++++++++ tests/ui/use_self.stderr | 8 +++++++- tests/ui/useless_conversion.fixed | 19 +++++++++++++++++++ tests/ui/useless_conversion.rs | 19 +++++++++++++++++++ tests/ui/useless_conversion.stderr | 20 +++++++++++++++++++- 9 files changed, 161 insertions(+), 14 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index aa4d16633ff..2ad6fa77f48 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; +use clippy_utils::ty::same_type_and_consts; use clippy_utils::{in_macro, meets_msrv, msrvs}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_hir::def::DefKind; use rustc_hir::{ - def, + self as hir, + def::{self, DefKind}, def_id::LocalDefId, intravisit::{walk_ty, NestedVisitorMap, Visitor}, Expr, ExprKind, FnRetTy, FnSig, GenericArg, HirId, Impl, ImplItemKind, Item, ItemKind, Node, Path, PathSegment, @@ -14,7 +14,7 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; -use rustc_middle::ty::{AssocKind, Ty, TyS}; +use rustc_middle::ty::{AssocKind, Ty}; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{BytePos, Span}; @@ -459,7 +459,7 @@ fn in_impl(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'_>) -> bool { fn should_lint_ty(hir_ty: &hir::Ty<'_>, ty: Ty<'_>, self_ty: Ty<'_>) -> bool { if_chain! { - if TyS::same_type(ty, self_ty); + if same_type_and_consts(ty, self_ty); if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind; then { !matches!(path.res, def::Res::SelfTy(..)) diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 7edb280be73..2be99fb761b 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,13 +1,13 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::source::{snippet, snippet_with_macro_callsite}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::{is_type_diagnostic_item, same_type_and_consts}; use clippy_utils::{get_parent_expr, match_def_path, match_trait_method, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, HirId, MatchSource}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, TyS}; +use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::sym; @@ -67,7 +67,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { if match_trait_method(cx, e, &paths::INTO) && &*name.ident.as_str() == "into" { let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(&args[0]); - if TyS::same_type(a, b) { + if same_type_and_consts(a, b) { let sugg = snippet_with_macro_callsite(cx, args[0].span, "").to_string(); span_lint_and_sugg( cx, @@ -90,7 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { } let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(&args[0]); - if TyS::same_type(a, b) { + if same_type_and_consts(a, b) { let sugg = snippet(cx, args[0].span, "").into_owned(); span_lint_and_sugg( cx, @@ -110,7 +110,8 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { if is_type_diagnostic_item(cx, a, sym::result_type); if let ty::Adt(_, substs) = a.kind(); if let Some(a_type) = substs.types().next(); - if TyS::same_type(a_type, b); + if same_type_and_consts(a_type, b); + then { span_lint_and_help( cx, @@ -137,7 +138,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { if is_type_diagnostic_item(cx, a, sym::result_type); if let ty::Adt(_, substs) = a.kind(); if let Some(a_type) = substs.types().next(); - if TyS::same_type(a_type, b); + if same_type_and_consts(a_type, b); then { let hint = format!("consider removing `{}()`", snippet(cx, path.span, "TryFrom::try_from")); @@ -154,7 +155,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { if_chain! { if match_def_path(cx, def_id, &paths::FROM_FROM); - if TyS::same_type(a, b); + if same_type_and_consts(a, b); then { let sugg = Sugg::hir_with_macro_callsite(cx, &args[0], "").maybe_par(); diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 64a80f2554f..e1f8aff3740 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -322,3 +322,27 @@ pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) { } inner(ty, 0) } + +/// Returns `true` if types `a` and `b` are same types having same `Const` generic args, +/// otherwise returns `false` +pub fn same_type_and_consts(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { + match (&a.kind(), &b.kind()) { + (&ty::Adt(did_a, substs_a), &ty::Adt(did_b, substs_b)) => { + if did_a != did_b { + return false; + } + + substs_a + .iter() + .zip(substs_b.iter()) + .all(|(arg_a, arg_b)| match (arg_a.unpack(), arg_b.unpack()) { + (GenericArgKind::Const(inner_a), GenericArgKind::Const(inner_b)) => inner_a == inner_b, + (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => { + same_type_and_consts(type_a, type_b) + }, + _ => true, + }) + }, + _ => a == b, + } +} diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed index 1282befdfb3..631da6fe066 100644 --- a/tests/ui/use_self.fixed +++ b/tests/ui/use_self.fixed @@ -462,3 +462,33 @@ mod issue6818 { a: i32, } } + +mod issue7206 { + struct MyStruct; + impl From> for MyStruct<'b'> { + fn from(_s: MyStruct<'a'>) -> Self { + Self + } + } + + // keep linting non-`Const` generic args + struct S<'a> { + inner: &'a str, + } + + struct S2 { + inner: T, + } + + impl S2 { + fn new() -> Self { + unimplemented!(); + } + } + + impl<'a> S2> { + fn new_again() -> Self { + Self::new() + } + } +} diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index 7aaac7b2414..7a10d755faa 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -462,3 +462,33 @@ mod issue6818 { a: i32, } } + +mod issue7206 { + struct MyStruct; + impl From> for MyStruct<'b'> { + fn from(_s: MyStruct<'a'>) -> Self { + Self + } + } + + // keep linting non-`Const` generic args + struct S<'a> { + inner: &'a str, + } + + struct S2 { + inner: T, + } + + impl S2 { + fn new() -> Self { + unimplemented!(); + } + } + + impl<'a> S2> { + fn new_again() -> Self { + S2::new() + } + } +} diff --git a/tests/ui/use_self.stderr b/tests/ui/use_self.stderr index a32a9b9157d..cf6222c9b45 100644 --- a/tests/ui/use_self.stderr +++ b/tests/ui/use_self.stderr @@ -162,5 +162,11 @@ error: unnecessary structure name repetition LL | A::new::(submod::B {}) | ^ help: use the applicable keyword: `Self` -error: aborting due to 27 previous errors +error: unnecessary structure name repetition + --> $DIR/use_self.rs:491:13 + | +LL | S2::new() + | ^^ help: use the applicable keyword: `Self` + +error: aborting due to 28 previous errors diff --git a/tests/ui/useless_conversion.fixed b/tests/ui/useless_conversion.fixed index 03977de9455..76aa82068d6 100644 --- a/tests/ui/useless_conversion.fixed +++ b/tests/ui/useless_conversion.fixed @@ -70,4 +70,23 @@ fn main() { let a: i32 = 1; let b: i32 = 1; let _ = (a + b) * 3; + + // see #7205 + let s: Foo<'a'> = Foo; + let _: Foo<'b'> = s.into(); + let s2: Foo<'a'> = Foo; + let _: Foo<'a'> = s2; + let s3: Foo<'a'> = Foo; + let _ = s3; + let s4: Foo<'a'> = Foo; + let _ = vec![s4, s4, s4].into_iter(); +} + +#[derive(Copy, Clone)] +struct Foo; + +impl From> for Foo<'b'> { + fn from(_s: Foo<'a'>) -> Self { + Foo + } } diff --git a/tests/ui/useless_conversion.rs b/tests/ui/useless_conversion.rs index f6e094c1661..ccee7abb404 100644 --- a/tests/ui/useless_conversion.rs +++ b/tests/ui/useless_conversion.rs @@ -70,4 +70,23 @@ fn main() { let a: i32 = 1; let b: i32 = 1; let _ = i32::from(a + b) * 3; + + // see #7205 + let s: Foo<'a'> = Foo; + let _: Foo<'b'> = s.into(); + let s2: Foo<'a'> = Foo; + let _: Foo<'a'> = s2.into(); + let s3: Foo<'a'> = Foo; + let _ = Foo::<'a'>::from(s3); + let s4: Foo<'a'> = Foo; + let _ = vec![s4, s4, s4].into_iter().into_iter(); +} + +#[derive(Copy, Clone)] +struct Foo; + +impl From> for Foo<'b'> { + fn from(_s: Foo<'a'>) -> Self { + Foo + } } diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index 26a33595031..e6760f700f3 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -70,5 +70,23 @@ error: useless conversion to the same type: `i32` LL | let _ = i32::from(a + b) * 3; | ^^^^^^^^^^^^^^^^ help: consider removing `i32::from()`: `(a + b)` -error: aborting due to 11 previous errors +error: useless conversion to the same type: `Foo<'a'>` + --> $DIR/useless_conversion.rs:78:23 + | +LL | let _: Foo<'a'> = s2.into(); + | ^^^^^^^^^ help: consider removing `.into()`: `s2` + +error: useless conversion to the same type: `Foo<'a'>` + --> $DIR/useless_conversion.rs:80:13 + | +LL | let _ = Foo::<'a'>::from(s3); + | ^^^^^^^^^^^^^^^^^^^^ help: consider removing `Foo::<'a'>::from()`: `s3` + +error: useless conversion to the same type: `std::vec::IntoIter>` + --> $DIR/useless_conversion.rs:82:13 + | +LL | let _ = vec![s4, s4, s4].into_iter().into_iter(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![s4, s4, s4].into_iter()` + +error: aborting due to 14 previous errors -- cgit 1.4.1-3-g733a5 From 8356c485a99890339022a7afd99fc13ae09e5805 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 17 May 2021 14:20:26 -0500 Subject: Fix manual_unwrap_or FP deref reference --- clippy_lints/src/manual_unwrap_or.rs | 4 +--- tests/ui/manual_unwrap_or.fixed | 7 +++++++ tests/ui/manual_unwrap_or.rs | 7 +++++++ 3 files changed, 15 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index d7e8d180f7e..2f579edd6ad 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -11,7 +11,6 @@ use rustc_hir::{Arm, Expr, ExprKind, PatKind}; use rustc_lint::LintContext; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::adjustment::Adjust; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -72,9 +71,8 @@ fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk); if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind; if path_to_local_id(unwrap_arm.body, binding_hir_id); + if cx.typeck_results().expr_adjustments(unwrap_arm.body).is_empty(); if !contains_return_break_continue_macro(or_arm.body); - if !cx.typeck_results().expr_adjustments(unwrap_arm.body).iter() - .any(|a| matches!(a.kind, Adjust::Deref(Some(..)))); then { Some(or_arm) } else { diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index 1efecb0ba12..3717f962745 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -171,4 +171,11 @@ fn format_name(name: Option<&Rc>) -> &str { } } +fn implicit_deref_ref() { + let _: &str = match Some(&"bye") { + None => "hi", + Some(s) => s, + }; +} + fn main() {} diff --git a/tests/ui/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs index 95d585ad18a..989adde1f5b 100644 --- a/tests/ui/manual_unwrap_or.rs +++ b/tests/ui/manual_unwrap_or.rs @@ -213,4 +213,11 @@ fn format_name(name: Option<&Rc>) -> &str { } } +fn implicit_deref_ref() { + let _: &str = match Some(&"bye") { + None => "hi", + Some(s) => s, + }; +} + fn main() {} -- cgit 1.4.1-3-g733a5 From be540e65965d6ab0e3a04d6fa05651a701e6b8a4 Mon Sep 17 00:00:00 2001 From: "Bruno A. Muciño" Date: Mon, 17 May 2021 21:59:08 -0500 Subject: Remove powi, "square can be computed more efficiently" powi(2) produces exactly the same native code as x * x --- clippy_lints/src/floating_point_arithmetic.rs | 12 +--------- tests/ui/floating_point_powi.fixed | 5 ++-- tests/ui/floating_point_powi.rs | 5 ++-- tests/ui/floating_point_powi.stderr | 34 +++++++++------------------ 4 files changed, 18 insertions(+), 38 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index e0b687b0205..08f28cd54c5 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -323,7 +323,7 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { cx, SUBOPTIMAL_FLOPS, parent.span, - "square can be computed more efficiently", + "multiply and add expressions can be calculated more efficiently and accurately", "consider using", format!( "{}.mul_add({}, {})", @@ -337,16 +337,6 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { return; } } - - span_lint_and_sugg( - cx, - SUBOPTIMAL_FLOPS, - expr.span, - "square can be computed more efficiently", - "consider using", - format!("{} * {}", Sugg::hir(cx, &args[0], ".."), Sugg::hir(cx, &args[0], "..")), - Applicability::MachineApplicable, - ); } } } diff --git a/tests/ui/floating_point_powi.fixed b/tests/ui/floating_point_powi.fixed index 56762400593..85f7c531e39 100644 --- a/tests/ui/floating_point_powi.fixed +++ b/tests/ui/floating_point_powi.fixed @@ -4,8 +4,6 @@ fn main() { let one = 1; let x = 3f32; - let _ = x * x; - let _ = x * x; let y = 4f32; let _ = x.mul_add(x, y); @@ -13,7 +11,10 @@ fn main() { let _ = x.mul_add(x, y).sqrt(); let _ = y.mul_add(y, x).sqrt(); // Cases where the lint shouldn't be applied + let _ = x.powi(2); + let _ = x.powi(1 + 1); let _ = x.powi(3); + let _ = x.powi(4) + y; let _ = x.powi(one + 1); let _ = (x.powi(2) + y.powi(2)).sqrt(); } diff --git a/tests/ui/floating_point_powi.rs b/tests/ui/floating_point_powi.rs index 1f800e4628d..ece61d1bec4 100644 --- a/tests/ui/floating_point_powi.rs +++ b/tests/ui/floating_point_powi.rs @@ -4,8 +4,6 @@ fn main() { let one = 1; let x = 3f32; - let _ = x.powi(2); - let _ = x.powi(1 + 1); let y = 4f32; let _ = x.powi(2) + y; @@ -13,7 +11,10 @@ fn main() { let _ = (x.powi(2) + y).sqrt(); let _ = (x + y.powi(2)).sqrt(); // Cases where the lint shouldn't be applied + let _ = x.powi(2); + let _ = x.powi(1 + 1); let _ = x.powi(3); + let _ = x.powi(4) + y; let _ = x.powi(one + 1); let _ = (x.powi(2) + y.powi(2)).sqrt(); } diff --git a/tests/ui/floating_point_powi.stderr b/tests/ui/floating_point_powi.stderr index d5a5f1bcca1..37d840988bb 100644 --- a/tests/ui/floating_point_powi.stderr +++ b/tests/ui/floating_point_powi.stderr @@ -1,40 +1,28 @@ -error: square can be computed more efficiently - --> $DIR/floating_point_powi.rs:7:13 - | -LL | let _ = x.powi(2); - | ^^^^^^^^^ help: consider using: `x * x` - | - = note: `-D clippy::suboptimal-flops` implied by `-D warnings` - -error: square can be computed more efficiently - --> $DIR/floating_point_powi.rs:8:13 - | -LL | let _ = x.powi(1 + 1); - | ^^^^^^^^^^^^^ help: consider using: `x * x` - -error: square can be computed more efficiently - --> $DIR/floating_point_powi.rs:11:13 +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:9:13 | LL | let _ = x.powi(2) + y; | ^^^^^^^^^^^^^ help: consider using: `x.mul_add(x, y)` + | + = note: `-D clippy::suboptimal-flops` implied by `-D warnings` -error: square can be computed more efficiently - --> $DIR/floating_point_powi.rs:12:13 +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:10:13 | LL | let _ = x + y.powi(2); | ^^^^^^^^^^^^^ help: consider using: `y.mul_add(y, x)` -error: square can be computed more efficiently - --> $DIR/floating_point_powi.rs:13:13 +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:11:13 | LL | let _ = (x.powi(2) + y).sqrt(); | ^^^^^^^^^^^^^^^ help: consider using: `x.mul_add(x, y)` -error: square can be computed more efficiently - --> $DIR/floating_point_powi.rs:14:13 +error: multiply and add expressions can be calculated more efficiently and accurately + --> $DIR/floating_point_powi.rs:12:13 | LL | let _ = (x + y.powi(2)).sqrt(); | ^^^^^^^^^^^^^^^ help: consider using: `y.mul_add(y, x)` -error: aborting due to 6 previous errors +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From a149ba26fa07b42b40f6e38f69ef74ecd756d68f Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 18 May 2021 10:51:59 -0400 Subject: Fix ICE in `implicit_return` async functions always return a value --- clippy_lints/src/implicit_return.rs | 6 +++++- tests/ui/crashes/ice-7231.rs | 10 ++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 tests/ui/crashes/ice-7231.rs (limited to 'tests') diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs index 30174fa2100..260a8f50157 100644 --- a/clippy_lints/src/implicit_return.rs +++ b/clippy_lints/src/implicit_return.rs @@ -147,7 +147,11 @@ fn lint_implicit_returns( visit_break_exprs(block, |break_expr, dest, sub_expr| { if dest.target_id.ok() == Some(expr.hir_id) { if call_site_span.is_none() && break_expr.span.ctxt() == ctxt { - lint_break(cx, break_expr.span, sub_expr.unwrap().span); + // At this point sub_expr can be `None` in async functions which either diverge, or return the + // unit type. + if let Some(sub_expr) = sub_expr { + lint_break(cx, break_expr.span, sub_expr.span); + } } else { // the break expression is from a macro call, add a return to the loop add_return = true; diff --git a/tests/ui/crashes/ice-7231.rs b/tests/ui/crashes/ice-7231.rs new file mode 100644 index 00000000000..5595d8d1d62 --- /dev/null +++ b/tests/ui/crashes/ice-7231.rs @@ -0,0 +1,10 @@ +// edition:2018 +#![allow(clippy::never_loop)] + +async fn f() { + loop { + break; + } +} + +fn main() {} -- cgit 1.4.1-3-g733a5 From 760f70312e6b894427cfc68a066bc1e87513337c Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 15 Apr 2021 12:20:43 -0400 Subject: Improve `multiple_inherent_impl` lint Treat different generic arguments as different types. Allow the lint to be ignored on the type definition, or any impl blocks. --- clippy_lints/src/inherent_impl.rs | 137 ++++++++++++++++++++++++++------------ clippy_lints/src/lib.rs | 2 +- tests/ui/impl.rs | 31 +++++++++ tests/ui/impl.stderr | 30 ++++++++- 4 files changed, 154 insertions(+), 46 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/inherent_impl.rs b/clippy_lints/src/inherent_impl.rs index c31013e49be..980bcc78f5d 100644 --- a/clippy_lints/src/inherent_impl.rs +++ b/clippy_lints/src/inherent_impl.rs @@ -1,12 +1,16 @@ //! lint on inherent implementations -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::in_macro; -use rustc_hir::def_id::DefIdMap; -use rustc_hir::{def_id, Crate, Impl, Item, ItemKind}; +use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::{in_macro, is_allowed}; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::{ + def_id::{LocalDefId, LOCAL_CRATE}, + Crate, Item, ItemKind, Node, +}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; +use std::collections::hash_map::Entry; declare_clippy_lint! { /// **What it does:** Checks for multiple inherent implementations of a struct @@ -40,51 +44,96 @@ declare_clippy_lint! { "Multiple inherent impl that could be grouped" } -#[allow(clippy::module_name_repetitions)] -#[derive(Default)] -pub struct MultipleInherentImpl { - impls: DefIdMap, -} - -impl_lint_pass!(MultipleInherentImpl => [MULTIPLE_INHERENT_IMPL]); +declare_lint_pass!(MultipleInherentImpl => [MULTIPLE_INHERENT_IMPL]); impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl { - fn check_item(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if let ItemKind::Impl(Impl { - ref generics, - of_trait: None, - .. - }) = item.kind + fn check_crate_post(&mut self, cx: &LateContext<'tcx>, _: &'tcx Crate<'_>) { + // Map from a type to it's first impl block. Needed to distinguish generic arguments. + // e.g. `Foo` and `Foo` + let mut type_map = FxHashMap::default(); + // List of spans to lint. (lint_span, first_span) + let mut lint_spans = Vec::new(); + + for (_, impl_ids) in cx + .tcx + .crate_inherent_impls(LOCAL_CRATE) + .inherent_impls + .iter() + .filter(|(id, impls)| { + impls.len() > 1 + // Check for `#[allow]` on the type definition + && !is_allowed( + cx, + MULTIPLE_INHERENT_IMPL, + cx.tcx.hir().local_def_id_to_hir_id(id.expect_local()), + ) + }) { - // Remember for each inherent implementation encountered its span and generics - // but filter out implementations that have generic params (type or lifetime) - // or are derived from a macro - if !in_macro(item.span) && generics.params.is_empty() { - self.impls.insert(item.def_id.to_def_id(), item.span); + for impl_id in impl_ids.iter().map(|id| id.expect_local()) { + match type_map.entry(cx.tcx.type_of(impl_id)) { + Entry::Vacant(e) => { + // Store the id for the first impl block of this type. The span is retrieved lazily. + e.insert(IdOrSpan::Id(impl_id)); + }, + Entry::Occupied(mut e) => { + if let Some(span) = get_impl_span(cx, impl_id) { + let first_span = match *e.get() { + IdOrSpan::Span(s) => s, + IdOrSpan::Id(id) => { + if let Some(s) = get_impl_span(cx, id) { + // Remember the span of the first block. + *e.get_mut() = IdOrSpan::Span(s); + s + } else { + // The first impl block isn't considered by the lint. Replace it with the + // current one. + *e.get_mut() = IdOrSpan::Span(span); + continue; + } + }, + }; + lint_spans.push((span, first_span)); + } + }, + } } + + // Switching to the next type definition, no need to keep the current entries around. + type_map.clear(); } - } - fn check_crate_post(&mut self, cx: &LateContext<'tcx>, krate: &'tcx Crate<'_>) { - if !krate.items.is_empty() { - // Retrieve all inherent implementations from the crate, grouped by type - for impls in cx.tcx.crate_inherent_impls(def_id::LOCAL_CRATE).inherent_impls.values() { - // Filter out implementations that have generic params (type or lifetime) - let mut impl_spans = impls.iter().filter_map(|impl_def| self.impls.get(impl_def)); - if let Some(initial_span) = impl_spans.next() { - impl_spans.for_each(|additional_span| { - span_lint_and_then( - cx, - MULTIPLE_INHERENT_IMPL, - *additional_span, - "multiple implementations of this structure", - |diag| { - diag.span_note(*initial_span, "first implementation here"); - }, - ) - }) - } - } + // `TyCtxt::crate_inherent_impls` doesn't have a defined order. Sort the lint output first. + lint_spans.sort_by_key(|x| x.0.lo()); + for (span, first_span) in lint_spans { + span_lint_and_note( + cx, + MULTIPLE_INHERENT_IMPL, + span, + "multiple implementations of this structure", + Some(first_span), + "first implementation here", + ); } } } + +/// Gets the span for the given impl block unless it's not being considered by the lint. +fn get_impl_span(cx: &LateContext<'_>, id: LocalDefId) -> Option { + let id = cx.tcx.hir().local_def_id_to_hir_id(id); + if let Node::Item(&Item { + kind: ItemKind::Impl(ref impl_item), + span, + .. + }) = cx.tcx.hir().get(id) + { + (!in_macro(span) && impl_item.generics.params.is_empty() && !is_allowed(cx, MULTIPLE_INHERENT_IMPL, id)) + .then(|| span) + } else { + None + } +} + +enum IdOrSpan { + Id(LocalDefId), + Span(Span), +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 78a95b00403..47c576bac48 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1154,7 +1154,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box suspicious_operation_groupings::SuspiciousOperationGroupings); store.register_late_pass(|| box suspicious_trait_impl::SuspiciousImpl); store.register_late_pass(|| box map_unit_fn::MapUnit); - store.register_late_pass(|| box inherent_impl::MultipleInherentImpl::default()); + store.register_late_pass(|| box inherent_impl::MultipleInherentImpl); store.register_late_pass(|| box neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd); store.register_late_pass(|| box unwrap::Unwrap); store.register_late_pass(|| box duration_subsec::DurationSubsec); diff --git a/tests/ui/impl.rs b/tests/ui/impl.rs index 1c46e3a5337..39443775015 100644 --- a/tests/ui/impl.rs +++ b/tests/ui/impl.rs @@ -33,4 +33,35 @@ impl fmt::Debug for MyStruct { } } +// issue #5772 +struct WithArgs(T); +impl WithArgs { + fn f1() {} +} +impl WithArgs { + fn f2() {} +} +impl WithArgs { + fn f3() {} +} + +// Ok, the struct is allowed to have multiple impls. +#[allow(clippy::multiple_inherent_impl)] +struct Allowed; +impl Allowed {} +impl Allowed {} +impl Allowed {} + +struct AllowedImpl; +#[allow(clippy::multiple_inherent_impl)] +impl AllowedImpl {} +// Ok, the first block is skipped by this lint. +impl AllowedImpl {} + +struct OneAllowedImpl; +impl OneAllowedImpl {} +#[allow(clippy::multiple_inherent_impl)] +impl OneAllowedImpl {} +impl OneAllowedImpl {} // Lint, only one of the three blocks is allowed. + fn main() {} diff --git a/tests/ui/impl.stderr b/tests/ui/impl.stderr index aab688cc2d8..8703ecac93e 100644 --- a/tests/ui/impl.stderr +++ b/tests/ui/impl.stderr @@ -31,5 +31,33 @@ LL | | fn first() {} LL | | } | |_^ -error: aborting due to 2 previous errors +error: multiple implementations of this structure + --> $DIR/impl.rs:44:1 + | +LL | / impl WithArgs { +LL | | fn f3() {} +LL | | } + | |_^ + | +note: first implementation here + --> $DIR/impl.rs:41:1 + | +LL | / impl WithArgs { +LL | | fn f2() {} +LL | | } + | |_^ + +error: multiple implementations of this structure + --> $DIR/impl.rs:65:1 + | +LL | impl OneAllowedImpl {} // Lint, only one of the three blocks is allowed. + | ^^^^^^^^^^^^^^^^^^^^^^ + | +note: first implementation here + --> $DIR/impl.rs:62:1 + | +LL | impl OneAllowedImpl {} + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From 6d4dc35882a55610e9da81237d62bea1c0c1634e Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 17 Apr 2021 20:35:39 -0400 Subject: Improve `needless_borrow` lint Suggest changing usages of ref bindings to match the new type Split out some cases into new lint `ref_binding_to_reference` --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 4 +- clippy_lints/src/needless_borrow.rs | 203 +++++++++++++++++++++++++++---- clippy_utils/src/higher.rs | 2 +- clippy_utils/src/lib.rs | 4 +- clippy_utils/src/visitors.rs | 37 ++---- tests/ui/needless_borrow.fixed | 17 --- tests/ui/needless_borrow.rs | 17 --- tests/ui/needless_borrow.stderr | 16 +-- tests/ui/needless_borrow_pat.rs | 151 +++++++++++++++++++++++ tests/ui/needless_borrow_pat.stderr | 112 +++++++++++++++++ tests/ui/ref_binding_to_reference.rs | 76 ++++++++++++ tests/ui/ref_binding_to_reference.stderr | 88 ++++++++++++++ 13 files changed, 626 insertions(+), 102 deletions(-) create mode 100644 tests/ui/needless_borrow_pat.rs create mode 100644 tests/ui/needless_borrow_pat.stderr create mode 100644 tests/ui/ref_binding_to_reference.rs create mode 100644 tests/ui/ref_binding_to_reference.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index da5a0712c95..abfe7f91f4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2622,6 +2622,7 @@ Released 2018-09-13 [`redundant_pub_crate`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pub_crate [`redundant_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_slicing [`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes +[`ref_binding_to_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_binding_to_reference [`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref [`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref [`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 5cd7d5f14e3..a263e8b0ca4 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -841,6 +841,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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, @@ -1116,6 +1117,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), @@ -1890,7 +1892,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box zero_div_zero::ZeroDiv); store.register_late_pass(|| box mutex_atomic::Mutex); store.register_late_pass(|| box needless_update::NeedlessUpdate); - store.register_late_pass(|| box needless_borrow::NeedlessBorrow); + store.register_late_pass(|| box needless_borrow::NeedlessBorrow::default()); store.register_late_pass(|| box needless_borrowed_ref::NeedlessBorrowedRef); store.register_late_pass(|| box no_effect::NoEffect); store.register_late_pass(|| box temporary_assignment::TemporaryAssignment); diff --git a/clippy_lints/src/needless_borrow.rs b/clippy_lints/src/needless_borrow.rs index 3adbbfb8e89..965b131770e 100644 --- a/clippy_lints/src/needless_borrow.rs +++ b/clippy_lints/src/needless_borrow.rs @@ -3,14 +3,18 @@ //! This lint is **warn** by default use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::{snippet_opt, snippet_with_applicability, snippet_with_context}; +use clippy_utils::{get_parent_expr, in_macro, path_to_local}; use if_chain::if_chain; +use rustc_ast::util::parser::PREC_POSTFIX; +use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; -use rustc_hir::{BindingAnnotation, BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind}; +use rustc_hir::{BindingAnnotation, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, Mutability, Pat, PatKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::Span; declare_clippy_lint! { /// **What it does:** Checks for address of operations (`&`) that are going to @@ -34,13 +38,65 @@ declare_clippy_lint! { "taking a reference that is going to be automatically dereferenced" } +declare_clippy_lint! { + /// **What it does:** Checks for `ref` bindings which create a reference to a reference. + /// + /// **Why is this bad?** The address-of operator at the use site is clearer about the need for a reference. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// // Bad + /// let x = Some(""); + /// if let Some(ref x) = x { + /// // use `x` here + /// } + /// + /// // Good + /// let x = Some(""); + /// if let Some(x) = x { + /// // use `&x` here + /// } + /// ``` + pub REF_BINDING_TO_REFERENCE, + pedantic, + "`ref` binding to a reference" +} + +impl_lint_pass!(NeedlessBorrow => [NEEDLESS_BORROW, REF_BINDING_TO_REFERENCE]); #[derive(Default)] -pub struct NeedlessBorrow; +pub struct NeedlessBorrow { + /// The body the first local was found in. Used to emit lints when the traversal of the body has + /// been finished. Note we can't lint at the end of every body as they can be nested within each + /// other. + current_body: Option, + /// The list of locals currently being checked by the lint. + /// If the value is `None`, then the binding has been seen as a ref pattern, but is not linted. + /// This is needed for or patterns where one of the branches can be linted, but another can not + /// be. + /// + /// e.g. `m!(x) | Foo::Bar(ref x)` + ref_locals: FxIndexMap>, +} -impl_lint_pass!(NeedlessBorrow => [NEEDLESS_BORROW]); +struct RefPat { + /// Whether every usage of the binding is dereferenced. + always_deref: bool, + /// The spans of all the ref bindings for this local. + spans: Vec, + /// The applicability of this suggestion. + app: Applicability, + /// All the replacements which need to be made. + replacements: Vec<(Span, String)>, +} impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { + if let Some(local) = path_to_local(e) { + self.check_local_usage(cx, e, local); + } + if e.span.from_expansion() { return; } @@ -81,35 +137,132 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow { } } } + fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) { - if pat.span.from_expansion() { - return; + if let PatKind::Binding(BindingAnnotation::Ref, id, name, _) = pat.kind { + if let Some(opt_prev_pat) = self.ref_locals.get_mut(&id) { + // This binding id has been seen before. Add this pattern to the list of changes. + if let Some(prev_pat) = opt_prev_pat { + if in_macro(pat.span) { + // Doesn't match the context of the previous pattern. Can't lint here. + *opt_prev_pat = None; + } else { + prev_pat.spans.push(pat.span); + prev_pat.replacements.push(( + pat.span, + snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut prev_pat.app) + .0 + .into(), + )); + } + } + return; + } + + if_chain! { + if !in_macro(pat.span); + if let ty::Ref(_, tam, _) = *cx.typeck_results().pat_ty(pat).kind(); + // only lint immutable refs, because borrowed `&mut T` cannot be moved out + if let ty::Ref(_, _, Mutability::Not) = *tam.kind(); + then { + let mut app = Applicability::MachineApplicable; + let snip = snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut app).0; + self.current_body = self.current_body.or(cx.enclosing_body); + self.ref_locals.insert( + id, + Some(RefPat { + always_deref: true, + spans: vec![pat.span], + app, + replacements: vec![(pat.span, snip.into())], + }), + ); + } + } } - if_chain! { - if let PatKind::Binding(BindingAnnotation::Ref, .., name, _) = pat.kind; - if let ty::Ref(_, tam, mutbl) = *cx.typeck_results().pat_ty(pat).kind(); - if mutbl == Mutability::Not; - if let ty::Ref(_, _, mutbl) = *tam.kind(); - // only lint immutable refs, because borrowed `&mut T` cannot be moved out - if mutbl == Mutability::Not; - then { + } + + fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) { + if Some(body.id()) == self.current_body { + for pat in self.ref_locals.drain(..).filter_map(|(_, x)| x) { + let replacements = pat.replacements; + let app = pat.app; span_lint_and_then( cx, - NEEDLESS_BORROW, - pat.span, + if pat.always_deref { + NEEDLESS_BORROW + } else { + REF_BINDING_TO_REFERENCE + }, + pat.spans, "this pattern creates a reference to a reference", |diag| { - if let Some(snippet) = snippet_opt(cx, name.span) { - diag.span_suggestion( - pat.span, - "change this to", - snippet, - Applicability::MachineApplicable, - ); - } - } + diag.multipart_suggestion("try this", replacements, app); + }, ) } + self.current_body = None; + } + } +} +impl NeedlessBorrow { + fn check_local_usage(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, local: HirId) { + if let Some(outer_pat) = self.ref_locals.get_mut(&local) { + if let Some(pat) = outer_pat { + // Check for auto-deref + if !matches!( + cx.typeck_results().expr_adjustments(e), + [ + Adjustment { + kind: Adjust::Deref(_), + .. + }, + Adjustment { + kind: Adjust::Deref(_), + .. + }, + .. + ] + ) { + match get_parent_expr(cx, e) { + // Field accesses are the same no matter the number of references. + Some(Expr { + kind: ExprKind::Field(..), + .. + }) => (), + Some(&Expr { + span, + kind: ExprKind::Unary(UnOp::Deref, _), + .. + }) if !in_macro(span) => { + // Remove explicit deref. + let snip = snippet_with_context(cx, e.span, span.ctxt(), "..", &mut pat.app).0; + pat.replacements.push((span, snip.into())); + }, + Some(parent) if !in_macro(parent.span) => { + // Double reference might be needed at this point. + if parent.precedence().order() == PREC_POSTFIX { + // Parentheses would be needed here, don't lint. + *outer_pat = None; + } else { + pat.always_deref = false; + let snip = snippet_with_context(cx, e.span, parent.span.ctxt(), "..", &mut pat.app).0; + pat.replacements.push((e.span, format!("&{}", snip))); + } + }, + _ if !in_macro(e.span) => { + // Double reference might be needed at this point. + pat.always_deref = false; + let snip = snippet_with_applicability(cx, e.span, "..", &mut pat.app); + pat.replacements.push((e.span, format!("&{}", snip))); + }, + // Edge case for macros. The span of the identifier will usually match the context of the + // binding, but not if the identifier was created in a macro. e.g. `concat_idents` and proc + // macros + _ => *outer_pat = None, + } + } + } } } } diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index 0c0e4d3b4ce..25b4491616c 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -70,7 +70,7 @@ pub fn range<'a>(expr: &'a hir::Expr<'_>) -> Option> { limits: ast::RangeLimits::Closed, }) }, - hir::ExprKind::Struct(ref path, ref fields, None) => match path { + hir::ExprKind::Struct(path, ref fields, None) => match path { hir::QPath::LangItem(hir::LangItem::RangeFull, _) => Some(Range { start: None, end: None, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 82250151aab..2b9b214daa7 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1254,12 +1254,12 @@ pub fn match_function_call<'tcx>( path: &[&str], ) -> Option<&'tcx [Expr<'tcx>]> { if_chain! { - if let ExprKind::Call(ref fun, ref args) = expr.kind; + if let ExprKind::Call(ref fun, args) = expr.kind; if let ExprKind::Path(ref qpath) = fun.kind; if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id(); if match_def_path(cx, fun_def_id, path); then { - return Some(&args) + return Some(args) } }; None diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index ecdc666b5f6..73f132eef4d 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -189,34 +189,21 @@ impl<'v> Visitor<'v> for LocalUsedVisitor<'v> { } } +/// A type which can be visited. pub trait Visitable<'tcx> { - fn visit>(self, v: &mut V); + /// Calls the corresponding `visit_*` function on the visitor. + fn visit>(self, visitor: &mut V); } -impl Visitable<'tcx> for &'tcx Expr<'tcx> { - fn visit>(self, v: &mut V) { - v.visit_expr(self) - } -} -impl Visitable<'tcx> for &'tcx Block<'tcx> { - fn visit>(self, v: &mut V) { - v.visit_block(self) - } -} -impl<'tcx> Visitable<'tcx> for &'tcx Stmt<'tcx> { - fn visit>(self, v: &mut V) { - v.visit_stmt(self) - } -} -impl<'tcx> Visitable<'tcx> for &'tcx Body<'tcx> { - fn visit>(self, v: &mut V) { - v.visit_body(self) - } -} -impl<'tcx> Visitable<'tcx> for &'tcx Arm<'tcx> { - fn visit>(self, v: &mut V) { - v.visit_arm(self) - } +macro_rules! visitable_ref { + ($t:ident, $f:ident) => { + impl Visitable<'tcx> for &'tcx $t<'tcx> { + fn visit>(self, visitor: &mut V) { + visitor.$f(self); + } + } + }; } +visitable_ref!(Block, visit_block); /// Calls the given function for each break expression. pub fn visit_break_exprs<'tcx>( diff --git a/tests/ui/needless_borrow.fixed b/tests/ui/needless_borrow.fixed index 5ae4a0e79b9..a87171dc3f2 100644 --- a/tests/ui/needless_borrow.fixed +++ b/tests/ui/needless_borrow.fixed @@ -18,7 +18,6 @@ fn main() { let vec = Vec::new(); let vec_val = g(&vec); // should not error, because `&Vec` derefs to `&[T]` h(&"foo"); // should not error, because the `&&str` is required, due to `&Trait` - if let Some(cake) = Some(&5) {} let garbl = match 42 { 44 => &a, 45 => { @@ -43,19 +42,3 @@ trait Trait {} impl<'a> Trait for &'a str {} fn h(_: &dyn Trait) {} -#[warn(clippy::needless_borrow)] -#[allow(dead_code)] -fn issue_1432() { - let mut v = Vec::::new(); - let _ = v.iter_mut().filter(|&ref a| a.is_empty()); - let _ = v.iter().filter(|&a| a.is_empty()); - - let _ = v.iter().filter(|&a| a.is_empty()); -} - -#[allow(dead_code)] -#[warn(clippy::needless_borrow)] -#[derive(Debug)] -enum Foo<'a> { - Str(&'a str), -} diff --git a/tests/ui/needless_borrow.rs b/tests/ui/needless_borrow.rs index 1e281316c8a..059dc8ceac3 100644 --- a/tests/ui/needless_borrow.rs +++ b/tests/ui/needless_borrow.rs @@ -18,7 +18,6 @@ fn main() { let vec = Vec::new(); let vec_val = g(&vec); // should not error, because `&Vec` derefs to `&[T]` h(&"foo"); // should not error, because the `&&str` is required, due to `&Trait` - if let Some(ref cake) = Some(&5) {} let garbl = match 42 { 44 => &a, 45 => { @@ -43,19 +42,3 @@ trait Trait {} impl<'a> Trait for &'a str {} fn h(_: &dyn Trait) {} -#[warn(clippy::needless_borrow)] -#[allow(dead_code)] -fn issue_1432() { - let mut v = Vec::::new(); - let _ = v.iter_mut().filter(|&ref a| a.is_empty()); - let _ = v.iter().filter(|&ref a| a.is_empty()); - - let _ = v.iter().filter(|&a| a.is_empty()); -} - -#[allow(dead_code)] -#[warn(clippy::needless_borrow)] -#[derive(Debug)] -enum Foo<'a> { - Str(&'a str), -} diff --git a/tests/ui/needless_borrow.stderr b/tests/ui/needless_borrow.stderr index bea4b41b803..6eddf26db06 100644 --- a/tests/ui/needless_borrow.stderr +++ b/tests/ui/needless_borrow.stderr @@ -6,23 +6,11 @@ LL | let c = x(&&a); | = note: `-D clippy::needless-borrow` implied by `-D warnings` -error: this pattern creates a reference to a reference - --> $DIR/needless_borrow.rs:21:17 - | -LL | if let Some(ref cake) = Some(&5) {} - | ^^^^^^^^ help: change this to: `cake` - error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:28:15 + --> $DIR/needless_borrow.rs:27:15 | LL | 46 => &&a, | ^^^ help: change this to: `&a` -error: this pattern creates a reference to a reference - --> $DIR/needless_borrow.rs:51:31 - | -LL | let _ = v.iter().filter(|&ref a| a.is_empty()); - | ^^^^^ help: change this to: `a` - -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors diff --git a/tests/ui/needless_borrow_pat.rs b/tests/ui/needless_borrow_pat.rs new file mode 100644 index 00000000000..f0926220755 --- /dev/null +++ b/tests/ui/needless_borrow_pat.rs @@ -0,0 +1,151 @@ +// edition:2018 +// FIXME: run-rustfix waiting on multi-span suggestions + +#![warn(clippy::needless_borrow)] +#![allow(clippy::needless_borrowed_reference)] + +fn f1(_: &str) {} +macro_rules! m1 { + ($e:expr) => { + f1($e); + }; +} +macro_rules! m3 { + ($i:ident) => { + Some(ref $i) + }; +} +macro_rules! if_chain { + (if $e:expr; $($rest:tt)*) => { + if $e { + if_chain!($($rest)*) + } + }; + + (if let $p:pat = $e:expr; $($rest:tt)*) => { + if let $p = $e { + if_chain!($($rest)*) + } + }; + + (then $b:block) => { + $b + }; +} + +#[allow(dead_code)] +fn main() { + let x = String::new(); + + // Ok, reference to a String. + let _: &String = match Some(x.clone()) { + Some(ref x) => x, + None => return, + }; + + // Ok, reference to a &mut String + let _: &&mut String = match Some(&mut x.clone()) { + Some(ref x) => x, + None => return, + }; + + // Ok, the pattern is from a macro + let _: &String = match Some(&x) { + m3!(x) => x, + None => return, + }; + + // Err, reference to a &String + let _: &String = match Some(&x) { + Some(ref x) => x, + None => return, + }; + + // Err, reference to a &String. + let _: &String = match Some(&x) { + Some(ref x) => *x, + None => return, + }; + + // Err, reference to a &String + let _: &String = match Some(&x) { + Some(ref x) => { + f1(x); + f1(*x); + x + }, + None => return, + }; + + // Err, reference to a &String + match Some(&x) { + Some(ref x) => m1!(x), + None => return, + }; + + // Err, reference to a &String + let _ = |&ref x: &&String| { + let _: &String = x; + }; + + // Err, reference to a &String + let (ref y,) = (&x,); + let _: &String = *y; + + let y = &&x; + // Ok, different y + let _: &String = *y; + + let x = (0, 0); + // Err, reference to a &u32. Don't suggest adding a reference to the field access. + let _: u32 = match Some(&x) { + Some(ref x) => x.0, + None => return, + }; + + enum E { + A(&'static u32), + B(&'static u32), + } + // Err, reference to &u32. + let _: &u32 = match E::A(&0) { + E::A(ref x) | E::B(ref x) => *x, + }; + + // Err, reference to &String. + if_chain! { + if true; + if let Some(ref x) = Some(&String::new()); + then { + f1(x); + } + } +} + +// Err, reference to a &String +fn f2<'a>(&ref x: &&'a String) -> &'a String { + let _: &String = x; + *x +} + +trait T1 { + // Err, reference to a &String + fn f(&ref x: &&String) { + let _: &String = x; + } +} + +struct S; +impl T1 for S { + // Err, reference to a &String + fn f(&ref x: &&String) { + let _: &String = *x; + } +} + +// Ok - used to error due to rustc bug +#[allow(dead_code)] +#[derive(Debug)] +enum Foo<'a> { + Str(&'a str), +} diff --git a/tests/ui/needless_borrow_pat.stderr b/tests/ui/needless_borrow_pat.stderr new file mode 100644 index 00000000000..32913d59f7a --- /dev/null +++ b/tests/ui/needless_borrow_pat.stderr @@ -0,0 +1,112 @@ +error: this pattern creates a reference to a reference + --> $DIR/needless_borrow_pat.rs:60:14 + | +LL | Some(ref x) => x, + | ^^^^^ help: try this: `x` + | + = note: `-D clippy::needless-borrow` implied by `-D warnings` + +error: this pattern creates a reference to a reference + --> $DIR/needless_borrow_pat.rs:66:14 + | +LL | Some(ref x) => *x, + | ^^^^^ + | +help: try this + | +LL | Some(x) => x, + | ^ ^ + +error: this pattern creates a reference to a reference + --> $DIR/needless_borrow_pat.rs:72:14 + | +LL | Some(ref x) => { + | ^^^^^ + | +help: try this + | +LL | Some(x) => { +LL | f1(x); +LL | f1(x); + | + +error: this pattern creates a reference to a reference + --> $DIR/needless_borrow_pat.rs:82:14 + | +LL | Some(ref x) => m1!(x), + | ^^^^^ help: try this: `x` + +error: this pattern creates a reference to a reference + --> $DIR/needless_borrow_pat.rs:87:15 + | +LL | let _ = |&ref x: &&String| { + | ^^^^^ help: try this: `x` + +error: this pattern creates a reference to a reference + --> $DIR/needless_borrow_pat.rs:92:10 + | +LL | let (ref y,) = (&x,); + | ^^^^^ + | +help: try this + | +LL | let (y,) = (&x,); +LL | let _: &String = y; + | + +error: this pattern creates a reference to a reference + --> $DIR/needless_borrow_pat.rs:102:14 + | +LL | Some(ref x) => x.0, + | ^^^^^ help: try this: `x` + +error: this pattern creates a reference to a reference + --> $DIR/needless_borrow_pat.rs:112:14 + | +LL | E::A(ref x) | E::B(ref x) => *x, + | ^^^^^ ^^^^^ + | +help: try this + | +LL | E::A(x) | E::B(x) => x, + | ^ ^ ^ + +error: this pattern creates a reference to a reference + --> $DIR/needless_borrow_pat.rs:118:21 + | +LL | if let Some(ref x) = Some(&String::new()); + | ^^^^^ help: try this: `x` + +error: this pattern creates a reference to a reference + --> $DIR/needless_borrow_pat.rs:126:12 + | +LL | fn f2<'a>(&ref x: &&'a String) -> &'a String { + | ^^^^^ + | +help: try this + | +LL | fn f2<'a>(&x: &&'a String) -> &'a String { +LL | let _: &String = x; +LL | x + | + +error: this pattern creates a reference to a reference + --> $DIR/needless_borrow_pat.rs:133:11 + | +LL | fn f(&ref x: &&String) { + | ^^^^^ help: try this: `x` + +error: this pattern creates a reference to a reference + --> $DIR/needless_borrow_pat.rs:141:11 + | +LL | fn f(&ref x: &&String) { + | ^^^^^ + | +help: try this + | +LL | fn f(&x: &&String) { +LL | let _: &String = x; + | + +error: aborting due to 12 previous errors + diff --git a/tests/ui/ref_binding_to_reference.rs b/tests/ui/ref_binding_to_reference.rs new file mode 100644 index 00000000000..c7235e1c221 --- /dev/null +++ b/tests/ui/ref_binding_to_reference.rs @@ -0,0 +1,76 @@ +// edition:2018 +// FIXME: run-rustfix waiting on multi-span suggestions + +#![warn(clippy::ref_binding_to_reference)] +#![allow(clippy::needless_borrowed_reference)] + +fn f1(_: &str) {} +macro_rules! m2 { + ($e:expr) => { + f1(*$e); + }; +} +macro_rules! m3 { + ($i:ident) => { + Some(ref $i) + }; +} + +#[allow(dead_code)] +fn main() { + let x = String::new(); + + // Ok, the pattern is from a macro + let _: &&String = match Some(&x) { + m3!(x) => x, + None => return, + }; + + // Err, reference to a &String + let _: &&String = match Some(&x) { + Some(ref x) => x, + None => return, + }; + + // Err, reference to a &String + let _: &&String = match Some(&x) { + Some(ref x) => { + f1(x); + f1(*x); + x + }, + None => return, + }; + + // Err, reference to a &String + match Some(&x) { + Some(ref x) => m2!(x), + None => return, + } + + // Err, reference to a &String + let _ = |&ref x: &&String| { + let _: &&String = x; + }; +} + +// Err, reference to a &String +fn f2<'a>(&ref x: &&'a String) -> &'a String { + let _: &&String = x; + *x +} + +trait T1 { + // Err, reference to a &String + fn f(&ref x: &&String) { + let _: &&String = x; + } +} + +struct S; +impl T1 for S { + // Err, reference to a &String + fn f(&ref x: &&String) { + let _: &&String = x; + } +} diff --git a/tests/ui/ref_binding_to_reference.stderr b/tests/ui/ref_binding_to_reference.stderr new file mode 100644 index 00000000000..00aeff4fefa --- /dev/null +++ b/tests/ui/ref_binding_to_reference.stderr @@ -0,0 +1,88 @@ +error: this pattern creates a reference to a reference + --> $DIR/ref_binding_to_reference.rs:31:14 + | +LL | Some(ref x) => x, + | ^^^^^ + | + = note: `-D clippy::ref-binding-to-reference` implied by `-D warnings` +help: try this + | +LL | Some(x) => &x, + | ^ ^^ + +error: this pattern creates a reference to a reference + --> $DIR/ref_binding_to_reference.rs:37:14 + | +LL | Some(ref x) => { + | ^^^^^ + | +help: try this + | +LL | Some(x) => { +LL | f1(x); +LL | f1(x); +LL | &x + | + +error: this pattern creates a reference to a reference + --> $DIR/ref_binding_to_reference.rs:47:14 + | +LL | Some(ref x) => m2!(x), + | ^^^^^ + | +help: try this + | +LL | Some(x) => m2!(&x), + | ^ ^^ + +error: this pattern creates a reference to a reference + --> $DIR/ref_binding_to_reference.rs:52:15 + | +LL | let _ = |&ref x: &&String| { + | ^^^^^ + | +help: try this + | +LL | let _ = |&x: &&String| { +LL | let _: &&String = &x; + | + +error: this pattern creates a reference to a reference + --> $DIR/ref_binding_to_reference.rs:58:12 + | +LL | fn f2<'a>(&ref x: &&'a String) -> &'a String { + | ^^^^^ + | +help: try this + | +LL | fn f2<'a>(&x: &&'a String) -> &'a String { +LL | let _: &&String = &x; +LL | x + | + +error: this pattern creates a reference to a reference + --> $DIR/ref_binding_to_reference.rs:65:11 + | +LL | fn f(&ref x: &&String) { + | ^^^^^ + | +help: try this + | +LL | fn f(&x: &&String) { +LL | let _: &&String = &x; + | + +error: this pattern creates a reference to a reference + --> $DIR/ref_binding_to_reference.rs:73:11 + | +LL | fn f(&ref x: &&String) { + | ^^^^^ + | +help: try this + | +LL | fn f(&x: &&String) { +LL | let _: &&String = &x; + | + +error: aborting due to 7 previous errors + -- cgit 1.4.1-3-g733a5 From 2eafec182d82fe85bcf88f78bd76ea143b5a6fba Mon Sep 17 00:00:00 2001 From: John Simon Date: Thu, 20 May 2021 10:12:54 -0400 Subject: Allow wparam and lparam in similar_names --- clippy_lints/src/non_expressive_names.rs | 1 + tests/ui/similar_names.rs | 4 ++++ tests/ui/similar_names.stderr | 4 ++-- 3 files changed, 7 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index 52661416de6..7b12363a3fe 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -125,6 +125,7 @@ const ALLOWED_TO_BE_SIMILAR: &[&[&str]] = &[ &["args", "arms"], &["qpath", "path"], &["lit", "lint"], + &["wparam", "lparam"], ]; struct SimilarNamesNameVisitor<'a, 'tcx, 'b>(&'b mut SimilarNamesLocalVisitor<'a, 'tcx>); diff --git a/tests/ui/similar_names.rs b/tests/ui/similar_names.rs index 5981980988b..2b1bc1f4859 100644 --- a/tests/ui/similar_names.rs +++ b/tests/ui/similar_names.rs @@ -72,6 +72,10 @@ fn main() { let rx1: i32; let tx_cake: i32; let rx_cake: i32; + + // names often used in win32 code (for example WindowProc) + let wparam: i32; + let lparam: i32; } fn foo() { diff --git a/tests/ui/similar_names.stderr b/tests/ui/similar_names.stderr index 0256f126a94..a7eb2be0778 100644 --- a/tests/ui/similar_names.stderr +++ b/tests/ui/similar_names.stderr @@ -92,13 +92,13 @@ LL | let parsee: i32; | ^^^^^^ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:81:16 + --> $DIR/similar_names.rs:85:16 | LL | bpple: sprang, | ^^^^^^ | note: existing binding defined here - --> $DIR/similar_names.rs:80:16 + --> $DIR/similar_names.rs:84:16 | LL | apple: spring, | ^^^^^^ -- cgit 1.4.1-3-g733a5 From e3a1ae7bfea265d0c452ce4511349c00ca0ee1b8 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Thu, 20 May 2021 22:29:09 +0200 Subject: Adding the ability to invalidate caches to force metadata collection --- Cargo.toml | 2 ++ tests/dogfood.rs | 31 +++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) (limited to 'tests') diff --git a/Cargo.toml b/Cargo.toml index 848476a9d05..458c28c2748 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,8 @@ derive-new = "0.5" regex = "1.4" quote = "1" syn = { version = "1", features = ["full"] } +# This is used by the `collect-metadata` alias. +filetime = "0.2" # A noop dependency that changes in the Rust repository, it's a bit of a hack. # See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust` diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 5d9f128753f..7de130c7dbe 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -179,8 +179,39 @@ fn dogfood_subprojects() { #[ignore] #[cfg(feature = "metadata-collector-lint")] fn run_metadata_collection_lint() { + use std::fs::File; + use std::time::SystemTime; + + // Setup for validation + let metadata_output_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("util/gh-pages/metadata_collection.json"); + let start_time = SystemTime::now(); + + // Run collection as is std::env::set_var("ENABLE_METADATA_COLLECTION", "1"); run_clippy_for_project("clippy_lints"); + + // Check if cargo caching got in the way + if let Ok(file) = File::open(metadata_output_path) { + if let Ok(metadata) = file.metadata() { + if let Ok(last_modification) = metadata.modified() { + if last_modification > start_time { + // The output file has been modified. Most likely by a hungry + // metadata collection monster. So We'll return. + return; + } + } + } + } + + // Force cargo to invalidate the caches + filetime::set_file_mtime( + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("clippy_lints/src/lib.rs"), + filetime::FileTime::now(), + ) + .unwrap(); + + // Running the collection again + run_clippy_for_project("clippy_lints"); } fn run_clippy_for_project(project: &str) { -- cgit 1.4.1-3-g733a5 From 7db0e4f791cf8baf3fe8e978a9056d0d8464a1bd Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 21 May 2021 12:27:40 -0400 Subject: Suggest `&mut iter` inside a closure for `while_let_on_iterator` --- clippy_lints/src/loops/while_let_on_iterator.rs | 11 +++++++---- clippy_utils/src/lib.rs | 10 ++++++---- tests/ui/while_let_on_iterator.fixed | 14 ++++++++++++++ tests/ui/while_let_on_iterator.rs | 14 ++++++++++++++ tests/ui/while_let_on_iterator.stderr | 10 ++++++++-- 5 files changed, 49 insertions(+), 10 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index 63560047578..d57588716a5 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -1,7 +1,9 @@ use super::WHILE_LET_ON_ITERATOR; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{get_enclosing_loop, is_refutable, is_trait_method, match_def_path, paths, visitors::is_res_used}; +use clippy_utils::{ + get_enclosing_loop_or_closure, is_refutable, is_trait_method, match_def_path, paths, visitors::is_res_used, +}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor}; @@ -315,9 +317,10 @@ fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr: } } - if let Some(e) = get_enclosing_loop(cx.tcx, loop_expr) { - // The iterator expression will be used on the next iteration unless it is declared within the outer - // loop. + if let Some(e) = get_enclosing_loop_or_closure(cx.tcx, loop_expr) { + // The iterator expression will be used on the next iteration (for loops), or on the next call (for + // closures) unless it is declared within the enclosing expression. TODO: Check for closures + // used where an `FnOnce` type is expected. let local_id = match iter_expr.path { Res::Local(id) => id, _ => return true, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 2b9b214daa7..d32c3ec929a 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -861,14 +861,16 @@ pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio }) } -/// Gets the loop enclosing the given expression, if any. -pub fn get_enclosing_loop(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> { +/// Gets the loop or closure enclosing the given expression, if any. +pub fn get_enclosing_loop_or_closure(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> { let map = tcx.hir(); for (_, node) in map.parent_iter(expr.hir_id) { match node { Node::Expr( - e @ Expr { - kind: ExprKind::Loop(..), + e + @ + Expr { + kind: ExprKind::Loop(..) | ExprKind::Closure(..), .. }, ) => return Some(e), diff --git a/tests/ui/while_let_on_iterator.fixed b/tests/ui/while_let_on_iterator.fixed index c3e2cf0c4a4..52e80ceee83 100644 --- a/tests/ui/while_let_on_iterator.fixed +++ b/tests/ui/while_let_on_iterator.fixed @@ -320,6 +320,20 @@ fn issue1924() { println!("iterator field {}", it.1); } +fn issue7249() { + let mut it = 0..10; + let mut x = || { + // Needs &mut, the closure can be called multiple times + for x in &mut it { + if x % 2 == 0 { + break; + } + } + }; + x(); + x(); +} + fn main() { let mut it = 0..20; for _ in it { diff --git a/tests/ui/while_let_on_iterator.rs b/tests/ui/while_let_on_iterator.rs index 1717006a449..5078a3c9028 100644 --- a/tests/ui/while_let_on_iterator.rs +++ b/tests/ui/while_let_on_iterator.rs @@ -320,6 +320,20 @@ fn issue1924() { println!("iterator field {}", it.1); } +fn issue7249() { + let mut it = 0..10; + let mut x = || { + // Needs &mut, the closure can be called multiple times + while let Some(x) = it.next() { + if x % 2 == 0 { + break; + } + } + }; + x(); + x(); +} + fn main() { let mut it = 0..20; while let Some(..) = it.next() { diff --git a/tests/ui/while_let_on_iterator.stderr b/tests/ui/while_let_on_iterator.stderr index eff559bef7e..cb0afeae15e 100644 --- a/tests/ui/while_let_on_iterator.stderr +++ b/tests/ui/while_let_on_iterator.stderr @@ -105,10 +105,16 @@ LL | while let Some(n) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in &mut it` error: this loop could be written as a `for` loop - --> $DIR/while_let_on_iterator.rs:325:5 + --> $DIR/while_let_on_iterator.rs:327:9 + | +LL | while let Some(x) = it.next() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in &mut it` + +error: this loop could be written as a `for` loop + --> $DIR/while_let_on_iterator.rs:339:5 | LL | while let Some(..) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in it` -error: aborting due to 18 previous errors +error: aborting due to 19 previous errors -- cgit 1.4.1-3-g733a5 From 60dd2b65dcb10f10c02e90300941bc698fa4a39b Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 21 May 2021 15:42:57 -0400 Subject: Fix `redundant_closure` for `vec![]` macro in a closure with arguments --- clippy_lints/src/eta_reduction.rs | 24 +++++++++++++----------- tests/ui/eta.fixed | 3 +++ tests/ui/eta.rs | 3 +++ tests/ui/eta.stderr | 16 ++++++++-------- 4 files changed, 27 insertions(+), 19 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 2f1aa53236d..afc5d546474 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -92,17 +92,19 @@ fn check_closure(cx: &LateContext<'_>, expr: &Expr<'_>) { let ex = &body.value; if ex.span.ctxt() != expr.span.ctxt() { - if let Some(VecArgs::Vec(&[])) = higher::vec_macro(cx, ex) { - // replace `|| vec![]` with `Vec::new` - span_lint_and_sugg( - cx, - REDUNDANT_CLOSURE, - expr.span, - "redundant closure", - "replace the closure with `Vec::new`", - "std::vec::Vec::new".into(), - Applicability::MachineApplicable, - ); + if decl.inputs.is_empty() { + if let Some(VecArgs::Vec(&[])) = higher::vec_macro(cx, ex) { + // replace `|| vec![]` with `Vec::new` + span_lint_and_sugg( + cx, + REDUNDANT_CLOSURE, + expr.span, + "redundant closure", + "replace the closure with `Vec::new`", + "std::vec::Vec::new".into(), + Applicability::MachineApplicable, + ); + } } // skip `foo(|| macro!())` return; diff --git a/tests/ui/eta.fixed b/tests/ui/eta.fixed index 2be2283e3fd..9e752311c67 100644 --- a/tests/ui/eta.fixed +++ b/tests/ui/eta.fixed @@ -48,6 +48,9 @@ fn main() { // See #515 let a: Option)>> = Some(vec![1i32, 2]).map(|v| -> Box)> { Box::new(v) }); + + // issue #7224 + let _: Option> = Some(0).map(|_| vec![]); } trait TestTrait { diff --git a/tests/ui/eta.rs b/tests/ui/eta.rs index f0373f9ccf6..44be4628cbd 100644 --- a/tests/ui/eta.rs +++ b/tests/ui/eta.rs @@ -48,6 +48,9 @@ fn main() { // See #515 let a: Option)>> = Some(vec![1i32, 2]).map(|v| -> Box)> { Box::new(v) }); + + // issue #7224 + let _: Option> = Some(0).map(|_| vec![]); } trait TestTrait { diff --git a/tests/ui/eta.stderr b/tests/ui/eta.stderr index 57ed6527966..8795d3b42c6 100644 --- a/tests/ui/eta.stderr +++ b/tests/ui/eta.stderr @@ -33,7 +33,7 @@ LL | let e = Some(1u8).map(|a| generic(a)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `generic` error: redundant closure - --> $DIR/eta.rs:89:51 + --> $DIR/eta.rs:92:51 | LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo()); | ^^^^^^^^^^^ help: replace the closure with the method itself: `TestStruct::foo` @@ -41,43 +41,43 @@ LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo()); = note: `-D clippy::redundant-closure-for-method-calls` implied by `-D warnings` error: redundant closure - --> $DIR/eta.rs:91:51 + --> $DIR/eta.rs:94:51 | LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo()); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `TestTrait::trait_foo` error: redundant closure - --> $DIR/eta.rs:94:42 + --> $DIR/eta.rs:97:42 | LL | let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear()); | ^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::vec::Vec::clear` error: redundant closure - --> $DIR/eta.rs:99:29 + --> $DIR/eta.rs:102:29 | LL | let e = Some("str").map(|s| s.to_string()); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::string::ToString::to_string` error: redundant closure - --> $DIR/eta.rs:101:27 + --> $DIR/eta.rs:104:27 | LL | let e = Some('a').map(|s| s.to_uppercase()); | ^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_uppercase` error: redundant closure - --> $DIR/eta.rs:104:65 + --> $DIR/eta.rs:107:65 | LL | let e: std::vec::Vec = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_ascii_uppercase` error: redundant closure - --> $DIR/eta.rs:187:27 + --> $DIR/eta.rs:190:27 | LL | let a = Some(1u8).map(|a| foo_ptr(a)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo_ptr` error: redundant closure - --> $DIR/eta.rs:192:27 + --> $DIR/eta.rs:195:27 | LL | let a = Some(1u8).map(|a| closure(a)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `closure` -- cgit 1.4.1-3-g733a5 From ae0d4da764fd751494fb270fd04c2c686eac1302 Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Sat, 22 May 2021 21:47:11 +0300 Subject: Fix invalid syntax in `from_iter_instead_of_collect` suggestion with "as Trait" --- .../src/methods/from_iter_instead_of_collect.rs | 47 ++++++++++++++-------- tests/ui/from_iter_instead_of_collect.fixed | 14 +++++++ tests/ui/from_iter_instead_of_collect.rs | 14 +++++++ tests/ui/from_iter_instead_of_collect.stderr | 40 ++++++++++-------- 4 files changed, 81 insertions(+), 34 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/from_iter_instead_of_collect.rs b/clippy_lints/src/methods/from_iter_instead_of_collect.rs index 28d0e8cd4ae..b4188d9ed30 100644 --- a/clippy_lints/src/methods/from_iter_instead_of_collect.rs +++ b/clippy_lints/src/methods/from_iter_instead_of_collect.rs @@ -37,6 +37,10 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Exp } fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'tcx>) -> String { + fn strip_angle_brackets(s: &str) -> Option<&str> { + s.strip_prefix('<')?.strip_suffix('>') + } + let call_site = expr.span.source_callsite(); if_chain! { if let Ok(snippet) = cx.sess().source_map().span_to_snippet(call_site); @@ -44,23 +48,32 @@ fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'tcx>) - if let Some((_, elements)) = snippet_split.split_last(); then { - // is there a type specifier? (i.e.: like `` in `collections::BTreeSet::::`) - if let Some(type_specifier) = snippet_split.iter().find(|e| e.starts_with('<') && e.ends_with('>')) { - // remove the type specifier from the path elements - let without_ts = elements.iter().filter_map(|e| { - if e == type_specifier { None } else { Some((*e).to_string()) } - }).collect::>(); - // join and add the type specifier at the end (i.e.: `collections::BTreeSet`) - format!("{}{}", without_ts.join("::"), type_specifier) - } else { - // type is not explicitly specified so wildcards are needed - // i.e.: 2 wildcards in `std::collections::BTreeMap<&i32, &char>` - let ty_str = ty.to_string(); - let start = ty_str.find('<').unwrap_or(0); - let end = ty_str.find('>').unwrap_or_else(|| ty_str.len()); - let nb_wildcard = ty_str[start..end].split(',').count(); - let wildcards = format!("_{}", ", _".repeat(nb_wildcard - 1)); - format!("{}<{}>", elements.join("::"), wildcards) + if_chain! { + if let [type_specifier, _] = snippet_split.as_slice(); + if let Some(type_specifier) = strip_angle_brackets(type_specifier); + if let Some((type_specifier, ..)) = type_specifier.split_once(" as "); + then { + type_specifier.to_string() + } else { + // is there a type specifier? (i.e.: like `` in `collections::BTreeSet::::`) + if let Some(type_specifier) = snippet_split.iter().find(|e| strip_angle_brackets(e).is_some()) { + // remove the type specifier from the path elements + let without_ts = elements.iter().filter_map(|e| { + if e == type_specifier { None } else { Some((*e).to_string()) } + }).collect::>(); + // join and add the type specifier at the end (i.e.: `collections::BTreeSet`) + format!("{}{}", without_ts.join("::"), type_specifier) + } else { + // type is not explicitly specified so wildcards are needed + // i.e.: 2 wildcards in `std::collections::BTreeMap<&i32, &char>` + let ty_str = ty.to_string(); + let start = ty_str.find('<').unwrap_or(0); + let end = ty_str.find('>').unwrap_or_else(|| ty_str.len()); + let nb_wildcard = ty_str[start..end].split(',').count(); + let wildcards = format!("_{}", ", _".repeat(nb_wildcard - 1)); + format!("{}<{}>", elements.join("::"), wildcards) + } + } } } else { ty.to_string() diff --git a/tests/ui/from_iter_instead_of_collect.fixed b/tests/ui/from_iter_instead_of_collect.fixed index b5f548810e6..12db43b5343 100644 --- a/tests/ui/from_iter_instead_of_collect.fixed +++ b/tests/ui/from_iter_instead_of_collect.fixed @@ -6,6 +6,20 @@ use std::collections::{BTreeMap, BTreeSet, HashMap, VecDeque}; use std::iter::FromIterator; +struct Foo(Vec); + +impl FromIterator for Foo { + fn from_iter>(_: T) -> Self { + todo!() + } +} + +impl<'a> FromIterator<&'a bool> for Foo { + fn from_iter>(iter: T) -> Self { + iter.into_iter().copied().collect::() + } +} + fn main() { let iter_expr = std::iter::repeat(5).take(5); let _ = iter_expr.collect::>(); diff --git a/tests/ui/from_iter_instead_of_collect.rs b/tests/ui/from_iter_instead_of_collect.rs index b842b5451d1..f5ec190e0cd 100644 --- a/tests/ui/from_iter_instead_of_collect.rs +++ b/tests/ui/from_iter_instead_of_collect.rs @@ -6,6 +6,20 @@ use std::collections::{BTreeMap, BTreeSet, HashMap, VecDeque}; use std::iter::FromIterator; +struct Foo(Vec); + +impl FromIterator for Foo { + fn from_iter>(_: T) -> Self { + todo!() + } +} + +impl<'a> FromIterator<&'a bool> for Foo { + fn from_iter>(iter: T) -> Self { + >::from_iter(iter.into_iter().copied()) + } +} + fn main() { let iter_expr = std::iter::repeat(5).take(5); let _ = Vec::from_iter(iter_expr); diff --git a/tests/ui/from_iter_instead_of_collect.stderr b/tests/ui/from_iter_instead_of_collect.stderr index 434734c9a21..8f08ac8c3ff 100644 --- a/tests/ui/from_iter_instead_of_collect.stderr +++ b/tests/ui/from_iter_instead_of_collect.stderr @@ -1,88 +1,94 @@ error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:11:13 + --> $DIR/from_iter_instead_of_collect.rs:19:9 | -LL | let _ = Vec::from_iter(iter_expr); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter_expr.collect::>()` +LL | >::from_iter(iter.into_iter().copied()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter.into_iter().copied().collect::()` | = note: `-D clippy::from-iter-instead-of-collect` implied by `-D warnings` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:13:13 + --> $DIR/from_iter_instead_of_collect.rs:25:13 + | +LL | let _ = Vec::from_iter(iter_expr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter_expr.collect::>()` + +error: usage of `FromIterator::from_iter` + --> $DIR/from_iter_instead_of_collect.rs:27:13 | LL | let _ = HashMap::::from_iter(vec![5, 5, 5, 5].iter().enumerate()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `vec![5, 5, 5, 5].iter().enumerate().collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:18:19 + --> $DIR/from_iter_instead_of_collect.rs:32:19 | LL | assert_eq!(a, Vec::from_iter(0..3)); | ^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:19:19 + --> $DIR/from_iter_instead_of_collect.rs:33:19 | LL | assert_eq!(a, Vec::::from_iter(0..3)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:21:17 + --> $DIR/from_iter_instead_of_collect.rs:35:17 | LL | let mut b = VecDeque::from_iter(0..3); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:24:17 + --> $DIR/from_iter_instead_of_collect.rs:38:17 | LL | let mut b = VecDeque::::from_iter(0..3); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:29:21 + --> $DIR/from_iter_instead_of_collect.rs:43:21 | LL | let mut b = collections::VecDeque::::from_iter(0..3); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:34:14 + --> $DIR/from_iter_instead_of_collect.rs:48:14 | LL | let bm = BTreeMap::from_iter(values.iter().cloned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `values.iter().cloned().collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:35:19 + --> $DIR/from_iter_instead_of_collect.rs:49:19 | LL | let mut bar = BTreeMap::from_iter(bm.range(0..2)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `bm.range(0..2).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:38:19 + --> $DIR/from_iter_instead_of_collect.rs:52:19 | LL | let mut bts = BTreeSet::from_iter(0..3); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:42:17 + --> $DIR/from_iter_instead_of_collect.rs:56:17 | LL | let _ = collections::BTreeSet::from_iter(0..3); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:43:17 + --> $DIR/from_iter_instead_of_collect.rs:57:17 | LL | let _ = collections::BTreeSet::::from_iter(0..3); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:46:15 + --> $DIR/from_iter_instead_of_collect.rs:60:15 | LL | for _i in Vec::from_iter([1, 2, 3].iter()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `[1, 2, 3].iter().collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:47:15 + --> $DIR/from_iter_instead_of_collect.rs:61:15 | LL | for _i in Vec::<&i32>::from_iter([1, 2, 3].iter()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `[1, 2, 3].iter().collect::>()` -error: aborting due to 14 previous errors +error: aborting due to 15 previous errors -- cgit 1.4.1-3-g733a5 From c21b965d43d5584c3e0df219e6638494d2dc3ea9 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 26 May 2021 16:13:57 -0500 Subject: Fix missing_docs_in_private_items FP --- clippy_lints/src/missing_doc.rs | 6 +++--- tests/ui/missing-doc-impl.rs | 5 ++++- tests/ui/missing-doc-impl.stderr | 8 +++++--- 3 files changed, 12 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index dfab3e8a931..ec1572c26c2 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -93,9 +93,9 @@ impl MissingDoc { return; } - let has_doc = attrs.iter().any(|a| { - a.is_doc_comment() || a.doc_str().is_some() || a.value_str().is_some() || Self::has_include(a.meta()) - }); + let has_doc = attrs + .iter() + .any(|a| a.doc_str().is_some() || Self::has_include(a.meta())); if !has_doc { span_lint( cx, diff --git a/tests/ui/missing-doc-impl.rs b/tests/ui/missing-doc-impl.rs index 57af84dcdf4..bfa9ef01b0e 100644 --- a/tests/ui/missing-doc-impl.rs +++ b/tests/ui/missing-doc-impl.rs @@ -67,7 +67,10 @@ impl PubFoo { pub fn foo() {} /// dox pub fn foo1() {} - fn foo2() {} + #[must_use = "yep"] + fn foo2() -> u32 { + 1 + } #[allow(clippy::missing_docs_in_private_items)] pub fn foo3() {} } diff --git a/tests/ui/missing-doc-impl.stderr b/tests/ui/missing-doc-impl.stderr index 7e10404ca00..d33d512475b 100644 --- a/tests/ui/missing-doc-impl.stderr +++ b/tests/ui/missing-doc-impl.stderr @@ -94,10 +94,12 @@ LL | pub fn foo() {} | ^^^^^^^^^^^^^^^ error: missing documentation for an associated function - --> $DIR/missing-doc-impl.rs:70:5 + --> $DIR/missing-doc-impl.rs:71:5 | -LL | fn foo2() {} - | ^^^^^^^^^^^^ +LL | / fn foo2() -> u32 { +LL | | 1 +LL | | } + | |_____^ error: aborting due to 15 previous errors -- cgit 1.4.1-3-g733a5 From 2e2021bbda7ce7b7095b79f07fe6a408a6cd8f07 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 6 May 2021 12:41:58 -0500 Subject: Add avoid_breaking_exported_api config option --- README.md | 1 + clippy_lints/src/utils/conf.rs | 2 ++ tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/README.md b/README.md index c6b67bb6221..6c556f579ca 100644 --- a/README.md +++ b/README.md @@ -147,6 +147,7 @@ Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml value` mapping eg. ```toml +avoid-breaking-exported-api = false blacklisted-names = ["toto", "tata", "titi"] cognitive-complexity-threshold = 30 ``` diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index fd2dddb3b96..d65270a9450 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -122,6 +122,8 @@ macro_rules! define_Conf { // N.B., this macro is parsed by util/lintlib.py define_Conf! { + /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION. Suppress lints whenever the suggested change would cause breakage for other crates. + (avoid_breaking_exported_api: bool = true), /// Lint: CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE. The minimum rust version that the project supports (msrv: Option = None), /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses 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 d83080b69f5..a7be00426c4 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 `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`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `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`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `third-party` at line 5 column 1 error: aborting due to previous error -- cgit 1.4.1-3-g733a5 From d7f47f280ec267c0583f7d38fc149a2351b923e6 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 26 May 2021 16:39:39 -0500 Subject: Use break api config for wrong_pub_self_convention --- clippy_lints/src/deprecated_lints.rs | 9 +++++ clippy_lints/src/lib.rs | 9 +++-- clippy_lints/src/methods/mod.rs | 40 +++++-------------- clippy_lints/src/methods/wrong_self_convention.rs | 13 ++---- tests/ui/def_id_nocore.rs | 2 +- tests/ui/module_name_repetitions.rs | 8 ---- tests/ui/wrong_self_convention.rs | 1 - tests/ui/wrong_self_convention.stderr | 48 +++++++++++------------ tests/ui/wrong_self_convention2.rs | 1 - tests/ui/wrong_self_convention2.stderr | 4 +- 10 files changed, 55 insertions(+), 80 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index 50ffc2e3f19..dd780ff87fe 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -141,3 +141,12 @@ declare_deprecated_lint! { pub FILTER_MAP, "this lint has been replaced by `manual_filter_map`, a more specific lint" } + +declare_deprecated_lint! { + /// **What it does:** Nothing. This lint has been deprecated. + /// + /// **Deprecation reason:** The `avoid_breaking_exported_api` config option was added, which + /// enables the `wrong_self_conversion` lint for public items. + pub 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.rs b/clippy_lints/src/lib.rs index cf54021202f..6dd2486afae 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -505,6 +505,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: "clippy::filter_map", "this lint has been replaced by `manual_filter_map`, a more specific lint", ); + 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` // begin register lints, do not remove this comment, it’s used in `update_lints` @@ -802,7 +806,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: methods::UNNECESSARY_LAZY_EVALUATIONS, methods::UNWRAP_USED, methods::USELESS_ASREF, - methods::WRONG_PUB_SELF_CONVENTION, methods::WRONG_SELF_CONVENTION, methods::ZST_OFFSET, minmax::MIN_MAX, @@ -1026,7 +1029,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(methods::FILETYPE_IS_FILE), LintId::of(methods::GET_UNWRAP), LintId::of(methods::UNWRAP_USED), - LintId::of(methods::WRONG_PUB_SELF_CONVENTION), LintId::of(misc::FLOAT_CMP_CONST), LintId::of(misc_early::UNNEEDED_FIELD_PATTERN), LintId::of(missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS), @@ -1862,7 +1864,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: }) }); - store.register_late_pass(move || box methods::Methods::new(msrv)); + let avoid_breaking_exported_api = conf.avoid_breaking_exported_api; + store.register_late_pass(move || box methods::Methods::new(avoid_breaking_exported_api, msrv)); store.register_late_pass(move || box matches::Matches::new(msrv)); store.register_early_pass(move || box manual_non_exhaustive::ManualNonExhaustive::new(msrv)); store.register_late_pass(move || box manual_strip::ManualStrip::new(msrv)); diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index cabfe8023ef..0b998dbf86c 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -282,30 +282,6 @@ declare_clippy_lint! { "defining a method named with an established prefix (like \"into_\") that takes `self` with the wrong convention" } -declare_clippy_lint! { - /// **What it does:** This is the same as - /// [`wrong_self_convention`](#wrong_self_convention), but for public items. - /// - /// **Why is this bad?** See [`wrong_self_convention`](#wrong_self_convention). - /// - /// **Known problems:** Actually *renaming* the function may break clients if - /// the function is part of the public interface. In that case, be mindful of - /// the stability guarantees you've given your users. - /// - /// **Example:** - /// ```rust - /// # struct X; - /// impl<'a> X { - /// pub fn as_str(self) -> &'a str { - /// "foo" - /// } - /// } - /// ``` - pub WRONG_PUB_SELF_CONVENTION, - restriction, - "defining a public method named with an established prefix (like \"into_\") that takes `self` with the wrong convention" -} - declare_clippy_lint! { /// **What it does:** Checks for usage of `ok().expect(..)`. /// @@ -1658,13 +1634,17 @@ declare_clippy_lint! { } pub struct Methods { + avoid_breaking_exported_api: bool, msrv: Option, } impl Methods { #[must_use] - pub fn new(msrv: Option) -> Self { - Self { msrv } + pub fn new(avoid_breaking_exported_api: bool, msrv: Option) -> Self { + Self { + avoid_breaking_exported_api, + msrv, + } } } @@ -1673,7 +1653,6 @@ impl_lint_pass!(Methods => [ EXPECT_USED, SHOULD_IMPLEMENT_TRAIT, WRONG_SELF_CONVENTION, - WRONG_PUB_SELF_CONVENTION, OK_EXPECT, MAP_UNWRAP_OR, RESULT_MAP_OR_INTO_OPTION, @@ -1838,11 +1817,13 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } } - if sig.decl.implicit_self.has_implicit_self() { + if sig.decl.implicit_self.has_implicit_self() + && !(self.avoid_breaking_exported_api + && cx.access_levels.is_exported(impl_item.hir_id())) + { wrong_self_convention::check( cx, &name, - item.vis.node.is_pub(), self_ty, first_arg_ty, first_arg.pat.span, @@ -1915,7 +1896,6 @@ impl<'tcx> LateLintPass<'tcx> for Methods { wrong_self_convention::check( cx, &item.ident.name.as_str(), - false, self_ty, first_arg_ty, first_arg_span, diff --git a/clippy_lints/src/methods/wrong_self_convention.rs b/clippy_lints/src/methods/wrong_self_convention.rs index 1773c26c251..a2e09e5ecec 100644 --- a/clippy_lints/src/methods/wrong_self_convention.rs +++ b/clippy_lints/src/methods/wrong_self_convention.rs @@ -6,7 +6,6 @@ use rustc_middle::ty::TyS; use rustc_span::source_map::Span; use std::fmt; -use super::WRONG_PUB_SELF_CONVENTION; use super::WRONG_SELF_CONVENTION; #[rustfmt::skip] @@ -21,9 +20,9 @@ const CONVENTIONS: [(&[Convention], &[SelfKind]); 9] = [ // Conversion using `to_` can use borrowed (non-Copy types) or owned (Copy types). // Source: https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv - (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(false), + (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(false), Convention::IsTraitItem(false), Convention::ImplementsTrait(false)], &[SelfKind::Ref]), - (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(true), + (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(true), Convention::IsTraitItem(false), Convention::ImplementsTrait(false)], &[SelfKind::Value]), ]; @@ -85,18 +84,12 @@ impl fmt::Display for Convention { pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, item_name: &str, - is_pub: bool, self_ty: &'tcx TyS<'tcx>, first_arg_ty: &'tcx TyS<'tcx>, first_arg_span: Span, implements_trait: bool, is_trait_item: bool, ) { - let lint = if is_pub { - WRONG_PUB_SELF_CONVENTION - } else { - WRONG_SELF_CONVENTION - }; if let Some((conventions, self_kinds)) = &CONVENTIONS.iter().find(|(convs, _)| { convs .iter() @@ -142,7 +135,7 @@ pub(super) fn check<'tcx>( span_lint_and_help( cx, - lint, + WRONG_SELF_CONVENTION, first_arg_span, &format!( "{} usually take {}", diff --git a/tests/ui/def_id_nocore.rs b/tests/ui/def_id_nocore.rs index 2a948d60b10..cba7666c2d8 100644 --- a/tests/ui/def_id_nocore.rs +++ b/tests/ui/def_id_nocore.rs @@ -20,7 +20,7 @@ fn start(_argc: isize, _argv: *const *const u8) -> isize { 0 } -pub struct A; +struct A; impl A { pub fn as_ref(self) -> &'static str { diff --git a/tests/ui/module_name_repetitions.rs b/tests/ui/module_name_repetitions.rs index 669bf01a84c..f5908cb5701 100644 --- a/tests/ui/module_name_repetitions.rs +++ b/tests/ui/module_name_repetitions.rs @@ -15,12 +15,4 @@ mod foo { pub struct Foobar; } -#[cfg(test)] -mod test { - #[test] - fn it_works() { - assert_eq!(2 + 2, 4); - } -} - fn main() {} diff --git a/tests/ui/wrong_self_convention.rs b/tests/ui/wrong_self_convention.rs index cdfbdb8b0db..151dd0c27d5 100644 --- a/tests/ui/wrong_self_convention.rs +++ b/tests/ui/wrong_self_convention.rs @@ -1,6 +1,5 @@ // edition:2018 #![warn(clippy::wrong_self_convention)] -#![warn(clippy::wrong_pub_self_convention)] #![allow(dead_code)] fn main() {} diff --git a/tests/ui/wrong_self_convention.stderr b/tests/ui/wrong_self_convention.stderr index 29f5ba82695..ce23317abf6 100644 --- a/tests/ui/wrong_self_convention.stderr +++ b/tests/ui/wrong_self_convention.stderr @@ -1,5 +1,5 @@ error: methods called `from_*` usually take no `self` - --> $DIR/wrong_self_convention.rs:18:17 + --> $DIR/wrong_self_convention.rs:17:17 | LL | fn from_i32(self) {} | ^^^^ @@ -8,7 +8,7 @@ LL | fn from_i32(self) {} = help: consider choosing a less ambiguous name error: methods called `from_*` usually take no `self` - --> $DIR/wrong_self_convention.rs:24:21 + --> $DIR/wrong_self_convention.rs:23:21 | LL | pub fn from_i64(self) {} | ^^^^ @@ -16,7 +16,7 @@ LL | pub fn from_i64(self) {} = help: consider choosing a less ambiguous name error: methods called `as_*` usually take `self` by reference or `self` by mutable reference - --> $DIR/wrong_self_convention.rs:36:15 + --> $DIR/wrong_self_convention.rs:35:15 | LL | fn as_i32(self) {} | ^^^^ @@ -24,7 +24,7 @@ LL | fn as_i32(self) {} = help: consider choosing a less ambiguous name error: methods called `into_*` usually take `self` by value - --> $DIR/wrong_self_convention.rs:38:17 + --> $DIR/wrong_self_convention.rs:37:17 | LL | fn into_i32(&self) {} | ^^^^^ @@ -32,7 +32,7 @@ LL | fn into_i32(&self) {} = help: consider choosing a less ambiguous name error: methods called `is_*` usually take `self` by reference or no `self` - --> $DIR/wrong_self_convention.rs:40:15 + --> $DIR/wrong_self_convention.rs:39:15 | LL | fn is_i32(self) {} | ^^^^ @@ -40,7 +40,7 @@ LL | fn is_i32(self) {} = help: consider choosing a less ambiguous name error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference - --> $DIR/wrong_self_convention.rs:42:15 + --> $DIR/wrong_self_convention.rs:41:15 | LL | fn to_i32(self) {} | ^^^^ @@ -48,7 +48,7 @@ LL | fn to_i32(self) {} = help: consider choosing a less ambiguous name error: methods called `from_*` usually take no `self` - --> $DIR/wrong_self_convention.rs:44:17 + --> $DIR/wrong_self_convention.rs:43:17 | LL | fn from_i32(self) {} | ^^^^ @@ -56,7 +56,7 @@ LL | fn from_i32(self) {} = help: consider choosing a less ambiguous name error: methods called `as_*` usually take `self` by reference or `self` by mutable reference - --> $DIR/wrong_self_convention.rs:46:19 + --> $DIR/wrong_self_convention.rs:45:19 | LL | pub fn as_i64(self) {} | ^^^^ @@ -64,7 +64,7 @@ LL | pub fn as_i64(self) {} = help: consider choosing a less ambiguous name error: methods called `into_*` usually take `self` by value - --> $DIR/wrong_self_convention.rs:47:21 + --> $DIR/wrong_self_convention.rs:46:21 | LL | pub fn into_i64(&self) {} | ^^^^^ @@ -72,7 +72,7 @@ LL | pub fn into_i64(&self) {} = help: consider choosing a less ambiguous name error: methods called `is_*` usually take `self` by reference or no `self` - --> $DIR/wrong_self_convention.rs:48:19 + --> $DIR/wrong_self_convention.rs:47:19 | LL | pub fn is_i64(self) {} | ^^^^ @@ -80,7 +80,7 @@ LL | pub fn is_i64(self) {} = help: consider choosing a less ambiguous name error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference - --> $DIR/wrong_self_convention.rs:49:19 + --> $DIR/wrong_self_convention.rs:48:19 | LL | pub fn to_i64(self) {} | ^^^^ @@ -88,7 +88,7 @@ LL | pub fn to_i64(self) {} = help: consider choosing a less ambiguous name error: methods called `from_*` usually take no `self` - --> $DIR/wrong_self_convention.rs:50:21 + --> $DIR/wrong_self_convention.rs:49:21 | LL | pub fn from_i64(self) {} | ^^^^ @@ -96,7 +96,7 @@ LL | pub fn from_i64(self) {} = help: consider choosing a less ambiguous name error: methods called `as_*` usually take `self` by reference or `self` by mutable reference - --> $DIR/wrong_self_convention.rs:95:19 + --> $DIR/wrong_self_convention.rs:94:19 | LL | fn as_i32(self) {} | ^^^^ @@ -104,7 +104,7 @@ LL | fn as_i32(self) {} = help: consider choosing a less ambiguous name error: methods called `into_*` usually take `self` by value - --> $DIR/wrong_self_convention.rs:98:25 + --> $DIR/wrong_self_convention.rs:97:25 | LL | fn into_i32_ref(&self) {} | ^^^^^ @@ -112,7 +112,7 @@ LL | fn into_i32_ref(&self) {} = help: consider choosing a less ambiguous name error: methods called `is_*` usually take `self` by reference or no `self` - --> $DIR/wrong_self_convention.rs:100:19 + --> $DIR/wrong_self_convention.rs:99:19 | LL | fn is_i32(self) {} | ^^^^ @@ -120,7 +120,7 @@ LL | fn is_i32(self) {} = help: consider choosing a less ambiguous name error: methods called `from_*` usually take no `self` - --> $DIR/wrong_self_convention.rs:104:21 + --> $DIR/wrong_self_convention.rs:103:21 | LL | fn from_i32(self) {} | ^^^^ @@ -128,7 +128,7 @@ LL | fn from_i32(self) {} = help: consider choosing a less ambiguous name error: methods called `as_*` usually take `self` by reference or `self` by mutable reference - --> $DIR/wrong_self_convention.rs:119:19 + --> $DIR/wrong_self_convention.rs:118:19 | LL | fn as_i32(self); | ^^^^ @@ -136,7 +136,7 @@ LL | fn as_i32(self); = help: consider choosing a less ambiguous name error: methods called `into_*` usually take `self` by value - --> $DIR/wrong_self_convention.rs:122:25 + --> $DIR/wrong_self_convention.rs:121:25 | LL | fn into_i32_ref(&self); | ^^^^^ @@ -144,7 +144,7 @@ LL | fn into_i32_ref(&self); = help: consider choosing a less ambiguous name error: methods called `is_*` usually take `self` by reference or no `self` - --> $DIR/wrong_self_convention.rs:124:19 + --> $DIR/wrong_self_convention.rs:123:19 | LL | fn is_i32(self); | ^^^^ @@ -152,7 +152,7 @@ LL | fn is_i32(self); = help: consider choosing a less ambiguous name error: methods called `from_*` usually take no `self` - --> $DIR/wrong_self_convention.rs:128:21 + --> $DIR/wrong_self_convention.rs:127:21 | LL | fn from_i32(self); | ^^^^ @@ -160,7 +160,7 @@ LL | fn from_i32(self); = help: consider choosing a less ambiguous name error: methods called `into_*` usually take `self` by value - --> $DIR/wrong_self_convention.rs:146:25 + --> $DIR/wrong_self_convention.rs:145:25 | LL | fn into_i32_ref(&self); | ^^^^^ @@ -168,7 +168,7 @@ LL | fn into_i32_ref(&self); = help: consider choosing a less ambiguous name error: methods called `from_*` usually take no `self` - --> $DIR/wrong_self_convention.rs:152:21 + --> $DIR/wrong_self_convention.rs:151:21 | LL | fn from_i32(self); | ^^^^ @@ -176,7 +176,7 @@ LL | fn from_i32(self); = help: consider choosing a less ambiguous name error: methods with the following characteristics: (`to_*` and `self` type is `Copy`) usually take `self` by value - --> $DIR/wrong_self_convention.rs:176:22 + --> $DIR/wrong_self_convention.rs:175:22 | LL | fn to_u64_v2(&self) -> u64 { | ^^^^^ @@ -184,7 +184,7 @@ LL | fn to_u64_v2(&self) -> u64 { = help: consider choosing a less ambiguous name error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference - --> $DIR/wrong_self_convention.rs:185:19 + --> $DIR/wrong_self_convention.rs:184:19 | LL | fn to_u64(self) -> u64 { | ^^^^ diff --git a/tests/ui/wrong_self_convention2.rs b/tests/ui/wrong_self_convention2.rs index 3a72174d03d..501bc1e6a85 100644 --- a/tests/ui/wrong_self_convention2.rs +++ b/tests/ui/wrong_self_convention2.rs @@ -1,6 +1,5 @@ // edition:2018 #![warn(clippy::wrong_self_convention)] -#![warn(clippy::wrong_pub_self_convention)] #![allow(dead_code)] fn main() {} diff --git a/tests/ui/wrong_self_convention2.stderr b/tests/ui/wrong_self_convention2.stderr index d2d74ce099e..0e0d066d656 100644 --- a/tests/ui/wrong_self_convention2.stderr +++ b/tests/ui/wrong_self_convention2.stderr @@ -1,5 +1,5 @@ error: methods called `from_*` usually take no `self` - --> $DIR/wrong_self_convention2.rs:56:29 + --> $DIR/wrong_self_convention2.rs:55:29 | LL | pub fn from_be_self(self) -> Self { | ^^^^ @@ -8,7 +8,7 @@ LL | pub fn from_be_self(self) -> Self { = help: consider choosing a less ambiguous name error: methods called `from_*` usually take no `self` - --> $DIR/wrong_self_convention2.rs:65:25 + --> $DIR/wrong_self_convention2.rs:64:25 | LL | fn from_be_self(self) -> Self; | ^^^^ -- cgit 1.4.1-3-g733a5 From ee79077d8008006f25fb591946bdc17e99c0b91a Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 6 May 2021 12:45:21 -0500 Subject: Use break api config for pass by value or ref --- clippy_lints/src/lib.rs | 1 + clippy_lints/src/pass_by_ref_or_value.rs | 13 +++++++++++-- tests/ui/trivially_copy_pass_by_ref.stderr | 8 +------- 3 files changed, 13 insertions(+), 9 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 6dd2486afae..30db45a34f9 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1947,6 +1947,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let pass_by_ref_or_value = pass_by_ref_or_value::PassByRefOrValue::new( conf.trivial_copy_size_limit, conf.pass_by_value_size_limit, + conf.avoid_breaking_exported_api, &sess.target, ); store.register_late_pass(move || box pass_by_ref_or_value); diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index 6b64846c24d..f6a70478559 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -102,10 +102,16 @@ declare_clippy_lint! { pub struct PassByRefOrValue { ref_min_size: u64, value_max_size: u64, + avoid_breaking_exported_api: bool, } impl<'tcx> PassByRefOrValue { - pub fn new(ref_min_size: Option, value_max_size: u64, target: &Target) -> Self { + pub fn new( + ref_min_size: Option, + value_max_size: u64, + avoid_breaking_exported_api: bool, + target: &Target, + ) -> Self { let ref_min_size = ref_min_size.unwrap_or_else(|| { let bit_width = u64::from(target.pointer_width); // Cap the calculated bit width at 32-bits to reduce @@ -120,10 +126,14 @@ impl<'tcx> PassByRefOrValue { Self { ref_min_size, value_max_size, + avoid_breaking_exported_api, } } fn check_poly_fn(&mut self, cx: &LateContext<'tcx>, hir_id: HirId, decl: &FnDecl<'_>, span: Option) { + if self.avoid_breaking_exported_api && cx.access_levels.is_exported(hir_id) { + return; + } let fn_def_id = cx.tcx.hir().local_def_id(hir_id); let fn_sig = cx.tcx.fn_sig(fn_def_id); @@ -184,7 +194,6 @@ impl<'tcx> PassByRefOrValue { } if_chain! { - if !cx.access_levels.is_exported(hir_id); if is_copy(cx, ty); if !is_self_ty(input); if let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes()); diff --git a/tests/ui/trivially_copy_pass_by_ref.stderr b/tests/ui/trivially_copy_pass_by_ref.stderr index ccc3cdb2b74..2b0005bbff1 100644 --- a/tests/ui/trivially_copy_pass_by_ref.stderr +++ b/tests/ui/trivially_copy_pass_by_ref.stderr @@ -88,12 +88,6 @@ error: this argument (N byte) is passed by reference, but would be more efficien LL | fn trait_method(&self, _foo: &Foo); | ^^^^ help: consider passing by value instead: `Foo` -error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) - --> $DIR/trivially_copy_pass_by_ref.rs:80:37 - | -LL | fn trait_method2(&self, _color: &Color); - | ^^^^^^ help: consider passing by value instead: `Color` - error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) --> $DIR/trivially_copy_pass_by_ref.rs:108:21 | @@ -106,5 +100,5 @@ error: this argument (N byte) is passed by reference, but would be more efficien LL | fn foo(x: &i32) { | ^^^^ help: consider passing by value instead: `i32` -error: aborting due to 17 previous errors +error: aborting due to 16 previous errors -- cgit 1.4.1-3-g733a5 From 3d77a2b8617f914b1e5503dd93f67a8334693ce0 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 6 May 2021 14:02:24 -0500 Subject: Use break api config for enum_variant_names --- clippy_lints/src/deprecated_lints.rs | 10 +++++ clippy_lints/src/enum_variants.rs | 80 +++++++++++++----------------------- clippy_lints/src/lib.rs | 8 ++-- tests/ui/enum_variants.rs | 7 ++-- tests/ui/enum_variants.stderr | 37 ++++++----------- 5 files changed, 59 insertions(+), 83 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index dd780ff87fe..04f3d77464f 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -142,6 +142,16 @@ declare_deprecated_lint! { "this lint has been replaced by `manual_filter_map`, a more specific lint" } +declare_deprecated_lint! { + /// **What it does:** Nothing. This lint has been deprecated. + /// + /// **Deprecation reason:** The `avoid_breaking_exported_api` config option was added, which + /// enables the `enum_variant_names` lint for public items. + /// ``` + pub PUB_ENUM_VARIANT_NAMES, + "set the `avoid_breaking_exported_api` config option to `false` to enable the `enum_variant_names` lint for public items" +} + declare_deprecated_lint! { /// **What it does:** Nothing. This lint has been deprecated. /// diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs index 0ecc0bc3eb6..b1a105a51c1 100644 --- a/clippy_lints/src/enum_variants.rs +++ b/clippy_lints/src/enum_variants.rs @@ -3,8 +3,8 @@ use clippy_utils::camel_case; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::source::is_present_in_source; -use rustc_ast::ast::{EnumDef, Item, ItemKind, VisibilityKind}; -use rustc_lint::{EarlyContext, EarlyLintPass, Lint}; +use rustc_hir::{EnumDef, Item, ItemKind}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_span::symbol::Symbol; @@ -39,36 +39,6 @@ declare_clippy_lint! { "enums where all variants share a prefix/postfix" } -declare_clippy_lint! { - /// **What it does:** Detects public enumeration variants that are - /// prefixed or suffixed by the same characters. - /// - /// **Why is this bad?** Public enumeration variant names should specify their variant, - /// not repeat the enumeration name. - /// - /// **Known problems:** None. - /// - /// **Example:** - /// ```rust - /// pub enum Cake { - /// BlackForestCake, - /// HummingbirdCake, - /// BattenbergCake, - /// } - /// ``` - /// Could be written as: - /// ```rust - /// pub enum Cake { - /// BlackForest, - /// Hummingbird, - /// Battenberg, - /// } - /// ``` - pub PUB_ENUM_VARIANT_NAMES, - pedantic, - "public enums where all variants share a prefix/postfix" -} - declare_clippy_lint! { /// **What it does:** Detects type names that are prefixed or suffixed by the /// containing module's name. @@ -127,21 +97,22 @@ declare_clippy_lint! { pub struct EnumVariantNames { modules: Vec<(Symbol, String)>, threshold: u64, + avoid_breaking_exported_api: bool, } impl EnumVariantNames { #[must_use] - pub fn new(threshold: u64) -> Self { + pub fn new(threshold: u64, avoid_breaking_exported_api: bool) -> Self { Self { modules: Vec::new(), threshold, + avoid_breaking_exported_api, } } } impl_lint_pass!(EnumVariantNames => [ ENUM_VARIANT_NAMES, - PUB_ENUM_VARIANT_NAMES, MODULE_NAME_REPETITIONS, MODULE_INCEPTION ]); @@ -167,33 +138,42 @@ fn partial_rmatch(post: &str, name: &str) -> usize { } fn check_variant( - cx: &EarlyContext<'_>, + cx: &LateContext<'_>, threshold: u64, - def: &EnumDef, + def: &EnumDef<'_>, item_name: &str, item_name_chars: usize, span: Span, - lint: &'static Lint, ) { if (def.variants.len() as u64) < threshold { return; } - for var in &def.variants { + for var in def.variants { let name = var.ident.name.as_str(); if partial_match(item_name, &name) == item_name_chars && name.chars().nth(item_name_chars).map_or(false, |c| !c.is_lowercase()) && name.chars().nth(item_name_chars + 1).map_or(false, |c| !c.is_numeric()) { - span_lint(cx, lint, var.span, "variant name starts with the enum's name"); + span_lint( + cx, + ENUM_VARIANT_NAMES, + var.span, + "variant name starts with the enum's name", + ); } if partial_rmatch(item_name, &name) == item_name_chars { - span_lint(cx, lint, var.span, "variant name ends with the enum's name"); + span_lint( + cx, + ENUM_VARIANT_NAMES, + var.span, + "variant name ends with the enum's name", + ); } } let first = &def.variants[0].ident.name.as_str(); let mut pre = &first[..camel_case::until(&*first)]; let mut post = &first[camel_case::from(&*first)..]; - for var in &def.variants { + for var in def.variants { let name = var.ident.name.as_str(); let pre_match = partial_match(pre, &name); @@ -226,7 +206,7 @@ fn check_variant( }; span_lint_and_help( cx, - lint, + ENUM_VARIANT_NAMES, span, &format!("all variants have the same {}fix: `{}`", what, value), None, @@ -261,14 +241,14 @@ fn to_camel_case(item_name: &str) -> String { s } -impl EarlyLintPass for EnumVariantNames { - fn check_item_post(&mut self, _cx: &EarlyContext<'_>, _item: &Item) { +impl LateLintPass<'_> for EnumVariantNames { + fn check_item_post(&mut self, _cx: &LateContext<'_>, _item: &Item<'_>) { let last = self.modules.pop(); assert!(last.is_some()); } #[allow(clippy::similar_names)] - fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { let item_name = item.ident.name.as_str(); let item_name_chars = item_name.chars().count(); let item_camel = to_camel_case(&item_name); @@ -286,7 +266,7 @@ impl EarlyLintPass for EnumVariantNames { ); } } - if item.vis.kind.is_pub() { + if item.vis.node.is_pub() { let matching = partial_match(mod_camel, &item_camel); let rmatching = partial_rmatch(mod_camel, &item_camel); let nchars = mod_camel.chars().count(); @@ -317,11 +297,9 @@ impl EarlyLintPass for EnumVariantNames { } } if let ItemKind::Enum(ref def, _) = item.kind { - let lint = match item.vis.kind { - VisibilityKind::Public => PUB_ENUM_VARIANT_NAMES, - _ => ENUM_VARIANT_NAMES, - }; - check_variant(cx, self.threshold, def, &item_name, item_name_chars, item.span, lint); + if !(self.avoid_breaking_exported_api && cx.access_levels.is_exported(item.hir_id())) { + check_variant(cx, self.threshold, def, &item_name, item_name_chars, item.span); + } } self.modules.push((item.ident.name, item_camel)); } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 30db45a34f9..bab389d2c05 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -505,6 +505,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: "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", @@ -622,7 +626,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: enum_variants::ENUM_VARIANT_NAMES, enum_variants::MODULE_INCEPTION, enum_variants::MODULE_NAME_REPETITIONS, - enum_variants::PUB_ENUM_VARIANT_NAMES, eq_op::EQ_OP, eq_op::OP_REF, erasing_op::ERASING_OP, @@ -1080,7 +1083,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(doc::MISSING_PANICS_DOC), LintId::of(empty_enum::EMPTY_ENUM), LintId::of(enum_variants::MODULE_NAME_REPETITIONS), - LintId::of(enum_variants::PUB_ENUM_VARIANT_NAMES), LintId::of(eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS), LintId::of(excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS), LintId::of(excessive_bools::STRUCT_EXCESSIVE_BOOLS), @@ -2015,7 +2017,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let literal_representation_threshold = conf.literal_representation_threshold; store.register_early_pass(move || box literal_representation::DecimalLiteralRepresentation::new(literal_representation_threshold)); let enum_variant_name_threshold = conf.enum_variant_name_threshold; - store.register_early_pass(move || box enum_variants::EnumVariantNames::new(enum_variant_name_threshold)); + store.register_late_pass(move || box enum_variants::EnumVariantNames::new(enum_variant_name_threshold, avoid_breaking_exported_api)); store.register_early_pass(|| box tabs_in_doc_comments::TabsInDocComments); let upper_case_acronyms_aggressive = conf.upper_case_acronyms_aggressive; store.register_early_pass(move || box upper_case_acronyms::UpperCaseAcronyms::new(upper_case_acronyms_aggressive)); diff --git a/tests/ui/enum_variants.rs b/tests/ui/enum_variants.rs index 4fefc0b43f1..083f5143e6e 100644 --- a/tests/ui/enum_variants.rs +++ b/tests/ui/enum_variants.rs @@ -1,5 +1,4 @@ -#![feature(non_ascii_idents)] -#![warn(clippy::enum_variant_names, clippy::pub_enum_variant_names)] +#![warn(clippy::enum_variant_names)] #![allow(non_camel_case_types, clippy::upper_case_acronyms)] enum FakeCallType { @@ -97,8 +96,8 @@ pub enum PubSeall { WithOut, } -#[allow(clippy::pub_enum_variant_names)] -mod allowed { +#[allow(clippy::enum_variant_names)] +pub mod allowed { pub enum PubAllowed { SomeThis, SomeThat, diff --git a/tests/ui/enum_variants.stderr b/tests/ui/enum_variants.stderr index ab7fff4507a..447fbb9e1bf 100644 --- a/tests/ui/enum_variants.stderr +++ b/tests/ui/enum_variants.stderr @@ -1,5 +1,5 @@ error: variant name ends with the enum's name - --> $DIR/enum_variants.rs:16:5 + --> $DIR/enum_variants.rs:15:5 | LL | cFoo, | ^^^^ @@ -7,25 +7,25 @@ LL | cFoo, = note: `-D clippy::enum-variant-names` implied by `-D warnings` error: variant name starts with the enum's name - --> $DIR/enum_variants.rs:27:5 + --> $DIR/enum_variants.rs:26:5 | LL | FoodGood, | ^^^^^^^^ error: variant name starts with the enum's name - --> $DIR/enum_variants.rs:28:5 + --> $DIR/enum_variants.rs:27:5 | LL | FoodMiddle, | ^^^^^^^^^^ error: variant name starts with the enum's name - --> $DIR/enum_variants.rs:29:5 + --> $DIR/enum_variants.rs:28:5 | LL | FoodBad, | ^^^^^^^ error: all variants have the same prefix: `Food` - --> $DIR/enum_variants.rs:26:1 + --> $DIR/enum_variants.rs:25:1 | LL | / enum Food { LL | | FoodGood, @@ -37,7 +37,7 @@ LL | | } = help: remove the prefixes and use full paths to the variants instead of glob imports error: all variants have the same prefix: `CallType` - --> $DIR/enum_variants.rs:36:1 + --> $DIR/enum_variants.rs:35:1 | LL | / enum BadCallType { LL | | CallTypeCall, @@ -49,7 +49,7 @@ LL | | } = help: remove the prefixes and use full paths to the variants instead of glob imports error: all variants have the same prefix: `Constant` - --> $DIR/enum_variants.rs:48:1 + --> $DIR/enum_variants.rs:47:1 | LL | / enum Consts { LL | | ConstantInt, @@ -61,7 +61,7 @@ LL | | } = help: remove the prefixes and use full paths to the variants instead of glob imports error: all variants have the same prefix: `With` - --> $DIR/enum_variants.rs:82:1 + --> $DIR/enum_variants.rs:81:1 | LL | / enum Seallll { LL | | WithOutCake, @@ -73,7 +73,7 @@ LL | | } = help: remove the prefixes and use full paths to the variants instead of glob imports error: all variants have the same prefix: `Prefix` - --> $DIR/enum_variants.rs:88:1 + --> $DIR/enum_variants.rs:87:1 | LL | / enum NonCaps { LL | | Prefix的, @@ -84,21 +84,8 @@ LL | | } | = help: remove the prefixes and use full paths to the variants instead of glob imports -error: all variants have the same prefix: `With` - --> $DIR/enum_variants.rs:94:1 - | -LL | / pub enum PubSeall { -LL | | WithOutCake, -LL | | WithOutTea, -LL | | WithOut, -LL | | } - | |_^ - | - = note: `-D clippy::pub-enum-variant-names` implied by `-D warnings` - = help: remove the prefixes and use full paths to the variants instead of glob imports - error: all variants have the same postfix: `IData` - --> $DIR/enum_variants.rs:137:1 + --> $DIR/enum_variants.rs:136:1 | LL | / enum IDataRequest { LL | | PutIData(String), @@ -110,7 +97,7 @@ LL | | } = help: remove the postfixes and use full paths to the variants instead of glob imports error: all variants have the same postfix: `HIData` - --> $DIR/enum_variants.rs:143:1 + --> $DIR/enum_variants.rs:142:1 | LL | / enum HIDataRequest { LL | | PutHIData(String), @@ -121,5 +108,5 @@ LL | | } | = help: remove the postfixes and use full paths to the variants instead of glob imports -error: aborting due to 12 previous errors +error: aborting due to 11 previous errors -- cgit 1.4.1-3-g733a5 From 1ce581d70698466879dc7acec0629032fb9006fd Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 6 May 2021 14:33:23 -0500 Subject: Use break api config for unnecessary_wraps --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/unnecessary_wraps.rs | 21 ++++++++++++++++----- tests/ui/unnecessary_wraps.rs | 2 +- 3 files changed, 18 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index bab389d2c05..9c9eba0d21e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1976,7 +1976,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box redundant_clone::RedundantClone); store.register_late_pass(|| box slow_vector_initialization::SlowVectorInit); store.register_late_pass(|| box unnecessary_sort_by::UnnecessarySortBy); - store.register_late_pass(|| box unnecessary_wraps::UnnecessaryWraps); + store.register_late_pass(move || box unnecessary_wraps::UnnecessaryWraps::new(avoid_breaking_exported_api)); store.register_late_pass(|| box assertions_on_constants::AssertionsOnConstants); store.register_late_pass(|| box transmuting_null::TransmutingNull); store.register_late_pass(|| box path_buf_push_overwrite::PathBufPushOverwrite); diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index f2f1410aed7..a85ffa6aa95 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -8,7 +8,7 @@ use rustc_hir::LangItem::{OptionSome, ResultOk}; use rustc_hir::{Body, ExprKind, FnDecl, HirId, Impl, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::sym; use rustc_span::Span; @@ -52,7 +52,19 @@ declare_clippy_lint! { "functions that only return `Ok` or `Some`" } -declare_lint_pass!(UnnecessaryWraps => [UNNECESSARY_WRAPS]); +pub struct UnnecessaryWraps { + avoid_breaking_exported_api: bool, +} + +impl_lint_pass!(UnnecessaryWraps => [UNNECESSARY_WRAPS]); + +impl UnnecessaryWraps { + pub fn new(avoid_breaking_exported_api: bool) -> Self { + Self { + avoid_breaking_exported_api, + } + } +} impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { fn check_fn( @@ -66,13 +78,12 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { ) { // Abort if public function/method or closure. match fn_kind { - FnKind::ItemFn(.., visibility) | FnKind::Method(.., Some(visibility)) => { - if visibility.node.is_pub() { + FnKind::ItemFn(..) | FnKind::Method(..) => { + if self.avoid_breaking_exported_api && cx.access_levels.is_exported(hir_id) { return; } }, FnKind::Closure => return, - FnKind::Method(..) => (), } // Abort if the method is implementing a trait or of it a trait method. diff --git a/tests/ui/unnecessary_wraps.rs b/tests/ui/unnecessary_wraps.rs index 54f22e3ee6a..63648ef5826 100644 --- a/tests/ui/unnecessary_wraps.rs +++ b/tests/ui/unnecessary_wraps.rs @@ -65,7 +65,7 @@ fn func10() -> Option<()> { unimplemented!() } -struct A; +pub struct A; impl A { // should not be linted -- cgit 1.4.1-3-g733a5 From c51472b4b09d22bdbb46027f08be54c4b285a725 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 6 May 2021 15:43:19 -0500 Subject: Add clippy.toml to project and tests --- clippy.toml | 1 + tests/clippy.toml | 1 + tests/compile-test.rs | 42 ++++++++++++++++++++++++++++++++---------- 3 files changed, 34 insertions(+), 10 deletions(-) create mode 100644 clippy.toml create mode 100644 tests/clippy.toml (limited to 'tests') diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 00000000000..cda8d17eed4 --- /dev/null +++ b/clippy.toml @@ -0,0 +1 @@ +avoid-breaking-exported-api = false diff --git a/tests/clippy.toml b/tests/clippy.toml new file mode 100644 index 00000000000..5eb7ac03541 --- /dev/null +++ b/tests/clippy.toml @@ -0,0 +1 @@ +# default config for tests, overrides clippy.toml at the project root diff --git a/tests/compile-test.rs b/tests/compile-test.rs index e1110721f6e..7d266a36bb6 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -4,8 +4,8 @@ use compiletest_rs as compiletest; use compiletest_rs::common::Mode as TestMode; -use std::env::{self, set_var, var}; -use std::ffi::OsStr; +use std::env::{self, remove_var, set_var, var_os}; +use std::ffi::{OsStr, OsString}; use std::fs; use std::io; use std::path::{Path, PathBuf}; @@ -88,9 +88,11 @@ fn default_config() -> compiletest::Config { config } -fn run_mode(cfg: &mut compiletest::Config) { +fn run_ui(cfg: &mut compiletest::Config) { cfg.mode = TestMode::Ui; cfg.src_base = Path::new("tests").join("ui"); + // use tests/clippy.toml + let _g = VarGuard::set("CARGO_MANIFEST_DIR", std::fs::canonicalize("tests").unwrap()); compiletest::run_tests(cfg); } @@ -114,7 +116,7 @@ fn run_ui_toml(config: &mut compiletest::Config) { continue; } let dir_path = dir.path(); - set_var("CARGO_MANIFEST_DIR", &dir_path); + let _g = VarGuard::set("CARGO_MANIFEST_DIR", &dir_path); for file in fs::read_dir(&dir_path)? { let file = file?; let file_path = file.path(); @@ -145,9 +147,7 @@ fn run_ui_toml(config: &mut compiletest::Config) { let tests = compiletest::make_tests(config); - let manifest_dir = var("CARGO_MANIFEST_DIR").unwrap_or_default(); let res = run_tests(config, tests); - set_var("CARGO_MANIFEST_DIR", &manifest_dir); match res { Ok(true) => {}, Ok(false) => panic!("Some tests failed"), @@ -208,7 +208,7 @@ fn run_ui_cargo(config: &mut compiletest::Config) { Some("main.rs") => {}, _ => continue, } - set_var("CLIPPY_CONF_DIR", case.path()); + let _g = VarGuard::set("CLIPPY_CONF_DIR", case.path()); let paths = compiletest::common::TestPaths { file: file_path, base: config.src_base.clone(), @@ -236,10 +236,8 @@ fn run_ui_cargo(config: &mut compiletest::Config) { let tests = compiletest::make_tests(config); let current_dir = env::current_dir().unwrap(); - let conf_dir = var("CLIPPY_CONF_DIR").unwrap_or_default(); let res = run_tests(config, &config.filters, tests); env::set_current_dir(current_dir).unwrap(); - set_var("CLIPPY_CONF_DIR", conf_dir); match res { Ok(true) => {}, @@ -260,8 +258,32 @@ fn prepare_env() { fn compile_test() { prepare_env(); let mut config = default_config(); - run_mode(&mut config); + run_ui(&mut config); run_ui_toml(&mut config); run_ui_cargo(&mut config); run_internal_tests(&mut config); } + +/// Restores an env var on drop +#[must_use] +struct VarGuard { + key: &'static str, + value: Option, +} + +impl VarGuard { + fn set(key: &'static str, val: impl AsRef) -> Self { + let value = var_os(key); + set_var(key, val); + Self { key, value } + } +} + +impl Drop for VarGuard { + fn drop(&mut self) { + match self.value.as_deref() { + None => remove_var(self.key), + Some(value) => set_var(self.key, value), + } + } +} -- cgit 1.4.1-3-g733a5 From 3af95846a2ab61238f1a8f9c16a52d4a8d2390b0 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 7 May 2021 11:19:35 -0500 Subject: Add deprecated lint tests --- tests/ui/deprecated.rs | 2 ++ tests/ui/deprecated.stderr | 14 +++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/ui/deprecated.rs b/tests/ui/deprecated.rs index dbf0b03af76..4ba9f0c1fcf 100644 --- a/tests/ui/deprecated.rs +++ b/tests/ui/deprecated.rs @@ -12,5 +12,7 @@ #[warn(clippy::unknown_clippy_lints)] #[warn(clippy::find_map)] #[warn(clippy::filter_map)] +#[warn(clippy::pub_enum_variant_names)] +#[warn(clippy::wrong_pub_self_convention)] fn main() {} diff --git a/tests/ui/deprecated.stderr b/tests/ui/deprecated.stderr index e5de839dbc5..03c9f438891 100644 --- a/tests/ui/deprecated.stderr +++ b/tests/ui/deprecated.stderr @@ -84,5 +84,17 @@ error: lint `clippy::filter_map` has been removed: this lint has been replaced b LL | #[warn(clippy::filter_map)] | ^^^^^^^^^^^^^^^^^^ -error: aborting due to 14 previous errors +error: lint `clippy::pub_enum_variant_names` has been removed: set the `avoid_breaking_exported_api` config option to `false` to enable the `enum_variant_names` lint for public items + --> $DIR/deprecated.rs:15:8 + | +LL | #[warn(clippy::pub_enum_variant_names)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: lint `clippy::wrong_pub_self_convention` has been removed: set the `avoid_breaking_exported_api` config option to `false` to enable the `wrong_self_convention` lint for public items + --> $DIR/deprecated.rs:16:8 + | +LL | #[warn(clippy::wrong_pub_self_convention)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 16 previous errors -- cgit 1.4.1-3-g733a5 From f3e77a454aef617998a016ca70c8dc0d281f208a Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 26 May 2021 22:07:53 -0500 Subject: Fix allow on some statement lints --- clippy_lints/src/loops/needless_collect.rs | 5 +++-- clippy_lints/src/misc.rs | 4 +++- clippy_lints/src/no_effect.rs | 13 +++++++------ clippy_utils/src/diagnostics.rs | 2 +- tests/ui/needless_collect_indirect.rs | 8 +++++++- tests/ui/no_effect.rs | 3 +++ 6 files changed, 24 insertions(+), 11 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/loops/needless_collect.rs index d3406780888..eb82c9c27c3 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/loops/needless_collect.rs @@ -1,5 +1,5 @@ use super::NEEDLESS_COLLECT; -use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; @@ -116,9 +116,10 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo // Suggest replacing iter_call with iter_replacement, and removing stmt let mut span = MultiSpan::from_span(collect_span); span.push_span_label(iter_call.span, "the iterator could be used here instead".into()); - span_lint_and_then( + span_lint_hir_and_then( cx, super::NEEDLESS_COLLECT, + init_expr.hir_id, span, NEEDLESS_COLLECT_MSG, |diag| { diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index b5d2549242b..5976b060ad2 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -355,8 +355,10 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints { if binop.node == BinOpKind::And || binop.node == BinOpKind::Or; if let Some(sugg) = Sugg::hir_opt(cx, a); then { - span_lint_and_then(cx, + span_lint_hir_and_then( + cx, SHORT_CIRCUIT_STATEMENT, + expr.hir_id, stmt.span, "boolean short circuit operator in statement may be clearer using an explicit test", |diag| { diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index cfcaf509471..b2206a82208 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::has_drop; use rustc_errors::Applicability; @@ -92,7 +92,7 @@ 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(cx, NO_EFFECT, stmt.span, "statement with no effect"); + span_lint_hir(cx, NO_EFFECT, expr.hir_id, stmt.span, "statement with no effect"); } else if let Some(reduced) = reduce_expression(cx, expr) { let mut snippet = String::new(); for e in reduced { @@ -106,14 +106,15 @@ impl<'tcx> LateLintPass<'tcx> for NoEffect { return; } } - span_lint_and_sugg( + span_lint_hir_and_then( cx, UNNECESSARY_OPERATION, + expr.hir_id, stmt.span, "statement can be reduced", - "replace it with", - snippet, - Applicability::MachineApplicable, + |diag| { + diag.span_suggestion(stmt.span, "replace it with", snippet, Applicability::MachineApplicable); + }, ); } } diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs index a1f5f5f3338..7c94474cb35 100644 --- a/clippy_utils/src/diagnostics.rs +++ b/clippy_utils/src/diagnostics.rs @@ -167,7 +167,7 @@ pub fn span_lint_hir_and_then( cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, - sp: Span, + sp: impl Into, msg: &str, f: impl FnOnce(&mut DiagnosticBuilder<'_>), ) { diff --git a/tests/ui/needless_collect_indirect.rs b/tests/ui/needless_collect_indirect.rs index 2458bf1e490..2c94235b8f5 100644 --- a/tests/ui/needless_collect_indirect.rs +++ b/tests/ui/needless_collect_indirect.rs @@ -1,4 +1,4 @@ -use std::collections::{BinaryHeap, HashMap, LinkedList, VecDeque}; +use std::collections::{BinaryHeap, HashMap, HashSet, LinkedList, VecDeque}; fn main() { let sample = [1; 5]; @@ -75,3 +75,9 @@ mod issue7110 { buffer.len() } } + +fn allow_test() { + #[allow(clippy::needless_collect)] + let v = [1].iter().collect::>(); + v.into_iter().collect::>(); +} diff --git a/tests/ui/no_effect.rs b/tests/ui/no_effect.rs index 8fbfcb79860..7ec845adfaa 100644 --- a/tests/ui/no_effect.rs +++ b/tests/ui/no_effect.rs @@ -91,6 +91,9 @@ fn main() { let s: String = "foo".into(); FooString { s: s }; + #[allow(clippy::no_effect)] + 0; + // Do not warn get_number(); unsafe { unsafe_fn() }; -- cgit 1.4.1-3-g733a5 From 6c54f61bebc5f3ee90e3904a19d9c166623972d1 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 27 May 2021 11:03:13 -0500 Subject: Move mini-macro to auxilary --- Cargo.toml | 1 - mini-macro/Cargo.toml | 14 -------------- mini-macro/src/lib.rs | 29 ----------------------------- tests/ui/auxiliary/proc_macro_derive.rs | 19 +++++++++++++++++++ tests/ui/crashes/procedural_macro.rs | 11 ----------- tests/ui/macro_use_imports.fixed | 3 ++- tests/ui/macro_use_imports.rs | 3 ++- tests/ui/macro_use_imports.stderr | 14 +++++++------- tests/ui/unseparated_prefix_literals.fixed | 3 ++- tests/ui/unseparated_prefix_literals.rs | 3 ++- tests/ui/unseparated_prefix_literals.stderr | 18 +++++++++--------- 11 files changed, 43 insertions(+), 75 deletions(-) delete mode 100644 mini-macro/Cargo.toml delete mode 100644 mini-macro/src/lib.rs delete mode 100644 tests/ui/crashes/procedural_macro.rs (limited to 'tests') diff --git a/Cargo.toml b/Cargo.toml index 458c28c2748..b003b15a11d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,6 @@ tempfile = { version = "3.1.0", optional = true } cargo_metadata = "0.12" compiletest_rs = { version = "0.6.0", features = ["tmp"] } tester = "0.9" -clippy-mini-macro-test = { version = "0.2", path = "mini-macro" } serde = { version = "1.0", features = ["derive"] } derive-new = "0.5" regex = "1.4" diff --git a/mini-macro/Cargo.toml b/mini-macro/Cargo.toml deleted file mode 100644 index 0d95c86aef0..00000000000 --- a/mini-macro/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "clippy-mini-macro-test" -version = "0.2.0" -authors = ["The Rust Clippy Developers"] -license = "MIT OR Apache-2.0" -description = "A macro to test clippy's procedural macro checks" -repository = "https://github.com/rust-lang/rust-clippy" -edition = "2018" - -[lib] -name = "clippy_mini_macro_test" -proc-macro = true - -[dependencies] diff --git a/mini-macro/src/lib.rs b/mini-macro/src/lib.rs deleted file mode 100644 index 2b793589049..00000000000 --- a/mini-macro/src/lib.rs +++ /dev/null @@ -1,29 +0,0 @@ -#![feature(proc_macro_quote)] -#![deny(rust_2018_idioms)] -// FIXME: Remove this attribute once the weird failure is gone. -#![allow(unused_extern_crates)] -extern crate proc_macro; - -use proc_macro::{quote, TokenStream}; - -#[proc_macro_derive(ClippyMiniMacroTest)] -/// # Panics -/// -/// Panics if the macro derivation fails -pub fn mini_macro(_: TokenStream) -> TokenStream { - quote!( - #[allow(unused)] - fn needless_take_by_value(s: String) { - println!("{}", s.len()); - } - #[allow(unused)] - fn needless_loop(items: &[u8]) { - for i in 0..items.len() { - println!("{}", items[i]); - } - } - fn line_wrapper() { - println!("{}", line!()); - } - ) -} diff --git a/tests/ui/auxiliary/proc_macro_derive.rs b/tests/ui/auxiliary/proc_macro_derive.rs index aebeaf34679..4b7b7fec78f 100644 --- a/tests/ui/auxiliary/proc_macro_derive.rs +++ b/tests/ui/auxiliary/proc_macro_derive.rs @@ -53,3 +53,22 @@ pub fn derive_use_self(_input: TokenStream) -> proc_macro::TokenStream { } } } + +#[proc_macro_derive(ClippyMiniMacroTest)] +pub fn mini_macro(_: TokenStream) -> TokenStream { + quote!( + #[allow(unused)] + fn needless_take_by_value(s: String) { + println!("{}", s.len()); + } + #[allow(unused)] + fn needless_loop(items: &[u8]) { + for i in 0..items.len() { + println!("{}", items[i]); + } + } + fn line_wrapper() { + println!("{}", line!()); + } + ) +} diff --git a/tests/ui/crashes/procedural_macro.rs b/tests/ui/crashes/procedural_macro.rs deleted file mode 100644 index c7468493380..00000000000 --- a/tests/ui/crashes/procedural_macro.rs +++ /dev/null @@ -1,11 +0,0 @@ -#[macro_use] -extern crate clippy_mini_macro_test; - -#[deny(warnings)] -fn main() { - let x = Foo; - println!("{:?}", x); -} - -#[derive(ClippyMiniMacroTest, Debug)] -struct Foo; diff --git a/tests/ui/macro_use_imports.fixed b/tests/ui/macro_use_imports.fixed index 51c66a46368..70d49d9f2c4 100644 --- a/tests/ui/macro_use_imports.fixed +++ b/tests/ui/macro_use_imports.fixed @@ -1,6 +1,7 @@ // compile-flags: --edition 2018 // aux-build:macro_rules.rs // aux-build:macro_use_helper.rs +// aux-build:proc_macro_derive.rs // run-rustfix // ignore-32bit @@ -12,7 +13,7 @@ extern crate macro_use_helper as mac; #[macro_use] -extern crate clippy_mini_macro_test as mini_mac; +extern crate proc_macro_derive as mini_mac; mod a { use mac::{pub_macro, inner_mod_macro, function_macro, ty_macro, pub_in_private_macro}; diff --git a/tests/ui/macro_use_imports.rs b/tests/ui/macro_use_imports.rs index 2011129bc94..68370023861 100644 --- a/tests/ui/macro_use_imports.rs +++ b/tests/ui/macro_use_imports.rs @@ -1,6 +1,7 @@ // compile-flags: --edition 2018 // aux-build:macro_rules.rs // aux-build:macro_use_helper.rs +// aux-build:proc_macro_derive.rs // run-rustfix // ignore-32bit @@ -12,7 +13,7 @@ extern crate macro_use_helper as mac; #[macro_use] -extern crate clippy_mini_macro_test as mini_mac; +extern crate proc_macro_derive as mini_mac; mod a { #[macro_use] diff --git a/tests/ui/macro_use_imports.stderr b/tests/ui/macro_use_imports.stderr index f8c86c8d917..49314b7506d 100644 --- a/tests/ui/macro_use_imports.stderr +++ b/tests/ui/macro_use_imports.stderr @@ -1,5 +1,5 @@ error: `macro_use` attributes are no longer needed in the Rust 2018 edition - --> $DIR/macro_use_imports.rs:18:5 + --> $DIR/macro_use_imports.rs:19:5 | LL | #[macro_use] | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{pub_macro, inner_mod_macro, function_macro, ty_macro, pub_in_private_macro};` @@ -7,22 +7,22 @@ LL | #[macro_use] = note: `-D clippy::macro-use-imports` implied by `-D warnings` error: `macro_use` attributes are no longer needed in the Rust 2018 edition - --> $DIR/macro_use_imports.rs:20:5 + --> $DIR/macro_use_imports.rs:25:5 | LL | #[macro_use] - | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mini_mac::ClippyMiniMacroTest;` + | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::inner::nested::string_add;` error: `macro_use` attributes are no longer needed in the Rust 2018 edition - --> $DIR/macro_use_imports.rs:22:5 + --> $DIR/macro_use_imports.rs:21:5 | LL | #[macro_use] - | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{inner::foofoo, inner::try_err};` + | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mini_mac::ClippyMiniMacroTest;` error: `macro_use` attributes are no longer needed in the Rust 2018 edition - --> $DIR/macro_use_imports.rs:24:5 + --> $DIR/macro_use_imports.rs:23:5 | LL | #[macro_use] - | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::inner::nested::string_add;` + | ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{inner::foofoo, inner::try_err};` error: aborting due to 4 previous errors diff --git a/tests/ui/unseparated_prefix_literals.fixed b/tests/ui/unseparated_prefix_literals.fixed index 3c422cc4fee..f0c2ba7ccdf 100644 --- a/tests/ui/unseparated_prefix_literals.fixed +++ b/tests/ui/unseparated_prefix_literals.fixed @@ -1,10 +1,11 @@ // run-rustfix +// aux-build:proc_macro_derive.rs #![warn(clippy::unseparated_literal_suffix)] #![allow(dead_code)] #[macro_use] -extern crate clippy_mini_macro_test; +extern crate proc_macro_derive; // Test for proc-macro attribute #[derive(ClippyMiniMacroTest)] diff --git a/tests/ui/unseparated_prefix_literals.rs b/tests/ui/unseparated_prefix_literals.rs index 09608661e0e..f44880b4147 100644 --- a/tests/ui/unseparated_prefix_literals.rs +++ b/tests/ui/unseparated_prefix_literals.rs @@ -1,10 +1,11 @@ // run-rustfix +// aux-build:proc_macro_derive.rs #![warn(clippy::unseparated_literal_suffix)] #![allow(dead_code)] #[macro_use] -extern crate clippy_mini_macro_test; +extern crate proc_macro_derive; // Test for proc-macro attribute #[derive(ClippyMiniMacroTest)] diff --git a/tests/ui/unseparated_prefix_literals.stderr b/tests/ui/unseparated_prefix_literals.stderr index a0c0be7a9d1..ab2f75e0c56 100644 --- a/tests/ui/unseparated_prefix_literals.stderr +++ b/tests/ui/unseparated_prefix_literals.stderr @@ -1,5 +1,5 @@ error: integer type suffix should be separated by an underscore - --> $DIR/unseparated_prefix_literals.rs:23:18 + --> $DIR/unseparated_prefix_literals.rs:24:18 | LL | let _fail1 = 1234i32; | ^^^^^^^ help: add an underscore: `1234_i32` @@ -7,43 +7,43 @@ LL | let _fail1 = 1234i32; = note: `-D clippy::unseparated-literal-suffix` implied by `-D warnings` error: integer type suffix should be separated by an underscore - --> $DIR/unseparated_prefix_literals.rs:24:18 + --> $DIR/unseparated_prefix_literals.rs:25:18 | LL | let _fail2 = 1234u32; | ^^^^^^^ help: add an underscore: `1234_u32` error: integer type suffix should be separated by an underscore - --> $DIR/unseparated_prefix_literals.rs:25:18 + --> $DIR/unseparated_prefix_literals.rs:26:18 | LL | let _fail3 = 1234isize; | ^^^^^^^^^ help: add an underscore: `1234_isize` error: integer type suffix should be separated by an underscore - --> $DIR/unseparated_prefix_literals.rs:26:18 + --> $DIR/unseparated_prefix_literals.rs:27:18 | LL | let _fail4 = 1234usize; | ^^^^^^^^^ help: add an underscore: `1234_usize` error: integer type suffix should be separated by an underscore - --> $DIR/unseparated_prefix_literals.rs:27:18 + --> $DIR/unseparated_prefix_literals.rs:28:18 | LL | let _fail5 = 0x123isize; | ^^^^^^^^^^ help: add an underscore: `0x123_isize` error: float type suffix should be separated by an underscore - --> $DIR/unseparated_prefix_literals.rs:31:19 + --> $DIR/unseparated_prefix_literals.rs:32:19 | LL | let _failf1 = 1.5f32; | ^^^^^^ help: add an underscore: `1.5_f32` error: float type suffix should be separated by an underscore - --> $DIR/unseparated_prefix_literals.rs:32:19 + --> $DIR/unseparated_prefix_literals.rs:33:19 | LL | let _failf2 = 1f32; | ^^^^ help: add an underscore: `1_f32` error: integer type suffix should be separated by an underscore - --> $DIR/unseparated_prefix_literals.rs:15:9 + --> $DIR/unseparated_prefix_literals.rs:16:9 | LL | 42usize | ^^^^^^^ help: add an underscore: `42_usize` @@ -54,7 +54,7 @@ LL | let _ = lit_from_macro!(); = note: this error originates in the macro `lit_from_macro` (in Nightly builds, run with -Z macro-backtrace for more info) error: integer type suffix should be separated by an underscore - --> $DIR/unseparated_prefix_literals.rs:40:16 + --> $DIR/unseparated_prefix_literals.rs:41:16 | LL | assert_eq!(4897u32, 32223); | ^^^^^^^ help: add an underscore: `4897_u32` -- cgit 1.4.1-3-g733a5 From 29b4b4c10d89b2278485ac0e24a393ef58290672 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 27 May 2021 16:25:37 -0500 Subject: Do not lint use_self on type parameters --- clippy_lints/src/use_self.rs | 2 +- tests/ui/use_self.fixed | 23 +++++++++++++++++++++++ tests/ui/use_self.rs | 25 ++++++++++++++++++++++++- tests/ui/use_self.stderr | 4 ++-- 4 files changed, 50 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index e79983134e4..f71dfd02499 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -386,7 +386,7 @@ fn should_lint_ty(hir_ty: &hir::Ty<'_>, ty: Ty<'_>, self_ty: Ty<'_>) -> bool { if same_type_and_consts(ty, self_ty); if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind; then { - !matches!(path.res, def::Res::SelfTy(..)) + !matches!(path.res, Res::SelfTy(..) | Res::Def(DefKind::TyParam, _)) } else { false } diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed index 631da6fe066..e2c28542efc 100644 --- a/tests/ui/use_self.fixed +++ b/tests/ui/use_self.fixed @@ -492,3 +492,26 @@ mod issue7206 { } } } + +mod self_is_ty_param { + trait Trait { + type Type; + type Hi; + + fn test(); + } + + impl Trait for I + where + I: Iterator, + I::Item: Trait, // changing this to Self would require + { + type Type = I; + type Hi = I::Item; + + fn test() { + let _: I::Item; + let _: I; // this could lint, but is questionable + } + } +} diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index 7a10d755faa..3cd99b9f5cd 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -279,7 +279,7 @@ mod generics { impl Foo { // `Self` is applicable here fn foo(value: T) -> Foo { - Foo { value } + Foo:: { value } } // `Cannot` use `Self` as a return type as the generic types are different @@ -492,3 +492,26 @@ mod issue7206 { } } } + +mod self_is_ty_param { + trait Trait { + type Type; + type Hi; + + fn test(); + } + + impl Trait for I + where + I: Iterator, + I::Item: Trait, // changing this to Self would require + { + type Type = I; + type Hi = I::Item; + + fn test() { + let _: I::Item; + let _: I; // this could lint, but is questionable + } + } +} diff --git a/tests/ui/use_self.stderr b/tests/ui/use_self.stderr index cf6222c9b45..6ac26c9e5a9 100644 --- a/tests/ui/use_self.stderr +++ b/tests/ui/use_self.stderr @@ -153,8 +153,8 @@ LL | fn foo(value: T) -> Foo { error: unnecessary structure name repetition --> $DIR/use_self.rs:282:13 | -LL | Foo { value } - | ^^^ help: use the applicable keyword: `Self` +LL | Foo:: { value } + | ^^^^^^^^ help: use the applicable keyword: `Self` error: unnecessary structure name repetition --> $DIR/use_self.rs:454:13 -- cgit 1.4.1-3-g733a5 From 4ba6afd1922b679eb533b8523f6e47a0da152afb Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 27 May 2021 15:16:26 -0400 Subject: Fix ICE in `too_many_lines` due to wrong assumptions on braces. --- clippy_lints/src/functions/too_many_lines.rs | 22 ++++++++++++++++------ tests/ui/crashes/auxiliary/ice-7272-aux.rs | 14 ++++++++++++++ tests/ui/crashes/ice-7272.rs | 12 ++++++++++++ 3 files changed, 42 insertions(+), 6 deletions(-) create mode 100644 tests/ui/crashes/auxiliary/ice-7272-aux.rs create mode 100644 tests/ui/crashes/ice-7272.rs (limited to 'tests') diff --git a/clippy_lints/src/functions/too_many_lines.rs b/clippy_lints/src/functions/too_many_lines.rs index c02c343dc58..a666fee1a4a 100644 --- a/clippy_lints/src/functions/too_many_lines.rs +++ b/clippy_lints/src/functions/too_many_lines.rs @@ -4,7 +4,7 @@ use rustc_middle::lint::in_external_macro; use rustc_span::Span; use clippy_utils::diagnostics::span_lint; -use clippy_utils::source::snippet; +use clippy_utils::source::snippet_opt; use super::TOO_MANY_LINES; @@ -13,15 +13,25 @@ pub(super) fn check_fn(cx: &LateContext<'_>, span: Span, body: &'tcx hir::Body<' return; } - let code_snippet = snippet(cx, body.value.span, ".."); + let code_snippet = match snippet_opt(cx, body.value.span) { + Some(s) => s, + _ => return, + }; let mut line_count: u64 = 0; let mut in_comment = false; let mut code_in_line; - // Skip the surrounding function decl. - let start_brace_idx = code_snippet.find('{').map_or(0, |i| i + 1); - let end_brace_idx = code_snippet.rfind('}').unwrap_or_else(|| code_snippet.len()); - let function_lines = code_snippet[start_brace_idx..end_brace_idx].lines(); + let function_lines = if matches!(body.value.kind, hir::ExprKind::Block(..)) + && code_snippet.as_bytes().first().copied() == Some(b'{') + && code_snippet.as_bytes().last().copied() == Some(b'}') + { + // Removing the braces from the enclosing block + &code_snippet[1..code_snippet.len() - 1] + } else { + &code_snippet + } + .trim() // Remove leading and trailing blank lines + .lines(); for mut line in function_lines { code_in_line = false; diff --git a/tests/ui/crashes/auxiliary/ice-7272-aux.rs b/tests/ui/crashes/auxiliary/ice-7272-aux.rs new file mode 100644 index 00000000000..780797e3c6a --- /dev/null +++ b/tests/ui/crashes/auxiliary/ice-7272-aux.rs @@ -0,0 +1,14 @@ +pub fn warn(_: T) {} + +macro_rules! define_macro { + ($d:tt $lower:ident $upper:ident) => { + #[macro_export] + macro_rules! $upper { + ($arg:tt) => { + $crate::$lower($arg) + }; + } + }; +} + +define_macro! {$ warn WARNING} diff --git a/tests/ui/crashes/ice-7272.rs b/tests/ui/crashes/ice-7272.rs new file mode 100644 index 00000000000..57ab6ca14f8 --- /dev/null +++ b/tests/ui/crashes/ice-7272.rs @@ -0,0 +1,12 @@ +// aux-build:ice-7272-aux.rs + +#![allow(clippy::no_effect)] + +extern crate ice_7272_aux; + +use ice_7272_aux::*; + +pub fn main() { + || WARNING!("Style changed!"); + || "}{"; +} -- cgit 1.4.1-3-g733a5 From 898b6a0e072c4f8ba741e83bd7fd12a4c2ccb1a2 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 30 May 2021 09:35:06 -0400 Subject: Add lint `suspicious_splitn` --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 3 ++ clippy_lints/src/methods/mod.rs | 36 +++++++++++++++- clippy_lints/src/methods/suspicious_splitn.rs | 52 +++++++++++++++++++++++ tests/ui/single_char_pattern.fixed | 4 +- tests/ui/single_char_pattern.rs | 4 +- tests/ui/single_char_pattern.stderr | 4 +- tests/ui/suspicious_splitn.rs | 16 ++++++++ tests/ui/suspicious_splitn.stderr | 59 +++++++++++++++++++++++++++ 9 files changed, 172 insertions(+), 7 deletions(-) create mode 100644 clippy_lints/src/methods/suspicious_splitn.rs create mode 100644 tests/ui/suspicious_splitn.rs create mode 100644 tests/ui/suspicious_splitn.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index abfe7f91f4b..59daa074282 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2671,6 +2671,7 @@ Released 2018-09-13 [`suspicious_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_map [`suspicious_op_assign_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_op_assign_impl [`suspicious_operation_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_operation_groupings +[`suspicious_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_splitn [`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting [`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments [`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 4c9c44f55d1..0e815be9bd5 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -779,6 +779,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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, @@ -1312,6 +1313,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), @@ -1688,6 +1690,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 0b998dbf86c..3904572c627 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -48,6 +48,7 @@ mod single_char_push_string; mod skip_while_next; mod string_extend_chars; mod suspicious_map; +mod suspicious_splitn; mod uninit_assumed_init; mod unnecessary_filter_map; mod unnecessary_fold; @@ -1633,6 +1634,35 @@ declare_clippy_lint! { "replace `.iter().count()` with `.len()`" } +declare_clippy_lint! { + /// **What it does:** Checks for calls to `splitn` and related functions with + /// either zero or one splits. + /// + /// **Why is this bad?** These calls don't actually split the value and are + /// likely to be intended as a different number. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // Bad + /// let s = ""; + /// for x in s.splitn(1, ":") { + /// // use x + /// } + /// + /// // Good + /// let s = ""; + /// for x in s.splitn(2, ":") { + /// // use x + /// } + /// ``` + pub SUSPICIOUS_SPLITN, + correctness, + "checks for `.splitn(0, ..)` and `.splitn(1, ..)`" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Option, @@ -1705,7 +1735,8 @@ impl_lint_pass!(Methods => [ MAP_COLLECT_RESULT_UNIT, FROM_ITER_INSTEAD_OF_COLLECT, INSPECT_FOR_EACH, - IMPLICIT_CLONE + IMPLICIT_CLONE, + SUSPICIOUS_SPLITN ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -2024,6 +2055,9 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio unnecessary_lazy_eval::check(cx, expr, recv, arg, "or"); } }, + ("splitn" | "splitn_mut" | "rsplitn" | "rsplitn_mut", [count_arg, _]) => { + suspicious_splitn::check(cx, name, expr, recv, count_arg); + }, ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg), ("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => { implicit_clone::check(cx, name, expr, recv, span); diff --git a/clippy_lints/src/methods/suspicious_splitn.rs b/clippy_lints/src/methods/suspicious_splitn.rs new file mode 100644 index 00000000000..43affe20dc5 --- /dev/null +++ b/clippy_lints/src/methods/suspicious_splitn.rs @@ -0,0 +1,52 @@ +use clippy_utils::diagnostics::span_lint_and_note; +use if_chain::if_chain; +use rustc_ast::LitKind; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_span::source_map::Spanned; + +use super::SUSPICIOUS_SPLITN; + +pub(super) fn check( + cx: &LateContext<'_>, + method_name: &str, + expr: &Expr<'_>, + self_arg: &Expr<'_>, + count_arg: &Expr<'_>, +) { + if_chain! { + // Ignore empty slice literal + if !matches!(self_arg.kind, ExprKind::Array([])); + // Ignore empty string literal + if !matches!(self_arg.kind, ExprKind::Lit(Spanned { node: LitKind::Str(s, _), .. }) if s.is_empty()); + if let ExprKind::Lit(count_lit) = &count_arg.kind; + if let LitKind::Int(count, _) = count_lit.node; + if count <= 1; + if let Some(call_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); + if let Some(impl_id) = cx.tcx.impl_of_method(call_id); + let lang_items = cx.tcx.lang_items(); + if lang_items.slice_impl() == Some(impl_id) || lang_items.str_impl() == Some(impl_id); + then { + let (msg, note_msg) = if count == 0 { + (format!("`{}` called with `0` splits", method_name), + "the resulting iterator will always return `None`") + } else { + (format!("`{}` called with `1` split", method_name), + if lang_items.slice_impl() == Some(impl_id) { + "the resulting iterator will always return the entire slice followed by `None`" + } else { + "the resulting iterator will always return the entire string followed by `None`" + }) + }; + + span_lint_and_note( + cx, + SUSPICIOUS_SPLITN, + expr.span, + &msg, + None, + note_msg, + ); + } + } +} diff --git a/tests/ui/single_char_pattern.fixed b/tests/ui/single_char_pattern.fixed index fcbe9af9f56..1abd2b7883d 100644 --- a/tests/ui/single_char_pattern.fixed +++ b/tests/ui/single_char_pattern.fixed @@ -25,8 +25,8 @@ fn main() { x.rsplit('x'); x.split_terminator('x'); x.rsplit_terminator('x'); - x.splitn(0, 'x'); - x.rsplitn(0, 'x'); + x.splitn(2, 'x'); + x.rsplitn(2, 'x'); x.matches('x'); x.rmatches('x'); x.match_indices('x'); diff --git a/tests/ui/single_char_pattern.rs b/tests/ui/single_char_pattern.rs index b8bc20f4070..e662bf34be2 100644 --- a/tests/ui/single_char_pattern.rs +++ b/tests/ui/single_char_pattern.rs @@ -25,8 +25,8 @@ fn main() { x.rsplit("x"); x.split_terminator("x"); x.rsplit_terminator("x"); - x.splitn(0, "x"); - x.rsplitn(0, "x"); + x.splitn(2, "x"); + x.rsplitn(2, "x"); x.matches("x"); x.rmatches("x"); x.match_indices("x"); diff --git a/tests/ui/single_char_pattern.stderr b/tests/ui/single_char_pattern.stderr index 6d94d8a34e3..22d4b2d460f 100644 --- a/tests/ui/single_char_pattern.stderr +++ b/tests/ui/single_char_pattern.stderr @@ -75,13 +75,13 @@ LL | x.rsplit_terminator("x"); error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:28:17 | -LL | x.splitn(0, "x"); +LL | x.splitn(2, "x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern --> $DIR/single_char_pattern.rs:29:18 | -LL | x.rsplitn(0, "x"); +LL | x.rsplitn(2, "x"); | ^^^ help: try using a `char` instead: `'x'` error: single-character string constant used as pattern diff --git a/tests/ui/suspicious_splitn.rs b/tests/ui/suspicious_splitn.rs new file mode 100644 index 00000000000..a944aa79cfe --- /dev/null +++ b/tests/ui/suspicious_splitn.rs @@ -0,0 +1,16 @@ +#![warn(clippy::suspicious_splitn)] + +fn main() { + let _ = "a,b,c".splitn(3, ','); + let _ = [0, 1, 2, 1, 3].splitn(3, |&x| x == 1); + let _ = "".splitn(0, ','); + let _ = [].splitn(0, |&x: &u32| x == 1); + + let _ = "a,b".splitn(0, ','); + let _ = "a,b".rsplitn(0, ','); + let _ = "a,b".splitn(1, ','); + let _ = [0, 1, 2].splitn(0, |&x| x == 1); + let _ = [0, 1, 2].splitn_mut(0, |&x| x == 1); + let _ = [0, 1, 2].splitn(1, |&x| x == 1); + let _ = [0, 1, 2].rsplitn_mut(1, |&x| x == 1); +} diff --git a/tests/ui/suspicious_splitn.stderr b/tests/ui/suspicious_splitn.stderr new file mode 100644 index 00000000000..2e4bfe93b94 --- /dev/null +++ b/tests/ui/suspicious_splitn.stderr @@ -0,0 +1,59 @@ +error: `splitn` called with `0` splits + --> $DIR/suspicious_splitn.rs:9:13 + | +LL | let _ = "a,b".splitn(0, ','); + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::suspicious-splitn` implied by `-D warnings` + = note: the resulting iterator will always return `None` + +error: `rsplitn` called with `0` splits + --> $DIR/suspicious_splitn.rs:10:13 + | +LL | let _ = "a,b".rsplitn(0, ','); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: the resulting iterator will always return `None` + +error: `splitn` called with `1` split + --> $DIR/suspicious_splitn.rs:11:13 + | +LL | let _ = "a,b".splitn(1, ','); + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: the resulting iterator will always return the entire string followed by `None` + +error: `splitn` called with `0` splits + --> $DIR/suspicious_splitn.rs:12:13 + | +LL | let _ = [0, 1, 2].splitn(0, |&x| x == 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the resulting iterator will always return `None` + +error: `splitn_mut` called with `0` splits + --> $DIR/suspicious_splitn.rs:13:13 + | +LL | let _ = [0, 1, 2].splitn_mut(0, |&x| x == 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the resulting iterator will always return `None` + +error: `splitn` called with `1` split + --> $DIR/suspicious_splitn.rs:14:13 + | +LL | let _ = [0, 1, 2].splitn(1, |&x| x == 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the resulting iterator will always return the entire slice followed by `None` + +error: `rsplitn_mut` called with `1` split + --> $DIR/suspicious_splitn.rs:15:13 + | +LL | let _ = [0, 1, 2].rsplitn_mut(1, |&x| x == 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the resulting iterator will always return the entire slice followed by `None` + +error: aborting due to 7 previous errors + -- cgit 1.4.1-3-g733a5 From 5fa08eaf53f0c895e73f4841c240389fda951554 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 30 May 2021 13:25:24 -0400 Subject: Evaluate constant expressions in `suspicious_splitn` --- clippy_lints/src/methods/mod.rs | 5 +++-- clippy_lints/src/methods/suspicious_splitn.rs | 16 ++++++++++------ tests/ui/suspicious_splitn.rs | 4 ++++ tests/ui/suspicious_splitn.stderr | 18 +++++++++++++++++- 4 files changed, 34 insertions(+), 9 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 3904572c627..a6e2e0baadb 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1635,8 +1635,9 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for calls to `splitn` and related functions with - /// either zero or one splits. + /// **What it does:** Checks for calls to [`splitn`] + /// (https://doc.rust-lang.org/std/primitive.str.html#method.splitn) and + /// related functions with either zero or one splits. /// /// **Why is this bad?** These calls don't actually split the value and are /// likely to be intended as a different number. diff --git a/clippy_lints/src/methods/suspicious_splitn.rs b/clippy_lints/src/methods/suspicious_splitn.rs index 43affe20dc5..a271df60572 100644 --- a/clippy_lints/src/methods/suspicious_splitn.rs +++ b/clippy_lints/src/methods/suspicious_splitn.rs @@ -1,3 +1,4 @@ +use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint_and_note; use if_chain::if_chain; use rustc_ast::LitKind; @@ -15,18 +16,21 @@ pub(super) fn check( count_arg: &Expr<'_>, ) { if_chain! { - // Ignore empty slice literal - if !matches!(self_arg.kind, ExprKind::Array([])); - // Ignore empty string literal - if !matches!(self_arg.kind, ExprKind::Lit(Spanned { node: LitKind::Str(s, _), .. }) if s.is_empty()); - if let ExprKind::Lit(count_lit) = &count_arg.kind; - if let LitKind::Int(count, _) = count_lit.node; + if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg); if count <= 1; if let Some(call_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if let Some(impl_id) = cx.tcx.impl_of_method(call_id); let lang_items = cx.tcx.lang_items(); if lang_items.slice_impl() == Some(impl_id) || lang_items.str_impl() == Some(impl_id); then { + // Ignore empty slice and string literals when used with a literal count. + if (matches!(self_arg.kind, ExprKind::Array([])) + || matches!(self_arg.kind, ExprKind::Lit(Spanned { node: LitKind::Str(s, _), .. }) if s.is_empty()) + ) && matches!(count_arg.kind, ExprKind::Lit(_)) + { + return; + } + let (msg, note_msg) = if count == 0 { (format!("`{}` called with `0` splits", method_name), "the resulting iterator will always return `None`") diff --git a/tests/ui/suspicious_splitn.rs b/tests/ui/suspicious_splitn.rs index a944aa79cfe..a21d94cf20b 100644 --- a/tests/ui/suspicious_splitn.rs +++ b/tests/ui/suspicious_splitn.rs @@ -13,4 +13,8 @@ fn main() { let _ = [0, 1, 2].splitn_mut(0, |&x| x == 1); let _ = [0, 1, 2].splitn(1, |&x| x == 1); let _ = [0, 1, 2].rsplitn_mut(1, |&x| x == 1); + + const X: usize = 0; + let _ = "a,b".splitn(X + 1, ','); + let _ = "a,b".splitn(X, ','); } diff --git a/tests/ui/suspicious_splitn.stderr b/tests/ui/suspicious_splitn.stderr index 2e4bfe93b94..b6220ae2393 100644 --- a/tests/ui/suspicious_splitn.stderr +++ b/tests/ui/suspicious_splitn.stderr @@ -55,5 +55,21 @@ LL | let _ = [0, 1, 2].rsplitn_mut(1, |&x| x == 1); | = note: the resulting iterator will always return the entire slice followed by `None` -error: aborting due to 7 previous errors +error: `splitn` called with `1` split + --> $DIR/suspicious_splitn.rs:18:13 + | +LL | let _ = "a,b".splitn(X + 1, ','); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the resulting iterator will always return the entire string followed by `None` + +error: `splitn` called with `0` splits + --> $DIR/suspicious_splitn.rs:19:13 + | +LL | let _ = "a,b".splitn(X, ','); + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: the resulting iterator will always return `None` + +error: aborting due to 9 previous errors -- cgit 1.4.1-3-g733a5 From 58491d386a35b7103dc6ab93f9c69eb1b8891fb9 Mon Sep 17 00:00:00 2001 From: Steven Engler Date: Sun, 30 May 2021 20:19:57 -0400 Subject: Update message for 'not_unsafe_ptr_arg_deref' lint --- clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs | 2 +- tests/ui/functions.stderr | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs index b8ea6990866..af759a48e10 100644 --- a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs +++ b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs @@ -116,7 +116,7 @@ impl<'a, 'tcx> DerefVisitor<'a, 'tcx> { self.cx, NOT_UNSAFE_PTR_ARG_DEREF, ptr.span, - "this public function dereferences a raw pointer but is not marked `unsafe`", + "this public function might dereference a raw pointer but is not marked `unsafe`", ); } } diff --git a/tests/ui/functions.stderr b/tests/ui/functions.stderr index 0a86568b18d..a2b8c2a384b 100644 --- a/tests/ui/functions.stderr +++ b/tests/ui/functions.stderr @@ -30,7 +30,7 @@ error: this function has too many arguments (8/7) LL | fn bad_method(_one: u32, _two: u32, _three: &str, _four: bool, _five: f32, _six: f32, _seven: bool, _eight: ()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: this public function dereferences a raw pointer but is not marked `unsafe` +error: this public function might dereference a raw pointer but is not marked `unsafe` --> $DIR/functions.rs:63:34 | LL | println!("{}", unsafe { *p }); @@ -38,49 +38,49 @@ LL | println!("{}", unsafe { *p }); | = note: `-D clippy::not-unsafe-ptr-arg-deref` implied by `-D warnings` -error: this public function dereferences a raw pointer but is not marked `unsafe` +error: this public function might dereference a raw pointer but is not marked `unsafe` --> $DIR/functions.rs:64:35 | LL | println!("{:?}", unsafe { p.as_ref() }); | ^ -error: this public function dereferences a raw pointer but is not marked `unsafe` +error: this public function might dereference a raw pointer but is not marked `unsafe` --> $DIR/functions.rs:65:33 | LL | unsafe { std::ptr::read(p) }; | ^ -error: this public function dereferences a raw pointer but is not marked `unsafe` +error: this public function might dereference a raw pointer but is not marked `unsafe` --> $DIR/functions.rs:76:30 | LL | println!("{}", unsafe { *p }); | ^ -error: this public function dereferences a raw pointer but is not marked `unsafe` +error: this public function might dereference a raw pointer but is not marked `unsafe` --> $DIR/functions.rs:77:31 | LL | println!("{:?}", unsafe { p.as_ref() }); | ^ -error: this public function dereferences a raw pointer but is not marked `unsafe` +error: this public function might dereference a raw pointer but is not marked `unsafe` --> $DIR/functions.rs:78:29 | LL | unsafe { std::ptr::read(p) }; | ^ -error: this public function dereferences a raw pointer but is not marked `unsafe` +error: this public function might dereference a raw pointer but is not marked `unsafe` --> $DIR/functions.rs:87:34 | LL | println!("{}", unsafe { *p }); | ^ -error: this public function dereferences a raw pointer but is not marked `unsafe` +error: this public function might dereference a raw pointer but is not marked `unsafe` --> $DIR/functions.rs:88:35 | LL | println!("{:?}", unsafe { p.as_ref() }); | ^ -error: this public function dereferences a raw pointer but is not marked `unsafe` +error: this public function might dereference a raw pointer but is not marked `unsafe` --> $DIR/functions.rs:89:33 | LL | unsafe { std::ptr::read(p) }; -- cgit 1.4.1-3-g733a5 From 97311f0906ca89656f5942b326a665fe98d84c17 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 22 May 2021 17:08:17 -0400 Subject: Add lint `manual_str_repeat` --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 3 + clippy_lints/src/mem_discriminant.rs | 3 +- clippy_lints/src/methods/clone_on_copy.rs | 5 +- clippy_lints/src/methods/manual_str_repeat.rs | 89 +++++++++++++++++++++++++++ clippy_lints/src/methods/mod.rs | 35 ++++++++++- clippy_utils/src/msrvs.rs | 1 + tests/ui/manual_str_repeat.fixed | 31 ++++++++++ tests/ui/manual_str_repeat.rs | 31 ++++++++++ tests/ui/manual_str_repeat.stderr | 46 ++++++++++++++ 10 files changed, 238 insertions(+), 7 deletions(-) create mode 100644 clippy_lints/src/methods/manual_str_repeat.rs create mode 100644 tests/ui/manual_str_repeat.fixed create mode 100644 tests/ui/manual_str_repeat.rs create mode 100644 tests/ui/manual_str_repeat.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 59daa074282..41af8e190dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2493,6 +2493,7 @@ Released 2018-09-13 [`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or [`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains [`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic +[`manual_str_repeat`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat [`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip [`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap [`manual_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 0e815be9bd5..e7dd3952b3a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -762,6 +762,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: methods::MANUAL_FILTER_MAP, methods::MANUAL_FIND_MAP, methods::MANUAL_SATURATING_ARITHMETIC, + methods::MANUAL_STR_REPEAT, methods::MAP_COLLECT_RESULT_UNIT, methods::MAP_FLATTEN, methods::MAP_UNWRAP_OR, @@ -1298,6 +1299,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(methods::MANUAL_FILTER_MAP), LintId::of(methods::MANUAL_FIND_MAP), LintId::of(methods::MANUAL_SATURATING_ARITHMETIC), + LintId::of(methods::MANUAL_STR_REPEAT), LintId::of(methods::MAP_COLLECT_RESULT_UNIT), LintId::of(methods::NEW_RET_NO_SELF), LintId::of(methods::OK_EXPECT), @@ -1735,6 +1737,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(loops::NEEDLESS_COLLECT), LintId::of(methods::EXPECT_FUN_CALL), 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), diff --git a/clippy_lints/src/mem_discriminant.rs b/clippy_lints/src/mem_discriminant.rs index a735c616f6e..aca96e06ef2 100644 --- a/clippy_lints/src/mem_discriminant.rs +++ b/clippy_lints/src/mem_discriminant.rs @@ -7,7 +7,6 @@ use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use std::iter; declare_clippy_lint! { /// **What it does:** Checks for calls of `mem::discriminant()` on a non-enum type. @@ -67,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for MemDiscriminant { } } - let derefs: String = iter::repeat('*').take(derefs_needed).collect(); + let derefs = "*".repeat(derefs_needed); diag.span_suggestion( param.span, "try dereferencing", diff --git a/clippy_lints/src/methods/clone_on_copy.rs b/clippy_lints/src/methods/clone_on_copy.rs index ce2e8fa8b10..1a32af5dc7a 100644 --- a/clippy_lints/src/methods/clone_on_copy.rs +++ b/clippy_lints/src/methods/clone_on_copy.rs @@ -8,7 +8,6 @@ use rustc_hir::{BindingAnnotation, Expr, ExprKind, MatchSource, Node, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, adjustment::Adjust}; use rustc_span::symbol::{sym, Symbol}; -use std::iter; use super::CLONE_DOUBLE_REF; use super::CLONE_ON_COPY; @@ -54,8 +53,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, ty = inner; n += 1; } - let refs: String = iter::repeat('&').take(n + 1).collect(); - let derefs: String = iter::repeat('*').take(n).collect(); + let refs = "&".repeat(n + 1); + let derefs = "*".repeat(n); let explicit = format!("<{}{}>::clone({})", refs, ty, snip); diag.span_suggestion( expr.span, diff --git a/clippy_lints/src/methods/manual_str_repeat.rs b/clippy_lints/src/methods/manual_str_repeat.rs new file mode 100644 index 00000000000..3f28412fbf7 --- /dev/null +++ b/clippy_lints/src/methods/manual_str_repeat.rs @@ -0,0 +1,89 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_context; +use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item, match_type}; +use clippy_utils::{is_expr_path_def_path, paths}; +use if_chain::if_chain; +use rustc_ast::util::parser::PREC_POSTFIX; +use rustc_ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, LangItem}; +use rustc_lint::LateContext; +use rustc_span::symbol::{sym, Symbol}; + +use super::MANUAL_STR_REPEAT; + +enum RepeatKind { + Str, + String, + Char, +} + +fn parse_repeat_arg(cx: &LateContext<'_>, e: &Expr<'_>) -> Option { + if let ExprKind::Lit(lit) = &e.kind { + match lit.node { + LitKind::Str(..) => Some(RepeatKind::Str), + LitKind::Char(_) => Some(RepeatKind::Char), + _ => None, + } + } else { + let ty = cx.typeck_results().expr_ty(e); + if is_type_diagnostic_item(cx, ty, sym::string_type) + || is_type_lang_item(cx, ty, LangItem::OwnedBox) + || match_type(cx, ty, &paths::COW) + { + Some(RepeatKind::String) + } else { + let ty = ty.peel_refs(); + (ty.is_str() || is_type_diagnostic_item(cx, ty, sym::string_type)).then(|| RepeatKind::Str) + } + } +} + +pub(super) fn check( + cx: &LateContext<'_>, + collect_expr: &Expr<'_>, + take_expr: &Expr<'_>, + take_self_arg: &Expr<'_>, + take_arg: &Expr<'_>, +) { + if_chain! { + if let ExprKind::Call(repeat_fn, [repeat_arg]) = take_self_arg.kind; + if is_expr_path_def_path(cx, repeat_fn, &paths::ITER_REPEAT); + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(collect_expr), sym::string_type); + if let Some(collect_id) = cx.typeck_results().type_dependent_def_id(collect_expr.hir_id); + if let Some(take_id) = cx.typeck_results().type_dependent_def_id(take_expr.hir_id); + if let Some(iter_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator); + if cx.tcx.trait_of_item(collect_id) == Some(iter_trait_id); + if cx.tcx.trait_of_item(take_id) == Some(iter_trait_id); + if let Some(repeat_kind) = parse_repeat_arg(cx, repeat_arg); + let ctxt = collect_expr.span.ctxt(); + if ctxt == take_expr.span.ctxt(); + if ctxt == take_self_arg.span.ctxt(); + then { + let mut app = Applicability::MachineApplicable; + let (val_snip, val_is_mac) = snippet_with_context(cx, repeat_arg.span, ctxt, "..", &mut app); + let count_snip = snippet_with_context(cx, take_arg.span, ctxt, "..", &mut app).0; + + let val_str = match repeat_kind { + RepeatKind::String => format!("(&{})", val_snip), + RepeatKind::Str if !val_is_mac && repeat_arg.precedence().order() < PREC_POSTFIX => { + format!("({})", val_snip) + }, + RepeatKind::Str => val_snip.into(), + RepeatKind::Char if val_snip == r#"'"'"# => r#""\"""#.into(), + RepeatKind::Char if val_snip == r#"'\''"# => r#""'""#.into(), + RepeatKind::Char => format!("\"{}\"", &val_snip[1..val_snip.len() - 1]), + }; + + span_lint_and_sugg( + cx, + MANUAL_STR_REPEAT, + collect_expr.span, + "manual implementation of `str::repeat` using iterators", + "try this", + format!("{}.repeat({})", val_str, count_snip), + app + ) + } + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index a6e2e0baadb..62a56434b3c 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -32,6 +32,7 @@ mod iter_nth_zero; mod iter_skip_next; mod iterator_step_by_zero; mod manual_saturating_arithmetic; +mod manual_str_repeat; mod map_collect_result_unit; mod map_flatten; mod map_unwrap_or; @@ -60,9 +61,12 @@ mod wrong_self_convention; mod zst_offset; use bind_instead_of_map::BindInsteadOfMap; -use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::ty::{contains_adt_constructor, contains_ty, implements_trait, is_copy, is_type_diagnostic_item}; use clippy_utils::{contains_return, get_trait_def_id, in_macro, iter_input_pats, paths, return_ty}; +use clippy_utils::{ + diagnostics::{span_lint, span_lint_and_help}, + meets_msrv, msrvs, +}; use if_chain::if_chain; use rustc_hir as hir; use rustc_hir::def::Res; @@ -1664,6 +1668,27 @@ declare_clippy_lint! { "checks for `.splitn(0, ..)` and `.splitn(1, ..)`" } +declare_clippy_lint! { + /// **What it does:** Checks for manual implementations of `str::repeat` + /// + /// **Why is this bad?** These are both harder to read, as well as less performant. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // Bad + /// let x: String = std::iter::repeat('x').take(10).collect(); + /// + /// // Good + /// let x: String = "x".repeat(10); + /// ``` + pub MANUAL_STR_REPEAT, + perf, + "manual implementation of `str::repeat`" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Option, @@ -1737,7 +1762,8 @@ impl_lint_pass!(Methods => [ FROM_ITER_INSTEAD_OF_COLLECT, INSPECT_FOR_EACH, IMPLICIT_CLONE, - SUSPICIOUS_SPLITN + SUSPICIOUS_SPLITN, + MANUAL_STR_REPEAT ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -1981,6 +2007,11 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio Some(("map", [m_recv, m_arg], _)) => { map_collect_result_unit::check(cx, expr, m_recv, m_arg, recv); }, + Some(("take", [take_self_arg, take_arg], _)) => { + if meets_msrv(msrv, &msrvs::STR_REPEAT) { + manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg); + } + }, _ => {}, }, ("count", []) => match method_call!(recv) { diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 00df04c0144..4a9c4fd0276 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -26,4 +26,5 @@ msrv_aliases! { 1,34,0 { TRY_FROM } 1,30,0 { ITERATOR_FIND_MAP } 1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST } + 1,16,0 { STR_REPEAT } } diff --git a/tests/ui/manual_str_repeat.fixed b/tests/ui/manual_str_repeat.fixed new file mode 100644 index 00000000000..62225e7a7f8 --- /dev/null +++ b/tests/ui/manual_str_repeat.fixed @@ -0,0 +1,31 @@ +// run-rustfix + +#![warn(clippy::manual_str_repeat)] + +use std::iter::repeat; + +fn main() { + let _: String = "test".repeat(10); + let _: String = "x".repeat(10); + let _: String = "'".repeat(10); + let _: String = "\"".repeat(10); + + let x = "test"; + let count = 10; + let _ = x.repeat(count + 2); + + macro_rules! m { + ($e:expr) => {{ $e }}; + } + + let _: String = m!("test").repeat(m!(count)); + + let x = &x; + let _: String = (*x).repeat(count); + + macro_rules! repeat_m { + ($e:expr) => {{ repeat($e) }}; + } + // Don't lint, repeat is from a macro. + let _: String = repeat_m!("test").take(count).collect(); +} diff --git a/tests/ui/manual_str_repeat.rs b/tests/ui/manual_str_repeat.rs new file mode 100644 index 00000000000..1acd66da275 --- /dev/null +++ b/tests/ui/manual_str_repeat.rs @@ -0,0 +1,31 @@ +// run-rustfix + +#![warn(clippy::manual_str_repeat)] + +use std::iter::repeat; + +fn main() { + let _: String = std::iter::repeat("test").take(10).collect(); + let _: String = std::iter::repeat('x').take(10).collect(); + let _: String = std::iter::repeat('\'').take(10).collect(); + let _: String = std::iter::repeat('"').take(10).collect(); + + let x = "test"; + let count = 10; + let _ = repeat(x).take(count + 2).collect::(); + + macro_rules! m { + ($e:expr) => {{ $e }}; + } + + let _: String = repeat(m!("test")).take(m!(count)).collect(); + + let x = &x; + let _: String = repeat(*x).take(count).collect(); + + macro_rules! repeat_m { + ($e:expr) => {{ repeat($e) }}; + } + // Don't lint, repeat is from a macro. + let _: String = repeat_m!("test").take(count).collect(); +} diff --git a/tests/ui/manual_str_repeat.stderr b/tests/ui/manual_str_repeat.stderr new file mode 100644 index 00000000000..ef67ad2a1fd --- /dev/null +++ b/tests/ui/manual_str_repeat.stderr @@ -0,0 +1,46 @@ +error: manual implementation of `str::repeat` using iterators + --> $DIR/manual_str_repeat.rs:8:21 + | +LL | let _: String = std::iter::repeat("test").take(10).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"test".repeat(10)` + | + = note: `-D clippy::manual-str-repeat` implied by `-D warnings` + +error: manual implementation of `str::repeat` using iterators + --> $DIR/manual_str_repeat.rs:9:21 + | +LL | let _: String = std::iter::repeat('x').take(10).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"x".repeat(10)` + +error: manual implementation of `str::repeat` using iterators + --> $DIR/manual_str_repeat.rs:10:21 + | +LL | let _: String = std::iter::repeat('/'').take(10).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"'".repeat(10)` + +error: manual implementation of `str::repeat` using iterators + --> $DIR/manual_str_repeat.rs:11:21 + | +LL | let _: String = std::iter::repeat('"').take(10).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"/"".repeat(10)` + +error: manual implementation of `str::repeat` using iterators + --> $DIR/manual_str_repeat.rs:15:13 + | +LL | let _ = repeat(x).take(count + 2).collect::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.repeat(count + 2)` + +error: manual implementation of `str::repeat` using iterators + --> $DIR/manual_str_repeat.rs:21:21 + | +LL | let _: String = repeat(m!("test")).take(m!(count)).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `m!("test").repeat(m!(count))` + +error: manual implementation of `str::repeat` using iterators + --> $DIR/manual_str_repeat.rs:24:21 + | +LL | let _: String = repeat(*x).take(count).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(*x).repeat(count)` + +error: aborting due to 7 previous errors + -- cgit 1.4.1-3-g733a5 From cfddf0927bd71b859bc1e749ec159285433a3849 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 23 May 2021 13:16:09 -0400 Subject: Fix type checks for `manual_str_repeat` --- clippy_lints/src/methods/manual_str_repeat.rs | 48 ++++++++++++++++----------- clippy_lints/src/methods/mod.rs | 7 ++-- clippy_lints/src/utils/conf.rs | 2 +- clippy_utils/src/sugg.rs | 27 +++++++++++++-- tests/ui/manual_str_repeat.fixed | 41 +++++++++++++++++++++-- tests/ui/manual_str_repeat.rs | 39 ++++++++++++++++++++-- tests/ui/manual_str_repeat.stderr | 42 ++++++++++++++++------- 7 files changed, 162 insertions(+), 44 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/manual_str_repeat.rs b/clippy_lints/src/methods/manual_str_repeat.rs index 3f28412fbf7..919e2628c52 100644 --- a/clippy_lints/src/methods/manual_str_repeat.rs +++ b/clippy_lints/src/methods/manual_str_repeat.rs @@ -1,40 +1,49 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_with_context; +use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; +use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item, match_type}; use clippy_utils::{is_expr_path_def_path, paths}; use if_chain::if_chain; -use rustc_ast::util::parser::PREC_POSTFIX; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::LateContext; -use rustc_span::symbol::{sym, Symbol}; +use rustc_middle::ty::{self, Ty, TyS}; +use rustc_span::symbol::sym; +use std::borrow::Cow; use super::MANUAL_STR_REPEAT; enum RepeatKind { - Str, String, - Char, + Char(char), +} + +fn get_ty_param(ty: Ty<'_>) -> Option> { + if let ty::Adt(_, subs) = ty.kind() { + subs.types().next() + } else { + None + } } fn parse_repeat_arg(cx: &LateContext<'_>, e: &Expr<'_>) -> Option { if let ExprKind::Lit(lit) = &e.kind { match lit.node { - LitKind::Str(..) => Some(RepeatKind::Str), - LitKind::Char(_) => Some(RepeatKind::Char), + LitKind::Str(..) => Some(RepeatKind::String), + LitKind::Char(c) => Some(RepeatKind::Char(c)), _ => None, } } else { let ty = cx.typeck_results().expr_ty(e); if is_type_diagnostic_item(cx, ty, sym::string_type) - || is_type_lang_item(cx, ty, LangItem::OwnedBox) - || match_type(cx, ty, &paths::COW) + || (is_type_lang_item(cx, ty, LangItem::OwnedBox) && get_ty_param(ty).map_or(false, TyS::is_str)) + || (match_type(cx, ty, &paths::COW) && get_ty_param(ty).map_or(false, TyS::is_str)) { Some(RepeatKind::String) } else { let ty = ty.peel_refs(); - (ty.is_str() || is_type_diagnostic_item(cx, ty, sym::string_type)).then(|| RepeatKind::Str) + (ty.is_str() || is_type_diagnostic_item(cx, ty, sym::string_type)).then(|| RepeatKind::String) } } } @@ -61,18 +70,19 @@ pub(super) fn check( if ctxt == take_self_arg.span.ctxt(); then { let mut app = Applicability::MachineApplicable; - let (val_snip, val_is_mac) = snippet_with_context(cx, repeat_arg.span, ctxt, "..", &mut app); let count_snip = snippet_with_context(cx, take_arg.span, ctxt, "..", &mut app).0; let val_str = match repeat_kind { - RepeatKind::String => format!("(&{})", val_snip), - RepeatKind::Str if !val_is_mac && repeat_arg.precedence().order() < PREC_POSTFIX => { - format!("({})", val_snip) - }, - RepeatKind::Str => val_snip.into(), - RepeatKind::Char if val_snip == r#"'"'"# => r#""\"""#.into(), - RepeatKind::Char if val_snip == r#"'\''"# => r#""'""#.into(), - RepeatKind::Char => format!("\"{}\"", &val_snip[1..val_snip.len() - 1]), + RepeatKind::Char(_) if repeat_arg.span.ctxt() != ctxt => return, + RepeatKind::Char('\'') => r#""'""#.into(), + RepeatKind::Char('"') => r#""\"""#.into(), + RepeatKind::Char(_) => + match snippet_with_applicability(cx, repeat_arg.span, "..", &mut app) { + Cow::Owned(s) => Cow::Owned(format!("\"{}\"", &s[1..s.len() - 1])), + s @ Cow::Borrowed(_) => s, + }, + RepeatKind::String => + Sugg::hir_with_context(cx, repeat_arg, ctxt, "..", &mut app).maybe_par().to_string().into(), }; span_lint_and_sugg( diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 62a56434b3c..c8ae972f18c 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -61,12 +61,9 @@ mod wrong_self_convention; mod zst_offset; use bind_instead_of_map::BindInsteadOfMap; +use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::ty::{contains_adt_constructor, contains_ty, implements_trait, is_copy, is_type_diagnostic_item}; -use clippy_utils::{contains_return, get_trait_def_id, in_macro, iter_input_pats, paths, return_ty}; -use clippy_utils::{ - diagnostics::{span_lint, span_lint_and_help}, - meets_msrv, msrvs, -}; +use clippy_utils::{contains_return, get_trait_def_id, in_macro, iter_input_pats, meets_msrv, msrvs, paths, return_ty}; use if_chain::if_chain; use rustc_hir as hir; use rustc_hir::def::Res; diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 1bd38dc042c..0e33ae740d9 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -124,7 +124,7 @@ macro_rules! define_Conf { define_Conf! { /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION. Suppress lints whenever the suggested change would cause breakage for other crates. (avoid_breaking_exported_api: bool = true), - /// Lint: CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE. The minimum rust version that the project supports + /// Lint: MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE. The minimum rust version that the project supports (msrv: Option = None), /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses (blacklisted_names: Vec = ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()), diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index e5a70f0beac..efc0ec50fdc 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -2,7 +2,7 @@ #![deny(clippy::missing_docs_in_private_items)] use crate::higher; -use crate::source::{snippet, snippet_opt, snippet_with_macro_callsite}; +use crate::source::{snippet, snippet_opt, snippet_with_context, snippet_with_macro_callsite}; use rustc_ast::util::parser::AssocOp; use rustc_ast::{ast, token}; use rustc_ast_pretty::pprust::token_kind_to_string; @@ -10,7 +10,7 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{EarlyContext, LateContext, LintContext}; use rustc_span::source_map::{CharPos, Span}; -use rustc_span::{BytePos, Pos}; +use rustc_span::{BytePos, Pos, SyntaxContext}; use std::borrow::Cow; use std::convert::TryInto; use std::fmt::Display; @@ -90,6 +90,29 @@ impl<'a> Sugg<'a> { Self::hir_from_snippet(expr, snippet) } + /// Same as `hir`, but first walks the span up to the given context. This will result in the + /// macro call, rather then the expansion, if the span is from a child context. If the span is + /// not from a child context, it will be used directly instead. + /// + /// e.g. Given the expression `&vec![]`, getting a snippet from the span for `vec![]` as a HIR + /// node would result in `box []`. If given the context of the address of expression, this + /// function will correctly get a snippet of `vec![]`. + pub fn hir_with_context( + cx: &LateContext<'_>, + expr: &hir::Expr<'_>, + ctxt: SyntaxContext, + default: &'a str, + applicability: &mut Applicability, + ) -> Self { + let (snippet, in_macro) = snippet_with_context(cx, expr.span, ctxt, default, applicability); + + if in_macro { + Sugg::NonParen(snippet) + } else { + Self::hir_from_snippet(expr, snippet) + } + } + /// Generate a suggestion for an expression with the given snippet. This is used by the `hir_*` /// function variants of `Sugg`, since these use different snippet functions. fn hir_from_snippet(expr: &hir::Expr<'_>, snippet: Cow<'a, str>) -> Self { diff --git a/tests/ui/manual_str_repeat.fixed b/tests/ui/manual_str_repeat.fixed index 62225e7a7f8..dc140257f32 100644 --- a/tests/ui/manual_str_repeat.fixed +++ b/tests/ui/manual_str_repeat.fixed @@ -1,8 +1,10 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![warn(clippy::manual_str_repeat)] -use std::iter::repeat; +use std::borrow::Cow; +use std::iter::{repeat, FromIterator}; fn main() { let _: String = "test".repeat(10); @@ -17,8 +19,8 @@ fn main() { macro_rules! m { ($e:expr) => {{ $e }}; } - - let _: String = m!("test").repeat(m!(count)); + // FIXME: macro args are fine + let _: String = repeat(m!("test")).take(m!(count)).collect(); let x = &x; let _: String = (*x).repeat(count); @@ -28,4 +30,37 @@ fn main() { } // Don't lint, repeat is from a macro. let _: String = repeat_m!("test").take(count).collect(); + + let x: Box = Box::from("test"); + let _: String = x.repeat(count); + + #[derive(Clone)] + struct S; + impl FromIterator> for String { + fn from_iter>>(_: T) -> Self { + Self::new() + } + } + // Don't lint, wrong box type + let _: String = repeat(Box::new(S)).take(count).collect(); + + let _: String = Cow::Borrowed("test").repeat(count); + + let x = "x".to_owned(); + let _: String = x.repeat(count); + + let x = 'x'; + // Don't lint, not char literal + let _: String = repeat(x).take(count).collect(); +} + +fn _msrv_1_15() { + #![clippy::msrv = "1.15"] + // `str::repeat` was stabilized in 1.16. Do not lint this + let _: String = std::iter::repeat("test").take(10).collect(); +} + +fn _msrv_1_16() { + #![clippy::msrv = "1.16"] + let _: String = "test".repeat(10); } diff --git a/tests/ui/manual_str_repeat.rs b/tests/ui/manual_str_repeat.rs index 1acd66da275..0d69c989b2e 100644 --- a/tests/ui/manual_str_repeat.rs +++ b/tests/ui/manual_str_repeat.rs @@ -1,8 +1,10 @@ // run-rustfix +#![feature(custom_inner_attributes)] #![warn(clippy::manual_str_repeat)] -use std::iter::repeat; +use std::borrow::Cow; +use std::iter::{repeat, FromIterator}; fn main() { let _: String = std::iter::repeat("test").take(10).collect(); @@ -17,7 +19,7 @@ fn main() { macro_rules! m { ($e:expr) => {{ $e }}; } - + // FIXME: macro args are fine let _: String = repeat(m!("test")).take(m!(count)).collect(); let x = &x; @@ -28,4 +30,37 @@ fn main() { } // Don't lint, repeat is from a macro. let _: String = repeat_m!("test").take(count).collect(); + + let x: Box = Box::from("test"); + let _: String = repeat(x).take(count).collect(); + + #[derive(Clone)] + struct S; + impl FromIterator> for String { + fn from_iter>>(_: T) -> Self { + Self::new() + } + } + // Don't lint, wrong box type + let _: String = repeat(Box::new(S)).take(count).collect(); + + let _: String = repeat(Cow::Borrowed("test")).take(count).collect(); + + let x = "x".to_owned(); + let _: String = repeat(x).take(count).collect(); + + let x = 'x'; + // Don't lint, not char literal + let _: String = repeat(x).take(count).collect(); +} + +fn _msrv_1_15() { + #![clippy::msrv = "1.15"] + // `str::repeat` was stabilized in 1.16. Do not lint this + let _: String = std::iter::repeat("test").take(10).collect(); +} + +fn _msrv_1_16() { + #![clippy::msrv = "1.16"] + let _: String = std::iter::repeat("test").take(10).collect(); } diff --git a/tests/ui/manual_str_repeat.stderr b/tests/ui/manual_str_repeat.stderr index ef67ad2a1fd..c6511689716 100644 --- a/tests/ui/manual_str_repeat.stderr +++ b/tests/ui/manual_str_repeat.stderr @@ -1,5 +1,5 @@ error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:8:21 + --> $DIR/manual_str_repeat.rs:10:21 | LL | let _: String = std::iter::repeat("test").take(10).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"test".repeat(10)` @@ -7,40 +7,58 @@ LL | let _: String = std::iter::repeat("test").take(10).collect(); = note: `-D clippy::manual-str-repeat` implied by `-D warnings` error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:9:21 + --> $DIR/manual_str_repeat.rs:11:21 | LL | let _: String = std::iter::repeat('x').take(10).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"x".repeat(10)` error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:10:21 + --> $DIR/manual_str_repeat.rs:12:21 | LL | let _: String = std::iter::repeat('/'').take(10).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"'".repeat(10)` error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:11:21 + --> $DIR/manual_str_repeat.rs:13:21 | LL | let _: String = std::iter::repeat('"').take(10).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"/"".repeat(10)` error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:15:13 + --> $DIR/manual_str_repeat.rs:17:13 | LL | let _ = repeat(x).take(count + 2).collect::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.repeat(count + 2)` error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:21:21 + --> $DIR/manual_str_repeat.rs:26:21 | -LL | let _: String = repeat(m!("test")).take(m!(count)).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `m!("test").repeat(m!(count))` +LL | let _: String = repeat(*x).take(count).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(*x).repeat(count)` error: manual implementation of `str::repeat` using iterators - --> $DIR/manual_str_repeat.rs:24:21 + --> $DIR/manual_str_repeat.rs:35:21 | -LL | let _: String = repeat(*x).take(count).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(*x).repeat(count)` +LL | let _: String = repeat(x).take(count).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.repeat(count)` + +error: manual implementation of `str::repeat` using iterators + --> $DIR/manual_str_repeat.rs:47:21 + | +LL | let _: String = repeat(Cow::Borrowed("test")).take(count).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Cow::Borrowed("test").repeat(count)` + +error: manual implementation of `str::repeat` using iterators + --> $DIR/manual_str_repeat.rs:50:21 + | +LL | let _: String = repeat(x).take(count).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `x.repeat(count)` + +error: manual implementation of `str::repeat` using iterators + --> $DIR/manual_str_repeat.rs:65:21 + | +LL | let _: String = std::iter::repeat("test").take(10).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"test".repeat(10)` -error: aborting due to 7 previous errors +error: aborting due to 10 previous errors -- cgit 1.4.1-3-g733a5 From c0f3c2fe277102f22efc21ce64dc3e3423b777d4 Mon Sep 17 00:00:00 2001 From: lyj Date: Thu, 3 Jun 2021 14:56:34 +0800 Subject: correct lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 2 ++ clippy_lints/src/types/mod.rs | 40 ++++++++++++++++++++++++++++++++++---- clippy_lints/src/types/rc_mutex.rs | 16 +++++++++------ tests/ui/rc_mutex.fixed | 28 ++++++++++++++++++++++++++ tests/ui/rc_mutex.rs | 28 ++++++++++++++++++++++++++ tests/ui/rc_mutex.stderr | 22 +++++++++++++++++++++ 7 files changed, 127 insertions(+), 10 deletions(-) create mode 100644 tests/ui/rc_mutex.fixed create mode 100644 tests/ui/rc_mutex.rs create mode 100644 tests/ui/rc_mutex.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 41af8e190dd..4fb2ce8dfc8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2611,6 +2611,7 @@ Released 2018-09-13 [`range_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_step_by_zero [`range_zip_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_zip_with_len [`rc_buffer`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer +[`rc_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex [`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation [`redundant_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone [`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index e7dd3952b3a..2b999da15ca 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -931,6 +931,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: types::LINKEDLIST, types::OPTION_OPTION, types::RC_BUFFER, + types::RC_MUTEX, types::REDUNDANT_ALLOCATION, types::TYPE_COMPLEXITY, types::VEC_BOX, @@ -1027,6 +1028,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index acf00825b7a..07dec2de827 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -3,11 +3,11 @@ mod box_vec; mod linked_list; mod option_option; mod rc_buffer; +mod rc_mutex; mod redundant_allocation; mod type_complexity; mod utils; mod vec_box; -mod rc_mutex; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; @@ -252,10 +252,42 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// TODO + /// **What it does:** Checks for `Rc>`. + /// + /// **Why is this bad?** `Rc>` may introduce a deadlock in single thread. Consider + /// using `Rc>` instead. + /// ```rust + /// fn main() { + /// use std::rc::Rc; + /// use std::sync::Mutex; + /// + /// let a: Rc> = Rc::new(Mutex::new(1)); + /// let a_clone = a.clone(); + /// let mut data = a.lock().unwrap(); + /// println!("{:?}", *a_clone.lock().unwrap()); + /// *data = 10; + /// } + /// ``` + /// + /// **Known problems:** `Rc>` may panic in runtime. + /// + /// **Example:** + /// ```rust,ignore + /// use std::rc::Rc; + /// use std::sync::Mutex; + /// fn foo(interned: Rc>) { ... } + /// ``` + /// + /// Better: + /// + /// ```rust,ignore + /// use std::rc::Rc; + /// use std::cell::RefCell + /// fn foo(interned: Rc>) { ... } + /// ``` pub RC_MUTEX, restriction, - "usage of Mutex inside Rc" + "usage of `Rc>`" } pub struct Types { @@ -263,7 +295,7 @@ pub struct Types { type_complexity_threshold: u64, } -impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION, RC_BUFFER, TYPE_COMPLEXITY,RC_MUTEX]); +impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION, RC_BUFFER, RC_MUTEX, TYPE_COMPLEXITY]); impl<'tcx> LateLintPass<'tcx> for Types { fn check_fn(&mut self, cx: &LateContext<'_>, _: FnKind<'_>, decl: &FnDecl<'_>, _: &Body<'_>, _: Span, id: HirId) { diff --git a/clippy_lints/src/types/rc_mutex.rs b/clippy_lints/src/types/rc_mutex.rs index b53b55fd01c..122df01d153 100644 --- a/clippy_lints/src/types/rc_mutex.rs +++ b/clippy_lints/src/types/rc_mutex.rs @@ -1,20 +1,24 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{ get_qpath_generic_tys,is_ty_param_diagnostic_item}; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{get_qpath_generic_tys, is_ty_param_diagnostic_item}; +use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{self as hir, def_id::DefId, QPath}; +use rustc_hir::{self as hir, def_id::DefId, QPath, TyKind}; use rustc_lint::LateContext; use rustc_span::symbol::sym; -// use rustc_middle::ty::Adt; use super::RC_MUTEX; pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { - if cx.tcx.is_diagnostic_item(sym::Rc, def_id) { - if let Some(ty) = is_ty_param_diagnostic_item(cx, qpath, sym!(mutex_type)) { + if_chain! { + if cx.tcx.is_diagnostic_item(sym::Rc, def_id) ; + if let Some(ty) = is_ty_param_diagnostic_item(cx, qpath, sym!(mutex_type)) ; + if let TyKind::Path(ref qpath_inner)=ty.kind; + + then{ let mut applicability = Applicability::MachineApplicable; - let inner_span = match get_qpath_generic_tys(qpath).skip(1).next() { + let inner_span = match get_qpath_generic_tys(qpath_inner).next() { Some(ty) => ty.span, None => return false, }; diff --git a/tests/ui/rc_mutex.fixed b/tests/ui/rc_mutex.fixed new file mode 100644 index 00000000000..b36b1d0914b --- /dev/null +++ b/tests/ui/rc_mutex.fixed @@ -0,0 +1,28 @@ +// run-rustfix +#![warn(clippy::rc_mutex)] +#![allow(unused_imports)] +#![allow(clippy::boxed_local, clippy::needless_pass_by_value)] +#![allow(clippy::blacklisted_name, unused_variables, dead_code)] + +use std::cell::RefCell; +use std::rc::Rc; +use std::sync::Mutex; + +pub struct MyStruct {} + +pub struct SubT { + foo: T, +} + +pub enum MyEnum { + One, + Two, +} + +pub fn test1(foo: Rc>) {} + +pub fn test2(foo: Rc>) {} + +pub fn test3(foo: Rc>>) {} + +fn main() {} diff --git a/tests/ui/rc_mutex.rs b/tests/ui/rc_mutex.rs new file mode 100644 index 00000000000..e6ec4549de9 --- /dev/null +++ b/tests/ui/rc_mutex.rs @@ -0,0 +1,28 @@ +// run-rustfix +#![warn(clippy::rc_mutex)] +#![allow(unused_imports)] +#![allow(clippy::boxed_local, clippy::needless_pass_by_value)] +#![allow(clippy::blacklisted_name, unused_variables, dead_code)] + +use std::cell::RefCell; +use std::rc::Rc; +use std::sync::Mutex; + +pub struct MyStruct {} + +pub struct SubT { + foo: T, +} + +pub enum MyEnum { + One, + Two, +} + +pub fn test1(foo: Rc>) {} + +pub fn test2(foo: Rc>) {} + +pub fn test3(foo: Rc>>) {} + +fn main() {} diff --git a/tests/ui/rc_mutex.stderr b/tests/ui/rc_mutex.stderr new file mode 100644 index 00000000000..ad0340dcf55 --- /dev/null +++ b/tests/ui/rc_mutex.stderr @@ -0,0 +1,22 @@ +error: you seem to be trying to use `Rc>`. Consider using `Rc>` + --> $DIR/rc_mutex.rs:22:22 + | +LL | pub fn test1(foo: Rc>) {} + | ^^^^^^^^^^^^ help: try: `Rc>` + | + = note: `-D clippy::rc-mutex` implied by `-D warnings` + +error: you seem to be trying to use `Rc>`. Consider using `Rc>` + --> $DIR/rc_mutex.rs:24:19 + | +LL | pub fn test2(foo: Rc>) {} + | ^^^^^^^^^^^^^^^^^ help: try: `Rc>` + +error: you seem to be trying to use `Rc>`. Consider using `Rc>` + --> $DIR/rc_mutex.rs:26:19 + | +LL | pub fn test3(foo: Rc>>) {} + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `Rc>>` + +error: aborting due to 3 previous errors + -- cgit 1.4.1-3-g733a5 From 38ab1a616631944e8cef21c9441e2ef2ffa3a594 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Fri, 4 Jun 2021 10:43:39 +0200 Subject: Add test for not linting on assert!(cfg!(..)). --- tests/ui/assertions_on_constants.rs | 3 +++ 1 file changed, 3 insertions(+) (limited to 'tests') diff --git a/tests/ui/assertions_on_constants.rs b/tests/ui/assertions_on_constants.rs index e989de65404..6617ca183a8 100644 --- a/tests/ui/assertions_on_constants.rs +++ b/tests/ui/assertions_on_constants.rs @@ -28,4 +28,7 @@ fn main() { debug_assert!(false); // #3948 assert_const!(3); assert_const!(-1); + + // Don't lint on this: + assert!(cfg!(feature = "hey") || cfg!(not(feature = "asdf"))); } -- cgit 1.4.1-3-g733a5 From a5ced1fc2bab6c23888a85b339f93dd4c6b40dac Mon Sep 17 00:00:00 2001 From: lyj Date: Sat, 5 Jun 2021 20:53:24 +0800 Subject: rc_mutex use span_lint instead of span_lint_and_sugg --- clippy_lints/src/types/rc_mutex.rs | 28 ++++++---------------------- tests/ui/rc_mutex.fixed | 28 ---------------------------- tests/ui/rc_mutex.rs | 1 - tests/ui/rc_mutex.stderr | 18 +++++++++--------- 4 files changed, 15 insertions(+), 60 deletions(-) delete mode 100644 tests/ui/rc_mutex.fixed (limited to 'tests') diff --git a/clippy_lints/src/types/rc_mutex.rs b/clippy_lints/src/types/rc_mutex.rs index 122df01d153..e8109f12324 100644 --- a/clippy_lints/src/types/rc_mutex.rs +++ b/clippy_lints/src/types/rc_mutex.rs @@ -1,9 +1,7 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{get_qpath_generic_tys, is_ty_param_diagnostic_item}; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::is_ty_param_diagnostic_item; use if_chain::if_chain; -use rustc_errors::Applicability; -use rustc_hir::{self as hir, def_id::DefId, QPath, TyKind}; +use rustc_hir::{self as hir, def_id::DefId, QPath}; use rustc_lint::LateContext; use rustc_span::symbol::sym; @@ -12,28 +10,14 @@ use super::RC_MUTEX; pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { if_chain! { if cx.tcx.is_diagnostic_item(sym::Rc, def_id) ; - if let Some(ty) = is_ty_param_diagnostic_item(cx, qpath, sym!(mutex_type)) ; - if let TyKind::Path(ref qpath_inner)=ty.kind; + if let Some(_) = is_ty_param_diagnostic_item(cx, qpath, sym!(mutex_type)) ; then{ - let mut applicability = Applicability::MachineApplicable; - - let inner_span = match get_qpath_generic_tys(qpath_inner).next() { - Some(ty) => ty.span, - None => return false, - }; - - span_lint_and_sugg( + span_lint( cx, RC_MUTEX, hir_ty.span, - "you seem to be trying to use `Rc>`. Consider using `Rc>`", - "try", - format!( - "Rc>", - snippet_with_applicability(cx, inner_span, "..", &mut applicability) - ), - applicability, + "found `Rc>`. Consider using `Rc>` instead", ); return true; } diff --git a/tests/ui/rc_mutex.fixed b/tests/ui/rc_mutex.fixed deleted file mode 100644 index b36b1d0914b..00000000000 --- a/tests/ui/rc_mutex.fixed +++ /dev/null @@ -1,28 +0,0 @@ -// run-rustfix -#![warn(clippy::rc_mutex)] -#![allow(unused_imports)] -#![allow(clippy::boxed_local, clippy::needless_pass_by_value)] -#![allow(clippy::blacklisted_name, unused_variables, dead_code)] - -use std::cell::RefCell; -use std::rc::Rc; -use std::sync::Mutex; - -pub struct MyStruct {} - -pub struct SubT { - foo: T, -} - -pub enum MyEnum { - One, - Two, -} - -pub fn test1(foo: Rc>) {} - -pub fn test2(foo: Rc>) {} - -pub fn test3(foo: Rc>>) {} - -fn main() {} diff --git a/tests/ui/rc_mutex.rs b/tests/ui/rc_mutex.rs index e6ec4549de9..922ffbf8da1 100644 --- a/tests/ui/rc_mutex.rs +++ b/tests/ui/rc_mutex.rs @@ -1,4 +1,3 @@ -// run-rustfix #![warn(clippy::rc_mutex)] #![allow(unused_imports)] #![allow(clippy::boxed_local, clippy::needless_pass_by_value)] diff --git a/tests/ui/rc_mutex.stderr b/tests/ui/rc_mutex.stderr index ad0340dcf55..efd8abacd38 100644 --- a/tests/ui/rc_mutex.stderr +++ b/tests/ui/rc_mutex.stderr @@ -1,22 +1,22 @@ -error: you seem to be trying to use `Rc>`. Consider using `Rc>` - --> $DIR/rc_mutex.rs:22:22 +error: Found `Rc>`. Consider using `Rc>` instead + --> $DIR/rc_mutex.rs:21:22 | LL | pub fn test1(foo: Rc>) {} - | ^^^^^^^^^^^^ help: try: `Rc>` + | ^^^^^^^^^^^^ | = note: `-D clippy::rc-mutex` implied by `-D warnings` -error: you seem to be trying to use `Rc>`. Consider using `Rc>` - --> $DIR/rc_mutex.rs:24:19 +error: Found `Rc>`. Consider using `Rc>` instead + --> $DIR/rc_mutex.rs:23:19 | LL | pub fn test2(foo: Rc>) {} - | ^^^^^^^^^^^^^^^^^ help: try: `Rc>` + | ^^^^^^^^^^^^^^^^^ -error: you seem to be trying to use `Rc>`. Consider using `Rc>` - --> $DIR/rc_mutex.rs:26:19 +error: Found `Rc>`. Consider using `Rc>` instead + --> $DIR/rc_mutex.rs:25:19 | LL | pub fn test3(foo: Rc>>) {} - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `Rc>>` + | ^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 3 previous errors -- cgit 1.4.1-3-g733a5 From e2ec85c6977c4c22a11d346f4f94773c527eddbf Mon Sep 17 00:00:00 2001 From: lyj Date: Sat, 5 Jun 2021 21:20:02 +0800 Subject: rc_mutex: add struct test --- tests/ui/rc_mutex.rs | 6 +++--- tests/ui/rc_mutex.stderr | 18 ++++++++++++------ 2 files changed, 15 insertions(+), 9 deletions(-) (limited to 'tests') diff --git a/tests/ui/rc_mutex.rs b/tests/ui/rc_mutex.rs index 922ffbf8da1..14a3cfd644b 100644 --- a/tests/ui/rc_mutex.rs +++ b/tests/ui/rc_mutex.rs @@ -1,13 +1,13 @@ #![warn(clippy::rc_mutex)] -#![allow(unused_imports)] #![allow(clippy::boxed_local, clippy::needless_pass_by_value)] #![allow(clippy::blacklisted_name, unused_variables, dead_code)] -use std::cell::RefCell; use std::rc::Rc; use std::sync::Mutex; -pub struct MyStruct {} +pub struct MyStruct { + foo: Rc>, +} pub struct SubT { foo: T, diff --git a/tests/ui/rc_mutex.stderr b/tests/ui/rc_mutex.stderr index efd8abacd38..ab59337a960 100644 --- a/tests/ui/rc_mutex.stderr +++ b/tests/ui/rc_mutex.stderr @@ -1,22 +1,28 @@ -error: Found `Rc>`. Consider using `Rc>` instead +error: found `Rc>`. Consider using `Rc>` instead + --> $DIR/rc_mutex.rs:9:10 + | +LL | foo: Rc>, + | ^^^^^^^^^^^^^^ + | + = note: `-D clippy::rc-mutex` implied by `-D warnings` + +error: found `Rc>`. Consider using `Rc>` instead --> $DIR/rc_mutex.rs:21:22 | LL | pub fn test1(foo: Rc>) {} | ^^^^^^^^^^^^ - | - = note: `-D clippy::rc-mutex` implied by `-D warnings` -error: Found `Rc>`. Consider using `Rc>` instead +error: found `Rc>`. Consider using `Rc>` instead --> $DIR/rc_mutex.rs:23:19 | LL | pub fn test2(foo: Rc>) {} | ^^^^^^^^^^^^^^^^^ -error: Found `Rc>`. Consider using `Rc>` instead +error: found `Rc>`. Consider using `Rc>` instead --> $DIR/rc_mutex.rs:25:19 | LL | pub fn test3(foo: Rc>>) {} | ^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From 896c19e2cf314b4d1111afb3d89722a1e2878bd3 Mon Sep 17 00:00:00 2001 From: lyj Date: Sat, 5 Jun 2021 21:28:52 +0800 Subject: rc_mutex: update doc --- clippy_lints/src/types/mod.rs | 18 +++--------------- clippy_lints/src/types/rc_mutex.rs | 2 +- tests/ui/rc_mutex.stderr | 8 ++++---- 3 files changed, 8 insertions(+), 20 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 07dec2de827..30b04e4174c 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -254,22 +254,10 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** Checks for `Rc>`. /// - /// **Why is this bad?** `Rc>` may introduce a deadlock in single thread. Consider - /// using `Rc>` instead. - /// ```rust - /// fn main() { - /// use std::rc::Rc; - /// use std::sync::Mutex; - /// - /// let a: Rc> = Rc::new(Mutex::new(1)); - /// let a_clone = a.clone(); - /// let mut data = a.lock().unwrap(); - /// println!("{:?}", *a_clone.lock().unwrap()); - /// *data = 10; - /// } - /// ``` + /// **Why is this bad?** `Rc` is used in single thread and `Mutex` is used in multi thread. + /// Consider using `Rc>` in single thread or `Arc>` in multi thread. /// - /// **Known problems:** `Rc>` may panic in runtime. + /// **Known problems:** None. /// /// **Example:** /// ```rust,ignore diff --git a/clippy_lints/src/types/rc_mutex.rs b/clippy_lints/src/types/rc_mutex.rs index e8109f12324..bd7a0ee6408 100644 --- a/clippy_lints/src/types/rc_mutex.rs +++ b/clippy_lints/src/types/rc_mutex.rs @@ -17,7 +17,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ cx, RC_MUTEX, hir_ty.span, - "found `Rc>`. Consider using `Rc>` instead", + "found `Rc>`. Consider using `Rc>` or `Arc>` instead", ); return true; } diff --git a/tests/ui/rc_mutex.stderr b/tests/ui/rc_mutex.stderr index ab59337a960..c32780acf9f 100644 --- a/tests/ui/rc_mutex.stderr +++ b/tests/ui/rc_mutex.stderr @@ -1,4 +1,4 @@ -error: found `Rc>`. Consider using `Rc>` instead +error: found `Rc>`. Consider using `Rc>` or `Arc>` instead --> $DIR/rc_mutex.rs:9:10 | LL | foo: Rc>, @@ -6,19 +6,19 @@ LL | foo: Rc>, | = note: `-D clippy::rc-mutex` implied by `-D warnings` -error: found `Rc>`. Consider using `Rc>` instead +error: found `Rc>`. Consider using `Rc>` or `Arc>` instead --> $DIR/rc_mutex.rs:21:22 | LL | pub fn test1(foo: Rc>) {} | ^^^^^^^^^^^^ -error: found `Rc>`. Consider using `Rc>` instead +error: found `Rc>`. Consider using `Rc>` or `Arc>` instead --> $DIR/rc_mutex.rs:23:19 | LL | pub fn test2(foo: Rc>) {} | ^^^^^^^^^^^^^^^^^ -error: found `Rc>`. Consider using `Rc>` instead +error: found `Rc>`. Consider using `Rc>` or `Arc>` instead --> $DIR/rc_mutex.rs:25:19 | LL | pub fn test3(foo: Rc>>) {} -- cgit 1.4.1-3-g733a5 From f877f54767008abb614f32b1196c00467cd60de4 Mon Sep 17 00:00:00 2001 From: lyj Date: Sat, 5 Jun 2021 22:42:48 +0800 Subject: rc_mutex: fix test --- tests/ui/rc_mutex.rs | 13 ++++++++++--- tests/ui/rc_mutex.stderr | 8 ++++---- 2 files changed, 14 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/tests/ui/rc_mutex.rs b/tests/ui/rc_mutex.rs index 14a3cfd644b..657a3ecf6a0 100644 --- a/tests/ui/rc_mutex.rs +++ b/tests/ui/rc_mutex.rs @@ -1,6 +1,5 @@ #![warn(clippy::rc_mutex)] -#![allow(clippy::boxed_local, clippy::needless_pass_by_value)] -#![allow(clippy::blacklisted_name, unused_variables, dead_code)] +#![allow(clippy::blacklisted_name)] use std::rc::Rc; use std::sync::Mutex; @@ -24,4 +23,12 @@ pub fn test2(foo: Rc>) {} pub fn test3(foo: Rc>>) {} -fn main() {} +fn main() { + test1(Rc::new(Mutex::new(1))); + test2(Rc::new(Mutex::new(MyEnum::One))); + test3(Rc::new(Mutex::new(SubT { foo: 1 }))); + + let _my_struct = MyStruct { + foo: Rc::new(Mutex::new(1)), + }; +} diff --git a/tests/ui/rc_mutex.stderr b/tests/ui/rc_mutex.stderr index c32780acf9f..8e58e2bc2d0 100644 --- a/tests/ui/rc_mutex.stderr +++ b/tests/ui/rc_mutex.stderr @@ -1,5 +1,5 @@ error: found `Rc>`. Consider using `Rc>` or `Arc>` instead - --> $DIR/rc_mutex.rs:9:10 + --> $DIR/rc_mutex.rs:8:10 | LL | foo: Rc>, | ^^^^^^^^^^^^^^ @@ -7,19 +7,19 @@ LL | foo: Rc>, = note: `-D clippy::rc-mutex` implied by `-D warnings` error: found `Rc>`. Consider using `Rc>` or `Arc>` instead - --> $DIR/rc_mutex.rs:21:22 + --> $DIR/rc_mutex.rs:20:22 | LL | pub fn test1(foo: Rc>) {} | ^^^^^^^^^^^^ error: found `Rc>`. Consider using `Rc>` or `Arc>` instead - --> $DIR/rc_mutex.rs:23:19 + --> $DIR/rc_mutex.rs:22:19 | LL | pub fn test2(foo: Rc>) {} | ^^^^^^^^^^^^^^^^^ error: found `Rc>`. Consider using `Rc>` or `Arc>` instead - --> $DIR/rc_mutex.rs:25:19 + --> $DIR/rc_mutex.rs:24:19 | LL | pub fn test3(foo: Rc>>) {} | ^^^^^^^^^^^^^^^^^^^^^^ -- cgit 1.4.1-3-g733a5 From 96747c1a460a241c42a9aa262eeb37b910f06760 Mon Sep 17 00:00:00 2001 From: Bastian Kersting Date: Sat, 5 Jun 2021 14:27:36 +0200 Subject: Enhance semicolon_if_nothing_returned according to #7324 --- clippy_lints/src/semicolon_if_nothing_returned.rs | 20 +++++++++-- clippy_utils/src/lib.rs | 5 +++ tests/ui/semicolon_if_nothing_returned.rs | 44 +++++++++++++++++++++++ tests/ui/semicolon_if_nothing_returned.stderr | 14 +++++++- 4 files changed, 80 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/semicolon_if_nothing_returned.rs b/clippy_lints/src/semicolon_if_nothing_returned.rs index 16e4d73851f..9e5d5b6e956 100644 --- a/clippy_lints/src/semicolon_if_nothing_returned.rs +++ b/clippy_lints/src/semicolon_if_nothing_returned.rs @@ -1,9 +1,9 @@ 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::{get_parent_expr_for_hir, in_macro, spans_on_same_line, sugg}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Block, ExprKind}; +use rustc_hir::{Block, BlockCheckMode, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -46,6 +46,22 @@ impl LateLintPass<'_> for SemicolonIfNothingReturned { if let snippet = snippet_with_macro_callsite(cx, expr.span, "}"); if !snippet.ends_with('}'); then { + // check if the block is inside a closure or an unsafe block and don't + // emit if the block is on the same line + if_chain! { + if let Some(parent) = get_parent_expr_for_hir(cx, block.hir_id); + + if !matches!(block.rules, BlockCheckMode::DefaultBlock) || + matches!(parent.kind, ExprKind::Closure(..) | ExprKind::Block(..)); + + if block.stmts.len() == 0; + + if spans_on_same_line(cx, parent.span, expr.span); + then { + return; + } + } + // filter out the desugared `for` loop if let ExprKind::DropTemps(..) = &expr.kind { return; diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 769836aaf18..be625eb2655 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -820,6 +820,11 @@ fn line_span(cx: &T, span: Span) -> Span { Span::new(line_start, span.hi(), span.ctxt()) } +/// Checks if two spans begin on the same line. +pub fn spans_on_same_line(cx: &T, left_span: Span, right_span: Span) -> bool { + line_span(cx, left_span).lo() == line_span(cx, right_span).lo() +} + /// Gets the parent node, if any. pub fn get_parent_node(tcx: TyCtxt<'_>, id: HirId) -> Option> { tcx.hir().parent_iter(id).next().map(|(_, node)| node) diff --git a/tests/ui/semicolon_if_nothing_returned.rs b/tests/ui/semicolon_if_nothing_returned.rs index 0abe2cca267..79ba7402f1f 100644 --- a/tests/ui/semicolon_if_nothing_returned.rs +++ b/tests/ui/semicolon_if_nothing_returned.rs @@ -17,6 +17,24 @@ fn basic101(x: i32) { y = x + 1 } +#[rustfmt::skip] +fn closure_error() { + let _d = || { + hello() + }; +} + +#[rustfmt::skip] +fn unsafe_checks_error() { + use std::mem::MaybeUninit; + use std::ptr; + + let mut s = MaybeUninit::::uninit(); + let _d = || unsafe { + ptr::drop_in_place(s.as_mut_ptr()) + }; +} + // this is fine fn print_sum(a: i32, b: i32) { println!("{}", a + b); @@ -53,3 +71,29 @@ fn loop_test(x: i32) { println!("{}", ext); } } + +fn closure() { + let _d = || hello(); +} + +#[rustfmt::skip] +fn closure_block() { + let _d = || { hello() }; +} + +unsafe fn some_unsafe_op() {} +unsafe fn some_other_unsafe_fn() {} + +fn do_something() { + unsafe { some_unsafe_op() }; + + unsafe { some_other_unsafe_fn() }; +} + +fn unsafe_checks() { + use std::mem::MaybeUninit; + use std::ptr; + + let mut s = MaybeUninit::::uninit(); + let _d = || unsafe { ptr::drop_in_place(s.as_mut_ptr()) }; +} diff --git a/tests/ui/semicolon_if_nothing_returned.stderr b/tests/ui/semicolon_if_nothing_returned.stderr index b73f8967538..e88ebe2ad35 100644 --- a/tests/ui/semicolon_if_nothing_returned.stderr +++ b/tests/ui/semicolon_if_nothing_returned.stderr @@ -18,5 +18,17 @@ error: consider adding a `;` to the last statement for consistent formatting LL | y = x + 1 | ^^^^^^^^^ help: add a `;` here: `y = x + 1;` -error: aborting due to 3 previous errors +error: consider adding a `;` to the last statement for consistent formatting + --> $DIR/semicolon_if_nothing_returned.rs:23:9 + | +LL | hello() + | ^^^^^^^ help: add a `;` here: `hello();` + +error: consider adding a `;` to the last statement for consistent formatting + --> $DIR/semicolon_if_nothing_returned.rs:34:9 + | +LL | ptr::drop_in_place(s.as_mut_ptr()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add a `;` here: `ptr::drop_in_place(s.as_mut_ptr());` + +error: aborting due to 5 previous errors -- cgit 1.4.1-3-g733a5 From d7a380e4b9808cd62d3774f027ecf2395ef2b286 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Sat, 5 Jun 2021 15:11:44 +0900 Subject: Fix FP in `default_numeric_fallback` with external macro expansion --- clippy_lints/src/default_numeric_fallback.rs | 4 +- tests/ui/auxiliary/macro_rules.rs | 7 ++++ tests/ui/default_numeric_fallback.rs | 23 +++++++++++ tests/ui/default_numeric_fallback.stderr | 61 ++++++++++++++++------------ 4 files changed, 69 insertions(+), 26 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index 759f7d4062d..a125376bffa 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -7,9 +7,10 @@ use rustc_hir::{ intravisit::{walk_expr, walk_stmt, NestedVisitorMap, Visitor}, Body, Expr, ExprKind, HirId, Lit, Stmt, StmtKind, }; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::{ hir::map::Map, + lint::in_external_macro, ty::{self, FloatTy, IntTy, PolyFnSig, Ty}, }; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -73,6 +74,7 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { /// Check whether a passed literal has potential to cause fallback or not. fn check_lit(&self, lit: &Lit, lit_ty: Ty<'tcx>) { if_chain! { + if !in_external_macro(self.cx.sess(), lit.span); if let Some(ty_bound) = self.ty_bounds.last(); if matches!(lit.node, LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed)); diff --git a/tests/ui/auxiliary/macro_rules.rs b/tests/ui/auxiliary/macro_rules.rs index d4470d3f407..170955e726c 100644 --- a/tests/ui/auxiliary/macro_rules.rs +++ b/tests/ui/auxiliary/macro_rules.rs @@ -106,3 +106,10 @@ macro_rules! field_reassign_with_default { } }; } + +#[macro_export] +macro_rules! default_numeric_fallback { + () => { + let x = 22; + }; +} diff --git a/tests/ui/default_numeric_fallback.rs b/tests/ui/default_numeric_fallback.rs index 43468872db0..c0625fd1b75 100644 --- a/tests/ui/default_numeric_fallback.rs +++ b/tests/ui/default_numeric_fallback.rs @@ -1,3 +1,5 @@ +// aux-build:macro_rules.rs + #![warn(clippy::default_numeric_fallback)] #![allow(unused)] #![allow(clippy::never_loop)] @@ -5,6 +7,9 @@ #![allow(clippy::unnecessary_operation)] #![allow(clippy::branches_sharing_code)] +#[macro_use] +extern crate macro_rules; + mod basic_expr { fn test() { // Should lint unsuffixed literals typed `i32`. @@ -133,4 +138,22 @@ mod method_calls { } } +mod in_macro { + macro_rules! internal_macro { + () => { + let x = 22; + }; + } + + // Should lint in internal macro. + fn internal() { + internal_macro!(); + } + + // Should NOT lint in external macro. + fn external() { + default_numeric_fallback!(); + } +} + fn main() {} diff --git a/tests/ui/default_numeric_fallback.stderr b/tests/ui/default_numeric_fallback.stderr index d1c4c8203dd..5862cd936ac 100644 --- a/tests/ui/default_numeric_fallback.stderr +++ b/tests/ui/default_numeric_fallback.stderr @@ -1,5 +1,5 @@ error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:11:17 + --> $DIR/default_numeric_fallback.rs:16:17 | LL | let x = 22; | ^^ help: consider adding suffix: `22_i32` @@ -7,142 +7,153 @@ LL | let x = 22; = note: `-D clippy::default-numeric-fallback` implied by `-D warnings` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:12:18 + --> $DIR/default_numeric_fallback.rs:17:18 | LL | let x = [1, 2, 3]; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:12:21 + --> $DIR/default_numeric_fallback.rs:17:21 | LL | let x = [1, 2, 3]; | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:12:24 + --> $DIR/default_numeric_fallback.rs:17:24 | LL | let x = [1, 2, 3]; | ^ help: consider adding suffix: `3_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:13:28 + --> $DIR/default_numeric_fallback.rs:18:28 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:13:31 + --> $DIR/default_numeric_fallback.rs:18:31 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:13:44 + --> $DIR/default_numeric_fallback.rs:18:44 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `3_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:13:47 + --> $DIR/default_numeric_fallback.rs:18:47 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `4_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:14:23 + --> $DIR/default_numeric_fallback.rs:19:23 | LL | let x = match 1 { | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:15:13 + --> $DIR/default_numeric_fallback.rs:20:13 | LL | 1 => 1, | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:15:18 + --> $DIR/default_numeric_fallback.rs:20:18 | LL | 1 => 1, | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:16:18 + --> $DIR/default_numeric_fallback.rs:21:18 | LL | _ => 2, | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:20:17 + --> $DIR/default_numeric_fallback.rs:25:17 | LL | let x = 0.12; | ^^^^ help: consider adding suffix: `0.12_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:38:21 + --> $DIR/default_numeric_fallback.rs:43:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:46:21 + --> $DIR/default_numeric_fallback.rs:51:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:52:21 + --> $DIR/default_numeric_fallback.rs:57:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:64:9 + --> $DIR/default_numeric_fallback.rs:69:9 | LL | 1 | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:70:27 + --> $DIR/default_numeric_fallback.rs:75:27 | LL | let f = || -> _ { 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:74:29 + --> $DIR/default_numeric_fallback.rs:79:29 | LL | let f = || -> i32 { 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:88:21 + --> $DIR/default_numeric_fallback.rs:93:21 | LL | generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:91:32 + --> $DIR/default_numeric_fallback.rs:96:32 | LL | let x: _ = generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:109:28 + --> $DIR/default_numeric_fallback.rs:114:28 | LL | GenericStruct { x: 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:112:36 + --> $DIR/default_numeric_fallback.rs:117:36 | LL | let _ = GenericStruct { x: 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback.rs:132:23 + --> $DIR/default_numeric_fallback.rs:137:23 | LL | s.generic_arg(1); | ^ help: consider adding suffix: `1_i32` -error: aborting due to 24 previous errors +error: default numeric fallback might occur + --> $DIR/default_numeric_fallback.rs:144:21 + | +LL | let x = 22; + | ^^ help: consider adding suffix: `22_i32` +... +LL | internal_macro!(); + | ------------------ in this macro invocation + | + = note: this error originates in the macro `internal_macro` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 25 previous errors -- cgit 1.4.1-3-g733a5 From bb3b58cfccdfbc4f95a2f03dc800aa87fd3fdd2c Mon Sep 17 00:00:00 2001 From: xFrednet Date: Mon, 7 Jun 2021 23:10:42 +0200 Subject: Reuse `is_expr_identity_function` for `flat_map_identity` --- clippy_lints/src/methods/flat_map_identity.rs | 44 +++++++-------------------- tests/ui/flat_map_identity.fixed | 5 ++- tests/ui/flat_map_identity.rs | 5 ++- tests/ui/flat_map_identity.stderr | 12 ++++++-- 4 files changed, 28 insertions(+), 38 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/flat_map_identity.rs b/clippy_lints/src/methods/flat_map_identity.rs index 25f8434cb94..6f911d79d0b 100644 --- a/clippy_lints/src/methods/flat_map_identity.rs +++ b/clippy_lints/src/methods/flat_map_identity.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{is_expr_path_def_path, is_trait_method, paths}; -use if_chain::if_chain; +use clippy_utils::{is_expr_identity_function, is_trait_method}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -15,36 +14,15 @@ pub(super) fn check<'tcx>( flat_map_arg: &'tcx hir::Expr<'_>, flat_map_span: Span, ) { - if is_trait_method(cx, expr, sym::Iterator) { - let apply_lint = |message: &str| { - span_lint_and_sugg( - cx, - FLAT_MAP_IDENTITY, - flat_map_span.with_hi(expr.span.hi()), - message, - "try", - "flatten()".to_string(), - Applicability::MachineApplicable, - ); - }; - - if_chain! { - if let hir::ExprKind::Closure(_, _, body_id, _, _) = flat_map_arg.kind; - let body = cx.tcx.hir().body(body_id); - - if let hir::PatKind::Binding(_, _, binding_ident, _) = body.params[0].pat.kind; - if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = body.value.kind; - - if path.segments.len() == 1; - if path.segments[0].ident.name == binding_ident.name; - - then { - apply_lint("called `flat_map(|x| x)` on an `Iterator`"); - } - } - - if is_expr_path_def_path(cx, flat_map_arg, &paths::CONVERT_IDENTITY) { - apply_lint("called `flat_map(std::convert::identity)` on an `Iterator`"); - } + if is_trait_method(cx, expr, sym::Iterator) && is_expr_identity_function(cx, flat_map_arg) { + span_lint_and_sugg( + cx, + FLAT_MAP_IDENTITY, + flat_map_span.with_hi(expr.span.hi()), + "use of `flat_map` with an identity function", + "try", + "flatten()".to_string(), + Applicability::MachineApplicable, + ); } } diff --git a/tests/ui/flat_map_identity.fixed b/tests/ui/flat_map_identity.fixed index dfe3bd47e13..1f4b880ef5b 100644 --- a/tests/ui/flat_map_identity.fixed +++ b/tests/ui/flat_map_identity.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![allow(unused_imports)] +#![allow(unused_imports, clippy::needless_return)] #![warn(clippy::flat_map_identity)] use std::convert; @@ -11,4 +11,7 @@ fn main() { let iterator = [[0, 1], [2, 3], [4, 5]].iter(); let _ = iterator.flatten(); + + let iterator = [[0, 1], [2, 3], [4, 5]].iter(); + let _ = iterator.flatten(); } diff --git a/tests/ui/flat_map_identity.rs b/tests/ui/flat_map_identity.rs index 393b9569255..de14a06d4e6 100644 --- a/tests/ui/flat_map_identity.rs +++ b/tests/ui/flat_map_identity.rs @@ -1,6 +1,6 @@ // run-rustfix -#![allow(unused_imports)] +#![allow(unused_imports, clippy::needless_return)] #![warn(clippy::flat_map_identity)] use std::convert; @@ -11,4 +11,7 @@ fn main() { let iterator = [[0, 1], [2, 3], [4, 5]].iter(); let _ = iterator.flat_map(convert::identity); + + let iterator = [[0, 1], [2, 3], [4, 5]].iter(); + let _ = iterator.flat_map(|x| return x); } diff --git a/tests/ui/flat_map_identity.stderr b/tests/ui/flat_map_identity.stderr index e4686ae5a54..e776c9fdf51 100644 --- a/tests/ui/flat_map_identity.stderr +++ b/tests/ui/flat_map_identity.stderr @@ -1,4 +1,4 @@ -error: called `flat_map(|x| x)` on an `Iterator` +error: use of `flat_map` with an identity function --> $DIR/flat_map_identity.rs:10:22 | LL | let _ = iterator.flat_map(|x| x); @@ -6,11 +6,17 @@ LL | let _ = iterator.flat_map(|x| x); | = note: `-D clippy::flat-map-identity` implied by `-D warnings` -error: called `flat_map(std::convert::identity)` on an `Iterator` +error: use of `flat_map` with an identity function --> $DIR/flat_map_identity.rs:13:22 | LL | let _ = iterator.flat_map(convert::identity); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()` -error: aborting due to 2 previous errors +error: use of `flat_map` with an identity function + --> $DIR/flat_map_identity.rs:16:22 + | +LL | let _ = iterator.flat_map(|x| return x); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()` + +error: aborting due to 3 previous errors -- cgit 1.4.1-3-g733a5 From 9e54ce865c67af65370e1ec3822742a22fd20dfe Mon Sep 17 00:00:00 2001 From: xFrednet Date: Mon, 7 Jun 2021 23:31:17 +0200 Subject: Reuse `is_expr_identity_function` for `filter_map_identity` --- clippy_lints/src/methods/filter_map_identity.rs | 40 +++++++------------------ tests/ui/filter_map_identity.fixed | 5 +++- tests/ui/filter_map_identity.rs | 5 +++- tests/ui/filter_map_identity.stderr | 14 ++++++--- 4 files changed, 29 insertions(+), 35 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/methods/filter_map_identity.rs b/clippy_lints/src/methods/filter_map_identity.rs index 403fe8d3546..d1b5e945dfd 100644 --- a/clippy_lints/src/methods/filter_map_identity.rs +++ b/clippy_lints/src/methods/filter_map_identity.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{is_expr_path_def_path, is_trait_method, path_to_local_id, paths}; -use if_chain::if_chain; +use clippy_utils::{is_expr_identity_function, is_trait_method}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -9,32 +8,15 @@ use rustc_span::{source_map::Span, sym}; use super::FILTER_MAP_IDENTITY; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_map_arg: &hir::Expr<'_>, filter_map_span: Span) { - if is_trait_method(cx, expr, sym::Iterator) { - let apply_lint = |message: &str| { - span_lint_and_sugg( - cx, - FILTER_MAP_IDENTITY, - filter_map_span.with_hi(expr.span.hi()), - message, - "try", - "flatten()".to_string(), - Applicability::MachineApplicable, - ); - }; - - if_chain! { - if let hir::ExprKind::Closure(_, _, body_id, _, _) = filter_map_arg.kind; - let body = cx.tcx.hir().body(body_id); - - if let hir::PatKind::Binding(_, binding_id, ..) = body.params[0].pat.kind; - if path_to_local_id(&body.value, binding_id); - then { - apply_lint("called `filter_map(|x| x)` on an `Iterator`"); - } - } - - if is_expr_path_def_path(cx, filter_map_arg, &paths::CONVERT_IDENTITY) { - apply_lint("called `filter_map(std::convert::identity)` on an `Iterator`"); - } + if is_trait_method(cx, expr, sym::Iterator) && is_expr_identity_function(cx, filter_map_arg) { + span_lint_and_sugg( + cx, + FILTER_MAP_IDENTITY, + filter_map_span.with_hi(expr.span.hi()), + "use of `filter_map` with an identity function", + "try", + "flatten()".to_string(), + Applicability::MachineApplicable, + ); } } diff --git a/tests/ui/filter_map_identity.fixed b/tests/ui/filter_map_identity.fixed index 23ce28d8e9b..a5860aa49b3 100644 --- a/tests/ui/filter_map_identity.fixed +++ b/tests/ui/filter_map_identity.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![allow(unused_imports)] +#![allow(unused_imports, clippy::needless_return)] #![warn(clippy::filter_map_identity)] fn main() { @@ -13,4 +13,7 @@ fn main() { use std::convert::identity; let iterator = vec![Some(1), None, Some(2)].into_iter(); let _ = iterator.flatten(); + + let iterator = vec![Some(1), None, Some(2)].into_iter(); + let _ = iterator.flatten(); } diff --git a/tests/ui/filter_map_identity.rs b/tests/ui/filter_map_identity.rs index e698df13eea..7e998b9cdf7 100644 --- a/tests/ui/filter_map_identity.rs +++ b/tests/ui/filter_map_identity.rs @@ -1,6 +1,6 @@ // run-rustfix -#![allow(unused_imports)] +#![allow(unused_imports, clippy::needless_return)] #![warn(clippy::filter_map_identity)] fn main() { @@ -13,4 +13,7 @@ fn main() { use std::convert::identity; let iterator = vec![Some(1), None, Some(2)].into_iter(); let _ = iterator.filter_map(identity); + + let iterator = vec![Some(1), None, Some(2)].into_iter(); + let _ = iterator.filter_map(|x| return x); } diff --git a/tests/ui/filter_map_identity.stderr b/tests/ui/filter_map_identity.stderr index 596a6320608..43c9fdca4fb 100644 --- a/tests/ui/filter_map_identity.stderr +++ b/tests/ui/filter_map_identity.stderr @@ -1,4 +1,4 @@ -error: called `filter_map(|x| x)` on an `Iterator` +error: use of `filter_map` with an identity function --> $DIR/filter_map_identity.rs:8:22 | LL | let _ = iterator.filter_map(|x| x); @@ -6,17 +6,23 @@ LL | let _ = iterator.filter_map(|x| x); | = note: `-D clippy::filter-map-identity` implied by `-D warnings` -error: called `filter_map(std::convert::identity)` on an `Iterator` +error: use of `filter_map` with an identity function --> $DIR/filter_map_identity.rs:11:22 | LL | let _ = iterator.filter_map(std::convert::identity); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()` -error: called `filter_map(std::convert::identity)` on an `Iterator` +error: use of `filter_map` with an identity function --> $DIR/filter_map_identity.rs:15:22 | LL | let _ = iterator.filter_map(identity); | ^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()` -error: aborting due to 3 previous errors +error: use of `filter_map` with an identity function + --> $DIR/filter_map_identity.rs:18:22 + | +LL | let _ = iterator.filter_map(|x| return x); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `flatten()` + +error: aborting due to 4 previous errors -- cgit 1.4.1-3-g733a5 From ea45e2a9cf13b59e6366a6cacb7f0088f9cadeda Mon Sep 17 00:00:00 2001 From: Devin Ragotzy Date: Wed, 2 Jun 2021 07:20:45 -0400 Subject: Add disallowed_types lint, this adds a field to the Conf struct Replace use of node_type -> node_type_opt, fix clippy warnings Don't walk the hir unnecessarily let the visitor do it --- CHANGELOG.md | 1 + clippy_lints/src/disallowed_type.rs | 122 +++++++++++++++++++++ clippy_lints/src/lib.rs | 5 + clippy_lints/src/utils/conf.rs | 2 + tests/ui-toml/toml_disallowed_type/clippy.toml | 9 ++ .../toml_disallowed_type/conf_disallowed_type.rs | 35 ++++++ .../conf_disallowed_type.stderr | 88 +++++++++++++++ .../toml_unknown_key/conf_unknown_key.stderr | 2 +- 8 files changed, 263 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/disallowed_type.rs create mode 100644 tests/ui-toml/toml_disallowed_type/clippy.toml create mode 100644 tests/ui-toml/toml_disallowed_type/conf_disallowed_type.rs create mode 100644 tests/ui-toml/toml_disallowed_type/conf_disallowed_type.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 41af8e190dd..13f0fc8f609 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2358,6 +2358,7 @@ Released 2018-09-13 [`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq [`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord [`disallowed_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method +[`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type [`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression [`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown [`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons diff --git a/clippy_lints/src/disallowed_type.rs b/clippy_lints/src/disallowed_type.rs new file mode 100644 index 00000000000..f8ac7d76d8f --- /dev/null +++ b/clippy_lints/src/disallowed_type.rs @@ -0,0 +1,122 @@ +use clippy_utils::diagnostics::span_lint; + +use rustc_data_structures::fx::FxHashSet; +use rustc_hir::{def::Res, Item, ItemKind, PolyTraitRef, TraitBoundModifier, Ty, TyKind, UseKind}; +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:** Denies the configured types in clippy.toml. + /// + /// **Why is this bad?** Some types are undesirable in certain contexts. + /// + /// **Known problems:** The fully qualified path must be used. This lint + /// doesn't support aliases or reexported names; be aware that many types + /// in `std` are actually reexports. + /// + /// For example, if you want to disallow `BTreeMap`, your clippy.toml + /// configuration would look like + /// `disallowed-methods = ["alloc::collections::btree::map::BTreeMap"]` and not + /// `disallowed-methods = ["std::collections::BTreeMap"]` as you might expect. + /// + /// N.B. There is no way to ban primitive types. + /// + /// **Example:** + /// + /// An example clippy.toml configuration: + /// ```toml + /// # clippy.toml + /// disallowed-methods = ["alloc::collections::btree::map::BTreeMap"] + /// ``` + /// + /// ```rust,ignore + /// use std::collections::BTreeMap; + /// // or its use + /// let x = std::collections::BTreeMap::new(); + /// ``` + /// Use instead: + /// ```rust,ignore + /// // A similar type that is allowed by the config + /// use std::collections::HashMap; + /// ``` + pub DISALLOWED_TYPE, + nursery, + "use of a disallowed type" +} +#[derive(Clone, Debug)] +pub struct DisallowedType { + disallowed: FxHashSet>, +} + +impl DisallowedType { + pub fn new(disallowed: &FxHashSet) -> Self { + Self { + disallowed: disallowed + .iter() + .map(|s| s.split("::").map(|seg| Symbol::intern(seg)).collect::>()) + .collect(), + } + } +} + +impl_lint_pass!(DisallowedType => [DISALLOWED_TYPE]); + +impl<'tcx> LateLintPass<'tcx> for DisallowedType { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if_chain! { + if let ItemKind::Use(path, UseKind::Single) = &item.kind; + if let Res::Def(_, id) = path.res; + let use_path = cx.get_def_path(id); + if let Some(name) = self.disallowed.iter().find(|path| **path == use_path); + then { + emit(cx, name, item.span,); + } + } + } + + fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) { + if_chain! { + if let TyKind::Path(path) = &ty.kind; + if let Some(did) = cx.qpath_res(path, ty.hir_id).opt_def_id(); + let use_path = cx.get_def_path(did); + if let Some(name) = self.disallowed.iter().find(|path| **path == use_path); + then { + emit(cx, name, path.span()); + } + } + } + + fn check_poly_trait_ref(&mut self, cx: &LateContext<'tcx>, poly: &'tcx PolyTraitRef<'tcx>, _: TraitBoundModifier) { + if_chain! { + if let Res::Def(_, did) = poly.trait_ref.path.res; + let use_path = cx.get_def_path(did); + if let Some(name) = self.disallowed.iter().find(|path| **path == use_path); + then { + emit(cx, name, poly.trait_ref.path.span); + } + } + } + + // TODO: if non primitive const generics are a thing + // fn check_generic_arg(&mut self, cx: &LateContext<'tcx>, arg: &'tcx GenericArg<'tcx>) { + // match arg { + // GenericArg::Const(c) => {}, + // } + // } + // fn check_generic_param(&mut self, cx: &LateContext<'tcx>, param: &'tcx GenericParam<'tcx>) { + // match param.kind { + // GenericParamKind::Const { .. } => {}, + // } + // } +} + +fn emit(cx: &LateContext<'_>, name: &[Symbol], span: Span) { + let name = name.iter().map(|s| s.to_ident_string()).collect::>().join("::"); + span_lint( + cx, + DISALLOWED_TYPE, + span, + &format!("`{}` is not allowed according to config", name), + ); +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index e7dd3952b3a..41c635d9e93 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -187,6 +187,7 @@ mod default_numeric_fallback; mod dereference; mod derive; mod disallowed_method; +mod disallowed_type; mod doc; mod double_comparison; mod double_parens; @@ -583,6 +584,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: derive::EXPL_IMPL_CLONE_ON_COPY, derive::UNSAFE_DERIVE_DESERIALIZE, disallowed_method::DISALLOWED_METHOD, + disallowed_type::DISALLOWED_TYPE, doc::DOC_MARKDOWN, doc::MISSING_ERRORS_DOC, doc::MISSING_PANICS_DOC, @@ -1761,6 +1763,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR), LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY), 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), @@ -2066,6 +2069,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || box if_then_some_else_none::IfThenSomeElseNone::new(msrv)); store.register_early_pass(|| box bool_assert_comparison::BoolAssertComparison); store.register_late_pass(|| box unused_async::UnusedAsync); + let disallowed_types = conf.disallowed_types.iter().cloned().collect::>(); + store.register_late_pass(move || box disallowed_type::DisallowedType::new(&disallowed_types)); } diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 0e33ae740d9..e7d901aa9f9 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -190,6 +190,8 @@ define_Conf! { (warn_on_all_wildcard_imports: bool = false), /// Lint: DISALLOWED_METHOD. The list of disallowed methods, written as fully qualified paths. (disallowed_methods: Vec = Vec::new()), + /// Lint: DISALLOWED_TYPE. The list of disallowed types, written as fully qualified paths. + (disallowed_types: Vec = Vec::new()), /// Lint: UNREADABLE_LITERAL. Should the fraction of a decimal be linted to include separators. (unreadable_literal_lint_fractions: bool = true), /// Lint: UPPER_CASE_ACRONYMS. Enables verbose mode. Triggers if there is more than one uppercase char next to each other diff --git a/tests/ui-toml/toml_disallowed_type/clippy.toml b/tests/ui-toml/toml_disallowed_type/clippy.toml new file mode 100644 index 00000000000..57dfdc584bb --- /dev/null +++ b/tests/ui-toml/toml_disallowed_type/clippy.toml @@ -0,0 +1,9 @@ +disallowed-types = [ + "std::collections::hash::map::HashMap", + "core::sync::atomic::AtomicU32", + "syn::ty::TypePath", + "proc_macro2::Ident", + "std::thread::Thread", + "std::time::Instant", + "std::io::Read", +] diff --git a/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.rs b/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.rs new file mode 100644 index 00000000000..567afb5aec1 --- /dev/null +++ b/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.rs @@ -0,0 +1,35 @@ +#![warn(clippy::disallowed_type)] + +extern crate quote; +extern crate syn; + +use std::sync as foo; +use std::sync::atomic::AtomicU32; +use std::time::Instant as Sneaky; + +struct HashMap; + +fn bad_return_type() -> fn() -> Sneaky { + todo!() +} + +fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) { + todo!() +} + +fn trait_obj(_: &dyn std::io::Read) { + todo!() +} + +static BAD: foo::atomic::AtomicPtr<()> = foo::atomic::AtomicPtr::new(std::ptr::null_mut()); + +#[allow(clippy::diverging_sub_expression)] +fn main() { + let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new(); + let _ = Sneaky::now(); + let _ = foo::atomic::AtomicU32::new(0); + static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1); + let _: std::collections::BTreeMap<(), syn::TypePath> = Default::default(); + let _ = syn::Ident::new("", todo!()); + let _ = HashMap; +} diff --git a/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.stderr b/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.stderr new file mode 100644 index 00000000000..e5ea23cc0bc --- /dev/null +++ b/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.stderr @@ -0,0 +1,88 @@ +error: `core::sync::atomic::AtomicU32` is not allowed according to config + --> $DIR/conf_disallowed_type.rs:7:1 + | +LL | use std::sync::atomic::AtomicU32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::disallowed-type` implied by `-D warnings` + +error: `std::time::Instant` is not allowed according to config + --> $DIR/conf_disallowed_type.rs:8:1 + | +LL | use std::time::Instant as Sneaky; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `std::time::Instant` is not allowed according to config + --> $DIR/conf_disallowed_type.rs:12:33 + | +LL | fn bad_return_type() -> fn() -> Sneaky { + | ^^^^^^ + +error: `std::time::Instant` is not allowed according to config + --> $DIR/conf_disallowed_type.rs:16:28 + | +LL | fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) { + | ^^^^^^ + +error: `core::sync::atomic::AtomicU32` is not allowed according to config + --> $DIR/conf_disallowed_type.rs:16:39 + | +LL | fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) { + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: `std::io::Read` is not allowed according to config + --> $DIR/conf_disallowed_type.rs:20:22 + | +LL | fn trait_obj(_: &dyn std::io::Read) { + | ^^^^^^^^^^^^^ + +error: `std::collections::hash::map::HashMap` is not allowed according to config + --> $DIR/conf_disallowed_type.rs:28:48 + | +LL | let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `std::collections::hash::map::HashMap` is not allowed according to config + --> $DIR/conf_disallowed_type.rs:28: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:29:13 + | +LL | let _ = Sneaky::now(); + | ^^^^^^ + +error: `core::sync::atomic::AtomicU32` is not allowed according to config + --> $DIR/conf_disallowed_type.rs:30:13 + | +LL | let _ = foo::atomic::AtomicU32::new(0); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: `core::sync::atomic::AtomicU32` is not allowed according to config + --> $DIR/conf_disallowed_type.rs:31:17 + | +LL | static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `core::sync::atomic::AtomicU32` is not allowed according to config + --> $DIR/conf_disallowed_type.rs:31:48 + | +LL | static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: `syn::ty::TypePath` is not allowed according to config + --> $DIR/conf_disallowed_type.rs:32:43 + | +LL | let _: std::collections::BTreeMap<(), syn::TypePath> = Default::default(); + | ^^^^^^^^^^^^^ + +error: `proc_macro2::Ident` is not allowed according to config + --> $DIR/conf_disallowed_type.rs:33:13 + | +LL | let _ = syn::Ident::new("", todo!()); + | ^^^^^^^^^^ + +error: aborting due to 14 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 a7be00426c4..06d70b66fda 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`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `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`, `third-party` at line 5 column 1 error: aborting due to previous error -- cgit 1.4.1-3-g733a5 From 0d3f289b66791912b976c6757a91842843b2c217 Mon Sep 17 00:00:00 2001 From: Thomas de Zeeuw Date: Wed, 9 Jun 2021 17:16:10 +0200 Subject: Add FreeBSD as identifier not needing ticks For the doc-markdown lint. --- clippy_lints/src/utils/conf.rs | 2 +- tests/ui/doc.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 0e33ae740d9..d7744c09c11 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -149,7 +149,7 @@ define_Conf! { "WebGL", "TensorFlow", "TrueType", - "iOS", "macOS", + "iOS", "macOS", "FreeBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase", diff --git a/tests/ui/doc.rs b/tests/ui/doc.rs index c946a047f1b..8afef6b23d4 100644 --- a/tests/ui/doc.rs +++ b/tests/ui/doc.rs @@ -64,7 +64,7 @@ fn test_units() { /// WebGL /// TensorFlow /// TrueType -/// iOS macOS +/// iOS macOS FreeBSD /// TeX LaTeX BibTeX BibLaTeX /// MinGW /// CamelCase (see also #2395) -- cgit 1.4.1-3-g733a5 From 44608b1857951ba82b630853da6fc6c2f40fde53 Mon Sep 17 00:00:00 2001 From: valentine-mario Date: Thu, 10 Jun 2021 09:12:06 +0100 Subject: added lint to check for full range of vector and suggest append --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 3 ++ .../src/methods/append_instead_of_extend.rs | 41 ++++++++++++++++ clippy_lints/src/methods/mod.rs | 33 ++++++++++++- tests/ui/append_instead_of_extend.fixed | 55 ++++++++++++++++++++++ tests/ui/append_instead_of_extend.rs | 55 ++++++++++++++++++++++ tests/ui/append_instead_of_extend.stderr | 22 +++++++++ 7 files changed, 208 insertions(+), 2 deletions(-) create mode 100644 clippy_lints/src/methods/append_instead_of_extend.rs create mode 100644 tests/ui/append_instead_of_extend.fixed create mode 100644 tests/ui/append_instead_of_extend.rs create mode 100644 tests/ui/append_instead_of_extend.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 13f0fc8f609..52d0e8c7cde 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2295,6 +2295,7 @@ Released 2018-09-13 [`absurd_extreme_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons [`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped +[`append_instead_of_extend`]: https://rust-lang.github.io/rust-clippy/master/index.html#append_instead_of_extend [`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant [`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions [`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 591bf68f927..5ab333f8aa1 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -730,6 +730,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: mem_replace::MEM_REPLACE_OPTION_WITH_NONE, mem_replace::MEM_REPLACE_WITH_DEFAULT, mem_replace::MEM_REPLACE_WITH_UNINIT, + methods::APPEND_INSTEAD_OF_EXTEND, methods::BIND_INSTEAD_OF_MAP, methods::BYTES_NTH, methods::CHARS_LAST_CMP, @@ -1276,6 +1277,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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::APPEND_INSTEAD_OF_EXTEND), LintId::of(methods::BIND_INSTEAD_OF_MAP), LintId::of(methods::BYTES_NTH), LintId::of(methods::CHARS_LAST_CMP), @@ -1736,6 +1738,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(large_enum_variant::LARGE_ENUM_VARIANT), LintId::of(loops::MANUAL_MEMCPY), LintId::of(loops::NEEDLESS_COLLECT), + LintId::of(methods::APPEND_INSTEAD_OF_EXTEND), LintId::of(methods::EXPECT_FUN_CALL), LintId::of(methods::ITER_NTH), LintId::of(methods::MANUAL_STR_REPEAT), diff --git a/clippy_lints/src/methods/append_instead_of_extend.rs b/clippy_lints/src/methods/append_instead_of_extend.rs new file mode 100644 index 00000000000..e39a5a1efd1 --- /dev/null +++ b/clippy_lints/src/methods/append_instead_of_extend.rs @@ -0,0 +1,41 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, LangItem}; +use rustc_lint::LateContext; +use rustc_span::symbol::sym; + +use super::APPEND_INSTEAD_OF_EXTEND; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) { + let ty = cx.typeck_results().expr_ty(recv).peel_refs(); + if_chain! { + if is_type_diagnostic_item(cx, ty, sym::vec_type); + //check source object + if let ExprKind::MethodCall(src_method, _, [drain_vec, drain_arg], _) = &arg.kind; + if src_method.ident.as_str() == "drain"; + if let src_ty = cx.typeck_results().expr_ty(drain_vec).peel_refs(); + if is_type_diagnostic_item(cx, src_ty, sym::vec_type); + //check drain range + if let src_ty_range = cx.typeck_results().expr_ty(drain_arg).peel_refs(); + if is_type_lang_item(cx, src_ty_range, LangItem::RangeFull); + then { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + APPEND_INSTEAD_OF_EXTEND, + expr.span, + "use of `extend` instead of `append` for adding the full range of a second vector", + "try this", + format!( + "{}.append(&mut {})", + snippet_with_applicability(cx, recv.span, "..", &mut applicability), + snippet_with_applicability(cx, drain_vec.span, "..", &mut applicability) + ), + applicability, + ); + } + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index d4f8cef4f37..b556dcb4dfe 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1,3 +1,4 @@ +mod append_instead_of_extend; mod bind_instead_of_map; mod bytes_nth; mod chars_cmp; @@ -1032,6 +1033,30 @@ declare_clippy_lint! { "using `.get().unwrap()` or `.get_mut().unwrap()` when using `[]` would work instead" } +declare_clippy_lint! { + /// **What it does:** Checks for occurrences where one vector gets extended instead of append + /// + /// **Why is this bad?** Using `append` instead of `extend` is more concise and faster + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let mut a = vec![1, 2, 3]; + /// let mut b = vec![4, 5, 6]; + /// + /// // Bad + /// a.extend(b.drain(..)); + /// + /// // Good + /// a.append(&mut b); + /// ``` + pub APPEND_INSTEAD_OF_EXTEND, + perf, + "using vec.append(&mut vec) to move the full range of a vecor to another" +} + declare_clippy_lint! { /// **What it does:** Checks for the use of `.extend(s.chars())` where s is a /// `&str` or `String`. @@ -1785,7 +1810,8 @@ impl_lint_pass!(Methods => [ INSPECT_FOR_EACH, IMPLICIT_CLONE, SUSPICIOUS_SPLITN, - MANUAL_STR_REPEAT + MANUAL_STR_REPEAT, + APPEND_INSTEAD_OF_EXTEND ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -2047,7 +2073,10 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv), _ => expect_used::check(cx, expr, recv), }, - ("extend", [arg]) => string_extend_chars::check(cx, expr, recv, arg), + ("extend", [arg]) => { + string_extend_chars::check(cx, expr, recv, arg); + append_instead_of_extend::check(cx, expr, recv, arg); + }, ("filter_map", [arg]) => { unnecessary_filter_map::check(cx, expr, arg); filter_map_identity::check(cx, expr, arg, span); diff --git a/tests/ui/append_instead_of_extend.fixed b/tests/ui/append_instead_of_extend.fixed new file mode 100644 index 00000000000..283358333cd --- /dev/null +++ b/tests/ui/append_instead_of_extend.fixed @@ -0,0 +1,55 @@ +// run-rustfix +#![warn(clippy::append_instead_of_extend)] +use std::collections::BinaryHeap; +fn main() { + //gets linted + let mut vec1 = vec![0u8; 1024]; + let mut vec2: std::vec::Vec = Vec::new(); + + vec2.append(&mut vec1); + + let mut vec3 = vec![0u8; 1024]; + let mut vec4: std::vec::Vec = Vec::new(); + + vec4.append(&mut vec3); + + let mut vec11: std::vec::Vec = Vec::new(); + + vec11.append(&mut return_vector()); + + //won't get linted it dosen't move the entire content of a vec into another + let mut test1 = vec![0u8, 10]; + let mut test2: std::vec::Vec = Vec::new(); + + test2.extend(test1.drain(4..10)); + + let mut vec3 = vec![0u8; 104]; + let mut vec7: std::vec::Vec = Vec::new(); + + vec3.append(&mut vec7); + + let mut vec5 = vec![0u8; 1024]; + let mut vec6: std::vec::Vec = Vec::new(); + + vec5.extend(vec6.drain(..4)); + + let mut vec9: std::vec::Vec = Vec::new(); + + return_vector().append(&mut vec9); + + //won't get linted because it is not a vec + + let mut heap = BinaryHeap::from(vec![1, 3]); + let mut heap2 = BinaryHeap::from(vec![]); + heap2.extend(heap.drain()) +} + +fn return_vector() -> Vec { + let mut new_vector = vec![]; + + for i in 1..10 { + new_vector.push(i) + } + + new_vector +} diff --git a/tests/ui/append_instead_of_extend.rs b/tests/ui/append_instead_of_extend.rs new file mode 100644 index 00000000000..abde5cdac5c --- /dev/null +++ b/tests/ui/append_instead_of_extend.rs @@ -0,0 +1,55 @@ +// run-rustfix +#![warn(clippy::append_instead_of_extend)] +use std::collections::BinaryHeap; +fn main() { + //gets linted + let mut vec1 = vec![0u8; 1024]; + let mut vec2: std::vec::Vec = Vec::new(); + + vec2.extend(vec1.drain(..)); + + let mut vec3 = vec![0u8; 1024]; + let mut vec4: std::vec::Vec = Vec::new(); + + vec4.extend(vec3.drain(..)); + + let mut vec11: std::vec::Vec = Vec::new(); + + vec11.extend(return_vector().drain(..)); + + //won't get linted it dosen't move the entire content of a vec into another + let mut test1 = vec![0u8, 10]; + let mut test2: std::vec::Vec = Vec::new(); + + test2.extend(test1.drain(4..10)); + + let mut vec3 = vec![0u8; 104]; + let mut vec7: std::vec::Vec = Vec::new(); + + vec3.append(&mut vec7); + + let mut vec5 = vec![0u8; 1024]; + let mut vec6: std::vec::Vec = Vec::new(); + + vec5.extend(vec6.drain(..4)); + + let mut vec9: std::vec::Vec = Vec::new(); + + return_vector().append(&mut vec9); + + //won't get linted because it is not a vec + + let mut heap = BinaryHeap::from(vec![1, 3]); + let mut heap2 = BinaryHeap::from(vec![]); + heap2.extend(heap.drain()) +} + +fn return_vector() -> Vec { + let mut new_vector = vec![]; + + for i in 1..10 { + new_vector.push(i) + } + + new_vector +} diff --git a/tests/ui/append_instead_of_extend.stderr b/tests/ui/append_instead_of_extend.stderr new file mode 100644 index 00000000000..9d309d981de --- /dev/null +++ b/tests/ui/append_instead_of_extend.stderr @@ -0,0 +1,22 @@ +error: use of `extend` instead of `append` for adding the full range of a second vector + --> $DIR/append_instead_of_extend.rs:9:5 + | +LL | vec2.extend(vec1.drain(..)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec2.append(&mut vec1)` + | + = note: `-D clippy::append-instead-of-extend` implied by `-D warnings` + +error: use of `extend` instead of `append` for adding the full range of a second vector + --> $DIR/append_instead_of_extend.rs:14:5 + | +LL | vec4.extend(vec3.drain(..)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec4.append(&mut vec3)` + +error: use of `extend` instead of `append` for adding the full range of a second vector + --> $DIR/append_instead_of_extend.rs:18:5 + | +LL | vec11.extend(return_vector().drain(..)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec11.append(&mut return_vector())` + +error: aborting due to 3 previous errors + -- cgit 1.4.1-3-g733a5 From 70ce0c2c55b18f7c9a4bc8d767c626f8d5948fae Mon Sep 17 00:00:00 2001 From: Devin Ragotzy Date: Fri, 11 Jun 2021 17:25:32 -0400 Subject: Remove requirement of fully qualified path for disallowed_method/type --- clippy_lints/src/disallowed_method.rs | 18 ++++++++++++--- clippy_lints/src/disallowed_type.rs | 27 +++++++++++++++------- tests/ui-toml/toml_disallowed_method/clippy.toml | 6 ++++- tests/ui-toml/toml_disallowed_type/clippy.toml | 6 ++--- .../conf_disallowed_type.stderr | 16 ++++++------- 5 files changed, 50 insertions(+), 23 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/disallowed_method.rs b/clippy_lints/src/disallowed_method.rs index ded0a0fff54..76a0cba30e5 100644 --- a/clippy_lints/src/disallowed_method.rs +++ b/clippy_lints/src/disallowed_method.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::fn_def_id; use rustc_data_structures::fx::FxHashSet; -use rustc_hir::Expr; +use rustc_hir::{def::Res, def_id::DefId, Crate, Expr}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Symbol; @@ -52,6 +52,7 @@ declare_clippy_lint! { #[derive(Clone, Debug)] pub struct DisallowedMethod { disallowed: FxHashSet>, + def_ids: FxHashSet<(DefId, Vec)>, } impl DisallowedMethod { @@ -61,6 +62,7 @@ impl DisallowedMethod { .iter() .map(|s| s.split("::").map(|seg| Symbol::intern(seg)).collect::>()) .collect(), + def_ids: FxHashSet::default(), } } } @@ -68,10 +70,20 @@ impl DisallowedMethod { impl_lint_pass!(DisallowedMethod => [DISALLOWED_METHOD]); impl<'tcx> LateLintPass<'tcx> for DisallowedMethod { + fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) { + for path in &self.disallowed { + let segs = path.iter().map(ToString::to_string).collect::>(); + if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &segs.iter().map(String::as_str).collect::>()) + { + self.def_ids.insert((id, path.clone())); + } + } + } + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let Some(def_id) = fn_def_id(cx, expr) { - let func_path = cx.get_def_path(def_id); - if self.disallowed.contains(&func_path) { + if self.def_ids.iter().any(|(id, _)| def_id == *id) { + let func_path = cx.get_def_path(def_id); let func_path_string = func_path .into_iter() .map(Symbol::to_ident_string) diff --git a/clippy_lints/src/disallowed_type.rs b/clippy_lints/src/disallowed_type.rs index f8ac7d76d8f..ab775d99b08 100644 --- a/clippy_lints/src/disallowed_type.rs +++ b/clippy_lints/src/disallowed_type.rs @@ -1,7 +1,9 @@ use clippy_utils::diagnostics::span_lint; use rustc_data_structures::fx::FxHashSet; -use rustc_hir::{def::Res, Item, ItemKind, PolyTraitRef, TraitBoundModifier, Ty, TyKind, UseKind}; +use rustc_hir::{ + def::Res, def_id::DefId, Crate, Item, ItemKind, PolyTraitRef, TraitBoundModifier, Ty, TyKind, UseKind, +}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{Span, Symbol}; @@ -47,6 +49,7 @@ declare_clippy_lint! { #[derive(Clone, Debug)] pub struct DisallowedType { disallowed: FxHashSet>, + def_ids: FxHashSet<(DefId, Vec)>, } impl DisallowedType { @@ -56,6 +59,7 @@ impl DisallowedType { .iter() .map(|s| s.split("::").map(|seg| Symbol::intern(seg)).collect::>()) .collect(), + def_ids: FxHashSet::default(), } } } @@ -63,12 +67,21 @@ impl DisallowedType { impl_lint_pass!(DisallowedType => [DISALLOWED_TYPE]); impl<'tcx> LateLintPass<'tcx> for DisallowedType { + fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) { + for path in &self.disallowed { + let segs = path.iter().map(ToString::to_string).collect::>(); + if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &segs.iter().map(String::as_str).collect::>()) + { + self.def_ids.insert((id, path.clone())); + } + } + } + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { if_chain! { if let ItemKind::Use(path, UseKind::Single) = &item.kind; - if let Res::Def(_, id) = path.res; - let use_path = cx.get_def_path(id); - if let Some(name) = self.disallowed.iter().find(|path| **path == use_path); + if let Res::Def(_, did) = path.res; + if let Some((_, name)) = self.def_ids.iter().find(|(id, _)| *id == did); then { emit(cx, name, item.span,); } @@ -79,8 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedType { if_chain! { if let TyKind::Path(path) = &ty.kind; if let Some(did) = cx.qpath_res(path, ty.hir_id).opt_def_id(); - let use_path = cx.get_def_path(did); - if let Some(name) = self.disallowed.iter().find(|path| **path == use_path); + if let Some((_, name)) = self.def_ids.iter().find(|(id, _)| *id == did); then { emit(cx, name, path.span()); } @@ -90,8 +102,7 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedType { fn check_poly_trait_ref(&mut self, cx: &LateContext<'tcx>, poly: &'tcx PolyTraitRef<'tcx>, _: TraitBoundModifier) { if_chain! { if let Res::Def(_, did) = poly.trait_ref.path.res; - let use_path = cx.get_def_path(did); - if let Some(name) = self.disallowed.iter().find(|path| **path == use_path); + if let Some((_, name)) = self.def_ids.iter().find(|(id, _)| *id == did); then { emit(cx, name, poly.trait_ref.path.span); } diff --git a/tests/ui-toml/toml_disallowed_method/clippy.toml b/tests/ui-toml/toml_disallowed_method/clippy.toml index c0df3b6e8af..a3245da6825 100644 --- a/tests/ui-toml/toml_disallowed_method/clippy.toml +++ b/tests/ui-toml/toml_disallowed_method/clippy.toml @@ -1 +1,5 @@ -disallowed-methods = ["core::iter::traits::iterator::Iterator::sum", "regex::re_unicode::Regex::is_match", "regex::re_unicode::Regex::new"] +disallowed-methods = [ + "std::iter::Iterator::sum", + "regex::Regex::is_match", + "regex::Regex::new" +] diff --git a/tests/ui-toml/toml_disallowed_type/clippy.toml b/tests/ui-toml/toml_disallowed_type/clippy.toml index 57dfdc584bb..2eff854c22c 100644 --- a/tests/ui-toml/toml_disallowed_type/clippy.toml +++ b/tests/ui-toml/toml_disallowed_type/clippy.toml @@ -1,7 +1,7 @@ disallowed-types = [ - "std::collections::hash::map::HashMap", - "core::sync::atomic::AtomicU32", - "syn::ty::TypePath", + "std::collections::HashMap", + "std::sync::atomic::AtomicU32", + "syn::TypePath", "proc_macro2::Ident", "std::thread::Thread", "std::time::Instant", 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 e5ea23cc0bc..4e6fd91fba1 100644 --- a/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.stderr +++ b/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.stderr @@ -1,4 +1,4 @@ -error: `core::sync::atomic::AtomicU32` is not allowed according to config +error: `std::sync::atomic::AtomicU32` is not allowed according to config --> $DIR/conf_disallowed_type.rs:7:1 | LL | use std::sync::atomic::AtomicU32; @@ -24,7 +24,7 @@ error: `std::time::Instant` is not allowed according to config LL | fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) { | ^^^^^^ -error: `core::sync::atomic::AtomicU32` is not allowed according to config +error: `std::sync::atomic::AtomicU32` is not allowed according to config --> $DIR/conf_disallowed_type.rs:16:39 | LL | fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) { @@ -36,13 +36,13 @@ error: `std::io::Read` is not allowed according to config LL | fn trait_obj(_: &dyn std::io::Read) { | ^^^^^^^^^^^^^ -error: `std::collections::hash::map::HashMap` is not allowed according to config +error: `std::collections::HashMap` is not allowed according to config --> $DIR/conf_disallowed_type.rs:28:48 | LL | let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: `std::collections::hash::map::HashMap` is not allowed according to config +error: `std::collections::HashMap` is not allowed according to config --> $DIR/conf_disallowed_type.rs:28:12 | LL | let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new(); @@ -54,25 +54,25 @@ error: `std::time::Instant` is not allowed according to config LL | let _ = Sneaky::now(); | ^^^^^^ -error: `core::sync::atomic::AtomicU32` is not allowed according to config +error: `std::sync::atomic::AtomicU32` is not allowed according to config --> $DIR/conf_disallowed_type.rs:30:13 | LL | let _ = foo::atomic::AtomicU32::new(0); | ^^^^^^^^^^^^^^^^^^^^^^ -error: `core::sync::atomic::AtomicU32` is not allowed according to config +error: `std::sync::atomic::AtomicU32` is not allowed according to config --> $DIR/conf_disallowed_type.rs:31:17 | LL | static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: `core::sync::atomic::AtomicU32` is not allowed according to config +error: `std::sync::atomic::AtomicU32` is not allowed according to config --> $DIR/conf_disallowed_type.rs:31:48 | LL | static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1); | ^^^^^^^^^^^^^^^^^^^^^^ -error: `syn::ty::TypePath` is not allowed according to config +error: `syn::TypePath` is not allowed according to config --> $DIR/conf_disallowed_type.rs:32:43 | LL | let _: std::collections::BTreeMap<(), syn::TypePath> = Default::default(); -- cgit 1.4.1-3-g733a5 From 723f515b6006706b88d22c8edb05330df6365a63 Mon Sep 17 00:00:00 2001 From: Devin Ragotzy Date: Sun, 30 May 2021 17:58:32 -0400 Subject: Add macro_braces lint to check for irregular brace use in certain macros Rename unconventional -> nonstandard, add config field Add standard_macro_braces fields so users can specify macro names and brace combinations to lint for in the clippy.toml file. Fix errors caused by nonstandard_macro_braces in other lint tests Fix users ability to override the default nonstandard macro braces Add type position macros impl `check_ty` --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 6 + clippy_lints/src/nonstandard_macro_braces.rs | 276 +++++++++++++++++++++ clippy_lints/src/utils/conf.rs | 16 +- tests/ui-toml/nonstandard_macro_braces/clippy.toml | 6 + .../conf_nonstandard_macro_braces.rs | 44 ++++ .../conf_nonstandard_macro_braces.stderr | 94 +++++++ .../toml_unknown_key/conf_unknown_key.stderr | 2 +- tests/ui/unnecessary_cast_fixable.fixed | 2 +- tests/ui/unnecessary_cast_fixable.rs | 2 +- tests/ui/vec.fixed | 2 +- tests/ui/vec.rs | 2 +- 12 files changed, 443 insertions(+), 10 deletions(-) create mode 100644 clippy_lints/src/nonstandard_macro_braces.rs create mode 100644 tests/ui-toml/nonstandard_macro_braces/clippy.toml create mode 100644 tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs create mode 100644 tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 13f0fc8f609..c30a0849348 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2575,6 +2575,7 @@ Released 2018-09-13 [`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions [`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 [`not_unsafe_ptr_arg_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#not_unsafe_ptr_arg_deref [`ok_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#ok_expect [`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 591bf68f927..e3fbd1adee9 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -293,6 +293,7 @@ mod no_effect; mod non_copy_const; mod non_expressive_names; mod non_octal_unix_permissions; +mod nonstandard_macro_braces; mod open_options; mod option_env_unwrap; mod option_if_let_else; @@ -844,6 +845,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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, @@ -1360,6 +1362,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES), LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS), + LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES), LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS), LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP), LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL), @@ -1536,6 +1539,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST), LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES), + LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES), LintId::of(ptr::CMP_NULL), LintId::of(ptr::PTR_ARG), LintId::of(ptr_eq::PTR_EQ), @@ -2040,6 +2044,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(move || box non_expressive_names::NonExpressiveNames { single_char_binding_names_threshold, }); + let macro_matcher = conf.standard_macro_braces.iter().cloned().collect::>(); + store.register_early_pass(move || box nonstandard_macro_braces::MacroBraces::new(¯o_matcher)); store.register_late_pass(|| box macro_use::MacroUseImports::default()); store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch); store.register_late_pass(|| box stable_sort_primitive::StableSortPrimitive); diff --git a/clippy_lints/src/nonstandard_macro_braces.rs b/clippy_lints/src/nonstandard_macro_braces.rs new file mode 100644 index 00000000000..1adad5be6dd --- /dev/null +++ b/clippy_lints/src/nonstandard_macro_braces.rs @@ -0,0 +1,276 @@ +use std::{ + fmt, + hash::{Hash, Hasher}, +}; + +use clippy_utils::{diagnostics::span_lint_and_help, in_macro, is_direct_expn_of, source::snippet_opt}; +use if_chain::if_chain; +use rustc_ast::ast; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::Span; +use serde::{de, Deserialize}; + +declare_clippy_lint! { + /// **What it does:** Checks that common macros are used with consistent bracing. + /// + /// **Why is this bad?** This is mostly a consistency lint although using () or [] + /// doesn't give you a semicolon in item position, which can be unexpected. + /// + /// **Known problems:** + /// None + /// + /// **Example:** + /// + /// ```rust + /// vec!{1, 2, 3}; + /// ``` + /// Use instead: + /// ```rust + /// vec![1, 2, 3]; + /// ``` + pub NONSTANDARD_MACRO_BRACES, + style, + "check consistent use of braces in macro" +} + +const BRACES: &[(&str, &str)] = &[("(", ")"), ("{", "}"), ("[", "]")]; + +/// The (name, (open brace, close brace), source snippet) +type MacroInfo<'a> = (&'a str, &'a (String, String), String); + +#[derive(Clone, Debug, Default)] +pub struct MacroBraces { + macro_braces: FxHashMap, + done: FxHashSet, +} + +impl MacroBraces { + pub fn new(conf: &FxHashSet) -> Self { + let macro_braces = macro_braces(conf.clone()); + Self { + macro_braces, + done: FxHashSet::default(), + } + } +} + +impl_lint_pass!(MacroBraces => [NONSTANDARD_MACRO_BRACES]); + +impl EarlyLintPass for MacroBraces { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { + if let Some((name, braces, snip)) = is_offending_macro(cx, item.span, self) { + let span = item.span.ctxt().outer_expn_data().call_site; + emit_help(cx, snip, braces, name, span); + self.done.insert(span); + } + } + + fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &ast::Stmt) { + if let Some((name, braces, snip)) = is_offending_macro(cx, stmt.span, self) { + let span = stmt.span.ctxt().outer_expn_data().call_site; + emit_help(cx, snip, braces, name, span); + self.done.insert(span); + } + } + + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { + if let Some((name, braces, snip)) = is_offending_macro(cx, expr.span, self) { + let span = expr.span.ctxt().outer_expn_data().call_site; + emit_help(cx, snip, braces, name, span); + self.done.insert(span); + } + } + + fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) { + if let Some((name, braces, snip)) = is_offending_macro(cx, ty.span, self) { + let span = ty.span.ctxt().outer_expn_data().call_site; + emit_help(cx, snip, braces, name, span); + self.done.insert(span); + } + } +} + +fn is_offending_macro<'a>(cx: &EarlyContext<'_>, span: Span, this: &'a MacroBraces) -> Option> { + if_chain! { + if in_macro(span); + if let Some((name, braces)) = find_matching_macro(span, &this.macro_braces); + if let Some(snip) = snippet_opt(cx, span.ctxt().outer_expn_data().call_site); + let c = snip.replace(" ", ""); // make formatting consistent + if !c.starts_with(&format!("{}!{}", name, braces.0)); + if !this.done.contains(&span.ctxt().outer_expn_data().call_site); + then { + Some((name, braces, snip)) + } else { + None + } + } +} + +fn emit_help(cx: &EarlyContext<'_>, snip: String, braces: &(String, String), name: &str, span: Span) { + let with_space = &format!("! {}", braces.0); + let without_space = &format!("!{}", braces.0); + let mut help = snip; + for b in BRACES.iter().filter(|b| b.0 != braces.0) { + help = help.replace(b.0, &braces.0).replace(b.1, &braces.1); + // Only `{` traditionally has space before the brace + if braces.0 != "{" && help.contains(with_space) { + help = help.replace(with_space, without_space); + } else if braces.0 == "{" && help.contains(without_space) { + help = help.replace(without_space, with_space); + } + } + span_lint_and_help( + cx, + NONSTANDARD_MACRO_BRACES, + span, + &format!("use of irregular braces for `{}!` macro", name), + Some(span), + &format!("consider writing `{}`", help), + ); +} + +fn find_matching_macro( + span: Span, + braces: &FxHashMap, +) -> Option<(&String, &(String, String))> { + braces + .iter() + .find(|(macro_name, _)| is_direct_expn_of(span, macro_name).is_some()) +} + +fn macro_braces(conf: FxHashSet) -> FxHashMap { + let mut braces = vec![ + macro_matcher!( + name: "print", + braces: ("(", ")"), + ), + macro_matcher!( + name: "println", + braces: ("(", ")"), + ), + macro_matcher!( + name: "eprint", + braces: ("(", ")"), + ), + macro_matcher!( + name: "eprintln", + braces: ("(", ")"), + ), + macro_matcher!( + name: "write", + braces: ("(", ")"), + ), + macro_matcher!( + name: "writeln", + braces: ("(", ")"), + ), + macro_matcher!( + name: "format", + braces: ("(", ")"), + ), + macro_matcher!( + name: "format_args", + braces: ("(", ")"), + ), + macro_matcher!( + name: "vec", + braces: ("[", "]"), + ), + ] + .into_iter() + .collect::>(); + // We want users items to override any existing items + for it in conf { + braces.insert(it.name, it.braces); + } + braces +} + +macro_rules! macro_matcher { + (name: $name:expr, braces: ($open:expr, $close:expr) $(,)?) => { + ($name.to_owned(), ($open.to_owned(), $close.to_owned())) + }; +} +pub(crate) use macro_matcher; + +#[derive(Clone, Debug)] +pub struct MacroMatcher { + name: String, + braces: (String, String), +} + +impl Hash for MacroMatcher { + fn hash(&self, state: &mut H) { + self.name.hash(state); + } +} + +impl PartialEq for MacroMatcher { + fn eq(&self, other: &Self) -> bool { + self.name == other.name + } +} +impl Eq for MacroMatcher {} + +impl<'de> Deserialize<'de> for MacroMatcher { + fn deserialize(deser: D) -> Result + where + D: de::Deserializer<'de>, + { + #[derive(Deserialize)] + #[serde(field_identifier, rename_all = "lowercase")] + enum Field { + Name, + Brace, + } + struct MacVisitor; + impl<'de> de::Visitor<'de> for MacVisitor { + type Value = MacroMatcher; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("struct MacroMatcher") + } + + fn visit_map(self, mut map: V) -> Result + where + V: de::MapAccess<'de>, + { + let mut name = None; + let mut brace: Option<&str> = None; + while let Some(key) = map.next_key()? { + match key { + Field::Name => { + if name.is_some() { + return Err(de::Error::duplicate_field("name")); + } + name = Some(map.next_value()?); + }, + Field::Brace => { + if brace.is_some() { + return Err(de::Error::duplicate_field("brace")); + } + brace = Some(map.next_value()?); + }, + } + } + let name = name.ok_or_else(|| de::Error::missing_field("name"))?; + let brace = brace.ok_or_else(|| de::Error::missing_field("brace"))?; + Ok(MacroMatcher { + name, + braces: BRACES + .iter() + .find(|b| b.0 == brace) + .map(|(o, c)| ((*o).to_owned(), (*c).to_owned())) + .ok_or_else(|| { + de::Error::custom(&format!("expected one of `(`, `{{`, `[` found `{}`", brace)) + })?, + }) + } + } + + const FIELDS: &[&str] = &["name", "brace"]; + deser.deserialize_struct("MacroMatcher", FIELDS, MacVisitor) + } +} diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index ad2cb27650e..cb335522f61 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -26,13 +26,13 @@ impl TryConf { macro_rules! define_Conf { ($( - #[doc = $doc:literal] + $(#[doc = $doc:literal])* $(#[conf_deprecated($dep:literal)])? ($name:ident: $ty:ty = $default:expr), )*) => { /// Clippy lint configuration pub struct Conf { - $(#[doc = $doc] pub $name: $ty,)* + $($(#[doc = $doc])* pub $name: $ty,)* } mod defaults { @@ -109,7 +109,7 @@ macro_rules! define_Conf { stringify!($name), stringify!($ty), format!("{:?}", super::defaults::$name()), - $doc, + concat!($($doc,)*), deprecation_reason, ) }, @@ -182,9 +182,9 @@ define_Conf! { (vec_box_size_threshold: u64 = 4096), /// Lint: TYPE_REPETITION_IN_BOUNDS. The maximum number of bounds a trait can have to be linted (max_trait_bounds: u64 = 3), - /// Lint: STRUCT_EXCESSIVE_BOOLS. The maximum number of bools a struct can have + /// Lint: STRUCT_EXCESSIVE_BOOLS. The maximum number of bool fields a struct can have (max_struct_bools: u64 = 3), - /// Lint: FN_PARAMS_EXCESSIVE_BOOLS. The maximum number of bools function parameters can have + /// Lint: FN_PARAMS_EXCESSIVE_BOOLS. The maximum number of bool parameters a function can have (max_fn_params_bools: u64 = 3), /// Lint: WILDCARD_IMPORTS. Whether to allow certain wildcard imports (prelude, super in tests). (warn_on_all_wildcard_imports: bool = false), @@ -198,6 +198,12 @@ define_Conf! { (upper_case_acronyms_aggressive: bool = false), /// Lint: _CARGO_COMMON_METADATA. For internal testing only, ignores the current `publish` settings in the Cargo manifest. (cargo_ignore_publish: bool = false), + /// Lint: NONSTANDARD_MACRO_BRACES. Enforce the named macros always use the braces specified. + /// + /// A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. + /// If the macro is could be used with a full path two `MacroMatcher`s have to be added one + /// with the full path `crate_name::macro_name` and one with just the macro name. + (standard_macro_braces: Vec = Vec::new()), } /// Search for the configuration file. diff --git a/tests/ui-toml/nonstandard_macro_braces/clippy.toml b/tests/ui-toml/nonstandard_macro_braces/clippy.toml new file mode 100644 index 00000000000..bced8948a02 --- /dev/null +++ b/tests/ui-toml/nonstandard_macro_braces/clippy.toml @@ -0,0 +1,6 @@ +standard-macro-braces = [ + { name = "quote", brace = "{" }, + { name = "quote::quote", brace = "{" }, + { name = "eprint", brace = "[" }, + { name = "type_pos", brace = "[" }, +] diff --git a/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs new file mode 100644 index 00000000000..4ae6864cbb0 --- /dev/null +++ b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.rs @@ -0,0 +1,44 @@ +// #![warn(clippy::nonstandard_macro_braces)] + +extern crate quote; + +use quote::quote; + +#[rustfmt::skip] +macro_rules! test { + () => { + vec!{0, 0, 0} + }; +} + +#[rustfmt::skip] +macro_rules! test2 { + ($($arg:tt)*) => { + format_args!($($arg)*) + }; +} + +macro_rules! type_pos { + ($what:ty) => { + Vec<$what> + }; +} + +#[rustfmt::skip] +fn main() { + let _ = vec! {1, 2, 3}; + let _ = format!["ugh {} stop being such a good compiler", "hello"]; + let _ = quote!(let x = 1;); + let _ = quote::quote!(match match match); + let _ = test!(); + let _ = vec![1,2,3]; + + let _ = quote::quote! {true || false}; + let _ = vec! [0 ,0 ,0]; + let _ = format!("fds{}fds", 10); + let _ = test2!["{}{}{}", 1, 2, 3]; + + let _: type_pos!(usize) = vec![]; + + eprint!("test if user config overrides defaults"); +} 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 new file mode 100644 index 00000000000..7bcd1829524 --- /dev/null +++ b/tests/ui-toml/nonstandard_macro_braces/conf_nonstandard_macro_braces.stderr @@ -0,0 +1,94 @@ +error: use of irregular braces for `vec!` macro + --> $DIR/conf_nonstandard_macro_braces.rs:29:13 + | +LL | let _ = vec! {1, 2, 3}; + | ^^^^^^^^^^^^^^ + | + = note: `-D clippy::nonstandard-macro-braces` implied by `-D warnings` +help: consider writing `vec![1, 2, 3]` + --> $DIR/conf_nonstandard_macro_braces.rs:29:13 + | +LL | let _ = vec! {1, 2, 3}; + | ^^^^^^^^^^^^^^ + +error: use of irregular braces for `format!` macro + --> $DIR/conf_nonstandard_macro_braces.rs:30:13 + | +LL | let _ = format!["ugh {} stop being such a good compiler", "hello"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider writing `format!("ugh () stop being such a good compiler", "hello")` + --> $DIR/conf_nonstandard_macro_braces.rs:30:13 + | +LL | let _ = format!["ugh {} stop being such a good compiler", "hello"]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: use of irregular braces for `quote!` macro + --> $DIR/conf_nonstandard_macro_braces.rs:31:13 + | +LL | let _ = quote!(let x = 1;); + | ^^^^^^^^^^^^^^^^^^ + | +help: consider writing `quote! {let x = 1;}` + --> $DIR/conf_nonstandard_macro_braces.rs:31:13 + | +LL | let _ = quote!(let x = 1;); + | ^^^^^^^^^^^^^^^^^^ + +error: use of irregular braces for `quote::quote!` macro + --> $DIR/conf_nonstandard_macro_braces.rs:32:13 + | +LL | let _ = quote::quote!(match match match); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider writing `quote::quote! {match match match}` + --> $DIR/conf_nonstandard_macro_braces.rs:32:13 + | +LL | let _ = quote::quote!(match match match); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: use of irregular braces for `vec!` macro + --> $DIR/conf_nonstandard_macro_braces.rs:10:9 + | +LL | vec!{0, 0, 0} + | ^^^^^^^^^^^^^ +... +LL | let _ = test!(); + | ------- in this macro invocation + | +help: consider writing `vec![0, 0, 0]` + --> $DIR/conf_nonstandard_macro_braces.rs:10:9 + | +LL | vec!{0, 0, 0} + | ^^^^^^^^^^^^^ +... +LL | let _ = test!(); + | ------- in this macro invocation + = note: this error originates in the macro `test` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: use of irregular braces for `type_pos!` macro + --> $DIR/conf_nonstandard_macro_braces.rs:41:12 + | +LL | let _: type_pos!(usize) = vec![]; + | ^^^^^^^^^^^^^^^^ + | +help: consider writing `type_pos![usize]` + --> $DIR/conf_nonstandard_macro_braces.rs:41:12 + | +LL | let _: type_pos!(usize) = vec![]; + | ^^^^^^^^^^^^^^^^ + +error: use of irregular braces for `eprint!` macro + --> $DIR/conf_nonstandard_macro_braces.rs:43:5 + | +LL | eprint!("test if user config overrides defaults"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider writing `eprint!["test if user config overrides defaults"];` + --> $DIR/conf_nonstandard_macro_braces.rs:43:5 + | +LL | eprint!("test if user config overrides defaults"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 7 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 06d70b66fda..f179479cbec 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`, `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`, `third-party` at line 5 column 1 error: aborting due to previous error diff --git a/tests/ui/unnecessary_cast_fixable.fixed b/tests/ui/unnecessary_cast_fixable.fixed index 7fbce58a82f..bda0f2c47cd 100644 --- a/tests/ui/unnecessary_cast_fixable.fixed +++ b/tests/ui/unnecessary_cast_fixable.fixed @@ -1,7 +1,7 @@ // run-rustfix #![warn(clippy::unnecessary_cast)] -#![allow(clippy::no_effect, clippy::unnecessary_operation)] +#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::nonstandard_macro_braces)] fn main() { // casting integer literal to float is unnecessary diff --git a/tests/ui/unnecessary_cast_fixable.rs b/tests/ui/unnecessary_cast_fixable.rs index a71363ea4d2..f7a4f2a5988 100644 --- a/tests/ui/unnecessary_cast_fixable.rs +++ b/tests/ui/unnecessary_cast_fixable.rs @@ -1,7 +1,7 @@ // run-rustfix #![warn(clippy::unnecessary_cast)] -#![allow(clippy::no_effect, clippy::unnecessary_operation)] +#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::nonstandard_macro_braces)] fn main() { // casting integer literal to float is unnecessary diff --git a/tests/ui/vec.fixed b/tests/ui/vec.fixed index da35f2e5c1b..318f9c2dceb 100644 --- a/tests/ui/vec.fixed +++ b/tests/ui/vec.fixed @@ -1,5 +1,5 @@ // run-rustfix - +#![allow(clippy::nonstandard_macro_braces)] #![warn(clippy::useless_vec)] #[derive(Debug)] diff --git a/tests/ui/vec.rs b/tests/ui/vec.rs index e9ed83e5c5a..d7673ce3e64 100644 --- a/tests/ui/vec.rs +++ b/tests/ui/vec.rs @@ -1,5 +1,5 @@ // run-rustfix - +#![allow(clippy::nonstandard_macro_braces)] #![warn(clippy::useless_vec)] #[derive(Debug)] -- cgit 1.4.1-3-g733a5 From d4c9fe7549f29ca573f5a58ac94eacd662cd1421 Mon Sep 17 00:00:00 2001 From: Igor Aleksanov Date: Sun, 20 Jun 2021 08:25:30 +0300 Subject: Improve visibility&helpfulness of the 'found multiple rlibs' error --- tests/compile-test.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 7d266a36bb6..72905bd948e 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -48,7 +48,13 @@ fn third_party_crates() -> String { && name.rsplit('.').next().map(|ext| ext.eq_ignore_ascii_case("rlib")) == Some(true) { if let Some(old) = crates.insert(dep, path.clone()) { - panic!("Found multiple rlibs for crate `{}`: `{:?}` and `{:?}", dep, old, path); + panic!( + "\n---------------------------------------------------\n\n \ + Found multiple rlibs for crate `{}`: `{:?}` and `{:?}`.\n \ + Probably, you need to run `cargo clean` before running tests again.\n \ + \n---------------------------------------------------\n", + dep, old, path + ); } break; } -- cgit 1.4.1-3-g733a5 From faa2fa7490423b30362b44034e9336c469556b6b Mon Sep 17 00:00:00 2001 From: Igor Aleksanov Date: Sun, 20 Jun 2021 08:32:43 +0300 Subject: Improve appearance --- tests/compile-test.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'tests') diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 72905bd948e..8640cfb963d 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -52,6 +52,7 @@ fn third_party_crates() -> String { "\n---------------------------------------------------\n\n \ Found multiple rlibs for crate `{}`: `{:?}` and `{:?}`.\n \ Probably, you need to run `cargo clean` before running tests again.\n \ + \nFor details on that error see https://github.com/rust-lang/rust-clippy/issues/7343 \ \n---------------------------------------------------\n", dep, old, path ); -- cgit 1.4.1-3-g733a5 From 8276f26a4d189e9898aabf09e16605f35fa92318 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Sun, 20 Jun 2021 13:48:44 +0200 Subject: Fix wrong config option being suggested for deprecated wrong_pub_self_convention lint Problem: for code like ```` fn main() { println!("Hello, world!"); } ```` clippy will issue a warning to use a clippy.toml option instead: ```` warning: lint `clippy::wrong_pub_self_convention` has been removed: set the `avoid_breaking_exported_api` config option to `false` to enable the `wrong_self_convention` lint for public items --> src/main.rs:2:9 | 2 | #![warn(clippy::wrong_pub_self_convention)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(renamed_and_removed_lints)]` on by default ```` But using the lint name as seen in the warning message echo "avoid_breaking_exported_api = true\n" > clippy.toml Will cause an error: ```` error: error reading Clippy's configuration file `/tmp/clippytest/clippy.toml`: unknown field `avoid_breaking_exported_api`, expected one of `avoid-breaking-exported-api`, ... ```` Replace the underscores with dashes in the deprecation message. changelog: avoid_breaking_exported_api: suggest correct clippy config toml option in the deprecation message --- clippy_lints/src/deprecated_lints.rs | 4 ++-- clippy_lints/src/lib.rs | 4 ++-- tests/ui/deprecated.stderr | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index 04f3d77464f..2933fbc9341 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -149,7 +149,7 @@ declare_deprecated_lint! { /// enables the `enum_variant_names` lint for public items. /// ``` pub PUB_ENUM_VARIANT_NAMES, - "set the `avoid_breaking_exported_api` config option to `false` to enable the `enum_variant_names` lint for public items" + "set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items" } declare_deprecated_lint! { @@ -158,5 +158,5 @@ declare_deprecated_lint! { /// **Deprecation reason:** The `avoid_breaking_exported_api` config option was added, which /// enables the `wrong_self_conversion` lint for public items. pub WRONG_PUB_SELF_CONVENTION, - "set the `avoid_breaking_exported_api` config option to `false` to enable the `wrong_self_convention` lint for public items" + "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.rs b/clippy_lints/src/lib.rs index cc12d9b0d45..03e093f4c03 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -484,11 +484,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ); 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", + "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", + "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` diff --git a/tests/ui/deprecated.stderr b/tests/ui/deprecated.stderr index 03c9f438891..0af6b500115 100644 --- a/tests/ui/deprecated.stderr +++ b/tests/ui/deprecated.stderr @@ -84,13 +84,13 @@ error: lint `clippy::filter_map` has been removed: this lint has been replaced b LL | #[warn(clippy::filter_map)] | ^^^^^^^^^^^^^^^^^^ -error: lint `clippy::pub_enum_variant_names` has been removed: set the `avoid_breaking_exported_api` config option to `false` to enable the `enum_variant_names` lint for public items +error: lint `clippy::pub_enum_variant_names` has been removed: set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items --> $DIR/deprecated.rs:15:8 | LL | #[warn(clippy::pub_enum_variant_names)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: lint `clippy::wrong_pub_self_convention` has been removed: set the `avoid_breaking_exported_api` config option to `false` to enable the `wrong_self_convention` lint for public items +error: lint `clippy::wrong_pub_self_convention` has been removed: set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items --> $DIR/deprecated.rs:16:8 | LL | #[warn(clippy::wrong_pub_self_convention)] -- cgit 1.4.1-3-g733a5 From 310a2043066212e4bc702d2a79dc50a1312ac94d Mon Sep 17 00:00:00 2001 From: Igor Aleksanov Date: Mon, 21 Jun 2021 06:57:57 +0300 Subject: Provide different message for bootstrapped compiler --- tests/compile-test.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 8640cfb963d..caa19e39ccd 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -48,13 +48,23 @@ fn third_party_crates() -> String { && name.rsplit('.').next().map(|ext| ext.eq_ignore_ascii_case("rlib")) == Some(true) { if let Some(old) = crates.insert(dep, path.clone()) { + // Check which action should be done in order to remove compiled deps. + // If pre-installed version of compiler is used, `cargo clean` will do. + // Otherwise (for bootstrapped compiler), the dependencies directory + // must be removed manually. + let suggested_action = if std::env::var_os("RUSTC_BOOTSTRAP").is_some() { + "remove the stageN-tools directory" + } else { + "run `cargo clean`" + }; + panic!( "\n---------------------------------------------------\n\n \ Found multiple rlibs for crate `{}`: `{:?}` and `{:?}`.\n \ - Probably, you need to run `cargo clean` before running tests again.\n \ + Probably, you need to {} before running tests again.\n \ \nFor details on that error see https://github.com/rust-lang/rust-clippy/issues/7343 \ \n---------------------------------------------------\n", - dep, old, path + dep, old, path, suggested_action ); } break; -- cgit 1.4.1-3-g733a5 From 20cb1bc7c1a381a702f4b81511ad4e92df69c34c Mon Sep 17 00:00:00 2001 From: Elliot Bobrow Date: Mon, 14 Jun 2021 12:23:33 -0700 Subject: check for unbalanced tick pairs in doc-markdown --- clippy_lints/src/doc.rs | 64 +++++++--- clippy_lints/src/if_let_mutex.rs | 2 +- tests/ui/doc.rs | 221 ----------------------------------- tests/ui/doc.stderr | 190 ------------------------------ tests/ui/doc/doc.rs | 221 +++++++++++++++++++++++++++++++++++ tests/ui/doc/doc.stderr | 190 ++++++++++++++++++++++++++++++ tests/ui/doc/unbalanced_ticks.rs | 36 ++++++ tests/ui/doc/unbalanced_ticks.stderr | 64 ++++++++++ 8 files changed, 562 insertions(+), 426 deletions(-) delete mode 100644 tests/ui/doc.rs delete mode 100644 tests/ui/doc.stderr create mode 100644 tests/ui/doc/doc.rs create mode 100644 tests/ui/doc/doc.stderr create mode 100644 tests/ui/doc/unbalanced_ticks.rs create mode 100644 tests/ui/doc/unbalanced_ticks.stderr (limited to 'tests') diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index e67ec4e06c5..4e164d33a05 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -1,4 +1,5 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_note}; +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}; use clippy_utils::{is_entrypoint_fn, is_expn_of, match_panic_def_id, method_chain_args, return_ty}; use if_chain::if_chain; @@ -37,7 +38,8 @@ declare_clippy_lint! { /// consider that. /// /// **Known problems:** Lots of bad docs won’t be fixed, what the lint checks - /// for is limited, and there are still false positives. + /// for is limited, and there are still false positives. HTML elements and their + /// content are not linted. /// /// In addition, when writing documentation comments, including `[]` brackets /// inside a link text would trip the parser. Therfore, documenting link with @@ -469,11 +471,11 @@ fn check_doc<'a, Events: Iterator, Range DocHeaders { // true if a safety header was found - use pulldown_cmark::CodeBlockKind; use pulldown_cmark::Event::{ Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text, }; - use pulldown_cmark::Tag::{CodeBlock, Heading, Link}; + use pulldown_cmark::Tag::{CodeBlock, Heading, Item, Link, Paragraph}; + use pulldown_cmark::{CodeBlockKind, CowStr}; let mut headers = DocHeaders { safety: false, @@ -485,6 +487,9 @@ fn check_doc<'a, Events: Iterator, Range, Span)> = Vec::new(); + let mut paragraph_span = spans.get(0).expect("function isn't called if doc comment is empty").1; for (event, range) in events { match event { Start(CodeBlock(ref kind)) => { @@ -510,13 +515,42 @@ fn check_doc<'a, Events: Iterator, Range in_link = Some(url), End(Link(..)) => in_link = None, - Start(Heading(_)) => in_heading = true, - End(Heading(_)) => in_heading = false, + Start(Heading(_) | Paragraph | Item) => { + if let Start(Heading(_)) = event { + in_heading = true; + } + ticks_unbalanced = false; + let (_, span) = get_current_span(spans, range.start); + paragraph_span = first_line_of_span(cx, span); + }, + End(Heading(_) | Paragraph | Item) => { + if let End(Heading(_)) = event { + in_heading = false; + } + if ticks_unbalanced { + span_lint_and_help( + cx, + DOC_MARKDOWN, + paragraph_span, + "backticks are unbalanced", + None, + "a backtick may be missing a pair", + ); + } else { + for (text, span) in text_to_check { + check_text(cx, valid_idents, &text, span); + } + } + text_to_check = Vec::new(); + }, Start(_tag) | End(_tag) => (), // We don't care about other tags Html(_html) => (), // HTML is weird, just ignore it SoftBreak | HardBreak | TaskListMarker(_) | Code(_) | Rule => (), FootnoteReference(text) | Text(text) => { - if Some(&text) == in_link.as_ref() { + let (begin, span) = get_current_span(spans, range.start); + paragraph_span = paragraph_span.with_hi(span.hi()); + ticks_unbalanced |= text.contains('`'); + if Some(&text) == in_link.as_ref() || ticks_unbalanced { // Probably a link of the form `` // Which are represented as a link to "http://example.com" with // text "http://example.com" by pulldown-cmark @@ -525,11 +559,6 @@ fn check_doc<'a, Events: Iterator, Range o, - Err(e) => e - 1, - }; - let (begin, span) = spans[index]; if in_code { if is_rust { let edition = edition.unwrap_or_else(|| cx.tcx.sess.edition()); @@ -538,8 +567,7 @@ fn check_doc<'a, Events: Iterator, Range, Range (usize, Span) { + let index = match spans.binary_search_by(|c| c.0.cmp(&idx)) { + Ok(o) => o, + Err(e) => e - 1, + }; + spans[index] +} + fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { fn has_needless_main(code: &str, edition: Edition) -> bool { rustc_driver::catch_fatal_errors(|| { diff --git a/clippy_lints/src/if_let_mutex.rs b/clippy_lints/src/if_let_mutex.rs index f661f7ede82..5403d76ea30 100644 --- a/clippy_lints/src/if_let_mutex.rs +++ b/clippy_lints/src/if_let_mutex.rs @@ -83,7 +83,7 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex { } } -/// Checks if `Mutex::lock` is called in the `if let _ = expr. +/// Checks if `Mutex::lock` is called in the `if let` expr. pub struct OppVisitor<'a, 'tcx> { mutex_lock_called: bool, found_mutex: Option<&'tcx Expr<'tcx>>, diff --git a/tests/ui/doc.rs b/tests/ui/doc.rs deleted file mode 100644 index 8afef6b23d4..00000000000 --- a/tests/ui/doc.rs +++ /dev/null @@ -1,221 +0,0 @@ -//! This file tests for the `DOC_MARKDOWN` lint. - -#![allow(dead_code, incomplete_features)] -#![warn(clippy::doc_markdown)] -#![feature(custom_inner_attributes, const_generics, const_evaluatable_checked, const_option)] -#![rustfmt::skip] - -/// The foo_bar function does _nothing_. See also foo::bar. (note the dot there) -/// Markdown is _weird_. I mean _really weird_. This \_ is ok. So is `_`. But not Foo::some_fun -/// which should be reported only once despite being __doubly bad__. -/// Here be ::a::global:path. -/// That's not code ~NotInCodeBlock~. -/// be_sure_we_got_to_the_end_of_it -fn foo_bar() { -} - -/// That one tests multiline ticks. -/// ```rust -/// foo_bar FOO_BAR -/// _foo bar_ -/// ``` -/// -/// ~~~rust -/// foo_bar FOO_BAR -/// _foo bar_ -/// ~~~ -/// be_sure_we_got_to_the_end_of_it -fn multiline_codeblock() { -} - -/// This _is a test for -/// multiline -/// emphasis_. -/// be_sure_we_got_to_the_end_of_it -fn test_emphasis() { -} - -/// This tests units. See also #835. -/// kiB MiB GiB TiB PiB EiB -/// kib Mib Gib Tib Pib Eib -/// kB MB GB TB PB EB -/// kb Mb Gb Tb Pb Eb -/// 32kiB 32MiB 32GiB 32TiB 32PiB 32EiB -/// 32kib 32Mib 32Gib 32Tib 32Pib 32Eib -/// 32kB 32MB 32GB 32TB 32PB 32EB -/// 32kb 32Mb 32Gb 32Tb 32Pb 32Eb -/// NaN -/// be_sure_we_got_to_the_end_of_it -fn test_units() { -} - -/// This tests allowed identifiers. -/// KiB MiB GiB TiB PiB EiB -/// DirectX -/// ECMAScript -/// GPLv2 GPLv3 -/// GitHub GitLab -/// IPv4 IPv6 -/// ClojureScript CoffeeScript JavaScript PureScript TypeScript -/// NaN NaNs -/// OAuth GraphQL -/// OCaml -/// OpenGL OpenMP OpenSSH OpenSSL OpenStreetMap OpenDNS -/// WebGL -/// TensorFlow -/// TrueType -/// iOS macOS FreeBSD -/// TeX LaTeX BibTeX BibLaTeX -/// MinGW -/// CamelCase (see also #2395) -/// be_sure_we_got_to_the_end_of_it -fn test_allowed() { -} - -/// This test has [a link_with_underscores][chunked-example] inside it. See #823. -/// See also [the issue tracker](https://github.com/rust-lang/rust-clippy/search?q=clippy::doc_markdown&type=Issues) -/// on GitHub (which is a camel-cased word, but is OK). And here is another [inline link][inline_link]. -/// It can also be [inline_link2]. -/// -/// [chunked-example]: https://en.wikipedia.org/wiki/Chunked_transfer_encoding#Example -/// [inline_link]: https://foobar -/// [inline_link2]: https://foobar -/// The `main` function is the entry point of the program. Here it only calls the `foo_bar` and -/// `multiline_ticks` functions. -/// -/// expression of the type `_ m c` (where `` -/// is one of {`&`, '|'} and `` is one of {`!=`, `>=`, `>` , -/// be_sure_we_got_to_the_end_of_it -fn main() { - foo_bar(); - multiline_codeblock(); - test_emphasis(); - test_units(); -} - -/// ## CamelCaseThing -/// Talks about `CamelCaseThing`. Titles should be ignored; see issue #897. -/// -/// # CamelCaseThing -/// -/// Not a title #897 CamelCaseThing -/// be_sure_we_got_to_the_end_of_it -fn issue897() { -} - -/// I am confused by brackets? (`x_y`) -/// I am confused by brackets? (foo `x_y`) -/// I am confused by brackets? (`x_y` foo) -/// be_sure_we_got_to_the_end_of_it -fn issue900() { -} - -/// Diesel queries also have a similar problem to [Iterator][iterator], where -/// /// More talking -/// returning them from a function requires exposing the implementation of that -/// function. The [`helper_types`][helper_types] module exists to help with this, -/// but you might want to hide the return type or have it conditionally change. -/// Boxing can achieve both. -/// -/// [iterator]: https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html -/// [helper_types]: ../helper_types/index.html -/// be_sure_we_got_to_the_end_of_it -fn issue883() { -} - -/// `foo_bar -/// baz_quz` -/// [foo -/// bar](https://doc.rust-lang.org/stable/std/iter/trait.IteratorFooBar.html) -fn multiline() { -} - -/** E.g., serialization of an empty list: FooBar -``` -That's in a code block: `PackedNode` -``` - -And BarQuz too. -be_sure_we_got_to_the_end_of_it -*/ -fn issue1073() { -} - -/** E.g., serialization of an empty list: FooBar -``` -That's in a code block: PackedNode -``` - -And BarQuz too. -be_sure_we_got_to_the_end_of_it -*/ -fn issue1073_alt() { -} - -/// Tests more than three quotes: -/// ```` -/// DoNotWarn -/// ``` -/// StillDont -/// ```` -/// be_sure_we_got_to_the_end_of_it -fn four_quotes() { -} - -/// See [NIST SP 800-56A, revision 2]. -/// -/// [NIST SP 800-56A, revision 2]: -/// https://github.com/rust-lang/rust-clippy/issues/902#issuecomment-261919419 -fn issue_902_comment() {} - -#[cfg_attr(feature = "a", doc = " ```")] -#[cfg_attr(not(feature = "a"), doc = " ```ignore")] -/// fn main() { -/// let s = "localhost:10000".to_string(); -/// println!("{}", s); -/// } -/// ``` -fn issue_1469() {} - -/** - * This is a doc comment that should not be a list - *This would also be an error under a strict common mark interpretation - */ -fn issue_1920() {} - -/// Ok: -/// -/// Not ok: http://www.unicode.org -/// Not ok: https://www.unicode.org -/// Not ok: http://www.unicode.org/ -/// Not ok: http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels -fn issue_1832() {} - -/// An iterator over mycrate::Collection's values. -/// It should not lint a `'static` lifetime in ticks. -fn issue_2210() {} - -/// This should not cause the lint to trigger: -/// #REQ-data-family.lint_partof_exists -fn issue_2343() {} - -/// This should not cause an ICE: -/// __|_ _|__||_| -fn pulldown_cmark_crash() {} - -// issue #7033 - const_evaluatable_checked ICE -struct S -where [(); N.checked_next_power_of_two().unwrap()]: { - arr: [T; N.checked_next_power_of_two().unwrap()], - n: usize, -} - -impl S -where [(); N.checked_next_power_of_two().unwrap()]: { - fn new() -> Self { - Self { - arr: [T::default(); N.checked_next_power_of_two().unwrap()], - n: 0, - } - } -} diff --git a/tests/ui/doc.stderr b/tests/ui/doc.stderr deleted file mode 100644 index 7eab8a85f09..00000000000 --- a/tests/ui/doc.stderr +++ /dev/null @@ -1,190 +0,0 @@ -error: you should put `foo_bar` between ticks in the documentation - --> $DIR/doc.rs:8:9 - | -LL | /// The foo_bar function does _nothing_. See also foo::bar. (note the dot there) - | ^^^^^^^ - | - = note: `-D clippy::doc-markdown` implied by `-D warnings` - -error: you should put `foo::bar` between ticks in the documentation - --> $DIR/doc.rs:8:51 - | -LL | /// The foo_bar function does _nothing_. See also foo::bar. (note the dot there) - | ^^^^^^^^ - -error: you should put `Foo::some_fun` between ticks in the documentation - --> $DIR/doc.rs:9:83 - | -LL | /// Markdown is _weird_. I mean _really weird_. This /_ is ok. So is `_`. But not Foo::some_fun - | ^^^^^^^^^^^^^ - -error: you should put `a::global:path` between ticks in the documentation - --> $DIR/doc.rs:11:15 - | -LL | /// Here be ::a::global:path. - | ^^^^^^^^^^^^^^ - -error: you should put `NotInCodeBlock` between ticks in the documentation - --> $DIR/doc.rs:12:22 - | -LL | /// That's not code ~NotInCodeBlock~. - | ^^^^^^^^^^^^^^ - -error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:13:5 - | -LL | /// be_sure_we_got_to_the_end_of_it - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:27:5 - | -LL | /// be_sure_we_got_to_the_end_of_it - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:34:5 - | -LL | /// be_sure_we_got_to_the_end_of_it - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:48:5 - | -LL | /// be_sure_we_got_to_the_end_of_it - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:71:5 - | -LL | /// be_sure_we_got_to_the_end_of_it - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: you should put `link_with_underscores` between ticks in the documentation - --> $DIR/doc.rs:75:22 - | -LL | /// This test has [a link_with_underscores][chunked-example] inside it. See #823. - | ^^^^^^^^^^^^^^^^^^^^^ - -error: you should put `inline_link2` between ticks in the documentation - --> $DIR/doc.rs:78:21 - | -LL | /// It can also be [inline_link2]. - | ^^^^^^^^^^^^ - -error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:88:5 - | -LL | /// be_sure_we_got_to_the_end_of_it - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: you should put `CamelCaseThing` between ticks in the documentation - --> $DIR/doc.rs:96:8 - | -LL | /// ## CamelCaseThing - | ^^^^^^^^^^^^^^ - -error: you should put `CamelCaseThing` between ticks in the documentation - --> $DIR/doc.rs:99:7 - | -LL | /// # CamelCaseThing - | ^^^^^^^^^^^^^^ - -error: you should put `CamelCaseThing` between ticks in the documentation - --> $DIR/doc.rs:101:22 - | -LL | /// Not a title #897 CamelCaseThing - | ^^^^^^^^^^^^^^ - -error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:102:5 - | -LL | /// be_sure_we_got_to_the_end_of_it - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:109:5 - | -LL | /// be_sure_we_got_to_the_end_of_it - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:122:5 - | -LL | /// be_sure_we_got_to_the_end_of_it - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: you should put `FooBar` between ticks in the documentation - --> $DIR/doc.rs:133:43 - | -LL | /** E.g., serialization of an empty list: FooBar - | ^^^^^^ - -error: you should put `BarQuz` between ticks in the documentation - --> $DIR/doc.rs:138:5 - | -LL | And BarQuz too. - | ^^^^^^ - -error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:139:1 - | -LL | be_sure_we_got_to_the_end_of_it - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: you should put `FooBar` between ticks in the documentation - --> $DIR/doc.rs:144:43 - | -LL | /** E.g., serialization of an empty list: FooBar - | ^^^^^^ - -error: you should put `BarQuz` between ticks in the documentation - --> $DIR/doc.rs:149:5 - | -LL | And BarQuz too. - | ^^^^^^ - -error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:150:1 - | -LL | be_sure_we_got_to_the_end_of_it - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation - --> $DIR/doc.rs:161:5 - | -LL | /// be_sure_we_got_to_the_end_of_it - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> $DIR/doc.rs:188:13 - | -LL | /// Not ok: http://www.unicode.org - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> $DIR/doc.rs:189:13 - | -LL | /// Not ok: https://www.unicode.org - | ^^^^^^^^^^^^^^^^^^^^^^^ - -error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> $DIR/doc.rs:190:13 - | -LL | /// Not ok: http://www.unicode.org/ - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> $DIR/doc.rs:191:13 - | -LL | /// Not ok: http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: you should put `mycrate::Collection` between ticks in the documentation - --> $DIR/doc.rs:194:22 - | -LL | /// An iterator over mycrate::Collection's values. - | ^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 31 previous errors - diff --git a/tests/ui/doc/doc.rs b/tests/ui/doc/doc.rs new file mode 100644 index 00000000000..8afef6b23d4 --- /dev/null +++ b/tests/ui/doc/doc.rs @@ -0,0 +1,221 @@ +//! This file tests for the `DOC_MARKDOWN` lint. + +#![allow(dead_code, incomplete_features)] +#![warn(clippy::doc_markdown)] +#![feature(custom_inner_attributes, const_generics, const_evaluatable_checked, const_option)] +#![rustfmt::skip] + +/// The foo_bar function does _nothing_. See also foo::bar. (note the dot there) +/// Markdown is _weird_. I mean _really weird_. This \_ is ok. So is `_`. But not Foo::some_fun +/// which should be reported only once despite being __doubly bad__. +/// Here be ::a::global:path. +/// That's not code ~NotInCodeBlock~. +/// be_sure_we_got_to_the_end_of_it +fn foo_bar() { +} + +/// That one tests multiline ticks. +/// ```rust +/// foo_bar FOO_BAR +/// _foo bar_ +/// ``` +/// +/// ~~~rust +/// foo_bar FOO_BAR +/// _foo bar_ +/// ~~~ +/// be_sure_we_got_to_the_end_of_it +fn multiline_codeblock() { +} + +/// This _is a test for +/// multiline +/// emphasis_. +/// be_sure_we_got_to_the_end_of_it +fn test_emphasis() { +} + +/// This tests units. See also #835. +/// kiB MiB GiB TiB PiB EiB +/// kib Mib Gib Tib Pib Eib +/// kB MB GB TB PB EB +/// kb Mb Gb Tb Pb Eb +/// 32kiB 32MiB 32GiB 32TiB 32PiB 32EiB +/// 32kib 32Mib 32Gib 32Tib 32Pib 32Eib +/// 32kB 32MB 32GB 32TB 32PB 32EB +/// 32kb 32Mb 32Gb 32Tb 32Pb 32Eb +/// NaN +/// be_sure_we_got_to_the_end_of_it +fn test_units() { +} + +/// This tests allowed identifiers. +/// KiB MiB GiB TiB PiB EiB +/// DirectX +/// ECMAScript +/// GPLv2 GPLv3 +/// GitHub GitLab +/// IPv4 IPv6 +/// ClojureScript CoffeeScript JavaScript PureScript TypeScript +/// NaN NaNs +/// OAuth GraphQL +/// OCaml +/// OpenGL OpenMP OpenSSH OpenSSL OpenStreetMap OpenDNS +/// WebGL +/// TensorFlow +/// TrueType +/// iOS macOS FreeBSD +/// TeX LaTeX BibTeX BibLaTeX +/// MinGW +/// CamelCase (see also #2395) +/// be_sure_we_got_to_the_end_of_it +fn test_allowed() { +} + +/// This test has [a link_with_underscores][chunked-example] inside it. See #823. +/// See also [the issue tracker](https://github.com/rust-lang/rust-clippy/search?q=clippy::doc_markdown&type=Issues) +/// on GitHub (which is a camel-cased word, but is OK). And here is another [inline link][inline_link]. +/// It can also be [inline_link2]. +/// +/// [chunked-example]: https://en.wikipedia.org/wiki/Chunked_transfer_encoding#Example +/// [inline_link]: https://foobar +/// [inline_link2]: https://foobar +/// The `main` function is the entry point of the program. Here it only calls the `foo_bar` and +/// `multiline_ticks` functions. +/// +/// expression of the type `_ m c` (where `` +/// is one of {`&`, '|'} and `` is one of {`!=`, `>=`, `>` , +/// be_sure_we_got_to_the_end_of_it +fn main() { + foo_bar(); + multiline_codeblock(); + test_emphasis(); + test_units(); +} + +/// ## CamelCaseThing +/// Talks about `CamelCaseThing`. Titles should be ignored; see issue #897. +/// +/// # CamelCaseThing +/// +/// Not a title #897 CamelCaseThing +/// be_sure_we_got_to_the_end_of_it +fn issue897() { +} + +/// I am confused by brackets? (`x_y`) +/// I am confused by brackets? (foo `x_y`) +/// I am confused by brackets? (`x_y` foo) +/// be_sure_we_got_to_the_end_of_it +fn issue900() { +} + +/// Diesel queries also have a similar problem to [Iterator][iterator], where +/// /// More talking +/// returning them from a function requires exposing the implementation of that +/// function. The [`helper_types`][helper_types] module exists to help with this, +/// but you might want to hide the return type or have it conditionally change. +/// Boxing can achieve both. +/// +/// [iterator]: https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html +/// [helper_types]: ../helper_types/index.html +/// be_sure_we_got_to_the_end_of_it +fn issue883() { +} + +/// `foo_bar +/// baz_quz` +/// [foo +/// bar](https://doc.rust-lang.org/stable/std/iter/trait.IteratorFooBar.html) +fn multiline() { +} + +/** E.g., serialization of an empty list: FooBar +``` +That's in a code block: `PackedNode` +``` + +And BarQuz too. +be_sure_we_got_to_the_end_of_it +*/ +fn issue1073() { +} + +/** E.g., serialization of an empty list: FooBar +``` +That's in a code block: PackedNode +``` + +And BarQuz too. +be_sure_we_got_to_the_end_of_it +*/ +fn issue1073_alt() { +} + +/// Tests more than three quotes: +/// ```` +/// DoNotWarn +/// ``` +/// StillDont +/// ```` +/// be_sure_we_got_to_the_end_of_it +fn four_quotes() { +} + +/// See [NIST SP 800-56A, revision 2]. +/// +/// [NIST SP 800-56A, revision 2]: +/// https://github.com/rust-lang/rust-clippy/issues/902#issuecomment-261919419 +fn issue_902_comment() {} + +#[cfg_attr(feature = "a", doc = " ```")] +#[cfg_attr(not(feature = "a"), doc = " ```ignore")] +/// fn main() { +/// let s = "localhost:10000".to_string(); +/// println!("{}", s); +/// } +/// ``` +fn issue_1469() {} + +/** + * This is a doc comment that should not be a list + *This would also be an error under a strict common mark interpretation + */ +fn issue_1920() {} + +/// Ok: +/// +/// Not ok: http://www.unicode.org +/// Not ok: https://www.unicode.org +/// Not ok: http://www.unicode.org/ +/// Not ok: http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels +fn issue_1832() {} + +/// An iterator over mycrate::Collection's values. +/// It should not lint a `'static` lifetime in ticks. +fn issue_2210() {} + +/// This should not cause the lint to trigger: +/// #REQ-data-family.lint_partof_exists +fn issue_2343() {} + +/// This should not cause an ICE: +/// __|_ _|__||_| +fn pulldown_cmark_crash() {} + +// issue #7033 - const_evaluatable_checked ICE +struct S +where [(); N.checked_next_power_of_two().unwrap()]: { + arr: [T; N.checked_next_power_of_two().unwrap()], + n: usize, +} + +impl S +where [(); N.checked_next_power_of_two().unwrap()]: { + fn new() -> Self { + Self { + arr: [T::default(); N.checked_next_power_of_two().unwrap()], + n: 0, + } + } +} diff --git a/tests/ui/doc/doc.stderr b/tests/ui/doc/doc.stderr new file mode 100644 index 00000000000..7eab8a85f09 --- /dev/null +++ b/tests/ui/doc/doc.stderr @@ -0,0 +1,190 @@ +error: you should put `foo_bar` between ticks in the documentation + --> $DIR/doc.rs:8:9 + | +LL | /// The foo_bar function does _nothing_. See also foo::bar. (note the dot there) + | ^^^^^^^ + | + = note: `-D clippy::doc-markdown` implied by `-D warnings` + +error: you should put `foo::bar` between ticks in the documentation + --> $DIR/doc.rs:8:51 + | +LL | /// The foo_bar function does _nothing_. See also foo::bar. (note the dot there) + | ^^^^^^^^ + +error: you should put `Foo::some_fun` between ticks in the documentation + --> $DIR/doc.rs:9:83 + | +LL | /// Markdown is _weird_. I mean _really weird_. This /_ is ok. So is `_`. But not Foo::some_fun + | ^^^^^^^^^^^^^ + +error: you should put `a::global:path` between ticks in the documentation + --> $DIR/doc.rs:11:15 + | +LL | /// Here be ::a::global:path. + | ^^^^^^^^^^^^^^ + +error: you should put `NotInCodeBlock` between ticks in the documentation + --> $DIR/doc.rs:12:22 + | +LL | /// That's not code ~NotInCodeBlock~. + | ^^^^^^^^^^^^^^ + +error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation + --> $DIR/doc.rs:13:5 + | +LL | /// be_sure_we_got_to_the_end_of_it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation + --> $DIR/doc.rs:27:5 + | +LL | /// be_sure_we_got_to_the_end_of_it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation + --> $DIR/doc.rs:34:5 + | +LL | /// be_sure_we_got_to_the_end_of_it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation + --> $DIR/doc.rs:48:5 + | +LL | /// be_sure_we_got_to_the_end_of_it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation + --> $DIR/doc.rs:71:5 + | +LL | /// be_sure_we_got_to_the_end_of_it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put `link_with_underscores` between ticks in the documentation + --> $DIR/doc.rs:75:22 + | +LL | /// This test has [a link_with_underscores][chunked-example] inside it. See #823. + | ^^^^^^^^^^^^^^^^^^^^^ + +error: you should put `inline_link2` between ticks in the documentation + --> $DIR/doc.rs:78:21 + | +LL | /// It can also be [inline_link2]. + | ^^^^^^^^^^^^ + +error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation + --> $DIR/doc.rs:88:5 + | +LL | /// be_sure_we_got_to_the_end_of_it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put `CamelCaseThing` between ticks in the documentation + --> $DIR/doc.rs:96:8 + | +LL | /// ## CamelCaseThing + | ^^^^^^^^^^^^^^ + +error: you should put `CamelCaseThing` between ticks in the documentation + --> $DIR/doc.rs:99:7 + | +LL | /// # CamelCaseThing + | ^^^^^^^^^^^^^^ + +error: you should put `CamelCaseThing` between ticks in the documentation + --> $DIR/doc.rs:101:22 + | +LL | /// Not a title #897 CamelCaseThing + | ^^^^^^^^^^^^^^ + +error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation + --> $DIR/doc.rs:102:5 + | +LL | /// be_sure_we_got_to_the_end_of_it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation + --> $DIR/doc.rs:109:5 + | +LL | /// be_sure_we_got_to_the_end_of_it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation + --> $DIR/doc.rs:122:5 + | +LL | /// be_sure_we_got_to_the_end_of_it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put `FooBar` between ticks in the documentation + --> $DIR/doc.rs:133:43 + | +LL | /** E.g., serialization of an empty list: FooBar + | ^^^^^^ + +error: you should put `BarQuz` between ticks in the documentation + --> $DIR/doc.rs:138:5 + | +LL | And BarQuz too. + | ^^^^^^ + +error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation + --> $DIR/doc.rs:139:1 + | +LL | be_sure_we_got_to_the_end_of_it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put `FooBar` between ticks in the documentation + --> $DIR/doc.rs:144:43 + | +LL | /** E.g., serialization of an empty list: FooBar + | ^^^^^^ + +error: you should put `BarQuz` between ticks in the documentation + --> $DIR/doc.rs:149:5 + | +LL | And BarQuz too. + | ^^^^^^ + +error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation + --> $DIR/doc.rs:150:1 + | +LL | be_sure_we_got_to_the_end_of_it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation + --> $DIR/doc.rs:161:5 + | +LL | /// be_sure_we_got_to_the_end_of_it + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put bare URLs between `<`/`>` or make a proper Markdown link + --> $DIR/doc.rs:188:13 + | +LL | /// Not ok: http://www.unicode.org + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put bare URLs between `<`/`>` or make a proper Markdown link + --> $DIR/doc.rs:189:13 + | +LL | /// Not ok: https://www.unicode.org + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put bare URLs between `<`/`>` or make a proper Markdown link + --> $DIR/doc.rs:190:13 + | +LL | /// Not ok: http://www.unicode.org/ + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put bare URLs between `<`/`>` or make a proper Markdown link + --> $DIR/doc.rs:191:13 + | +LL | /// Not ok: http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: you should put `mycrate::Collection` between ticks in the documentation + --> $DIR/doc.rs:194:22 + | +LL | /// An iterator over mycrate::Collection's values. + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 31 previous errors + diff --git a/tests/ui/doc/unbalanced_ticks.rs b/tests/ui/doc/unbalanced_ticks.rs new file mode 100644 index 00000000000..78e87bc6906 --- /dev/null +++ b/tests/ui/doc/unbalanced_ticks.rs @@ -0,0 +1,36 @@ +//! This file tests for the `DOC_MARKDOWN` lint, specifically cases +//! where ticks are unbalanced (see issue #6753). + +#![allow(dead_code)] +#![warn(clippy::doc_markdown)] + +/// This is a doc comment with `unbalanced_tick marks and several words that +/// should be `encompassed_by` tick marks because they `contain_underscores`. +/// Because of the initial `unbalanced_tick` pair, the error message is +/// very `confusing_and_misleading`. +fn main() {} + +/// This paragraph has `unbalanced_tick marks and should stop_linting. +/// +/// This paragraph is fine and should_be linted normally. +/// +/// Double unbalanced backtick from ``here to here` should lint. +/// +/// Double balanced back ticks ``start end`` is fine. +fn multiple_paragraphs() {} + +/// ``` +/// // Unbalanced tick mark in code block shouldn't warn: +/// ` +/// ``` +fn in_code_block() {} + +/// # `Fine` +/// +/// ## not_fine +/// +/// ### `unbalanced +/// +/// - This `item has unbalanced tick marks +/// - This item needs backticks_here +fn other_markdown() {} diff --git a/tests/ui/doc/unbalanced_ticks.stderr b/tests/ui/doc/unbalanced_ticks.stderr new file mode 100644 index 00000000000..45ca34e2a8c --- /dev/null +++ b/tests/ui/doc/unbalanced_ticks.stderr @@ -0,0 +1,64 @@ +error: backticks are unbalanced + --> $DIR/unbalanced_ticks.rs:7:1 + | +LL | / /// This is a doc comment with `unbalanced_tick marks and several words that +LL | | /// should be `encompassed_by` tick marks because they `contain_underscores`. +LL | | /// Because of the initial `unbalanced_tick` pair, the error message is +LL | | /// very `confusing_and_misleading`. + | |____________________________________^ + | + = note: `-D clippy::doc-markdown` implied by `-D warnings` + = help: a backtick may be missing a pair + +error: backticks are unbalanced + --> $DIR/unbalanced_ticks.rs:13:1 + | +LL | /// This paragraph has `unbalanced_tick marks and should stop_linting. + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: a backtick may be missing a pair + +error: you should put `should_be` between ticks in the documentation + --> $DIR/unbalanced_ticks.rs:15:32 + | +LL | /// This paragraph is fine and should_be linted normally. + | ^^^^^^^^^ + +error: backticks are unbalanced + --> $DIR/unbalanced_ticks.rs:17:1 + | +LL | /// Double unbalanced backtick from ``here to here` should lint. + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: a backtick may be missing a pair + +error: you should put `not_fine` between ticks in the documentation + --> $DIR/unbalanced_ticks.rs:30:8 + | +LL | /// ## not_fine + | ^^^^^^^^ + +error: backticks are unbalanced + --> $DIR/unbalanced_ticks.rs:32:1 + | +LL | /// ### `unbalanced + | ^^^^^^^^^^^^^^^^^^^ + | + = help: a backtick may be missing a pair + +error: backticks are unbalanced + --> $DIR/unbalanced_ticks.rs:34:1 + | +LL | /// - This `item has unbalanced tick marks + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: a backtick may be missing a pair + +error: you should put `backticks_here` between ticks in the documentation + --> $DIR/unbalanced_ticks.rs:35:23 + | +LL | /// - This item needs backticks_here + | ^^^^^^^^^^^^^^ + +error: aborting due to 8 previous errors + -- cgit 1.4.1-3-g733a5 From d2087ad00d5443f906a3603a1ba2718e87458239 Mon Sep 17 00:00:00 2001 From: Joe Ranweiler Date: Tue, 22 Jun 2021 18:44:04 -0700 Subject: Remove bad cast in test, cover more cases --- tests/ui/zero_offset.rs | 18 ++++++++++----- tests/ui/zero_offset.stderr | 55 ++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 61 insertions(+), 12 deletions(-) (limited to 'tests') diff --git a/tests/ui/zero_offset.rs b/tests/ui/zero_offset.rs index 2de904376ad..d3f8c21937e 100644 --- a/tests/ui/zero_offset.rs +++ b/tests/ui/zero_offset.rs @@ -1,12 +1,18 @@ fn main() { unsafe { - let x = &() as *const (); - x.offset(0); - x.wrapping_add(0); - x.sub(0); - x.wrapping_sub(0); + let m = &mut () as *mut (); + m.offset(0); + m.wrapping_add(0); + m.sub(0); + m.wrapping_sub(0); - let y = &1 as *const u8; + let c = &() as *const (); + c.offset(0); + c.wrapping_add(0); + c.sub(0); + c.wrapping_sub(0); + + let y = &1 as *const i32; y.offset(0); } } diff --git a/tests/ui/zero_offset.stderr b/tests/ui/zero_offset.stderr index cfcd7de2b3d..b12c8e9a73c 100644 --- a/tests/ui/zero_offset.stderr +++ b/tests/ui/zero_offset.stderr @@ -1,9 +1,52 @@ -error[E0606]: casting `&i32` as `*const u8` is invalid - --> $DIR/zero_offset.rs:9:17 +error: offset calculation on zero-sized value + --> $DIR/zero_offset.rs:4:9 | -LL | let y = &1 as *const u8; - | ^^^^^^^^^^^^^^^ +LL | m.offset(0); + | ^^^^^^^^^^^ + | + = note: `#[deny(clippy::zst_offset)]` on by default + +error: offset calculation on zero-sized value + --> $DIR/zero_offset.rs:5:9 + | +LL | m.wrapping_add(0); + | ^^^^^^^^^^^^^^^^^ + +error: offset calculation on zero-sized value + --> $DIR/zero_offset.rs:6:9 + | +LL | m.sub(0); + | ^^^^^^^^ + +error: offset calculation on zero-sized value + --> $DIR/zero_offset.rs:7:9 + | +LL | m.wrapping_sub(0); + | ^^^^^^^^^^^^^^^^^ + +error: offset calculation on zero-sized value + --> $DIR/zero_offset.rs:10:9 + | +LL | c.offset(0); + | ^^^^^^^^^^^ + +error: offset calculation on zero-sized value + --> $DIR/zero_offset.rs:11:9 + | +LL | c.wrapping_add(0); + | ^^^^^^^^^^^^^^^^^ + +error: offset calculation on zero-sized value + --> $DIR/zero_offset.rs:12:9 + | +LL | c.sub(0); + | ^^^^^^^^ + +error: offset calculation on zero-sized value + --> $DIR/zero_offset.rs:13:9 + | +LL | c.wrapping_sub(0); + | ^^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error: aborting due to 8 previous errors -For more information about this error, try `rustc --explain E0606`. -- cgit 1.4.1-3-g733a5 From 642239c857124b109f43f7223baddbe8694c84ee Mon Sep 17 00:00:00 2001 From: Joe Ranweiler Date: Tue, 22 Jun 2021 19:02:34 -0700 Subject: Update var name in test --- tests/ui/zero_offset.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/ui/zero_offset.rs b/tests/ui/zero_offset.rs index d3f8c21937e..6c190a4c86c 100644 --- a/tests/ui/zero_offset.rs +++ b/tests/ui/zero_offset.rs @@ -12,7 +12,7 @@ fn main() { c.sub(0); c.wrapping_sub(0); - let y = &1 as *const i32; - y.offset(0); + let sized = &1 as *const i32; + sized.offset(0); } } -- cgit 1.4.1-3-g733a5 From 9492de584319c0331619991e5b201ea6e00b9b2f Mon Sep 17 00:00:00 2001 From: Devin Ragotzy Date: Mon, 31 May 2021 14:15:17 -0400 Subject: Add import_rename lint, this adds a field on the Conf struct Rename lint and fix review issues --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 5 + clippy_lints/src/missing_enforced_import_rename.rs | 102 +++++++++++++++++++++ clippy_lints/src/utils/conf.rs | 9 ++ .../missing_enforced_import_rename/clippy.toml | 10 ++ .../conf_missing_enforced_import_rename.rs | 16 ++++ .../conf_missing_enforced_import_rename.stderr | 40 ++++++++ .../toml_unknown_key/conf_unknown_key.stderr | 2 +- 8 files changed, 184 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/missing_enforced_import_rename.rs create mode 100644 tests/ui-toml/missing_enforced_import_rename/clippy.toml create mode 100644 tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs create mode 100644 tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 67c2b41b200..5f62a172035 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2657,6 +2657,7 @@ Released 2018-09-13 [`misrefactored_assign_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#misrefactored_assign_op [`missing_const_for_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn [`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items +[`missing_enforced_import_renames`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames [`missing_errors_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_errors_doc [`missing_inline_in_public_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_inline_in_public_items [`missing_panics_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_panics_doc diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 03e093f4c03..9cffeeb0224 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -267,6 +267,7 @@ mod misc; mod misc_early; mod missing_const_for_fn; mod missing_doc; +mod missing_enforced_import_rename; mod missing_inline; mod modulo_arithmetic; mod multiple_crate_versions; @@ -813,6 +814,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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, modulo_arithmetic::MODULO_ARITHMETIC, multiple_crate_versions::MULTIPLE_CRATE_VERSIONS, @@ -1017,6 +1019,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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(modulo_arithmetic::MODULO_ARITHMETIC), LintId::of(panic_in_result_fn::PANIC_IN_RESULT_FN), @@ -2077,6 +2080,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box unused_async::UnusedAsync); let disallowed_types = conf.disallowed_types.iter().cloned().collect::>(); store.register_late_pass(move || box disallowed_type::DisallowedType::new(&disallowed_types)); + let import_renames = conf.enforced_import_renames.clone(); + store.register_late_pass(move || box missing_enforced_import_rename::ImportRename::new(import_renames.clone())); } diff --git a/clippy_lints/src/missing_enforced_import_rename.rs b/clippy_lints/src/missing_enforced_import_rename.rs new file mode 100644 index 00000000000..59565350f72 --- /dev/null +++ b/clippy_lints/src/missing_enforced_import_rename.rs @@ -0,0 +1,102 @@ +use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet_opt}; + +use rustc_data_structures::fx::FxHashMap; +use rustc_errors::Applicability; +use rustc_hir::{def::Res, def_id::DefId, Crate, Item, ItemKind, UseKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::Symbol; + +use crate::utils::conf::Rename; + +declare_clippy_lint! { + /// **What it does:** Checks for imports that do not rename the item as specified + /// in the `enforce-import-renames` config option. + /// + /// **Why is this bad?** Consistency is important, if a project has defined import + /// renames they should be followed. More practically, some item names are too + /// vague outside of their defining scope this can enforce a more meaningful naming. + /// + /// **Known problems:** None + /// + /// **Example:** + /// + /// An example clippy.toml configuration: + /// ```toml + /// # clippy.toml + /// enforced-import-renames = [ { path = "serde_json::Value", rename = "JsonValue" }] + /// ``` + /// + /// ```rust,ignore + /// use serde_json::Value; + /// ``` + /// Use instead: + /// ```rust,ignore + /// use serde_json::Value as JsonValue; + /// ``` + pub MISSING_ENFORCED_IMPORT_RENAMES, + restriction, + "enforce import renames" +} + +pub struct ImportRename { + conf_renames: Vec, + renames: FxHashMap, +} + +impl ImportRename { + pub fn new(conf_renames: Vec) -> Self { + Self { + conf_renames, + renames: FxHashMap::default(), + } + } +} + +impl_lint_pass!(ImportRename => [MISSING_ENFORCED_IMPORT_RENAMES]); + +impl LateLintPass<'_> for ImportRename { + fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) { + for Rename { path, rename } in &self.conf_renames { + if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &path.split("::").collect::>()) { + self.renames.insert(id, Symbol::intern(rename)); + } + } + } + + fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + if_chain! { + if let ItemKind::Use(path, UseKind::Single) = &item.kind; + if let Res::Def(_, id) = path.res; + if let Some(name) = self.renames.get(&id); + // Remove semicolon since it is not present for nested imports + let span_without_semi = cx.sess().source_map().span_until_char(item.span, ';'); + if let Some(snip) = snippet_opt(cx, span_without_semi); + if let Some(import) = match snip.split_once(" as ") { + None => Some(snip.as_str()), + Some((import, rename)) => { + if rename.trim() == &*name.as_str() { + None + } else { + Some(import.trim()) + } + }, + }; + then { + span_lint_and_sugg( + cx, + MISSING_ENFORCED_IMPORT_RENAMES, + span_without_semi, + "this import should be renamed", + "try", + format!( + "{} as {}", + import, + name, + ), + Applicability::MachineApplicable, + ); + } + } + } +} diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 2f8064499fd..6fc4998318c 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -8,6 +8,13 @@ use std::error::Error; use std::path::{Path, PathBuf}; use std::{env, fmt, fs, io}; +/// Holds information used by `MISSING_ENFORCED_IMPORT_RENAMES` lint. +#[derive(Clone, Debug, Deserialize)] +pub struct Rename { + pub path: String, + pub rename: String, +} + /// Conf with parse errors #[derive(Default)] pub struct TryConf { @@ -203,6 +210,8 @@ define_Conf! { (cargo_ignore_publish: bool = false), /// Lint: NONSTANDARD_MACRO_BRACES. Enforce the named macros always use the braces specified.
A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. If the macro is could be used with a full path two `MacroMatcher`s have to be added one with the full path `crate_name::macro_name` and one with just the macro name. (standard_macro_braces: Vec = Vec::new()), + /// Lint: MISSING_ENFORCED_IMPORT_RENAMES. The list of imports to always rename, a fully qualified path followed by the rename. + (enforced_import_renames: Vec = Vec::new()), } /// Search for the configuration file. diff --git a/tests/ui-toml/missing_enforced_import_rename/clippy.toml b/tests/ui-toml/missing_enforced_import_rename/clippy.toml new file mode 100644 index 00000000000..05ba822874d --- /dev/null +++ b/tests/ui-toml/missing_enforced_import_rename/clippy.toml @@ -0,0 +1,10 @@ +enforced-import-renames = [ + { path = "std::option::Option", rename = "Maybe" }, + { path = "std::process::Child", rename = "Kid" }, + { path = "std::process::exit", rename = "goodbye" }, + { path = "std::collections::BTreeMap", rename = "Map" }, + { path = "std::clone", rename = "foo" }, + { path = "std::thread::sleep", rename = "thread_sleep" }, + { path = "std::any::type_name", rename = "ident" }, + { path = "std::sync::Mutex", rename = "StdMutie" } +] diff --git a/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs b/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs new file mode 100644 index 00000000000..f60058c8628 --- /dev/null +++ b/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs @@ -0,0 +1,16 @@ +#![warn(clippy::missing_enforced_import_renames)] + +use std::alloc as colla; +use std::option::Option as Maybe; +use std::process::{exit as wrong_exit, Child as Kid}; +use std::thread::sleep; +#[rustfmt::skip] +use std::{ + any::{type_name, Any}, + clone, + sync :: Mutex, +}; + +fn main() { + use std::collections::BTreeMap as OopsWrongRename; +} diff --git a/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.stderr b/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.stderr new file mode 100644 index 00000000000..45de8fdffef --- /dev/null +++ b/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.stderr @@ -0,0 +1,40 @@ +error: this import should be renamed + --> $DIR/conf_missing_enforced_import_rename.rs:5:20 + | +LL | use std::process::{exit as wrong_exit, Child as Kid}; + | ^^^^^^^^^^^^^^^^^^ help: try: `exit as goodbye` + | + = note: `-D clippy::missing-enforced-import-renames` implied by `-D warnings` + +error: this import should be renamed + --> $DIR/conf_missing_enforced_import_rename.rs:6:1 + | +LL | use std::thread::sleep; + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `use std::thread::sleep as thread_sleep` + +error: this import should be renamed + --> $DIR/conf_missing_enforced_import_rename.rs:9:11 + | +LL | any::{type_name, Any}, + | ^^^^^^^^^ help: try: `type_name as ident` + +error: this import should be renamed + --> $DIR/conf_missing_enforced_import_rename.rs:10:5 + | +LL | clone, + | ^^^^^ help: try: `clone as foo` + +error: this import should be renamed + --> $DIR/conf_missing_enforced_import_rename.rs:11:5 + | +LL | sync :: Mutex, + | ^^^^^^^^^^^^^ help: try: `sync :: Mutex as StdMutie` + +error: this import should be renamed + --> $DIR/conf_missing_enforced_import_rename.rs:15:5 + | +LL | use std::collections::BTreeMap as OopsWrongRename; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `use std::collections::BTreeMap as Map` + +error: aborting due to 6 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 f179479cbec..aa7bfa2cc8c 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`, `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`, `third-party` at line 5 column 1 error: aborting due to previous error -- cgit 1.4.1-3-g733a5 From 39856b17ec5a7deec4fa2c394e595616089fc6bd Mon Sep 17 00:00:00 2001 From: Igor Aleksanov Date: Tue, 22 Jun 2021 15:34:46 +0300 Subject: Improve lint message for match-same-arms lint Simplify error message producing & remove extra example --- clippy_lints/src/matches.rs | 3 ++- tests/ui/match_same_arms.stderr | 7 +++++++ tests/ui/match_same_arms2.stderr | 7 +++++++ 3 files changed, 16 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index cd3e3b97928..386349d9f59 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -2266,7 +2266,8 @@ fn lint_match_arms<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) { ), ); } else { - diag.span_help(i.pat.span, &format!("consider refactoring into `{} | {}`", lhs, rhs)); + diag.span_help(i.pat.span, &format!("consider refactoring into `{} | {}`", lhs, rhs,)) + .help("...or consider changing the match arm bodies"); } }, ); diff --git a/tests/ui/match_same_arms.stderr b/tests/ui/match_same_arms.stderr index 0549886a1e8..e48451acfe8 100644 --- a/tests/ui/match_same_arms.stderr +++ b/tests/ui/match_same_arms.stderr @@ -32,6 +32,7 @@ help: consider refactoring into `(1, .., 3) | (.., 3)` | LL | (1, .., 3) => 42, | ^^^^^^^^^^ + = help: ...or consider changing the match arm bodies error: this `match` has identical arm bodies --> $DIR/match_same_arms.rs:24:15 @@ -49,6 +50,7 @@ help: consider refactoring into `42 | 51` | LL | 42 => 1, | ^^ + = help: ...or consider changing the match arm bodies error: this `match` has identical arm bodies --> $DIR/match_same_arms.rs:26:15 @@ -66,6 +68,7 @@ help: consider refactoring into `41 | 52` | LL | 41 => 2, | ^^ + = help: ...or consider changing the match arm bodies error: this `match` has identical arm bodies --> $DIR/match_same_arms.rs:32:14 @@ -83,6 +86,7 @@ help: consider refactoring into `1 | 2` | LL | 1 => 2, | ^ + = help: ...or consider changing the match arm bodies error: this `match` has identical arm bodies --> $DIR/match_same_arms.rs:33:14 @@ -100,6 +104,7 @@ help: consider refactoring into `1 | 3` | LL | 1 => 2, | ^ + = help: ...or consider changing the match arm bodies error: this `match` has identical arm bodies --> $DIR/match_same_arms.rs:33:14 @@ -117,6 +122,7 @@ help: consider refactoring into `2 | 3` | LL | 2 => 2, //~ ERROR 2nd matched arms have same body | ^ + = help: ...or consider changing the match arm bodies error: this `match` has identical arm bodies --> $DIR/match_same_arms.rs:50:55 @@ -134,6 +140,7 @@ help: consider refactoring into `CommandInfo::BuiltIn { name, .. } | CommandInfo | LL | CommandInfo::BuiltIn { name, .. } => name.to_string(), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: ...or consider changing the match arm bodies error: aborting due to 8 previous errors diff --git a/tests/ui/match_same_arms2.stderr b/tests/ui/match_same_arms2.stderr index 430021a0f7f..e1ed12f9370 100644 --- a/tests/ui/match_same_arms2.stderr +++ b/tests/ui/match_same_arms2.stderr @@ -53,6 +53,7 @@ help: consider refactoring into `42 | 51` | LL | 42 => foo(), | ^^ + = help: ...or consider changing the match arm bodies error: this `match` has identical arm bodies --> $DIR/match_same_arms2.rs:40:17 @@ -70,6 +71,7 @@ help: consider refactoring into `Some(_) | None` | LL | Some(_) => 24, | ^^^^^^^ + = help: ...or consider changing the match arm bodies error: this `match` has identical arm bodies --> $DIR/match_same_arms2.rs:62:28 @@ -87,6 +89,7 @@ help: consider refactoring into `(Some(a), None) | (None, Some(a))` | LL | (Some(a), None) => bar(a), | ^^^^^^^^^^^^^^^ + = help: ...or consider changing the match arm bodies error: this `match` has identical arm bodies --> $DIR/match_same_arms2.rs:68:26 @@ -104,6 +107,7 @@ help: consider refactoring into `(Some(a), ..) | (.., Some(a))` | LL | (Some(a), ..) => bar(a), | ^^^^^^^^^^^^^ + = help: ...or consider changing the match arm bodies error: this `match` has identical arm bodies --> $DIR/match_same_arms2.rs:102:29 @@ -121,6 +125,7 @@ help: consider refactoring into `(Ok(x), Some(_)) | (Ok(_), Some(x))` | LL | (Ok(x), Some(_)) => println!("ok {}", x), | ^^^^^^^^^^^^^^^^ + = help: ...or consider changing the match arm bodies = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: this `match` has identical arm bodies @@ -139,6 +144,7 @@ help: consider refactoring into `Ok(3) | Ok(_)` | LL | Ok(3) => println!("ok"), | ^^^^^ + = help: ...or consider changing the match arm bodies = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: this `match` has identical arm bodies @@ -163,6 +169,7 @@ help: consider refactoring into `0 | 1` | LL | 0 => { | ^ + = help: ...or consider changing the match arm bodies error: match expression looks like `matches!` macro --> $DIR/match_same_arms2.rs:162:16 -- cgit 1.4.1-3-g733a5 From 28d3873ef52fd1e4f9efa115d18235e92fecfa83 Mon Sep 17 00:00:00 2001 From: Igor Aleksanov Date: Sat, 19 Jun 2021 21:14:05 +0300 Subject: Do not spawn blacklisted_name lint in test context Fix detecting of the 'test' attribute Update UI test to actually check that warning is not triggered in the test code Fix approach for detecting the test module Add nested test case Remove code duplication by extracting 'is_test_module_or_function' into 'clippy_utils' Cleanup the code --- clippy_lints/src/blacklisted_name.rs | 31 ++++++++++++++++++++++++++++--- clippy_lints/src/wildcard_imports.rs | 12 ++++-------- clippy_utils/src/lib.rs | 12 ++++++++++++ tests/ui/blacklisted_name.rs | 12 ++++++++++++ 4 files changed, 56 insertions(+), 11 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/blacklisted_name.rs b/clippy_lints/src/blacklisted_name.rs index b26ef33e056..8eb94f3c28e 100644 --- a/clippy_lints/src/blacklisted_name.rs +++ b/clippy_lints/src/blacklisted_name.rs @@ -1,6 +1,6 @@ -use clippy_utils::diagnostics::span_lint; +use clippy_utils::{diagnostics::span_lint, is_test_module_or_function}; use rustc_data_structures::fx::FxHashSet; -use rustc_hir::{Pat, PatKind}; +use rustc_hir::{Item, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -25,18 +25,37 @@ declare_clippy_lint! { #[derive(Clone, Debug)] pub struct BlacklistedName { blacklist: FxHashSet, + test_modules_deep: u32, } impl BlacklistedName { pub fn new(blacklist: FxHashSet) -> Self { - Self { blacklist } + Self { + blacklist, + test_modules_deep: 0, + } + } + + fn in_test_module(&self) -> bool { + self.test_modules_deep != 0 } } impl_lint_pass!(BlacklistedName => [BLACKLISTED_NAME]); impl<'tcx> LateLintPass<'tcx> for BlacklistedName { + fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + if is_test_module_or_function(cx.tcx, item) { + self.test_modules_deep = self.test_modules_deep.saturating_add(1); + } + } + fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) { + // Check whether we are under the `test` attribute. + if self.in_test_module() { + return; + } + if let PatKind::Binding(.., ident, _) = pat.kind { if self.blacklist.contains(&ident.name.to_string()) { span_lint( @@ -48,4 +67,10 @@ impl<'tcx> LateLintPass<'tcx> for BlacklistedName { } } } + + fn check_item_post(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + if is_test_module_or_function(cx.tcx, item) { + self.test_modules_deep = self.test_modules_deep.saturating_sub(1); + } + } } diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index 51c1117d206..520586b3a1f 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::in_macro; use clippy_utils::source::{snippet, snippet_with_applicability}; +use clippy_utils::{in_macro, is_test_module_or_function}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{ @@ -106,7 +106,7 @@ impl_lint_pass!(WildcardImports => [ENUM_GLOB_USE, WILDCARD_IMPORTS]); impl LateLintPass<'_> for WildcardImports { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - if is_test_module_or_function(item) { + if is_test_module_or_function(cx.tcx, item) { self.test_modules_deep = self.test_modules_deep.saturating_add(1); } if item.vis.node.is_pub() || item.vis.node.is_pub_restricted() { @@ -183,8 +183,8 @@ impl LateLintPass<'_> for WildcardImports { } } - fn check_item_post(&mut self, _: &LateContext<'_>, item: &Item<'_>) { - if is_test_module_or_function(item) { + fn check_item_post(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + if is_test_module_or_function(cx.tcx, item) { self.test_modules_deep = self.test_modules_deep.saturating_sub(1); } } @@ -208,7 +208,3 @@ fn is_prelude_import(segments: &[PathSegment<'_>]) -> bool { fn is_super_only_import(segments: &[PathSegment<'_>]) -> bool { segments.len() == 1 && segments[0].ident.name == kw::Super } - -fn is_test_module_or_function(item: &Item<'_>) -> bool { - matches!(item.kind, ItemKind::Mod(..)) && item.ident.name.as_str().contains("test") -} diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 66e75b0c206..ef4854afc83 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1727,3 +1727,15 @@ pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { } } } + +/// 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; + } + } + + matches!(item.kind, ItemKind::Mod(..)) && item.ident.name.as_str().contains("test") +} diff --git a/tests/ui/blacklisted_name.rs b/tests/ui/blacklisted_name.rs index cb15bdd2f1b..57d7139fef5 100644 --- a/tests/ui/blacklisted_name.rs +++ b/tests/ui/blacklisted_name.rs @@ -43,3 +43,15 @@ fn issue_1647_ref_mut() { let ref mut baz = 0; if let Some(ref mut quux) = Some(42) {} } + +mod tests { + fn issue_7305() { + // `blackisted_name` lint should not be triggered inside of the test code. + let foo = 0; + + // Check that even in nested functions warning is still not triggere. + fn nested() { + let foo = 0; + } + } +} -- cgit 1.4.1-3-g733a5 From f02632cee63137fb0184ecb3828fdd6d8f9ce6dd Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Mon, 14 Jun 2021 14:09:17 -0500 Subject: Move some lints to suspicious --- clippy_lints/src/assign_ops.rs | 2 +- clippy_lints/src/attrs.rs | 2 +- clippy_lints/src/eval_order_dependence.rs | 2 +- clippy_lints/src/float_equality_without_abs.rs | 2 +- clippy_lints/src/formatting.rs | 6 ++--- clippy_lints/src/lib.rs | 31 ++++++++++++++------------ clippy_lints/src/loops/mod.rs | 6 ++--- clippy_lints/src/methods/mod.rs | 2 +- clippy_lints/src/mut_key.rs | 2 +- clippy_lints/src/suspicious_trait_impl.rs | 4 ++-- tests/ui/mut_key.stderr | 2 +- tests/ui/suspicious_arithmetic_impl.stderr | 2 +- 12 files changed, 33 insertions(+), 30 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/assign_ops.rs b/clippy_lints/src/assign_ops.rs index bc6eec0051a..a8c527fe2e3 100644 --- a/clippy_lints/src/assign_ops.rs +++ b/clippy_lints/src/assign_ops.rs @@ -55,7 +55,7 @@ declare_clippy_lint! { /// a += a + b; /// ``` pub MISREFACTORED_ASSIGN_OP, - complexity, + suspicious, "having a variable on both sides of an assign op" } diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 932cd58bf62..f272ed010a1 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -173,7 +173,7 @@ declare_clippy_lint! { /// #![deny(clippy::as_conversions)] /// ``` pub BLANKET_CLIPPY_RESTRICTION_LINTS, - style, + suspicious, "enabling the complete restriction group" } diff --git a/clippy_lints/src/eval_order_dependence.rs b/clippy_lints/src/eval_order_dependence.rs index 5fdf5bc9e9d..03a8b40df55 100644 --- a/clippy_lints/src/eval_order_dependence.rs +++ b/clippy_lints/src/eval_order_dependence.rs @@ -38,7 +38,7 @@ declare_clippy_lint! { /// let a = tmp + x; /// ``` pub EVAL_ORDER_DEPENDENCE, - complexity, + suspicious, "whether a variable read occurs before a write depends on sub-expression evaluation order" } diff --git a/clippy_lints/src/float_equality_without_abs.rs b/clippy_lints/src/float_equality_without_abs.rs index b5ebe5f90ba..1e503cc795c 100644 --- a/clippy_lints/src/float_equality_without_abs.rs +++ b/clippy_lints/src/float_equality_without_abs.rs @@ -36,7 +36,7 @@ declare_clippy_lint! { /// } /// ``` pub FLOAT_EQUALITY_WITHOUT_ABS, - correctness, + suspicious, "float equality check without `.abs()`" } diff --git a/clippy_lints/src/formatting.rs b/clippy_lints/src/formatting.rs index 3bd6a09d365..8aefb8d46f6 100644 --- a/clippy_lints/src/formatting.rs +++ b/clippy_lints/src/formatting.rs @@ -22,7 +22,7 @@ declare_clippy_lint! { /// a =- 42; // confusing, should it be `a -= 42` or `a = -42`? /// ``` pub SUSPICIOUS_ASSIGNMENT_FORMATTING, - style, + suspicious, "suspicious formatting of `*=`, `-=` or `!=`" } @@ -44,7 +44,7 @@ declare_clippy_lint! { /// } /// ``` pub SUSPICIOUS_UNARY_OP_FORMATTING, - style, + suspicious, "suspicious formatting of unary `-` or `!` on the RHS of a BinOp" } @@ -80,7 +80,7 @@ declare_clippy_lint! { /// } /// ``` pub SUSPICIOUS_ELSE_FORMATTING, - style, + suspicious, "suspicious formatting of `else`" } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 4ce1d511d27..20eea61d78d 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1461,7 +1461,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), LintId::of(blacklisted_name::BLACKLISTED_NAME), LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON), @@ -1479,9 +1478,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(eq_op::OP_REF), LintId::of(eta_reduction::REDUNDANT_CLOSURE), LintId::of(float_literal::EXCESSIVE_PRECISION), - 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), @@ -1494,7 +1490,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(len_zero::LEN_ZERO), LintId::of(literal_representation::INCONSISTENT_DIGIT_GROUPING), LintId::of(literal_representation::UNUSUAL_BYTE_GROUPINGS), - LintId::of(loops::EMPTY_LOOP), LintId::of(loops::FOR_KV_MAP), LintId::of(loops::NEEDLESS_RANGE_LOOP), LintId::of(loops::SAME_ITEM_PUSH), @@ -1574,7 +1569,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ]); store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec![ - LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP), LintId::of(attrs::DEPRECATED_CFG_ATTR), LintId::of(booleans::NONMINIMAL_BOOL), LintId::of(casts::CHAR_LIT_AS_U8), @@ -1584,7 +1578,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(double_parens::DOUBLE_PARENS), LintId::of(duration_subsec::DURATION_SUBSEC), LintId::of(eval_order_dependence::DIVERGING_SUB_EXPRESSION), - LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE), LintId::of(explicit_write::EXPLICIT_WRITE), LintId::of(format::USELESS_FORMAT), LintId::of(functions::TOO_MANY_ARGUMENTS), @@ -1595,7 +1588,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(lifetimes::NEEDLESS_LIFETIMES), LintId::of(loops::EXPLICIT_COUNTER_LOOP), LintId::of(loops::MANUAL_FLATTEN), - LintId::of(loops::MUT_RANGE_BOUND), LintId::of(loops::SINGLE_ELEMENT_LOOP), LintId::of(loops::WHILE_LET_LOOP), LintId::of(manual_strip::MANUAL_STRIP), @@ -1619,7 +1611,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(methods::OPTION_FILTER_MAP), LintId::of(methods::SEARCH_IS_SOME), LintId::of(methods::SKIP_WHILE_NEXT), - LintId::of(methods::SUSPICIOUS_MAP), LintId::of(methods::UNNECESSARY_FILTER_MAP), LintId::of(methods::USELESS_ASREF), LintId::of(misc::SHORT_CIRCUIT_STATEMENT), @@ -1688,7 +1679,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT), LintId::of(eq_op::EQ_OP), LintId::of(erasing_op::ERASING_OP), - LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS), LintId::of(formatting::POSSIBLE_MISSING_COMMA), LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF), LintId::of(if_let_mutex::IF_LET_MUTEX), @@ -1698,7 +1688,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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::FOR_LOOPS_OVER_FALLIBLES), LintId::of(loops::ITER_NEXT_LOOP), LintId::of(loops::NEVER_LOOP), LintId::of(loops::WHILE_IMMUTABLE_CONDITION), @@ -1713,7 +1702,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(misc::CMP_NAN), LintId::of(misc::FLOAT_CMP), LintId::of(misc::MODULO_ONE), - LintId::of(mut_key::MUTABLE_KEY_TYPE), 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), @@ -1724,8 +1712,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), - LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), LintId::of(swap::ALMOST_SWAPPED), LintId::of(to_string_in_display::TO_STRING_IN_DISPLAY), LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE), @@ -1742,6 +1728,23 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: 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), diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index a4bc3e6bd10..56a123b69c6 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -199,7 +199,7 @@ declare_clippy_lint! { /// } /// ``` pub FOR_LOOPS_OVER_FALLIBLES, - correctness, + suspicious, "for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`" } @@ -313,7 +313,7 @@ declare_clippy_lint! { /// loop {} /// ``` pub EMPTY_LOOP, - style, + suspicious, "empty `loop {}`, which should block or sleep" } @@ -401,7 +401,7 @@ declare_clippy_lint! { /// } /// ``` pub MUT_RANGE_BOUND, - complexity, + suspicious, "for loop over a range where one of the bounds is a mutable variable" } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 21585543b0a..283fcf281df 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1248,7 +1248,7 @@ declare_clippy_lint! { /// let _ = (0..3).map(|x| x + 2).count(); /// ``` pub SUSPICIOUS_MAP, - complexity, + suspicious, "suspicious usage of map" } diff --git a/clippy_lints/src/mut_key.rs b/clippy_lints/src/mut_key.rs index 1786d5805d7..6c87136e5e1 100644 --- a/clippy_lints/src/mut_key.rs +++ b/clippy_lints/src/mut_key.rs @@ -50,7 +50,7 @@ declare_clippy_lint! { /// } /// ``` pub MUTABLE_KEY_TYPE, - correctness, + suspicious, "Check for mutable `Map`/`Set` key type" } diff --git a/clippy_lints/src/suspicious_trait_impl.rs b/clippy_lints/src/suspicious_trait_impl.rs index 512abde11a6..2203ab57b10 100644 --- a/clippy_lints/src/suspicious_trait_impl.rs +++ b/clippy_lints/src/suspicious_trait_impl.rs @@ -26,7 +26,7 @@ declare_clippy_lint! { /// } /// ``` pub SUSPICIOUS_ARITHMETIC_IMPL, - correctness, + suspicious, "suspicious use of operators in impl of arithmetic trait" } @@ -47,7 +47,7 @@ declare_clippy_lint! { /// } /// ``` pub SUSPICIOUS_OP_ASSIGN_IMPL, - correctness, + suspicious, "suspicious use of operators in impl of OpAssign trait" } diff --git a/tests/ui/mut_key.stderr b/tests/ui/mut_key.stderr index 8d6a259c7e3..a8460b06ca6 100644 --- a/tests/ui/mut_key.stderr +++ b/tests/ui/mut_key.stderr @@ -4,7 +4,7 @@ error: mutable key type LL | fn should_not_take_this_arg(m: &mut HashMap, _n: usize) -> HashSet { | ^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: `#[deny(clippy::mutable_key_type)]` on by default + = note: `-D clippy::mutable-key-type` implied by `-D warnings` error: mutable key type --> $DIR/mut_key.rs:27:72 diff --git a/tests/ui/suspicious_arithmetic_impl.stderr b/tests/ui/suspicious_arithmetic_impl.stderr index 388fc740082..63fc9ecb79a 100644 --- a/tests/ui/suspicious_arithmetic_impl.stderr +++ b/tests/ui/suspicious_arithmetic_impl.stderr @@ -12,7 +12,7 @@ error: suspicious use of binary operator in `AddAssign` impl LL | *self = *self - other; | ^ | - = note: `#[deny(clippy::suspicious_op_assign_impl)]` on by default + = note: `-D clippy::suspicious-op-assign-impl` implied by `-D warnings` error: suspicious use of binary operator in `MulAssign` impl --> $DIR/suspicious_arithmetic_impl.rs:32:16 -- cgit 1.4.1-3-g733a5 From 3e5956339482e06dfcb8690dee9211b77aa6e218 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Sat, 26 Jun 2021 14:50:42 +0200 Subject: Don't use exact definition of std's ErrorKind in test. Every time we add something to this enum in std, this test breaks. --- tests/ui/auxiliary/non-exhaustive-enum.rs | 23 +++++++++++++++++++++++ tests/ui/wildcard_enum_match_arm.fixed | 5 ++++- tests/ui/wildcard_enum_match_arm.rs | 5 ++++- tests/ui/wildcard_enum_match_arm.stderr | 12 ++++++------ 4 files changed, 37 insertions(+), 8 deletions(-) create mode 100644 tests/ui/auxiliary/non-exhaustive-enum.rs (limited to 'tests') diff --git a/tests/ui/auxiliary/non-exhaustive-enum.rs b/tests/ui/auxiliary/non-exhaustive-enum.rs new file mode 100644 index 00000000000..67d4d255701 --- /dev/null +++ b/tests/ui/auxiliary/non-exhaustive-enum.rs @@ -0,0 +1,23 @@ +#[non_exhaustive] +pub enum ErrorKind { + NotFound, + PermissionDenied, + ConnectionRefused, + ConnectionReset, + ConnectionAborted, + NotConnected, + AddrInUse, + AddrNotAvailable, + BrokenPipe, + AlreadyExists, + WouldBlock, + InvalidInput, + InvalidData, + TimedOut, + WriteZero, + Interrupted, + Other, + UnexpectedEof, + Unsupported, + OutOfMemory, +} diff --git a/tests/ui/wildcard_enum_match_arm.fixed b/tests/ui/wildcard_enum_match_arm.fixed index 5ad27bb1450..2b8f260397c 100644 --- a/tests/ui/wildcard_enum_match_arm.fixed +++ b/tests/ui/wildcard_enum_match_arm.fixed @@ -1,4 +1,5 @@ // run-rustfix +// aux-build:non-exhaustive-enum.rs #![deny(clippy::wildcard_enum_match_arm)] #![allow( @@ -11,7 +12,9 @@ clippy::diverging_sub_expression )] -use std::io::ErrorKind; +extern crate non_exhaustive_enum; + +use non_exhaustive_enum::ErrorKind; #[derive(Clone, Copy, Debug, Eq, PartialEq)] enum Color { diff --git a/tests/ui/wildcard_enum_match_arm.rs b/tests/ui/wildcard_enum_match_arm.rs index adca0738bba..1f7bcffd5f5 100644 --- a/tests/ui/wildcard_enum_match_arm.rs +++ b/tests/ui/wildcard_enum_match_arm.rs @@ -1,4 +1,5 @@ // run-rustfix +// aux-build:non-exhaustive-enum.rs #![deny(clippy::wildcard_enum_match_arm)] #![allow( @@ -11,7 +12,9 @@ clippy::diverging_sub_expression )] -use std::io::ErrorKind; +extern crate non_exhaustive_enum; + +use non_exhaustive_enum::ErrorKind; #[derive(Clone, Copy, Debug, Eq, PartialEq)] enum Color { diff --git a/tests/ui/wildcard_enum_match_arm.stderr b/tests/ui/wildcard_enum_match_arm.stderr index 73f6a4a80c9..80ba5ee4f04 100644 --- a/tests/ui/wildcard_enum_match_arm.stderr +++ b/tests/ui/wildcard_enum_match_arm.stderr @@ -1,35 +1,35 @@ error: wildcard match will also match any future added variants - --> $DIR/wildcard_enum_match_arm.rs:39:9 + --> $DIR/wildcard_enum_match_arm.rs:42:9 | LL | _ => eprintln!("Not red"), | ^ help: try this: `Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan` | note: the lint level is defined here - --> $DIR/wildcard_enum_match_arm.rs:3:9 + --> $DIR/wildcard_enum_match_arm.rs:4:9 | LL | #![deny(clippy::wildcard_enum_match_arm)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: wildcard match will also match any future added variants - --> $DIR/wildcard_enum_match_arm.rs:43:9 + --> $DIR/wildcard_enum_match_arm.rs:46:9 | LL | _not_red => eprintln!("Not red"), | ^^^^^^^^ help: try this: `_not_red @ Color::Green | _not_red @ Color::Blue | _not_red @ Color::Rgb(..) | _not_red @ Color::Cyan` error: wildcard match will also match any future added variants - --> $DIR/wildcard_enum_match_arm.rs:47:9 + --> $DIR/wildcard_enum_match_arm.rs:50:9 | LL | not_red => format!("{:?}", not_red), | ^^^^^^^ help: try this: `not_red @ Color::Green | not_red @ Color::Blue | not_red @ Color::Rgb(..) | not_red @ Color::Cyan` error: wildcard match will also match any future added variants - --> $DIR/wildcard_enum_match_arm.rs:63:9 + --> $DIR/wildcard_enum_match_arm.rs:66:9 | LL | _ => "No red", | ^ help: try this: `Color::Red | Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan` error: wildcard matches known variants and will also match future added variants - --> $DIR/wildcard_enum_match_arm.rs:80:9 + --> $DIR/wildcard_enum_match_arm.rs:83:9 | LL | _ => {}, | ^ help: try this: `ErrorKind::PermissionDenied | ErrorKind::ConnectionRefused | ErrorKind::ConnectionReset | ErrorKind::ConnectionAborted | ErrorKind::NotConnected | ErrorKind::AddrInUse | ErrorKind::AddrNotAvailable | ErrorKind::BrokenPipe | ErrorKind::AlreadyExists | ErrorKind::WouldBlock | ErrorKind::InvalidInput | ErrorKind::InvalidData | ErrorKind::TimedOut | ErrorKind::WriteZero | ErrorKind::Interrupted | ErrorKind::Other | ErrorKind::UnexpectedEof | ErrorKind::Unsupported | ErrorKind::OutOfMemory | _` -- cgit 1.4.1-3-g733a5 From 38569c03eb0d0917698d83aea5fbbc35acf7305c Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Sat, 26 Jun 2021 15:22:15 +0200 Subject: Don't suggest unstable and doc(hidden) variants. --- clippy_lints/src/matches.rs | 8 ++++---- clippy_utils/src/attrs.rs | 5 +++++ tests/ui/auxiliary/non-exhaustive-enum.rs | 2 ++ 3 files changed, 11 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index cd3e3b97928..da5ac96e3db 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -992,9 +992,9 @@ impl CommonPrefixSearcher<'a> { } } -fn is_doc_hidden(cx: &LateContext<'_>, variant_def: &VariantDef) -> bool { +fn is_hidden(cx: &LateContext<'_>, variant_def: &VariantDef) -> bool { let attrs = cx.tcx.get_attrs(variant_def.def_id); - clippy_utils::attrs::is_doc_hidden(attrs) + clippy_utils::attrs::is_doc_hidden(attrs) || clippy_utils::attrs::is_unstable(attrs) } #[allow(clippy::too_many_lines)] @@ -1033,7 +1033,7 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) // Accumulate the variants which should be put in place of the wildcard because they're not // already covered. - let mut missing_variants: Vec<_> = adt_def.variants.iter().collect(); + let mut missing_variants: Vec<_> = adt_def.variants.iter().filter(|x| !is_hidden(cx, x)).collect(); let mut path_prefix = CommonPrefixSearcher::None; for arm in arms { @@ -1118,7 +1118,7 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) match missing_variants.as_slice() { [] => (), - [x] if !adt_def.is_variant_list_non_exhaustive() && !is_doc_hidden(cx, x) => span_lint_and_sugg( + [x] if !adt_def.is_variant_list_non_exhaustive() => span_lint_and_sugg( cx, MATCH_WILDCARD_FOR_SINGLE_VARIANTS, wildcard_span, diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index 0318c483959..c19b558cd8c 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -157,3 +157,8 @@ pub fn is_doc_hidden(attrs: &[ast::Attribute]) -> bool { .filter_map(ast::Attribute::meta_item_list) .any(|l| attr::list_contains_name(&l, sym::hidden)) } + +/// Return true if the attributes contain `#[unstable]` +pub fn is_unstable(attrs: &[ast::Attribute]) -> bool { + attrs.iter().any(|attr| attr.has_name(sym::unstable)) +} diff --git a/tests/ui/auxiliary/non-exhaustive-enum.rs b/tests/ui/auxiliary/non-exhaustive-enum.rs index 67d4d255701..18560bc5e1e 100644 --- a/tests/ui/auxiliary/non-exhaustive-enum.rs +++ b/tests/ui/auxiliary/non-exhaustive-enum.rs @@ -20,4 +20,6 @@ pub enum ErrorKind { UnexpectedEof, Unsupported, OutOfMemory, + #[doc(hidden)] + Uncategorized, } -- cgit 1.4.1-3-g733a5 From 018be41deedd086191b8ce45895164e0aa7046b0 Mon Sep 17 00:00:00 2001 From: Igor Aleksanov Date: Wed, 30 Jun 2021 19:06:33 +0300 Subject: Implement 'disallowed_script_idents' lint --- CHANGELOG.md | 1 + clippy_lints/Cargo.toml | 1 + clippy_lints/src/disallowed_script_idents.rs | 112 +++++++++++++++++++++ clippy_lints/src/lib.rs | 6 +- clippy_lints/src/utils/conf.rs | 2 + .../toml_unknown_key/conf_unknown_key.stderr | 2 +- tests/ui/disallowed_script_idents.rs | 10 ++ tests/ui/disallowed_script_idents.stderr | 20 ++++ 8 files changed, 152 insertions(+), 2 deletions(-) create mode 100644 clippy_lints/src/disallowed_script_idents.rs create mode 100644 tests/ui/disallowed_script_idents.rs create mode 100644 tests/ui/disallowed_script_idents.stderr (limited to 'tests') diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f62a172035..f3a80703238 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2487,6 +2487,7 @@ Released 2018-09-13 [`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq [`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord [`disallowed_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method +[`disallowed_script_idents`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_script_idents [`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type [`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression [`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index d3d12062f07..42cf7547f51 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -23,6 +23,7 @@ serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", optional = true } toml = "0.5.3" unicode-normalization = "0.1" +unicode-script = { version = "0.5.3", default-features = false } semver = "0.11" rustc-semver = "1.1.0" # NOTE: cargo requires serde feat in its url dep diff --git a/clippy_lints/src/disallowed_script_idents.rs b/clippy_lints/src/disallowed_script_idents.rs new file mode 100644 index 00000000000..12c525634c5 --- /dev/null +++ b/clippy_lints/src/disallowed_script_idents.rs @@ -0,0 +1,112 @@ +use clippy_utils::diagnostics::span_lint; +use rustc_ast::ast; +use rustc_data_structures::fx::FxHashSet; +use rustc_lint::{EarlyContext, EarlyLintPass, Level}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use unicode_script::{Script, UnicodeScript}; + +declare_clippy_lint! { + /// **What it does:** Checks for usage of unicode scripts other than those explicitly allowed + /// by the lint config. + /// + /// This lint doesn't take into account non-text scripts such as `Unknown` and `Linear_A`. + /// It also ignores the `Common` script type. + /// While configuring, be sure to use official script name [aliases] from + /// [the list of supported scripts][supported_scripts]. + /// + /// See also: [`non_ascii_idents`]. + /// + /// [aliases]: http://www.unicode.org/reports/tr24/tr24-31.html#Script_Value_Aliases + /// [supported_scripts]: https://www.unicode.org/iso15924/iso15924-codes.html + /// + /// **Why is this bad?** It may be not desired to have many different scripts for + /// identifiers in the codebase. + /// + /// Note that if you only want to allow plain English, you might want to use + /// built-in [`non_ascii_idents`] lint instead. + /// + /// [`non_ascii_idents`]: https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html#non-ascii-idents + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// // Assuming that `clippy.toml` contains the following line: + /// // allowed-locales = ["Latin", "Cyrillic"] + /// let counter = 10; // OK, latin is allowed. + /// let счётчик = 10; // OK, cyrillic is allowed. + /// let zähler = 10; // OK, it's still latin. + /// let カウンタ = 10; // Will spawn the lint. + /// ``` + pub DISALLOWED_SCRIPT_IDENTS, + restriction, + "usage of non-allowed Unicode scripts" +} + +#[derive(Clone, Debug)] +pub struct DisallowedScriptIdents { + whitelist: FxHashSet